MediaWiki  master
ApiProtect.php
Go to the documentation of this file.
1 <?php
29 
33 class ApiProtect extends ApiBase {
34 
36 
37  private RestrictionStore $restrictionStore;
38 
46  public function __construct(
47  ApiMain $mainModule,
48  $moduleName,
49  WatchlistManager $watchlistManager,
50  UserOptionsLookup $userOptionsLookup,
51  RestrictionStore $restrictionStore
52  ) {
53  parent::__construct( $mainModule, $moduleName );
54  $this->restrictionStore = $restrictionStore;
55 
56  // Variables needed in ApiWatchlistTrait trait
57  $this->watchlistExpiryEnabled = $this->getConfig()->get( MainConfigNames::WatchlistExpiry );
58  $this->watchlistMaxDuration =
59  $this->getConfig()->get( MainConfigNames::WatchlistExpiryMaxDuration );
60  $this->watchlistManager = $watchlistManager;
61  $this->userOptionsLookup = $userOptionsLookup;
62  }
63 
64  public function execute() {
65  $params = $this->extractRequestParams();
66 
67  $pageObj = $this->getTitleOrPageId( $params, 'fromdbmaster' );
68  $titleObj = $pageObj->getTitle();
69  $this->getErrorFormatter()->setContextTitle( $titleObj );
70 
71  $this->checkTitleUserPermissions( $titleObj, 'protect' );
72 
73  $user = $this->getUser();
74  $tags = $params['tags'];
75 
76  // Check if user can add tags
77  if ( $tags !== null ) {
78  $ableToTag = ChangeTags::canAddTagsAccompanyingChange( $tags, $this->getAuthority() );
79  if ( !$ableToTag->isOK() ) {
80  $this->dieStatus( $ableToTag );
81  }
82  }
83 
84  $expiry = (array)$params['expiry'];
85  if ( count( $expiry ) != count( $params['protections'] ) ) {
86  if ( count( $expiry ) == 1 ) {
87  $expiry = array_fill( 0, count( $params['protections'] ), $expiry[0] );
88  } else {
89  $this->dieWithError( [
90  'apierror-toofewexpiries',
91  count( $expiry ),
92  count( $params['protections'] )
93  ] );
94  }
95  }
96 
97  $restrictionTypes = $this->restrictionStore->listApplicableRestrictionTypes( $titleObj );
98  $levels = $this->getPermissionManager()->getNamespaceRestrictionLevels(
99  $titleObj->getNamespace(),
100  $user
101  );
102 
103  $protections = [];
104  $expiryarray = [];
105  $resultProtections = [];
106  foreach ( $params['protections'] as $i => $prot ) {
107  $p = explode( '=', $prot );
108  $protections[$p[0]] = ( $p[1] == 'all' ? '' : $p[1] );
109 
110  if ( $titleObj->exists() && $p[0] == 'create' ) {
111  $this->dieWithError( 'apierror-create-titleexists' );
112  }
113  if ( !$titleObj->exists() && $p[0] != 'create' ) {
114  $this->dieWithError( 'apierror-missingtitle-createonly' );
115  }
116 
117  if ( !in_array( $p[0], $restrictionTypes ) && $p[0] != 'create' ) {
118  $this->dieWithError( [ 'apierror-protect-invalidaction', wfEscapeWikiText( $p[0] ) ] );
119  }
120  if ( !in_array( $p[1], $levels ) && $p[1] != 'all' ) {
121  $this->dieWithError( [ 'apierror-protect-invalidlevel', wfEscapeWikiText( $p[1] ) ] );
122  }
123 
124  if ( wfIsInfinity( $expiry[$i] ) ) {
125  $expiryarray[$p[0]] = 'infinity';
126  } else {
127  $exp = strtotime( $expiry[$i] );
128  if ( $exp < 0 || !$exp ) {
129  $this->dieWithError( [ 'apierror-invalidexpiry', wfEscapeWikiText( $expiry[$i] ) ] );
130  }
131 
132  $exp = wfTimestamp( TS_MW, $exp );
133  if ( $exp < wfTimestampNow() ) {
134  $this->dieWithError( [ 'apierror-pastexpiry', wfEscapeWikiText( $expiry[$i] ) ] );
135  }
136  $expiryarray[$p[0]] = $exp;
137  }
138  $resultProtections[] = [
139  $p[0] => $protections[$p[0]],
140  'expiry' => ApiResult::formatExpiry( $expiryarray[$p[0]], 'infinite' ),
141  ];
142  }
143 
144  $cascade = $params['cascade'];
145 
146  $watch = $params['watch'] ? 'watch' : $params['watchlist'];
147  $watchlistExpiry = $this->getExpiryFromParams( $params );
148  $this->setWatch( $watch, $titleObj, $user, 'watchdefault', $watchlistExpiry );
149 
150  $status = $pageObj->doUpdateRestrictions(
151  $protections,
152  $expiryarray,
153  $cascade,
154  $params['reason'],
155  $user,
156  $tags ?? []
157  );
158 
159  if ( !$status->isOK() ) {
160  $this->dieStatus( $status );
161  }
162  $res = [
163  'title' => $titleObj->getPrefixedText(),
164  'reason' => $params['reason']
165  ];
166  if ( $cascade ) {
167  $res['cascade'] = true;
168  }
169  $res['protections'] = $resultProtections;
170  $result = $this->getResult();
171  ApiResult::setIndexedTagName( $res['protections'], 'protection' );
172  $result->addValue( null, $this->getModuleName(), $res );
173  }
174 
175  public function mustBePosted() {
176  return true;
177  }
178 
179  public function isWriteMode() {
180  return true;
181  }
182 
183  public function getAllowedParams() {
184  return [
185  'title' => [
186  ParamValidator::PARAM_TYPE => 'string',
187  ],
188  'pageid' => [
189  ParamValidator::PARAM_TYPE => 'integer',
190  ],
191  'protections' => [
192  ParamValidator::PARAM_ISMULTI => true,
193  ParamValidator::PARAM_REQUIRED => true,
194  ],
195  'expiry' => [
196  ParamValidator::PARAM_ISMULTI => true,
197  ParamValidator::PARAM_ALLOW_DUPLICATES => true,
198  ParamValidator::PARAM_DEFAULT => 'infinite',
199  ],
200  'reason' => '',
201  'tags' => [
202  ParamValidator::PARAM_TYPE => 'tags',
203  ParamValidator::PARAM_ISMULTI => true,
204  ],
205  'cascade' => false,
206  'watch' => [
207  ParamValidator::PARAM_DEFAULT => false,
208  ParamValidator::PARAM_DEPRECATED => true,
209  ],
210  ] + $this->getWatchlistParams();
211  }
212 
213  public function needsToken() {
214  return 'csrf';
215  }
216 
217  protected function getExamplesMessages() {
218  $title = Title::newMainPage()->getPrefixedText();
219  $mp = rawurlencode( $title );
220 
221  return [
222  "action=protect&title={$mp}&token=123ABC&" .
223  'protections=edit=sysop|move=sysop&cascade=&expiry=20070901163000|never'
224  => 'apihelp-protect-example-protect',
225  "action=protect&title={$mp}&token=123ABC&" .
226  'protections=edit=all|move=all&reason=Lifting%20restrictions'
227  => 'apihelp-protect-example-unprotect',
228  "action=protect&title={$mp}&token=123ABC&" .
229  'protections=&reason=Lifting%20restrictions'
230  => 'apihelp-protect-example-unprotect2',
231  ];
232  }
233 
234  public function getHelpUrls() {
235  return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Protect';
236  }
237 }
getExpiryFromParams(array $params)
Get formatted expiry from the given parameters, or null if no expiry was provided.
setWatch(string $watch, Title $title, User $user, ?string $userOption=null, ?string $expiry=null)
Set a watch (or unwatch) based the based on a watchlist parameter.
getWatchlistParams(array $watchOptions=[])
Get additional allow params specific to watchlisting.
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
wfIsInfinity( $str)
Determine input string is represents as infinity.
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
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:63
dieWithError( $msg, $code=null, $data=null, $httpCode=0)
Abort execution with an error.
Definition: ApiBase.php:1516
getErrorFormatter()
Definition: ApiBase.php:679
getPermissionManager()
Obtain a PermissionManager instance that subclasses may use in their authorization checks.
Definition: ApiBase.php:728
getResult()
Get the result object.
Definition: ApiBase.php:668
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition: ApiBase.php:808
checkTitleUserPermissions( $pageIdentity, $actions, array $options=[])
Helper function for permission-denied errors.
Definition: ApiBase.php:1660
getModuleName()
Get the name of the module being executed by this instance.
Definition: ApiBase.php:529
getTitleOrPageId( $params, $load=false)
Attempts to load a WikiPage object from a title or pageid parameter, if possible.
Definition: ApiBase.php:1081
dieStatus(StatusValue $status)
Throw an ApiUsageException based on the Status object.
Definition: ApiBase.php:1571
This is the main API class, used for both external and internal processing.
Definition: ApiMain.php:64
isWriteMode()
Indicates whether this module requires write mode.
Definition: ApiProtect.php:179
getExamplesMessages()
Returns usage examples for this module.
Definition: ApiProtect.php:217
needsToken()
Returns the token type this module requires in order to execute.
Definition: ApiProtect.php:213
__construct(ApiMain $mainModule, $moduleName, WatchlistManager $watchlistManager, UserOptionsLookup $userOptionsLookup, RestrictionStore $restrictionStore)
Definition: ApiProtect.php:46
mustBePosted()
Indicates whether this module must be called with a POST request.
Definition: ApiProtect.php:175
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
Definition: ApiProtect.php:183
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
Definition: ApiProtect.php:64
getHelpUrls()
Return links to more detailed help pages about the module.
Definition: ApiProtect.php:234
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
Definition: ApiResult.php:604
static formatExpiry( $expiry, $infinity='infinity')
Format an expiry timestamp for API output.
Definition: ApiResult.php:1199
static canAddTagsAccompanyingChange(array $tags, Authority $performer=null, $checkBlock=true)
Is it OK to allow the user to apply all the specified tags at the same time as they edit/make the cha...
Definition: ChangeTags.php:396
A class containing constants representing the names of configuration variables.
Represents a title within MediaWiki.
Definition: Title.php:76
Provides access to user options.
Service for formatting and validating API parameters.
trait ApiWatchlistTrait
An ApiWatchlistTrait adds class properties and convenience methods for APIs that allow you to watch a...