ApiOptions.php 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. <?php
  2. /**
  3. * Copyright © 2012 Szymon Świerkosz beau@adres.pl
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License along
  16. * with this program; if not, write to the Free Software Foundation, Inc.,
  17. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. * http://www.gnu.org/copyleft/gpl.html
  19. *
  20. * @file
  21. */
  22. use MediaWiki\MediaWikiServices;
  23. /**
  24. * API module that facilitates the changing of user's preferences.
  25. * Requires API write mode to be enabled.
  26. *
  27. * @ingroup API
  28. */
  29. class ApiOptions extends ApiBase {
  30. /**
  31. * Changes preferences of the current user.
  32. */
  33. public function execute() {
  34. if ( $this->getUser()->isAnon() ) {
  35. $this->dieWithError(
  36. [ 'apierror-mustbeloggedin', $this->msg( 'action-editmyoptions' ) ], 'notloggedin'
  37. );
  38. }
  39. $this->checkUserRightsAny( 'editmyoptions' );
  40. $params = $this->extractRequestParams();
  41. $changed = false;
  42. if ( isset( $params['optionvalue'] ) && !isset( $params['optionname'] ) ) {
  43. $this->dieWithError( [ 'apierror-missingparam', 'optionname' ] );
  44. }
  45. // Load the user from the master to reduce CAS errors on double post (T95839)
  46. $user = $this->getUser()->getInstanceForUpdate();
  47. if ( !$user ) {
  48. $this->dieWithError(
  49. [ 'apierror-mustbeloggedin', $this->msg( 'action-editmyoptions' ) ], 'notloggedin'
  50. );
  51. }
  52. if ( $params['reset'] ) {
  53. $user->resetOptions( $params['resetkinds'], $this->getContext() );
  54. $changed = true;
  55. }
  56. $changes = [];
  57. if ( $params['change'] ) {
  58. foreach ( $params['change'] as $entry ) {
  59. $array = explode( '=', $entry, 2 );
  60. $changes[$array[0]] = isset( $array[1] ) ? $array[1] : null;
  61. }
  62. }
  63. if ( isset( $params['optionname'] ) ) {
  64. $newValue = isset( $params['optionvalue'] ) ? $params['optionvalue'] : null;
  65. $changes[$params['optionname']] = $newValue;
  66. }
  67. if ( !$changed && !count( $changes ) ) {
  68. $this->dieWithError( 'apierror-nochanges' );
  69. }
  70. $preferencesFactory = MediaWikiServices::getInstance()->getPreferencesFactory();
  71. $prefs = $preferencesFactory->getFormDescriptor( $user, $this->getContext() );
  72. $prefsKinds = $user->getOptionKinds( $this->getContext(), $changes );
  73. $htmlForm = null;
  74. foreach ( $changes as $key => $value ) {
  75. switch ( $prefsKinds[$key] ) {
  76. case 'registered':
  77. // Regular option.
  78. if ( $htmlForm === null ) {
  79. // We need a dummy HTMLForm for the validate callback...
  80. $htmlForm = new HTMLForm( [], $this );
  81. }
  82. $field = HTMLForm::loadInputFromParameters( $key, $prefs[$key], $htmlForm );
  83. $validation = $field->validate( $value, $user->getOptions() );
  84. break;
  85. case 'registered-multiselect':
  86. case 'registered-checkmatrix':
  87. // A key for a multiselect or checkmatrix option.
  88. $validation = true;
  89. $value = $value !== null ? (bool)$value : null;
  90. break;
  91. case 'userjs':
  92. // Allow non-default preferences prefixed with 'userjs-', to be set by user scripts
  93. if ( strlen( $key ) > 255 ) {
  94. $validation = $this->msg( 'apiwarn-validationfailed-keytoolong', Message::numParam( 255 ) );
  95. } elseif ( preg_match( '/[^a-zA-Z0-9_-]/', $key ) !== 0 ) {
  96. $validation = $this->msg( 'apiwarn-validationfailed-badchars' );
  97. } else {
  98. $validation = true;
  99. }
  100. break;
  101. case 'special':
  102. $validation = $this->msg( 'apiwarn-validationfailed-cannotset' );
  103. break;
  104. case 'unused':
  105. default:
  106. $validation = $this->msg( 'apiwarn-validationfailed-badpref' );
  107. break;
  108. }
  109. if ( $validation === true ) {
  110. $user->setOption( $key, $value );
  111. $changed = true;
  112. } else {
  113. $this->addWarning( [ 'apiwarn-validationfailed', wfEscapeWikiText( $key ), $validation ] );
  114. }
  115. }
  116. if ( $changed ) {
  117. // Commit changes
  118. $user->saveSettings();
  119. }
  120. $this->getResult()->addValue( null, $this->getModuleName(), 'success' );
  121. }
  122. public function mustBePosted() {
  123. return true;
  124. }
  125. public function isWriteMode() {
  126. return true;
  127. }
  128. public function getAllowedParams() {
  129. $optionKinds = User::listOptionKinds();
  130. $optionKinds[] = 'all';
  131. return [
  132. 'reset' => false,
  133. 'resetkinds' => [
  134. ApiBase::PARAM_TYPE => $optionKinds,
  135. ApiBase::PARAM_DFLT => 'all',
  136. ApiBase::PARAM_ISMULTI => true
  137. ],
  138. 'change' => [
  139. ApiBase::PARAM_ISMULTI => true,
  140. ],
  141. 'optionname' => [
  142. ApiBase::PARAM_TYPE => 'string',
  143. ],
  144. 'optionvalue' => [
  145. ApiBase::PARAM_TYPE => 'string',
  146. ],
  147. ];
  148. }
  149. public function needsToken() {
  150. return 'csrf';
  151. }
  152. public function getHelpUrls() {
  153. return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Options';
  154. }
  155. protected function getExamplesMessages() {
  156. return [
  157. 'action=options&reset=&token=123ABC'
  158. => 'apihelp-options-example-reset',
  159. 'action=options&change=skin=vector|hideminor=1&token=123ABC'
  160. => 'apihelp-options-example-change',
  161. 'action=options&reset=&change=skin=monobook&optionname=nickname&' .
  162. 'optionvalue=[[User:Beau|Beau]]%20([[User_talk:Beau|talk]])&token=123ABC'
  163. => 'apihelp-options-example-complex',
  164. ];
  165. }
  166. }