MediaWiki master
ApiProtect.php
Go to the documentation of this file.
1<?php
9namespace MediaWiki\Api;
10
11use InvalidArgumentException;
22use Wikimedia\Timestamp\TimestampFormat as TS;
23
27class ApiProtect extends ApiBase {
28
30
31 public function __construct(
32 ApiMain $mainModule,
33 string $moduleName,
34 WatchlistManager $watchlistManager,
35 WatchedItemStoreInterface $watchedItemStore,
36 UserOptionsLookup $userOptionsLookup,
37 private readonly RestrictionStore $restrictionStore,
38 ) {
39 parent::__construct( $mainModule, $moduleName );
40
41 // Variables needed in ApiWatchlistTrait trait
42 $this->watchlistExpiryEnabled = $this->getConfig()->get( MainConfigNames::WatchlistExpiry );
43 $this->watchlistMaxDuration =
45 $this->watchlistManager = $watchlistManager;
46 $this->watchedItemStore = $watchedItemStore;
47 $this->userOptionsLookup = $userOptionsLookup;
48 }
49
50 public function execute() {
51 $params = $this->extractRequestParams();
52
53 $pageObj = $this->getTitleOrPageId( $params, 'fromdbmaster' );
54 $titleObj = $pageObj->getTitle();
55 $this->getErrorFormatter()->setContextTitle( $titleObj );
56
57 $this->checkTitleUserPermissions( $titleObj, 'protect' );
58
59 $user = $this->getUser();
60 $tags = $params['tags'];
61
62 // Check if user can add tags
63 if ( $tags !== null ) {
64 $ableToTag = ChangeTags::canAddTagsAccompanyingChange( $tags, $this->getAuthority() );
65 if ( !$ableToTag->isOK() ) {
66 $this->dieStatus( $ableToTag );
67 }
68 }
69
70 $expiry = (array)$params['expiry'];
71 if ( count( $expiry ) != count( $params['protections'] ) ) {
72 if ( count( $expiry ) == 1 ) {
73 $expiry = array_fill( 0, count( $params['protections'] ), $expiry[0] );
74 } else {
75 $this->dieWithError( [
76 'apierror-toofewexpiries',
77 count( $expiry ),
78 count( $params['protections'] )
79 ] );
80 }
81 }
82
83 $restrictionTypes = $this->restrictionStore->listApplicableRestrictionTypes( $titleObj );
84 $levels = $this->getPermissionManager()->getNamespaceRestrictionLevels(
85 $titleObj->getNamespace(),
86 $user
87 );
88
89 $protections = [];
90 $expiries = [];
91 $resultProtections = [];
92 foreach ( $params['protections'] as $i => $prot ) {
93 $p = explode( '=', $prot );
94 $protections[$p[0]] = ( $p[1] == 'all' ? '' : $p[1] );
95
96 if ( $titleObj->exists() && $p[0] == 'create' ) {
97 $this->dieWithError( 'apierror-create-titleexists' );
98 }
99 if ( !$titleObj->exists() && $p[0] != 'create' ) {
100 $this->dieWithError( 'apierror-missingtitle-createonly' );
101 }
102
103 if ( !in_array( $p[0], $restrictionTypes ) && $p[0] != 'create' ) {
104 $this->dieWithError( [ 'apierror-protect-invalidaction', wfEscapeWikiText( $p[0] ) ] );
105 }
106 if ( !in_array( $p[1], $levels ) && $p[1] != 'all' ) {
107 $this->dieWithError( [ 'apierror-protect-invalidlevel', wfEscapeWikiText( $p[1] ) ] );
108 }
109
110 try {
111 $expiries[$p[0]] = ExpiryDef::normalizeExpiry( $expiry[$i], TS::MW );
112 } catch ( InvalidArgumentException ) {
113 $this->dieWithError( [ 'apierror-invalidexpiry', wfEscapeWikiText( $expiry[$i] ) ] );
114 }
115 if ( $expiries[$p[0]] < MWTimestamp::now( TS::MW ) ) {
116 $this->dieWithError( [ 'apierror-pastexpiry', wfEscapeWikiText( $expiry[$i] ) ] );
117 }
118
119 $resultProtections[] = [
120 $p[0] => $protections[$p[0]],
121 'expiry' => ApiResult::formatExpiry( $expiries[$p[0]], 'infinite' ),
122 ];
123 }
124
125 $cascade = $params['cascade'];
126
127 $watch = $params['watch'] ? 'watch' : $params['watchlist'];
128 $watchlistExpiry = $this->getExpiryFromParams( $params, $titleObj, $user );
129 $this->setWatch( $watch, $titleObj, $user, 'watchdefault', $watchlistExpiry );
130
131 $status = $pageObj->doUpdateRestrictions(
132 $protections,
133 $expiries,
134 $cascade,
135 $params['reason'],
136 $user,
137 $tags ?? []
138 );
139
140 if ( !$status->isOK() ) {
141 $this->dieStatus( $status );
142 }
143 $res = [
144 'title' => $titleObj->getPrefixedText(),
145 'reason' => $params['reason']
146 ];
147 if ( $cascade ) {
148 $res['cascade'] = true;
149 }
150 $res['protections'] = $resultProtections;
151 $result = $this->getResult();
152 ApiResult::setIndexedTagName( $res['protections'], 'protection' );
153 $result->addValue( null, $this->getModuleName(), $res );
154 }
155
157 public function mustBePosted() {
158 return true;
159 }
160
162 public function isWriteMode() {
163 return true;
164 }
165
167 public function getAllowedParams() {
168 return [
169 'title' => [
170 ParamValidator::PARAM_TYPE => 'string',
171 ],
172 'pageid' => [
173 ParamValidator::PARAM_TYPE => 'integer',
174 ],
175 'protections' => [
176 ParamValidator::PARAM_ISMULTI => true,
177 ParamValidator::PARAM_REQUIRED => true,
178 ],
179 'expiry' => [
180 ParamValidator::PARAM_ISMULTI => true,
181 ParamValidator::PARAM_ALLOW_DUPLICATES => true,
182 ParamValidator::PARAM_DEFAULT => 'infinite',
183 ],
184 'reason' => '',
185 'tags' => [
186 ParamValidator::PARAM_TYPE => 'tags',
187 ParamValidator::PARAM_ISMULTI => true,
188 ],
189 'cascade' => false,
190 'watch' => [
191 ParamValidator::PARAM_DEFAULT => false,
192 ParamValidator::PARAM_DEPRECATED => true,
193 ],
194 ] + $this->getWatchlistParams();
195 }
196
198 public function needsToken() {
199 return 'csrf';
200 }
201
203 protected function getExamplesMessages() {
204 $title = Title::newMainPage()->getPrefixedText();
205 $mp = rawurlencode( $title );
206
207 return [
208 "action=protect&title={$mp}&token=123ABC&" .
209 'protections=edit=sysop|move=sysop&cascade=&expiry=20070901163000|never'
210 => 'apihelp-protect-example-protect',
211 "action=protect&title={$mp}&token=123ABC&" .
212 'protections=edit=all|move=all&reason=Lifting%20restrictions'
213 => 'apihelp-protect-example-unprotect',
214 "action=protect&title={$mp}&token=123ABC&" .
215 'protections=&reason=Lifting%20restrictions'
216 => 'apihelp-protect-example-unprotect2',
217 ];
218 }
219
221 public function getHelpUrls() {
222 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Protect';
223 }
224}
225
227class_alias( ApiProtect::class, 'ApiProtect' );
wfEscapeWikiText( $input)
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:60
dieWithError( $msg, $code=null, $data=null, $httpCode=0)
Abort execution with an error.
Definition ApiBase.php:1506
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:542
getResult()
Get the result object.
Definition ApiBase.php:681
dieStatus(StatusValue $status)
Throw an ApiUsageException based on the Status object.
Definition ApiBase.php:1557
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:822
getPermissionManager()
Obtain a PermissionManager instance that subclasses may use in their authorization checks.
Definition ApiBase.php:741
checkTitleUserPermissions(PageIdentity $pageIdentity, $actions, array $options=[])
Helper function for permission-denied errors.
Definition ApiBase.php:1638
getTitleOrPageId( $params, $load=false)
Attempts to load a WikiPage object from a title or pageid parameter, if possible.
Definition ApiBase.php:1146
This is the main API class, used for both external and internal processing.
Definition ApiMain.php:67
needsToken()
Returns the token type this module requires in order to execute.Modules are strongly encouraged to us...
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
isWriteMode()
Indicates whether this module requires write access to the wiki.API modules must override this method...
__construct(ApiMain $mainModule, string $moduleName, WatchlistManager $watchlistManager, WatchedItemStoreInterface $watchedItemStore, UserOptionsLookup $userOptionsLookup, private readonly RestrictionStore $restrictionStore,)
getHelpUrls()
Return links to more detailed help pages about the module.1.25, returning boolean false is deprecated...
getExamplesMessages()
Returns usage examples for this module.Return value has query strings as keys, with values being eith...
mustBePosted()
Indicates whether this module must be called with a POST request.Implementations of this method must ...
static formatExpiry( $expiry, $infinity='infinity')
Format an expiry timestamp for API output.
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
Recent changes tagging.
A class containing constants representing the names of configuration variables.
const WatchlistExpiry
Name constant for the WatchlistExpiry setting, for use with Config::get()
const WatchlistExpiryMaxDuration
Name constant for the WatchlistExpiryMaxDuration setting, for use with Config::get()
Represents a title within MediaWiki.
Definition Title.php:69
Provides access to user options.
Library for creating and parsing MW-style timestamps.
Service for formatting and validating API parameters.
Type definition for expiry timestamps.
Definition ExpiryDef.php:18
trait ApiWatchlistTrait
An ApiWatchlistTrait adds class properties and convenience methods for APIs that allow you to watch a...
setWatch(string $watch, PageIdentity $page, 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.
getExpiryFromParams(array $params, ?PageIdentity $page=null, ?UserIdentity $user=null, string $userOption='watchdefault-expiry')
Get formatted expiry from the given parameters.