MediaWiki master
ApiOptions.php
Go to the documentation of this file.
1<?php
30
37class ApiOptions extends ApiBase {
39 private $userForUpdates;
40
41 private UserOptionsManager $userOptionsManager;
42 private PreferencesFactory $preferencesFactory;
43
50 public function __construct(
51 ApiMain $main,
52 $action,
53 UserOptionsManager $userOptionsManager = null,
54 PreferencesFactory $preferencesFactory = null
55 ) {
56 parent::__construct( $main, $action );
61 $services = MediaWikiServices::getInstance();
62 $this->userOptionsManager = $userOptionsManager ?? $services->getUserOptionsManager();
63 $this->preferencesFactory = $preferencesFactory ?? $services->getPreferencesFactory();
64 }
65
69 public function execute() {
70 $user = $this->getUserForUpdates();
71 if ( !$user || !$user->isNamed() ) {
72 $this->dieWithError(
73 [ 'apierror-mustbeloggedin', $this->msg( 'action-editmyoptions' ) ], 'notloggedin'
74 );
75 }
76
77 $this->checkUserRightsAny( 'editmyoptions' );
78
80 $changed = false;
81
82 if ( isset( $params['optionvalue'] ) && !isset( $params['optionname'] ) ) {
83 $this->dieWithError( [ 'apierror-missingparam', 'optionname' ] );
84 }
85
86 $resetKinds = $params['resetkinds'];
87 if ( !$params['reset'] ) {
88 $resetKinds = [];
89 }
90
91 $changes = [];
92 if ( $params['change'] ) {
93 foreach ( $params['change'] as $entry ) {
94 $array = explode( '=', $entry, 2 );
95 $changes[$array[0]] = $array[1] ?? null;
96 }
97 }
98 if ( isset( $params['optionname'] ) ) {
99 $newValue = $params['optionvalue'] ?? null;
100 $changes[$params['optionname']] = $newValue;
101 }
102
103 $this->getHookRunner()->onApiOptions( $this, $user, $changes, $resetKinds );
104
105 if ( $resetKinds ) {
106 $this->resetPreferences( $resetKinds );
107 $changed = true;
108 }
109
110 if ( !$changed && !count( $changes ) ) {
111 $this->dieWithError( 'apierror-nochanges' );
112 }
113
114 $prefs = $this->getPreferences();
115 $prefsKinds = $this->userOptionsManager->getOptionKinds( $user, $this->getContext(), $changes );
116
117 $htmlForm = new HTMLForm( DefaultPreferencesFactory::simplifyFormDescriptor( $prefs ), $this );
118 foreach ( $changes as $key => $value ) {
119 switch ( $prefsKinds[$key] ) {
120 case 'registered':
121 // Regular option.
122 if ( $value === null ) {
123 // Reset it
124 $validation = true;
125 } else {
126 // Validate
127 $field = $htmlForm->getField( $key );
128 $validation = $field->validate( $value, $this->userOptionsManager->getOptions( $user ) );
129 }
130 break;
131 case 'registered-multiselect':
132 case 'registered-checkmatrix':
133 // A key for a multiselect or checkmatrix option.
134 // TODO: Apply validation properly.
135 $validation = true;
136 $value = $value !== null ? (bool)$value : null;
137 break;
138 case 'userjs':
139 // Allow non-default preferences prefixed with 'userjs-', to be set by user scripts
140 if ( strlen( $key ) > 255 ) {
141 $validation = $this->msg( 'apiwarn-validationfailed-keytoolong', Message::numParam( 255 ) );
142 } elseif ( preg_match( '/[^a-zA-Z0-9_-]/', $key ) !== 0 ) {
143 $validation = $this->msg( 'apiwarn-validationfailed-badchars' );
144 } else {
145 $validation = true;
146 }
147
148 LoggerFactory::getInstance( 'api-warning' )->info(
149 'ApiOptions: Setting userjs option',
150 [
151 'phab' => 'T259073',
152 'OptionName' => substr( $key, 0, 255 ),
153 'OptionValue' => substr( $value ?? '', 0, 255 ),
154 'OptionSize' => strlen( $value ?? '' ),
155 'OptionValidation' => $validation,
156 'UserId' => $user->getId(),
157 'RequestIP' => $this->getRequest()->getIP(),
158 'RequestUA' => $this->getRequest()->getHeader( 'User-Agent' )
159 ]
160 );
161 break;
162 case 'special':
163 $validation = $this->msg( 'apiwarn-validationfailed-cannotset' );
164 break;
165 case 'unused':
166 default:
167 $validation = $this->msg( 'apiwarn-validationfailed-badpref' );
168 break;
169 }
170 if ( $validation === true && is_string( $value ) &&
171 strlen( $value ) > UserOptionsManager::MAX_BYTES_OPTION_VALUE
172 ) {
173 $validation = $this->msg(
174 'apiwarn-validationfailed-valuetoolong',
175 Message::numParam( UserOptionsManager::MAX_BYTES_OPTION_VALUE )
176 );
177 }
178 if ( $validation === true ) {
179 $this->setPreference( $key, $value );
180 $changed = true;
181 } else {
182 $this->addWarning( [ 'apiwarn-validationfailed', wfEscapeWikiText( $key ), $validation ] );
183 }
184 }
185
186 if ( $changed ) {
187 $this->commitChanges();
188 }
189
190 $this->getResult()->addValue( null, $this->getModuleName(), 'success' );
191 }
192
198 protected function getUserForUpdates() {
199 if ( !$this->userForUpdates ) {
200 $this->userForUpdates = $this->getUser()->getInstanceForUpdate();
201 }
202
203 return $this->userForUpdates;
204 }
205
210 protected function getPreferences() {
211 return $this->preferencesFactory->getFormDescriptor( $this->getUserForUpdates(),
212 $this->getContext() );
213 }
214
218 protected function resetPreferences( array $kinds ) {
219 $this->userOptionsManager->resetOptions( $this->getUserForUpdates(), $this->getContext(), $kinds );
220 }
221
228 protected function setPreference( $preference, $value ) {
229 $this->userOptionsManager->setOption( $this->getUserForUpdates(), $preference, $value );
230 }
231
235 protected function commitChanges() {
236 $this->getUserForUpdates()->saveSettings();
237 }
238
239 public function mustBePosted() {
240 return true;
241 }
242
243 public function isWriteMode() {
244 return true;
245 }
246
247 public function getAllowedParams() {
248 $optionKinds = $this->userOptionsManager->listOptionKinds();
249 $optionKinds[] = 'all';
250
251 return [
252 'reset' => false,
253 'resetkinds' => [
254 ParamValidator::PARAM_TYPE => $optionKinds,
255 ParamValidator::PARAM_DEFAULT => 'all',
256 ParamValidator::PARAM_ISMULTI => true
257 ],
258 'change' => [
259 ParamValidator::PARAM_ISMULTI => true,
260 ],
261 'optionname' => [
262 ParamValidator::PARAM_TYPE => 'string',
263 ],
264 'optionvalue' => [
265 ParamValidator::PARAM_TYPE => 'string',
266 ],
267 ];
268 }
269
270 public function needsToken() {
271 return 'csrf';
272 }
273
274 public function getHelpUrls() {
275 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Options';
276 }
277
278 protected function getExamplesMessages() {
279 return [
280 'action=options&reset=&token=123ABC'
281 => 'apihelp-options-example-reset',
282 'action=options&change=skin=vector|hideminor=1&token=123ABC'
283 => 'apihelp-options-example-change',
284 'action=options&reset=&change=skin=monobook&optionname=nickname&' .
285 'optionvalue=[[User:Beau|Beau]]%20([[User_talk:Beau|talk]])&token=123ABC'
286 => 'apihelp-options-example-complex',
287 ];
288 }
289}
getUser()
wfEscapeWikiText( $input)
Escapes the given text so that it may be output using addWikiText() without any linking,...
getContext()
array $params
The job parameters.
This abstract class implements many basic API functions, and is the base of all API classes.
Definition ApiBase.php:64
dieWithError( $msg, $code=null, $data=null, $httpCode=0)
Abort execution with an error.
Definition ApiBase.php:1533
checkUserRightsAny( $rights)
Helper function for permission-denied errors.
Definition ApiBase.php:1649
getResult()
Get the result object.
Definition ApiBase.php:671
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:811
addWarning( $msg, $code=null, $data=null)
Add a warning for this module.
Definition ApiBase.php:1451
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:532
getHookRunner()
Get an ApiHookRunner for running core API hooks.
Definition ApiBase.php:756
This is the main API class, used for both external and internal processing.
Definition ApiMain.php:65
API module that facilitates the changing of user's preferences.
resetPreferences(array $kinds)
commitChanges()
Applies changes to user preferences.
isWriteMode()
Indicates whether this module requires write mode.
getExamplesMessages()
Returns usage examples for this module.
setPreference( $preference, $value)
Sets one user preference to be applied by commitChanges()
needsToken()
Returns the token type this module requires in order to execute.
getHelpUrls()
Return links to more detailed help pages about the module.
execute()
Changes preferences of the current user.
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
__construct(ApiMain $main, $action, UserOptionsManager $userOptionsManager=null, PreferencesFactory $preferencesFactory=null)
getPreferences()
Returns preferences form descriptor.
mustBePosted()
Indicates whether this module must be called with a POST request.
getUserForUpdates()
Load the user from the primary to reduce CAS errors on double post (T95839)
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
Create PSR-3 logger objects.
Service locator for MediaWiki core services.
This is the default implementation of PreferencesFactory.
A service class to control user options.
internal since 1.36
Definition User.php:93
Service for formatting and validating API parameters.
A PreferencesFactory is a MediaWiki service that provides the definitions of preferences for a given ...