aboutsummaryrefslogtreecommitdiffstats
path: root/includes/api/ApiOptions.php
diff options
context:
space:
mode:
authorTim Starling <tstarling@wikimedia.org>2024-05-29 16:13:39 +1000
committerTim Starling <tstarling@wikimedia.org>2024-06-12 02:08:49 +0000
commitca615f369a209075a6a694b34120049e61113995 (patch)
tree35c7dea53b648ef3e0b7fe98fcde8fc05f83b480 /includes/api/ApiOptions.php
parentcdc5178150c10de9c897e85f2c900e37919e0946 (diff)
downloadmediawikicore-ca615f369a209075a6a694b34120049e61113995.tar.gz
mediawikicore-ca615f369a209075a6a694b34120049e61113995.zip
user: Split a base class out of ApiOptions
This gives us the flexibility to add features to core without affecting GlobalPreferences. Split getUserForUpdates() into asserting and non-asserting variants since most things are using it without checking for a null return. Bug: T323076 Change-Id: I53e5c409a650397fde03a8578b0182f0b97927a9
Diffstat (limited to 'includes/api/ApiOptions.php')
-rw-r--r--includes/api/ApiOptions.php218
1 files changed, 8 insertions, 210 deletions
diff --git a/includes/api/ApiOptions.php b/includes/api/ApiOptions.php
index e2b9cc53bacc..8dea4ad5de32 100644
--- a/includes/api/ApiOptions.php
+++ b/includes/api/ApiOptions.php
@@ -20,13 +20,9 @@
* @file
*/
-use MediaWiki\Logger\LoggerFactory;
use MediaWiki\MediaWikiServices;
-use MediaWiki\Preferences\DefaultPreferencesFactory;
use MediaWiki\Preferences\PreferencesFactory;
use MediaWiki\User\Options\UserOptionsManager;
-use MediaWiki\User\User;
-use Wikimedia\ParamValidator\ParamValidator;
/**
* API module that facilitates the changing of user's preferences.
@@ -34,13 +30,7 @@ use Wikimedia\ParamValidator\ParamValidator;
*
* @ingroup API
*/
-class ApiOptions extends ApiBase {
- /** @var User User account to modify */
- private $userForUpdates;
-
- private UserOptionsManager $userOptionsManager;
- private PreferencesFactory $preferencesFactory;
-
+class ApiOptions extends ApiOptionsBase {
/**
* @param ApiMain $main
* @param string $action
@@ -53,226 +43,34 @@ class ApiOptions extends ApiBase {
UserOptionsManager $userOptionsManager = null,
PreferencesFactory $preferencesFactory = null
) {
- parent::__construct( $main, $action );
/**
* This class is extended by GlobalPreferences extension.
* So it falls back to the global state.
*/
$services = MediaWikiServices::getInstance();
- $this->userOptionsManager = $userOptionsManager ?? $services->getUserOptionsManager();
- $this->preferencesFactory = $preferencesFactory ?? $services->getPreferencesFactory();
+ $userOptionsManager ??= $services->getUserOptionsManager();
+ $preferencesFactory ??= $services->getPreferencesFactory();
+ parent::__construct( $main, $action, $userOptionsManager, $preferencesFactory );
}
- /**
- * Changes preferences of the current user.
- */
- public function execute() {
- $user = $this->getUserForUpdates();
- if ( !$user || !$user->isNamed() ) {
- $this->dieWithError(
- [ 'apierror-mustbeloggedin', $this->msg( 'action-editmyoptions' ) ], 'notloggedin'
- );
- }
-
- $this->checkUserRightsAny( 'editmyoptions' );
-
- $params = $this->extractRequestParams();
- $changed = false;
-
- if ( isset( $params['optionvalue'] ) && !isset( $params['optionname'] ) ) {
- $this->dieWithError( [ 'apierror-missingparam', 'optionname' ] );
- }
-
- $resetKinds = $params['resetkinds'];
- if ( !$params['reset'] ) {
- $resetKinds = [];
- }
-
- $changes = [];
- if ( $params['change'] ) {
- foreach ( $params['change'] as $entry ) {
- $array = explode( '=', $entry, 2 );
- $changes[$array[0]] = $array[1] ?? null;
- }
- }
- if ( isset( $params['optionname'] ) ) {
- $newValue = $params['optionvalue'] ?? null;
- $changes[$params['optionname']] = $newValue;
- }
-
+ protected function runHook( $user, $changes, $resetKinds ) {
$this->getHookRunner()->onApiOptions( $this, $user, $changes, $resetKinds );
-
- if ( $resetKinds ) {
- $this->resetPreferences( $resetKinds );
- $changed = true;
- }
-
- if ( !$changed && !count( $changes ) ) {
- $this->dieWithError( 'apierror-nochanges' );
- }
-
- $prefs = $this->getPreferences();
- $prefsKinds = $this->preferencesFactory->getResetKinds( $user, $this->getContext(), $changes );
-
- $htmlForm = new HTMLForm( DefaultPreferencesFactory::simplifyFormDescriptor( $prefs ), $this );
- foreach ( $changes as $key => $value ) {
- switch ( $prefsKinds[$key] ) {
- case 'registered':
- // Regular option.
- if ( $value === null ) {
- // Reset it
- $validation = true;
- } else {
- // Validate
- $field = $htmlForm->getField( $key );
- $validation = $field->validate( $value, $this->userOptionsManager->getOptions( $user ) );
- }
- break;
- case 'registered-multiselect':
- case 'registered-checkmatrix':
- // A key for a multiselect or checkmatrix option.
- // TODO: Apply validation properly.
- $validation = true;
- $value = $value !== null ? (bool)$value : null;
- break;
- case 'userjs':
- // Allow non-default preferences prefixed with 'userjs-', to be set by user scripts
- if ( strlen( $key ) > 255 ) {
- $validation = $this->msg( 'apiwarn-validationfailed-keytoolong', Message::numParam( 255 ) );
- } elseif ( preg_match( '/[^a-zA-Z0-9_-]/', $key ) !== 0 ) {
- $validation = $this->msg( 'apiwarn-validationfailed-badchars' );
- } else {
- $validation = true;
- }
-
- LoggerFactory::getInstance( 'api-warning' )->info(
- 'ApiOptions: Setting userjs option',
- [
- 'phab' => 'T259073',
- 'OptionName' => substr( $key, 0, 255 ),
- 'OptionValue' => substr( $value ?? '', 0, 255 ),
- 'OptionSize' => strlen( $value ?? '' ),
- 'OptionValidation' => $validation,
- 'UserId' => $user->getId(),
- 'RequestIP' => $this->getRequest()->getIP(),
- 'RequestUA' => $this->getRequest()->getHeader( 'User-Agent' )
- ]
- );
- break;
- case 'special':
- $validation = $this->msg( 'apiwarn-validationfailed-cannotset' );
- break;
- case 'unused':
- default:
- $validation = $this->msg( 'apiwarn-validationfailed-badpref' );
- break;
- }
- if ( $validation === true && is_string( $value ) &&
- strlen( $value ) > UserOptionsManager::MAX_BYTES_OPTION_VALUE
- ) {
- $validation = $this->msg(
- 'apiwarn-validationfailed-valuetoolong',
- Message::numParam( UserOptionsManager::MAX_BYTES_OPTION_VALUE )
- );
- }
- if ( $validation === true ) {
- $this->setPreference( $key, $value );
- $changed = true;
- } else {
- $this->addWarning( [ 'apiwarn-validationfailed', wfEscapeWikiText( $key ), $validation ] );
- }
- }
-
- if ( $changed ) {
- $this->commitChanges();
- }
-
- $this->getResult()->addValue( null, $this->getModuleName(), 'success' );
- }
-
- /**
- * Load the user from the primary to reduce CAS errors on double post (T95839)
- *
- * @return User|null
- */
- protected function getUserForUpdates() {
- if ( !$this->userForUpdates ) {
- $this->userForUpdates = $this->getUser()->getInstanceForUpdate();
- }
-
- return $this->userForUpdates;
}
- /**
- * Returns preferences form descriptor
- * @return mixed[][]
- */
- protected function getPreferences() {
- return $this->preferencesFactory->getFormDescriptor( $this->getUserForUpdates(),
- $this->getContext() );
- }
-
- /**
- * @param string[] $kinds One or more types returned by UserOptionsManager::listOptionKinds() or 'all'
- */
protected function resetPreferences( array $kinds ) {
- $optionNames = $this->preferencesFactory->getOptionNamesForReset(
+ $optionNames = $this->getPreferencesFactory()->getOptionNamesForReset(
$this->getUserForUpdates(), $this->getContext(), $kinds );
- $this->userOptionsManager->resetOptionsByName( $this->getUserForUpdates(), $optionNames );
+ $this->getUserOptionsManager()->resetOptionsByName( $this->getUserForUpdates(), $optionNames );
}
- /**
- * Sets one user preference to be applied by commitChanges()
- *
- * @param string $preference
- * @param mixed $value
- */
protected function setPreference( $preference, $value ) {
- $this->userOptionsManager->setOption( $this->getUserForUpdates(), $preference, $value );
+ $this->getUserOptionsManager()->setOption( $this->getUserForUpdates(), $preference, $value );
}
- /**
- * Applies changes to user preferences
- */
protected function commitChanges() {
$this->getUserForUpdates()->saveSettings();
}
- public function mustBePosted() {
- return true;
- }
-
- public function isWriteMode() {
- return true;
- }
-
- public function getAllowedParams() {
- $optionKinds = $this->preferencesFactory->listResetKinds();
- $optionKinds[] = 'all';
-
- return [
- 'reset' => false,
- 'resetkinds' => [
- ParamValidator::PARAM_TYPE => $optionKinds,
- ParamValidator::PARAM_DEFAULT => 'all',
- ParamValidator::PARAM_ISMULTI => true
- ],
- 'change' => [
- ParamValidator::PARAM_ISMULTI => true,
- ],
- 'optionname' => [
- ParamValidator::PARAM_TYPE => 'string',
- ],
- 'optionvalue' => [
- ParamValidator::PARAM_TYPE => 'string',
- ],
- ];
- }
-
- public function needsToken() {
- return 'csrf';
- }
-
public function getHelpUrls() {
return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Options';
}