From d115a799235833e59f75958b9fdf011a19f0061b Mon Sep 17 00:00:00 2001 From: Sethakill Date: Wed, 11 May 2016 15:42:12 +0200 Subject: [PATCH] SpecialActiveUsers: Change checkboxes to dropdown Moved form to new function and fixed text about cached version. Bug: T116354 Change-Id: I29ae63472536f99f7f9546f30d86e7bd324d3094 --- includes/FormOptions.php | 8 ++ includes/specials/SpecialActiveusers.php | 111 +++++++++++------- includes/specials/pagers/ActiveUsersPager.php | 42 ++----- languages/i18n/en.json | 3 +- languages/i18n/qqq.json | 5 +- tests/phpunit/includes/FormOptionsTest.php | 12 +- 6 files changed, 92 insertions(+), 89 deletions(-) diff --git a/includes/FormOptions.php b/includes/FormOptions.php index 5e5e8d4011..826f3bbb54 100644 --- a/includes/FormOptions.php +++ b/includes/FormOptions.php @@ -52,6 +52,9 @@ class FormOptions implements ArrayAccess { * This is useful for the namespace selector. */ const INTNULL = 3; + /** Array type, maps guessType() to WebRequest::getArray() + * @since 1.28 */ + const ARR = 5; /* @} */ /** @@ -120,6 +123,8 @@ class FormOptions implements ArrayAccess { return self::FLOAT; } elseif ( is_string( $data ) ) { return self::STRING; + } elseif ( is_array( $data ) ) { + return self::ARR; } else { throw new MWException( 'Unsupported datatype' ); } @@ -358,6 +363,9 @@ class FormOptions implements ArrayAccess { case self::INTNULL: $value = $r->getIntOrNull( $name ); break; + case self::ARR: + $value = $r->getArray( $name ); + break; default: throw new MWException( 'Unsupported datatype' ); } diff --git a/includes/specials/SpecialActiveusers.php b/includes/specials/SpecialActiveusers.php index 2da441b880..531c330328 100644 --- a/includes/specials/SpecialActiveusers.php +++ b/includes/specials/SpecialActiveusers.php @@ -51,8 +51,7 @@ class SpecialActiveUsers extends SpecialPage { $opts = new FormOptions(); $opts->add( 'username', '' ); - $opts->add( 'hidebots', false, FormOptions::BOOL ); - $opts->add( 'hidesysops', false, FormOptions::BOOL ); + $opts->add( 'groups', [] ); $opts->fetchValuesFromRequest( $this->getRequest() ); @@ -60,32 +59,32 @@ class SpecialActiveUsers extends SpecialPage { $opts->setValue( 'username', $par ); } - // Mention the level of cache staleness... - $cacheText = ''; - $dbr = wfGetDB( DB_REPLICA, 'recentchanges' ); - $rcMax = $dbr->selectField( 'recentchanges', 'MAX(rc_timestamp)', '', __METHOD__ ); - if ( $rcMax ) { - $cTime = $dbr->selectField( 'querycache_info', - 'qci_timestamp', - [ 'qci_type' => 'activeusers' ], - __METHOD__ + $pager = new ActiveUsersPager( $this->getContext(), $opts ); + $usersBody = $pager->getBody(); + + $this->buildForm(); + + if ( $usersBody ) { + $out->addHTML( + $pager->getNavigationBar() . + Html::rawElement( 'ul', [], $usersBody ) . + $pager->getNavigationBar() ); - if ( $cTime ) { - $secondsOld = wfTimestamp( TS_UNIX, $rcMax ) - wfTimestamp( TS_UNIX, $cTime ); - } else { - $rcMin = $dbr->selectField( 'recentchanges', 'MIN(rc_timestamp)' ); - $secondsOld = time() - wfTimestamp( TS_UNIX, $rcMin ); - } - if ( $secondsOld > 0 ) { - $cacheTxt = '
' . $this->msg( 'cachedspecial-viewing-cached-ttl' ) - ->durationParams( $secondsOld ); - } + } else { + $out->addWikiMsg( 'activeusers-noresult' ); } + } - $pager = new ActiveUsersPager( $this->getContext(), $opts ); - $usersBody = $pager->getBody(); + /** + * Generate and output the form + */ + protected function buildForm() { + $groups = User::getAllGroups(); - $days = $this->getConfig()->get( 'ActiveUserDays' ); + foreach ( $groups as $group ) { + $msg = User::getGroupName( $group ); + $options[$msg] = $group; + } $formDescriptor = [ 'username' => [ @@ -94,38 +93,60 @@ class SpecialActiveUsers extends SpecialPage { 'label-message' => 'activeusers-from', ], - 'hidebots' => [ - 'type' => 'check', - 'name' => 'hidebots', - 'label-message' => 'activeusers-hidebots', - 'default' => false, - ], - - 'hidesysops' => [ - 'type' => 'check', - 'name' => 'hidesysops', - 'label-message' => 'activeusers-hidesysops', - 'default' => false, + 'groups' => [ + 'type' => 'multiselect', + 'dropdown' => true, + 'flatlist' => true, + 'name' => 'groups', + 'label-message' => 'activeusers-groups', + 'options' => $options, ], ]; - $htmlForm = HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() ) - ->setIntro( $this->msg( 'activeusers-intro' )->numParams( $days ) . $cacheText ) + HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() ) + // For the 'multiselect' field values to be preserved on submit + ->setFormIdentifier( 'specialactiveusers' ) + ->setIntro( $this->getIntroText() ) ->setWrapperLegendMsg( 'activeusers' ) ->setSubmitTextMsg( 'activeusers-submit' ) + // prevent setting subpage and 'username' parameter at the same time + ->setAction( $this->getPageTitle()->getLocalURL() ) ->setMethod( 'get' ) ->prepareForm() ->displayForm( false ); + } - if ( $usersBody ) { - $out->addHTML( - $pager->getNavigationBar() . - Html::rawElement( 'ul', [], $usersBody ) . - $pager->getNavigationBar() + /** + * Return introductory message. + * @return string + */ + protected function getIntroText() { + $days = $this->getConfig()->get( 'ActiveUserDays' ); + + $intro = $this->msg( 'activeusers-intro' )->numParams( $days )->parse(); + + // Mention the level of cache staleness... + $dbr = wfGetDB( DB_REPLICA, 'recentchanges' ); + $rcMax = $dbr->selectField( 'recentchanges', 'MAX(rc_timestamp)', '', __METHOD__ ); + if ( $rcMax ) { + $cTime = $dbr->selectField( 'querycache_info', + 'qci_timestamp', + [ 'qci_type' => 'activeusers' ], + __METHOD__ ); - } else { - $out->addWikiMsg( 'activeusers-noresult' ); + if ( $cTime ) { + $secondsOld = wfTimestamp( TS_UNIX, $rcMax ) - wfTimestamp( TS_UNIX, $cTime ); + } else { + $rcMin = $dbr->selectField( 'recentchanges', 'MIN(rc_timestamp)' ); + $secondsOld = time() - wfTimestamp( TS_UNIX, $rcMin ); + } + if ( $secondsOld > 0 ) { + $intro .= $this->msg( 'cachedspecial-viewing-cached-ttl' ) + ->durationParams( $secondsOld )->parseAsBlock(); + } } + + return $intro; } protected function getGroupName() { diff --git a/includes/specials/pagers/ActiveUsersPager.php b/includes/specials/pagers/ActiveUsersPager.php index 73ab0ad729..ea906b7d50 100644 --- a/includes/specials/pagers/ActiveUsersPager.php +++ b/includes/specials/pagers/ActiveUsersPager.php @@ -36,14 +36,9 @@ class ActiveUsersPager extends UsersPager { protected $opts; /** - * @var array - */ - protected $hideGroups = []; - - /** - * @var array + * @var string[] */ - protected $hideRights = []; + protected $groups; /** * @var array @@ -68,12 +63,7 @@ class ActiveUsersPager extends UsersPager { } } - if ( $opts->getValue( 'hidebots' ) == 1 ) { - $this->hideRights[] = 'bot'; - } - if ( $opts->getValue( 'hidesysops' ) == 1 ) { - $this->hideGroups[] = 'sysop'; - } + $this->groups = $opts->getValue( 'groups' ); } function getIndexField() { @@ -85,6 +75,7 @@ class ActiveUsersPager extends UsersPager { $activeUserSeconds = $this->getConfig()->get( 'ActiveUserDays' ) * 86400; $timestamp = $dbr->timestamp( wfTimestamp( TS_UNIX ) - $activeUserSeconds ); + $tables = [ 'querycachetwo', 'user', 'recentchanges' ]; $conds = [ 'qcc_type' => 'activeusers', 'qcc_namespace' => NS_USER, @@ -98,6 +89,11 @@ class ActiveUsersPager extends UsersPager { if ( $this->requestedUser != '' ) { $conds[] = 'qcc_title >= ' . $dbr->addQuotes( $this->requestedUser ); } + if ( $this->groups !== [] ) { + $tables[] = 'user_groups'; + $conds[] = 'ug_user = user_id'; + $conds['ug_group'] = $this->groups; + } if ( !$this->getUser()->isAllowed( 'hideuser' ) ) { $conds[] = 'NOT EXISTS (' . $dbr->selectSQLText( 'ipblocks', '1', [ 'ipb_user=user_id', 'ipb_deleted' => 1 ] @@ -111,7 +107,7 @@ class ActiveUsersPager extends UsersPager { } return [ - 'tables' => [ 'querycachetwo', 'user', 'recentchanges' ], + 'tables' => $tables, 'fields' => [ 'user_name', 'user_id', 'recentedits' => 'COUNT(*)', 'qcc_title' ], 'options' => $options, 'conds' => $conds @@ -154,26 +150,8 @@ class ActiveUsersPager extends UsersPager { $list = []; $user = User::newFromId( $row->user_id ); - // User right filter - foreach ( $this->hideRights as $right ) { - // Calling User::getRights() within the loop so that - // if the hideRights() filter is empty, we don't have to - // trigger the lazy-init of the big userrights array in the - // User object - if ( in_array( $right, $user->getRights() ) ) { - return ''; - } - } - - // User group filter - // Note: This is a different loop than for user rights, - // because we're reusing it to build the group links - // at the same time $groups_list = self::getGroups( intval( $row->user_id ), $this->userGroupCache ); foreach ( $groups_list as $group ) { - if ( in_array( $group, $this->hideGroups ) ) { - return ''; - } $list[] = self::buildGroupLink( $group, $userName ); } diff --git a/languages/i18n/en.json b/languages/i18n/en.json index fc8ba1f54f..699e547668 100644 --- a/languages/i18n/en.json +++ b/languages/i18n/en.json @@ -1989,8 +1989,7 @@ "activeusers-intro": "This is a list of users who had some kind of activity within the last $1 {{PLURAL:$1|day|days}}.", "activeusers-count": "$1 {{PLURAL:$1|action|actions}} in the last {{PLURAL:$3|day|$3 days}}", "activeusers-from": "Display users starting at:", - "activeusers-hidebots": "Hide bots", - "activeusers-hidesysops": "Hide administrators", + "activeusers-groups": "Display users belonging to groups:", "activeusers-noresult": "No users found.", "activeusers-submit": "Display active users", "listgrouprights": "User group rights", diff --git a/languages/i18n/qqq.json b/languages/i18n/qqq.json index 915f629349..7b9682c55e 100644 --- a/languages/i18n/qqq.json +++ b/languages/i18n/qqq.json @@ -2172,9 +2172,8 @@ "activeusers-summary": "{{doc-specialpagesummary|activeusers}}", "activeusers-intro": "Used as introduction in [[Special:ActiveUsers]]. Parameters:\n* $1 - number of days ($wgActiveUserDays)", "activeusers-count": "Used in [[Special:ActiveUsers]] to show the active user's recent action count in brackets ([]).\n* $1 is the number of recent actions\n* $2 is the user's name for use with GENDER (optional)\n* $3 is the maximum number of days of the RecentChangesList", - "activeusers-from": "Used as label for checkbox in the form on [[Special:ActiveUsers]].\n\nidentical with {{msg-mw|listusersfrom}}\n\nSee also:\n* {{msg-mw|activeusers|legend for the form}}\n* {{msg-mw|activeusers-hidebots|label for checkbox}}\n* {{msg-mw|activeusers-hidesysops|label for checkbox}}", - "activeusers-hidebots": "Used as label for checkbox in the form on [[Special:ActiveUsers]].\n\nSee also:\n* {{msg-mw|activeusers|legend for the form}}\n* {{msg-mw|activeusers-from|label for input box}}\n* {{msg-mw|activeusers-hidesysops|label for checkbox}}", - "activeusers-hidesysops": "Used as label for checkbox in the form on [[Special:ActiveUsers]].\n\nSee also:\n* {{msg-mw|activeusers|legend for the form}}\n* {{msg-mw|activeusers-from|label for input box}}\n* {{msg-mw|activeusers-hidebots|label for checkbox}}", + "activeusers-from": "Used as label for checkbox in the form on [[Special:ActiveUsers]].\n\nidentical with {{msg-mw|listusersfrom}}\n\nSee also:\n* {{msg-mw|activeusers|legend for the form}}", + "activeusers-groups": "Used as label on [[Special:ActiveUsers]].", "activeusers-noresult": "identical with {{msg-mw|listusers-noresult}}", "activeusers-submit": "Used as label for button in the form on [[Special:ActiveUsers]]", "listgrouprights": "The name of the special page [[Special:ListGroupRights]].", diff --git a/tests/phpunit/includes/FormOptionsTest.php b/tests/phpunit/includes/FormOptionsTest.php index 5c4d1c05d7..e491d610c1 100644 --- a/tests/phpunit/includes/FormOptionsTest.php +++ b/tests/phpunit/includes/FormOptionsTest.php @@ -52,6 +52,9 @@ class FormOptionsTest extends MediaWikiTestCase { private function assertGuessString( $data ) { $this->guess( FormOptions::STRING, $data ); } + private function assertGuessArray( $data ) { + $this->guess( FormOptions::ARR, $data ); + } /** Generic helper */ private function guess( $expected, $data ) { @@ -84,15 +87,10 @@ class FormOptionsTest extends MediaWikiTestCase { $this->assertGuessString( '5' ); $this->assertGuessString( '0' ); $this->assertGuessString( '1.5' ); - } - /** - * @expectedException MWException - * @covers FormOptions::guessType - */ - public function testGuessTypeOnArrayThrowException() { - $this->object->guessType( [ 'foo' ] ); + $this->assertGuessArray( [ 'foo' ] ); } + /** * @expectedException MWException * @covers FormOptions::guessType -- 2.20.1