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