MediaWiki REL1_34
ApiMove.php
Go to the documentation of this file.
1<?php
24
29class ApiMove extends ApiBase {
30
31 public function execute() {
33
34 $user = $this->getUser();
35 $params = $this->extractRequestParams();
36
37 $this->requireOnlyOneParameter( $params, 'from', 'fromid' );
38
39 if ( isset( $params['from'] ) ) {
40 $fromTitle = Title::newFromText( $params['from'] );
41 if ( !$fromTitle || $fromTitle->isExternal() ) {
42 $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $params['from'] ) ] );
43 }
44 } elseif ( isset( $params['fromid'] ) ) {
45 $fromTitle = Title::newFromID( $params['fromid'] );
46 if ( !$fromTitle ) {
47 $this->dieWithError( [ 'apierror-nosuchpageid', $params['fromid'] ] );
48 }
49 }
50
51 if ( !$fromTitle->exists() ) {
52 $this->dieWithError( 'apierror-missingtitle' );
53 }
54 $fromTalk = $fromTitle->getTalkPage();
55
56 $toTitle = Title::newFromText( $params['to'] );
57 if ( !$toTitle || $toTitle->isExternal() ) {
58 $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $params['to'] ) ] );
59 }
60 $toTalk = $toTitle->getTalkPageIfDefined();
61
62 $repoGroup = MediaWikiServices::getInstance()->getRepoGroup();
63 if ( $toTitle->getNamespace() == NS_FILE
64 && !$repoGroup->getLocalRepo()->findFile( $toTitle )
65 && $repoGroup->findFile( $toTitle )
66 ) {
67 if ( !$params['ignorewarnings'] &&
68 $this->getPermissionManager()->userHasRight( $user, 'reupload-shared' ) ) {
69 $this->dieWithError( 'apierror-fileexists-sharedrepo-perm' );
70 } elseif ( !$this->getPermissionManager()->userHasRight( $user, 'reupload-shared' ) ) {
71 $this->dieWithError( 'apierror-cantoverwrite-sharedfile' );
72 }
73 }
74
75 // Rate limit
76 if ( $user->pingLimiter( 'move' ) ) {
77 $this->dieWithError( 'apierror-ratelimited' );
78 }
79
80 // Check if the user is allowed to add the specified changetags
81 if ( $params['tags'] ) {
82 $ableToTag = ChangeTags::canAddTagsAccompanyingChange( $params['tags'], $user );
83 if ( !$ableToTag->isOK() ) {
84 $this->dieStatus( $ableToTag );
85 }
86 }
87
88 // Move the page
89 $toTitleExists = $toTitle->exists();
90 $status = $this->movePage( $fromTitle, $toTitle, $params['reason'], !$params['noredirect'],
91 $params['tags'] ?: [] );
92 if ( !$status->isOK() ) {
93 $user->spreadAnyEditBlock();
94 $this->dieStatus( $status );
95 }
96
97 $r = [
98 'from' => $fromTitle->getPrefixedText(),
99 'to' => $toTitle->getPrefixedText(),
100 'reason' => $params['reason']
101 ];
102
103 // NOTE: we assume that if the old title exists, it's because it was re-created as
104 // a redirect to the new title. This is not safe, but what we did before was
105 // even worse: we just determined whether a redirect should have been created,
106 // and reported that it was created if it should have, without any checks.
107 $r['redirectcreated'] = $fromTitle->exists();
108
109 $r['moveoverredirect'] = $toTitleExists;
110
111 // Move the talk page
112 if ( $params['movetalk'] && $toTalk && $fromTalk->exists() && !$fromTitle->isTalkPage() ) {
113 $toTalkExists = $toTalk->exists();
114 $status = $this->movePage(
115 $fromTalk,
116 $toTalk,
117 $params['reason'],
118 !$params['noredirect'],
119 $params['tags'] ?: []
120 );
121 if ( $status->isOK() ) {
122 $r['talkfrom'] = $fromTalk->getPrefixedText();
123 $r['talkto'] = $toTalk->getPrefixedText();
124 $r['talkmoveoverredirect'] = $toTalkExists;
125 } else {
126 // We're not going to dieWithError() on failure, since we already changed something
127 $r['talkmove-errors'] = $this->getErrorFormatter()->arrayFromStatus( $status );
128 }
129 }
130
131 $result = $this->getResult();
132
133 // Move subpages
134 if ( $params['movesubpages'] ) {
135 $r['subpages'] = $this->moveSubpages(
136 $fromTitle,
137 $toTitle,
138 $params['reason'],
139 $params['noredirect'],
140 $params['tags'] ?: []
141 );
142 ApiResult::setIndexedTagName( $r['subpages'], 'subpage' );
143
144 if ( $params['movetalk'] ) {
145 $r['subpages-talk'] = $this->moveSubpages(
146 $fromTalk,
147 $toTalk,
148 $params['reason'],
149 $params['noredirect'],
150 $params['tags'] ?: []
151 );
152 ApiResult::setIndexedTagName( $r['subpages-talk'], 'subpage' );
153 }
154 }
155
156 $watch = 'preferences';
157 if ( isset( $params['watchlist'] ) ) {
158 $watch = $params['watchlist'];
159 }
160
161 // Watch pages
162 $this->setWatch( $watch, $fromTitle, 'watchmoves' );
163 $this->setWatch( $watch, $toTitle, 'watchmoves' );
164
165 $result->addValue( null, $this->getModuleName(), $r );
166 }
167
176 protected function movePage( Title $from, Title $to, $reason, $createRedirect, $changeTags ) {
177 $mp = MediaWikiServices::getInstance()->getMovePageFactory()->newMovePage( $from, $to );
178 $valid = $mp->isValidMove();
179 if ( !$valid->isOK() ) {
180 return $valid;
181 }
182
183 $user = $this->getUser();
184 $permStatus = $mp->checkPermissions( $user, $reason );
185 if ( !$permStatus->isOK() ) {
186 return $permStatus;
187 }
188
189 // Check suppressredirect permission
190 if ( !$this->getPermissionManager()->userHasRight( $user, 'suppressredirect' ) ) {
191 $createRedirect = true;
192 }
193
194 return $mp->move( $user, $reason, $createRedirect, $changeTags );
195 }
196
205 public function moveSubpages( $fromTitle, $toTitle, $reason, $noredirect, $changeTags = [] ) {
206 $retval = [];
207
208 $mp = new MovePage( $fromTitle, $toTitle );
209 $result =
210 $mp->moveSubpagesIfAllowed( $this->getUser(), $reason, !$noredirect, $changeTags );
211 if ( !$result->isOK() ) {
212 // This means the whole thing failed
213 return [ 'errors' => $this->getErrorFormatter()->arrayFromStatus( $result ) ];
214 }
215
216 // At least some pages could be moved
217 // Report each of them separately
218 foreach ( $result->getValue() as $oldTitle => $status ) {
219 $r = [ 'from' => $oldTitle ];
220 if ( $status->isOK() ) {
221 $r['to'] = $status->getValue();
222 } else {
223 $r['errors'] = $this->getErrorFormatter()->arrayFromStatus( $status );
224 }
225 $retval[] = $r;
226 }
227
228 return $retval;
229 }
230
231 public function mustBePosted() {
232 return true;
233 }
234
235 public function isWriteMode() {
236 return true;
237 }
238
239 public function getAllowedParams() {
240 return [
241 'from' => null,
242 'fromid' => [
243 ApiBase::PARAM_TYPE => 'integer'
244 ],
245 'to' => [
246 ApiBase::PARAM_TYPE => 'string',
248 ],
249 'reason' => '',
250 'movetalk' => false,
251 'movesubpages' => false,
252 'noredirect' => false,
253 'watchlist' => [
254 ApiBase::PARAM_DFLT => 'preferences',
256 'watch',
257 'unwatch',
258 'preferences',
259 'nochange'
260 ],
261 ],
262 'ignorewarnings' => false,
263 'tags' => [
264 ApiBase::PARAM_TYPE => 'tags',
266 ],
267 ];
268 }
269
270 public function needsToken() {
271 return 'csrf';
272 }
273
274 protected function getExamplesMessages() {
275 return [
276 'action=move&from=Badtitle&to=Goodtitle&token=123ABC&' .
277 'reason=Misspelled%20title&movetalk=&noredirect='
278 => 'apihelp-move-example-move',
279 ];
280 }
281
282 public function getHelpUrls() {
283 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Move';
284 }
285}
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:42
const PARAM_REQUIRED
(boolean) Is the parameter required?
Definition ApiBase.php:118
dieWithError( $msg, $code=null, $data=null, $httpCode=null)
Abort execution with an error.
Definition ApiBase.php:2014
const PARAM_TYPE
(string|string[]) Either an array of allowed value strings, or a string type as described below.
Definition ApiBase.php:94
setWatch( $watch, $titleObj, $userOption=null)
Set a watch (or unwatch) based the based on a watchlist parameter.
Definition ApiBase.php:1750
getErrorFormatter()
Get the error formatter.
Definition ApiBase.php:654
const PARAM_DFLT
(null|boolean|integer|string) Default value of the parameter.
Definition ApiBase.php:55
getPermissionManager()
Obtain a PermissionManager instance that subclasses may use in their authorization checks.
Definition ApiBase.php:710
getResult()
Get the result object.
Definition ApiBase.php:640
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:761
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:520
dieStatus(StatusValue $status)
Throw an ApiUsageException based on the Status object.
Definition ApiBase.php:2086
useTransactionalTimeLimit()
Call wfTransactionalTimeLimit() if this request was POSTed.
Definition ApiBase.php:1871
requireOnlyOneParameter( $params, $required)
Die if none or more than one of a certain set of parameters is set and not false.
Definition ApiBase.php:893
const PARAM_ISMULTI
(boolean) Accept multiple pipe-separated values for this parameter (e.g.
Definition ApiBase.php:58
API Module to move pages.
Definition ApiMove.php:29
needsToken()
Returns the token type this module requires in order to execute.
Definition ApiMove.php:270
isWriteMode()
Indicates whether this module requires write mode.
Definition ApiMove.php:235
moveSubpages( $fromTitle, $toTitle, $reason, $noredirect, $changeTags=[])
Definition ApiMove.php:205
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
Definition ApiMove.php:31
mustBePosted()
Indicates whether this module must be called with a POST request.
Definition ApiMove.php:231
getHelpUrls()
Return links to more detailed help pages about the module.
Definition ApiMove.php:282
movePage(Title $from, Title $to, $reason, $createRedirect, $changeTags)
Definition ApiMove.php:176
getExamplesMessages()
Returns usage examples for this module.
Definition ApiMove.php:274
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
Definition ApiMove.php:239
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
static canAddTagsAccompanyingChange(array $tags, User $user=null)
Is it OK to allow the user to apply all the specified tags at the same time as they edit/make the cha...
MediaWikiServices is the service locator for the application scope of MediaWiki.
Handles the backend logic of moving a page from one title to another.
Definition MovePage.php:36
Represents a title within MediaWiki.
Definition Title.php:42
const NS_FILE
Definition Defines.php:75
return true
Definition router.php:94