MediaWiki REL1_39
ApiOptions.php
Go to the documentation of this file.
1<?php
29
36class ApiOptions extends ApiBase {
38 private $userForUpdates;
39
41 private $userOptionsManager;
42
44 private $preferencesFactory;
45
52 public function __construct(
53 ApiMain $main,
54 $action,
55 UserOptionsManager $userOptionsManager = null,
56 PreferencesFactory $preferencesFactory = null
57 ) {
58 parent::__construct( $main, $action );
63 $services = MediaWikiServices::getInstance();
64 $this->userOptionsManager = $userOptionsManager ?? $services->getUserOptionsManager();
65 $this->preferencesFactory = $preferencesFactory ?? $services->getPreferencesFactory();
66 }
67
71 public function execute() {
72 $user = $this->getUserForUpdates();
73 if ( !$user || !$user->isRegistered() ) {
74 $this->dieWithError(
75 [ 'apierror-mustbeloggedin', $this->msg( 'action-editmyoptions' ) ], 'notloggedin'
76 );
77 }
78
79 $this->checkUserRightsAny( 'editmyoptions' );
80
81 $params = $this->extractRequestParams();
82 $changed = false;
83
84 if ( isset( $params['optionvalue'] ) && !isset( $params['optionname'] ) ) {
85 $this->dieWithError( [ 'apierror-missingparam', 'optionname' ] );
86 }
87
88 $resetKinds = $params['resetkinds'];
89 if ( !$params['reset'] ) {
90 $resetKinds = [];
91 }
92
93 $changes = [];
94 if ( $params['change'] ) {
95 foreach ( $params['change'] as $entry ) {
96 $array = explode( '=', $entry, 2 );
97 $changes[$array[0]] = $array[1] ?? null;
98 }
99 }
100 if ( isset( $params['optionname'] ) ) {
101 $newValue = $params['optionvalue'] ?? null;
102 $changes[$params['optionname']] = $newValue;
103 }
104
105 $this->getHookRunner()->onApiOptions( $this, $user, $changes, $resetKinds );
106
107 if ( $resetKinds ) {
108 $this->resetPreferences( $resetKinds );
109 $changed = true;
110 }
111
112 if ( !$changed && !count( $changes ) ) {
113 $this->dieWithError( 'apierror-nochanges' );
114 }
115
116 $prefs = $this->getPreferences();
117 $prefsKinds = $this->userOptionsManager->getOptionKinds( $user, $this->getContext(), $changes );
118
119 $htmlForm = new HTMLForm( DefaultPreferencesFactory::simplifyFormDescriptor( $prefs ), $this );
120 foreach ( $changes as $key => $value ) {
121 switch ( $prefsKinds[$key] ) {
122 case 'registered':
123 // Regular option.
124 if ( $value === null ) {
125 // Reset it
126 $validation = true;
127 } else {
128 // Validate
129 $field = $htmlForm->getField( $key );
130 $validation = $field->validate( $value, $this->userOptionsManager->getOptions( $user ) );
131 }
132 break;
133 case 'registered-multiselect':
134 case 'registered-checkmatrix':
135 // A key for a multiselect or checkmatrix option.
136 // TODO: Apply validation properly.
137 $validation = true;
138 $value = $value !== null ? (bool)$value : null;
139 break;
140 case 'userjs':
141 // Allow non-default preferences prefixed with 'userjs-', to be set by user scripts
142 if ( strlen( $key ) > 255 ) {
143 $validation = $this->msg( 'apiwarn-validationfailed-keytoolong', Message::numParam( 255 ) );
144 } elseif ( preg_match( '/[^a-zA-Z0-9_-]/', $key ) !== 0 ) {
145 $validation = $this->msg( 'apiwarn-validationfailed-badchars' );
146 } else {
147 $validation = true;
148 }
149
150 LoggerFactory::getInstance( 'api-warning' )->info(
151 'ApiOptions: Setting userjs option',
152 [
153 'phab' => 'T259073',
154 'OptionName' => substr( $key, 0, 255 ),
155 'OptionValue' => substr( $value ?? '', 0, 255 ),
156 'OptionSize' => strlen( $value ?? '' ),
157 'OptionValidation' => $validation,
158 'UserId' => $user->getId(),
159 'RequestIP' => $this->getRequest()->getIP(),
160 'RequestUA' => $this->getRequest()->getHeader( 'User-Agent' )
161 ]
162 );
163 break;
164 case 'special':
165 $validation = $this->msg( 'apiwarn-validationfailed-cannotset' );
166 break;
167 case 'unused':
168 default:
169 $validation = $this->msg( 'apiwarn-validationfailed-badpref' );
170 break;
171 }
172 if ( $validation === true && is_string( $value ) &&
173 strlen( $value ) > UserOptionsManager::MAX_BYTES_OPTION_VALUE
174 ) {
175 $validation = $this->msg(
176 'apiwarn-validationfailed-valuetoolong',
177 Message::numParam( UserOptionsManager::MAX_BYTES_OPTION_VALUE )
178 );
179 }
180 if ( $validation === true ) {
181 $this->setPreference( $key, $value );
182 $changed = true;
183 } else {
184 $this->addWarning( [ 'apiwarn-validationfailed', wfEscapeWikiText( $key ), $validation ] );
185 }
186 }
187
188 if ( $changed ) {
189 $this->commitChanges();
190 }
191
192 $this->getResult()->addValue( null, $this->getModuleName(), 'success' );
193 }
194
200 protected function getUserForUpdates() {
201 if ( !$this->userForUpdates ) {
202 $this->userForUpdates = $this->getUser()->getInstanceForUpdate();
203 }
204
205 return $this->userForUpdates;
206 }
207
212 protected function getPreferences() {
213 return $this->preferencesFactory->getFormDescriptor( $this->getUserForUpdates(),
214 $this->getContext() );
215 }
216
220 protected function resetPreferences( array $kinds ) {
221 $this->userOptionsManager->resetOptions( $this->getUserForUpdates(), $this->getContext(), $kinds );
222 }
223
230 protected function setPreference( $preference, $value ) {
231 $this->userOptionsManager->setOption( $this->getUserForUpdates(), $preference, $value );
232 }
233
237 protected function commitChanges() {
238 $this->getUserForUpdates()->saveSettings();
239 }
240
241 public function mustBePosted() {
242 return true;
243 }
244
245 public function isWriteMode() {
246 return true;
247 }
248
249 public function getAllowedParams() {
250 $optionKinds = $this->userOptionsManager->listOptionKinds();
251 $optionKinds[] = 'all';
252
253 return [
254 'reset' => false,
255 'resetkinds' => [
256 ParamValidator::PARAM_TYPE => $optionKinds,
257 ParamValidator::PARAM_DEFAULT => 'all',
258 ParamValidator::PARAM_ISMULTI => true
259 ],
260 'change' => [
261 ParamValidator::PARAM_ISMULTI => true,
262 ],
263 'optionname' => [
264 ParamValidator::PARAM_TYPE => 'string',
265 ],
266 'optionvalue' => [
267 ParamValidator::PARAM_TYPE => 'string',
268 ],
269 ];
270 }
271
272 public function needsToken() {
273 return 'csrf';
274 }
275
276 public function getHelpUrls() {
277 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Options';
278 }
279
280 protected function getExamplesMessages() {
281 return [
282 'action=options&reset=&token=123ABC'
283 => 'apihelp-options-example-reset',
284 'action=options&change=skin=vector|hideminor=1&token=123ABC'
285 => 'apihelp-options-example-change',
286 'action=options&reset=&change=skin=monobook&optionname=nickname&' .
287 'optionvalue=[[User:Beau|Beau]]%20([[User_talk:Beau|talk]])&token=123ABC'
288 => 'apihelp-options-example-complex',
289 ];
290 }
291}
getUser()
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
getContext()
This abstract class implements many basic API functions, and is the base of all API classes.
Definition ApiBase.php:56
dieWithError( $msg, $code=null, $data=null, $httpCode=0)
Abort execution with an error.
Definition ApiBase.php:1454
checkUserRightsAny( $rights, $user=null)
Helper function for permission-denied errors.
Definition ApiBase.php:1560
getResult()
Get the result object.
Definition ApiBase.php:629
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:765
addWarning( $msg, $code=null, $data=null)
Add a warning for this module.
Definition ApiBase.php:1372
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:498
getHookRunner()
Get an ApiHookRunner for running core API hooks.
Definition ApiBase.php:711
This is the main API class, used for both external and internal processing.
Definition ApiMain.php:52
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()
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition HTMLForm.php:150
PSR-3 logger instance factory.
Service locator for MediaWiki core services.
This is the default implementation of PreferencesFactory.
A service class to control user options.
static numParam( $num)
Definition Message.php:1145
internal since 1.36
Definition User.php:70
Service for formatting and validating API parameters.
A PreferencesFactory is a MediaWiki service that provides the definitions of preferences for a given ...
return true
Definition router.php:92