aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJamie Kuppens <jamie.kuppens@thisdot.co>2024-07-17 15:26:54 -0700
committerJamie Kuppens <jamie.kuppens@thisdot.co>2024-09-16 08:45:58 -0700
commit2537859104ede85e9b584979fc24576238b00e0c (patch)
treec17088f79e33255ca2f5395bfe9fcc2db073ff6a
parentfd07d1eadcf665d6f862cac0ce380d4ae92302c6 (diff)
downloadmediawikicore-2537859104ede85e9b584979fc24576238b00e0c.tar.gz
mediawikicore-2537859104ede85e9b584979fc24576238b00e0c.zip
Update user widgets to support named and temp account exclusion
* This change introduces two new optional parameters to the 'allusers' API call named 'excludenamed' and 'excludetemp' that allows filtering accounts based on whether they're named accounts or temporary accounts. This is tested by using the temporary matchPattern configured with $wgAutoCreateTempUser. * User widgets that call 'allusers' have been updated so that 'excludenamed' and 'excludetemp' can be optionally set so that named or temporary accounts may not returned with user suggestions. Bug: T332030 Change-Id: I6563ae610017fd1cd35c36ba65906041f7f68c4b
-rw-r--r--includes/api/ApiQuery.php1
-rw-r--r--includes/api/ApiQueryAllUsers.php29
-rw-r--r--includes/api/i18n/en.json2
-rw-r--r--includes/api/i18n/qqq.json2
-rw-r--r--includes/htmlform/fields/HTMLUserTextField.php19
-rw-r--r--includes/htmlform/fields/HTMLUsersMultiselectField.php8
-rw-r--r--includes/widget/UserInputWidget.php22
-rw-r--r--includes/widget/UsersMultiselectWidget.php24
-rw-r--r--resources/src/mediawiki.widgets/mw.widgets.UserInputWidget.js8
-rw-r--r--resources/src/mediawiki.widgets/mw.widgets.UsersMultiselectWidget.js12
-rw-r--r--tests/phpunit/includes/api/query/ApiQueryAllUsersTest.php171
11 files changed, 284 insertions, 14 deletions
diff --git a/includes/api/ApiQuery.php b/includes/api/ApiQuery.php
index b6d9dd887f87..0562c559383f 100644
--- a/includes/api/ApiQuery.php
+++ b/includes/api/ApiQuery.php
@@ -314,6 +314,7 @@ class ApiQuery extends ApiBase {
'UserGroupManager',
'GroupPermissionsLookup',
'ContentLanguage',
+ 'TempUserConfig',
]
],
'backlinks' => [
diff --git a/includes/api/ApiQueryAllUsers.php b/includes/api/ApiQueryAllUsers.php
index 351aa00556d7..454618f3effb 100644
--- a/includes/api/ApiQueryAllUsers.php
+++ b/includes/api/ApiQueryAllUsers.php
@@ -22,6 +22,7 @@
use MediaWiki\MainConfigNames;
use MediaWiki\Permissions\GroupPermissionsLookup;
+use MediaWiki\User\TempUser\TempUserConfig;
use MediaWiki\User\UserFactory;
use MediaWiki\User\UserGroupManager;
use Wikimedia\ParamValidator\ParamValidator;
@@ -41,6 +42,7 @@ class ApiQueryAllUsers extends ApiQueryBase {
private UserGroupManager $userGroupManager;
private GroupPermissionsLookup $groupPermissionsLookup;
private Language $contentLanguage;
+ private TempUserConfig $tempUserConfig;
/**
* @param ApiQuery $query
@@ -49,6 +51,7 @@ class ApiQueryAllUsers extends ApiQueryBase {
* @param UserGroupManager $userGroupManager
* @param GroupPermissionsLookup $groupPermissionsLookup
* @param Language $contentLanguage
+ * @param TempUserConfig $tempUserConfig
*/
public function __construct(
ApiQuery $query,
@@ -56,13 +59,15 @@ class ApiQueryAllUsers extends ApiQueryBase {
UserFactory $userFactory,
UserGroupManager $userGroupManager,
GroupPermissionsLookup $groupPermissionsLookup,
- Language $contentLanguage
+ Language $contentLanguage,
+ TempUserConfig $tempUserConfig
) {
parent::__construct( $query, $moduleName, 'au' );
$this->userFactory = $userFactory;
$this->userGroupManager = $userGroupManager;
$this->groupPermissionsLookup = $groupPermissionsLookup;
$this->contentLanguage = $contentLanguage;
+ $this->tempUserConfig = $tempUserConfig;
}
/**
@@ -125,6 +130,22 @@ class ApiQueryAllUsers extends ApiQueryBase {
);
}
+ $excludeNamed = $params['excludenamed'];
+ $excludeTemp = $params['excludetemp'];
+
+ if ( $this->tempUserConfig->isKnown() ) {
+ if ( $excludeTemp ) {
+ $this->addWhere(
+ $this->tempUserConfig->getMatchCondition( $db, 'user_name', IExpression::NOT_LIKE )
+ );
+ }
+ if ( $excludeNamed ) {
+ $this->addWhere(
+ $this->tempUserConfig->getMatchCondition( $db, 'user_name', IExpression::LIKE )
+ );
+ }
+ }
+
if ( $params['rights'] !== null && count( $params['rights'] ) ) {
$groups = [];
// TODO: this does not properly account for $wgRevokePermissions
@@ -429,6 +450,12 @@ class ApiQueryAllUsers extends ApiQueryBase {
],
],
'attachedwiki' => null,
+ 'excludenamed' => [
+ ParamValidator::PARAM_TYPE => 'boolean',
+ ],
+ 'excludetemp' => [
+ ParamValidator::PARAM_TYPE => 'boolean',
+ ],
];
}
diff --git a/includes/api/i18n/en.json b/includes/api/i18n/en.json
index 5170aa07db45..ecd1dd158ca2 100644
--- a/includes/api/i18n/en.json
+++ b/includes/api/i18n/en.json
@@ -668,6 +668,8 @@
"apihelp-query+allusers-summary": "Enumerate all registered users.",
"apihelp-query+allusers-param-from": "The username to start enumerating from.",
"apihelp-query+allusers-param-to": "The username to stop enumerating at.",
+ "apihelp-query+allusers-param-excludenamed": "Exclude users of named accounts.",
+ "apihelp-query+allusers-param-excludetemp": "Exclude users of temporary accounts.",
"apihelp-query+allusers-param-prefix": "Search for all users that begin with this value.",
"apihelp-query+allusers-param-dir": "Direction to sort in.",
"apihelp-query+allusers-param-group": "Only include users in the given groups. Does not include implicit or auto-promoted groups like *, user, or autoconfirmed.",
diff --git a/includes/api/i18n/qqq.json b/includes/api/i18n/qqq.json
index af86b03c4b80..40a06bfc6d34 100644
--- a/includes/api/i18n/qqq.json
+++ b/includes/api/i18n/qqq.json
@@ -639,6 +639,8 @@
"apihelp-query+allusers-summary": "{{doc-apihelp-summary|query+allusers}}",
"apihelp-query+allusers-param-from": "{{doc-apihelp-param|query+allusers|from}}",
"apihelp-query+allusers-param-to": "{{doc-apihelp-param|query+allusers|to}}",
+ "apihelp-query+allusers-param-excludenamed": "{{doc-apihelp-param|query+allusers|excludenamed}}",
+ "apihelp-query+allusers-param-excludetemp": "{{doc-apihelp-param|query+allusers|excludetemp}}",
"apihelp-query+allusers-param-prefix": "{{doc-apihelp-param|query+allusers|prefix}}",
"apihelp-query+allusers-param-dir": "{{doc-apihelp-param|query+allusers|dir}}",
"apihelp-query+allusers-param-group": "{{doc-apihelp-param|query+allusers|group}}",
diff --git a/includes/htmlform/fields/HTMLUserTextField.php b/includes/htmlform/fields/HTMLUserTextField.php
index 0478c167d931..d19c50c9f719 100644
--- a/includes/htmlform/fields/HTMLUserTextField.php
+++ b/includes/htmlform/fields/HTMLUserTextField.php
@@ -19,6 +19,8 @@ use Wikimedia\IPUtils;
* 'ipallowed' parameter must be set to true if this parameter is set to true.
* 'iprange' - Whether an IP address range is interpreted as "valid"
* 'iprangelimits' - Specifies the valid IP ranges for IPv4 and IPv6 in an array.
+ * 'excludenamed' - Whether to exclude named users or not.
+ * 'excludetemp' - Whether to exclude temporary users or not.
*
* @stable to extend
* @since 1.26
@@ -39,6 +41,8 @@ class HTMLUserTextField extends HTMLTextField {
'IPv4' => 0,
'IPv6' => 0,
],
+ 'excludenamed' => false,
+ 'excludetemp' => false,
]
);
@@ -65,6 +69,13 @@ class HTMLUserTextField extends HTMLTextField {
) ) {
return $this->msg( 'htmlform-user-not-exists', $user->getName() );
}
+
+ // check if the user account type matches the account type filter
+ $excludeNamed = $this->mParams['excludenamed'] ?? null;
+ $excludeTemp = $this->mParams['excludetemp'] ?? null;
+ if ( ( $excludeTemp && $user->isTemp() ) || ( $excludeNamed && $user->isNamed() ) ) {
+ return $this->msg( 'htmlform-user-not-valid', $user->getName() );
+ }
} else {
// not a valid username
$valid = false;
@@ -135,6 +146,14 @@ class HTMLUserTextField extends HTMLTextField {
}
protected function getInputWidget( $params ) {
+ if ( isset( $this->mParams['excludenamed'] ) ) {
+ $params['excludenamed'] = $this->mParams['excludenamed'];
+ }
+
+ if ( isset( $this->mParams['excludetemp'] ) ) {
+ $params['excludetemp'] = $this->mParams['excludetemp'];
+ }
+
return new UserInputWidget( $params );
}
diff --git a/includes/htmlform/fields/HTMLUsersMultiselectField.php b/includes/htmlform/fields/HTMLUsersMultiselectField.php
index 4b338014784c..19bd2f753bd9 100644
--- a/includes/htmlform/fields/HTMLUsersMultiselectField.php
+++ b/includes/htmlform/fields/HTMLUsersMultiselectField.php
@@ -124,6 +124,14 @@ class HTMLUsersMultiselectField extends HTMLUserTextField {
$params['ipRangeLimits'] = $this->mParams['iprangelimits'];
}
+ if ( isset( $this->mParams['excludenamed'] ) ) {
+ $params['excludeNamed'] = $this->mParams['excludenamed'];
+ }
+
+ if ( isset( $this->mParams['excludetemp'] ) ) {
+ $params['excludeTemp'] = $this->mParams['excludetemp'];
+ }
+
if ( isset( $this->mParams['input'] ) ) {
$params['input'] = $this->mParams['input'];
}
diff --git a/includes/widget/UserInputWidget.php b/includes/widget/UserInputWidget.php
index efb1359b58d4..874f05f66869 100644
--- a/includes/widget/UserInputWidget.php
+++ b/includes/widget/UserInputWidget.php
@@ -11,6 +11,11 @@ use OOUI\TextInputWidget;
* @license MIT
*/
class UserInputWidget extends TextInputWidget {
+ /** @var bool */
+ protected $excludeNamed;
+
+ /** @var bool */
+ protected $excludeTemp;
/**
* @param array $config Configuration options
@@ -18,6 +23,14 @@ class UserInputWidget extends TextInputWidget {
public function __construct( array $config = [] ) {
parent::__construct( $config );
+ if ( isset( $config['excludenamed'] ) ) {
+ $this->excludeNamed = $config['excludenamed'];
+ }
+
+ if ( isset( $config['excludetemp'] ) ) {
+ $this->excludeTemp = $config['excludetemp'];
+ }
+
// Initialization
$this->addClasses( [ 'mw-widget-userInputWidget' ] );
}
@@ -28,6 +41,15 @@ class UserInputWidget extends TextInputWidget {
public function getConfig( &$config ) {
$config['$overlay'] = true;
+
+ if ( $this->excludeNamed !== null ) {
+ $config['excludenamed'] = $this->excludeNamed;
+ }
+
+ if ( $this->excludeTemp !== null ) {
+ $config['excludetemp'] = $this->excludeTemp;
+ }
+
return parent::getConfig( $config );
}
}
diff --git a/includes/widget/UsersMultiselectWidget.php b/includes/widget/UsersMultiselectWidget.php
index 143cfc7fb64c..9e4b55b9b5a9 100644
--- a/includes/widget/UsersMultiselectWidget.php
+++ b/includes/widget/UsersMultiselectWidget.php
@@ -18,11 +18,19 @@ class UsersMultiselectWidget extends TagMultiselectWidget {
/** @var array */
protected $ipRangeLimits;
+ /** @var bool */
+ protected $excludeNamed;
+
+ /** @var bool */
+ protected $excludeTemp;
+
/**
* @param array $config Configuration options
* - bool $config['ipAllowed'] Accept valid IP addresses
* - bool $config['ipRangeAllowed'] Accept valid IP ranges
* - array $config['ipRangeLimits'] Maximum allowed IP range sizes
+ * - bool $config['excludeNamed'] Exclude named accounts
+ * - bool $config['excludeTemp'] Exclude temporary accounts
*/
public function __construct( array $config = [] ) {
parent::__construct( $config );
@@ -38,6 +46,14 @@ class UsersMultiselectWidget extends TagMultiselectWidget {
if ( isset( $config['ipRangeLimits'] ) ) {
$this->ipRangeLimits = $config['ipRangeLimits'];
}
+
+ if ( isset( $config['excludeNamed'] ) ) {
+ $this->excludeNamed = $config['excludeNamed'];
+ }
+
+ if ( isset( $config['excludeTemp'] ) ) {
+ $this->excludeTemp = $config['excludeTemp'];
+ }
}
protected function getJavaScriptClassName() {
@@ -57,6 +73,14 @@ class UsersMultiselectWidget extends TagMultiselectWidget {
$config['ipRangeLimits'] = $this->ipRangeLimits;
}
+ if ( $this->excludeNamed !== null ) {
+ $config['excludeNamed'] = $this->excludeNamed;
+ }
+
+ if ( $this->excludeTemp !== null ) {
+ $config['excludeTemp'] = $this->excludeTemp;
+ }
+
return parent::getConfig( $config );
}
diff --git a/resources/src/mediawiki.widgets/mw.widgets.UserInputWidget.js b/resources/src/mediawiki.widgets/mw.widgets.UserInputWidget.js
index cfd7d7a73b47..bf3f16515de9 100644
--- a/resources/src/mediawiki.widgets/mw.widgets.UserInputWidget.js
+++ b/resources/src/mediawiki.widgets/mw.widgets.UserInputWidget.js
@@ -17,6 +17,8 @@
* @description Create a mw.widgets.UserInputWidget object.
* @param {Object} [config] Configuration options
* @param {number} [config.limit=10] Number of results to show
+ * @param {boolean} [config.excludenamed] Whether to exclude named users or not
+ * @param {boolean} [config.excludetemp] Whether to exclude temporary users or not
* @param {mw.Api} [config.api] API object to use, creates a default mw.Api instance if not specified
*/
mw.widgets.UserInputWidget = function MwWidgetsUserInputWidget( config ) {
@@ -31,6 +33,8 @@
// Properties
this.limit = config.limit || 10;
+ this.excludeNamed = config.excludenamed || false;
+ this.excludeTemp = config.excludetemp || false;
this.api = config.api || new mw.Api();
// Initialization
@@ -80,7 +84,9 @@
action: 'query',
list: 'allusers',
auprefix: this.value,
- aulimit: this.limit
+ aulimit: this.limit,
+ auexcludenamed: this.excludeNamed,
+ auexcludetemp: this.excludeTemp
} );
};
diff --git a/resources/src/mediawiki.widgets/mw.widgets.UsersMultiselectWidget.js b/resources/src/mediawiki.widgets/mw.widgets.UsersMultiselectWidget.js
index cdf0a9d82192..72db6d96df69 100644
--- a/resources/src/mediawiki.widgets/mw.widgets.UsersMultiselectWidget.js
+++ b/resources/src/mediawiki.widgets/mw.widgets.UsersMultiselectWidget.js
@@ -30,6 +30,8 @@
* @param {Object} [config.ipRangeLimits] Maximum allowed IP ranges (defaults match HTMLUserTextField.php)
* @param {number} [config.ipRangeLimits.IPv4 = 16] Maximum allowed IPv4 range
* @param {number} [config.ipRangeLimits.IPv6 = 32] Maximum allowed IPv6 range
+ * @param {boolean} [config.excludenamed] Whether to exclude named users or not
+ * @param {boolean} [config.excludetemp] Whether to exclude temporary users or not
*/
mw.widgets.UsersMultiselectWidget = function MwWidgetsUsersMultiselectWidget( config ) {
// Config initialization
@@ -40,7 +42,9 @@
ipRangeLimits: {
IPv4: 16,
IPv6: 32
- }
+ },
+ excludeNamed: false,
+ excludeTemp: false
}, config );
// Parent constructor
@@ -54,6 +58,8 @@
this.ipAllowed = config.ipAllowed;
this.ipRangeAllowed = config.ipRangeAllowed;
this.ipRangeLimits = config.ipRangeLimits;
+ this.excludeNamed = config.excludeNamed;
+ this.excludeTemp = config.excludeTemp;
if ( 'name' in config ) {
// Use this instead of <input type="hidden">, because hidden inputs do not have separate
@@ -138,7 +144,9 @@
action: 'query',
list: 'allusers',
auprefix: inputValue,
- aulimit: this.limit
+ aulimit: this.limit,
+ auexcludenamed: this.excludeNamed,
+ auexcludetemp: this.excludeTemp
} ).done( ( response ) => {
let suggestions = response.query.allusers;
diff --git a/tests/phpunit/includes/api/query/ApiQueryAllUsersTest.php b/tests/phpunit/includes/api/query/ApiQueryAllUsersTest.php
index e2adc863ea3c..9aa42dc66c0f 100644
--- a/tests/phpunit/includes/api/query/ApiQueryAllUsersTest.php
+++ b/tests/phpunit/includes/api/query/ApiQueryAllUsersTest.php
@@ -5,6 +5,7 @@ namespace MediaWiki\Tests\Api\Query;
use MediaWiki\Permissions\UltimateAuthority;
use MediaWiki\Tests\Api\ApiTestCase;
use MediaWiki\Tests\Unit\Permissions\MockAuthorityTrait;
+use MediaWiki\Tests\User\TempUser\TempUserTestTrait;
use MediaWiki\User\User;
/**
@@ -16,20 +17,36 @@ use MediaWiki\User\User;
*/
class ApiQueryAllUsersTest extends ApiTestCase {
use MockAuthorityTrait;
+ use TempUserTestTrait;
private const USER_PREFIX = 'ApiQueryAllUsersTest ';
+ private const TEMP_USER_PREFIX = '~';
+
+ private const TEMP_USER_CONFIG = [
+ 'genPattern' => self::TEMP_USER_PREFIX . '$1',
+ 'matchPattern' => self::TEMP_USER_PREFIX . '$1',
+ 'reservedPattern' => self::TEMP_USER_PREFIX . '$1',
+ ];
+
/** @var User[] */
private static $usersAdded = [];
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->enableAutoCreateTempUser( self::TEMP_USER_CONFIG );
+ }
+
public function addDBDataOnce() {
$groupManager = $this->getServiceContainer()->getUserGroupManager();
$userA = $this->getMutableTestUser( [], self::USER_PREFIX . 'A' )->getUser();
$userB = $this->getMutableTestUser( [], self::USER_PREFIX . 'B' )->getUser();
$userC = $this->getMutableTestUser( [], self::USER_PREFIX . 'C' )->getUser();
+ $userD = $this->getMutableTestUser( [], self::TEMP_USER_PREFIX . 'D' )->getUser();
$groupManager->addUserToGroup( $userB, 'bot' );
$groupManager->addUserToGroup( $userC, 'bot' );
- self::$usersAdded = [ $userA, $userB, $userC ];
+ self::$usersAdded = [ $userA, $userB, $userC, $userD ];
}
public function testPrefix() {
@@ -41,23 +58,37 @@ class ApiQueryAllUsersTest extends ApiTestCase {
$this->assertArrayHasKey( 'query', $result[0] );
$this->assertArrayHasKey( 'allusers', $result[0]['query'] );
- $this->assertContains( self::$usersAdded[0]->getName(), $result[0]['query']['allusers'][0] );
- $this->assertContains( self::$usersAdded[1]->getName(), $result[0]['query']['allusers'][1] );
- $this->assertContains( self::$usersAdded[2]->getName(), $result[0]['query']['allusers'][2] );
+ $this->assertApiResultsHasUser( self::$usersAdded[0]->getName(), $result );
+ $this->assertApiResultsHasUser( self::$usersAdded[1]->getName(), $result );
+ $this->assertApiResultsHasUser( self::$usersAdded[2]->getName(), $result );
}
public function testImplicitRights() {
$result = $this->doApiRequest( [
'action' => 'query',
'list' => 'allusers',
- 'aurights' => 'stashedit'
+ 'auprefix' => self::USER_PREFIX,
+ 'aurights' => 'stashedit',
] );
$this->assertArrayHasKey( 'query', $result[0] );
$this->assertArrayHasKey( 'allusers', $result[0]['query'] );
- $this->assertContains( self::$usersAdded[0]->getName(), $result[0]['query']['allusers'][0] );
- $this->assertContains( self::$usersAdded[1]->getName(), $result[0]['query']['allusers'][1] );
- $this->assertContains( self::$usersAdded[2]->getName(), $result[0]['query']['allusers'][2] );
+ $this->assertApiResultsHasUser( self::$usersAdded[0]->getName(), $result );
+ $this->assertApiResultsHasUser( self::$usersAdded[1]->getName(), $result );
+ $this->assertApiResultsHasUser( self::$usersAdded[2]->getName(), $result );
+ }
+
+ public function testTempUserImplicitRights() {
+ $result = $this->doApiRequest( [
+ 'action' => 'query',
+ 'list' => 'allusers',
+ 'auprefix' => self::TEMP_USER_PREFIX,
+ 'aurights' => 'stashedit',
+ ] );
+
+ $this->assertArrayHasKey( 'query', $result[0] );
+ $this->assertArrayHasKey( 'allusers', $result[0]['query'] );
+ $this->assertApiResultsHasUser( self::$usersAdded[3]->getName(), $result );
}
public function testPermissions() {
@@ -70,8 +101,8 @@ class ApiQueryAllUsersTest extends ApiTestCase {
$this->assertArrayHasKey( 'query', $result[0] );
$this->assertArrayHasKey( 'allusers', $result[0]['query'] );
- $this->assertContains( self::$usersAdded[1]->getName(), $result[0]['query']['allusers'][0] );
- $this->assertContains( self::$usersAdded[2]->getName(), $result[0]['query']['allusers'][1] );
+ $this->assertApiResultsHasUser( self::$usersAdded[1]->getName(), $result );
+ $this->assertApiResultsHasUser( self::$usersAdded[2]->getName(), $result );
}
public function testHiddenUser() {
@@ -212,4 +243,124 @@ class ApiQueryAllUsersTest extends ApiTestCase {
$this->assertContains( 'protect', $result[0]['query']['allusers'][0]['rights'] );
$this->assertNotContains( 'protect', $result[0]['query']['allusers'][1]['rights'] );
}
+
+ public function testNamedOnly() {
+ $result = $this->doApiRequest( [
+ 'action' => 'query',
+ 'list' => 'allusers',
+ 'auexcludetemp' => true,
+ ] );
+
+ $this->assertArrayHasKey( 'query', $result[0] );
+ $this->assertArrayHasKey( 'allusers', $result[0]['query'] );
+ $this->assertApiResultsHasUser( self::$usersAdded[0]->getName(), $result );
+ $this->assertApiResultsHasUser( self::$usersAdded[1]->getName(), $result );
+ $this->assertApiResultsHasUser( self::$usersAdded[2]->getName(), $result );
+ $this->assertApiResultsNotHasUser( self::$usersAdded[3]->getName(), $result );
+ }
+
+ public function testNamedOnlyTempDisabled() {
+ $this->disableAutoCreateTempUser(
+ array_merge( self::TEMP_USER_CONFIG, [ 'known' => false ] )
+ );
+
+ $result = $this->doApiRequest( [
+ 'action' => 'query',
+ 'list' => 'allusers',
+ 'auexcludetemp' => true,
+ ] );
+
+ $this->assertArrayHasKey( 'query', $result[0] );
+ $this->assertArrayHasKey( 'allusers', $result[0]['query'] );
+ $this->assertApiResultsHasUser( self::$usersAdded[0]->getName(), $result );
+ $this->assertApiResultsHasUser( self::$usersAdded[1]->getName(), $result );
+ $this->assertApiResultsHasUser( self::$usersAdded[2]->getName(), $result );
+ // The temp user from the setup will return as the filter will be disabled.
+ $this->assertApiResultsHasUser( self::$usersAdded[3]->getName(), $result );
+ }
+
+ public function testNamedOnlyTempKnown() {
+ $this->disableAutoCreateTempUser(
+ array_merge( self::TEMP_USER_CONFIG, [ 'enabled' => false, 'known' => true ] )
+ );
+
+ $result = $this->doApiRequest( [
+ 'action' => 'query',
+ 'list' => 'allusers',
+ 'auexcludetemp' => true,
+ ] );
+
+ $this->assertArrayHasKey( 'query', $result[0] );
+ $this->assertArrayHasKey( 'allusers', $result[0]['query'] );
+ $this->assertApiResultsHasUser( self::$usersAdded[0]->getName(), $result );
+ $this->assertApiResultsHasUser( self::$usersAdded[1]->getName(), $result );
+ $this->assertApiResultsHasUser( self::$usersAdded[2]->getName(), $result );
+ $this->assertApiResultsNotHasUser( self::$usersAdded[3]->getName(), $result );
+ }
+
+ public function testTempOnly() {
+ $result = $this->doApiRequest( [
+ 'action' => 'query',
+ 'list' => 'allusers',
+ 'auexcludenamed' => true,
+ ] );
+
+ $this->assertArrayHasKey( 'query', $result[0] );
+ $this->assertArrayHasKey( 'allusers', $result[0]['query'] );
+ $this->assertApiResultsHasUser( self::$usersAdded[3]->getName(), $result );
+ $this->assertCount( 1, $result[0]['query']['allusers'] );
+ }
+
+ public function testTempOnlyTempDisabled() {
+ $this->disableAutoCreateTempUser();
+
+ $result = $this->doApiRequest( [
+ 'action' => 'query',
+ 'list' => 'allusers',
+ 'auexcludenamed' => true,
+ ] );
+
+ $this->assertArrayHasKey( 'query', $result[0] );
+ $this->assertArrayHasKey( 'allusers', $result[0]['query'] );
+ // We'll get some count higher than 1 if the feature is disabled as the
+ // filter gets ignored in that case.
+ $this->assertNotCount( 1, $result[0]['query']['allusers'] );
+ }
+
+ public function testTempOnlyTempKnown() {
+ $this->disableAutoCreateTempUser(
+ array_merge( self::TEMP_USER_CONFIG, [ 'enabled' => false, 'known' => true ] )
+ );
+
+ $result = $this->doApiRequest( [
+ 'action' => 'query',
+ 'list' => 'allusers',
+ 'auexcludenamed' => true,
+ ] );
+
+ $this->assertArrayHasKey( 'query', $result[0] );
+ $this->assertArrayHasKey( 'allusers', $result[0]['query'] );
+ $this->assertApiResultsHasUser( self::$usersAdded[3]->getName(), $result );
+ $this->assertCount( 1, $result[0]['query']['allusers'] );
+ }
+
+ private function assertApiResultsHasUser( $username, $results ) {
+ $this->assertNotFalse(
+ array_search(
+ $username,
+ array_column( $results[0]['query']['allusers'], 'name' )
+ ),
+ "Failed to assert that '{$username}' is in the AllUsers API response"
+ );
+ }
+
+ private function assertApiResultsNotHasUser( $username, $results ) {
+ $this->assertFalse(
+ array_search(
+ $username,
+ array_column( $results[0]['query']['allusers'], 'name' )
+ ),
+ "Failed to assert that '{$username}' is not in the AllUsers API response"
+ );
+ }
}