MediaWiki  1.34.0
ApiMove.php
Go to the documentation of this file.
1 <?php
24 
29 class 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',
265  ApiBase::PARAM_ISMULTI => true,
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 }
Title\newFromText
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:316
ApiBase\PARAM_REQUIRED
const PARAM_REQUIRED
(boolean) Is the parameter required?
Definition: ApiBase.php:118
ApiMove\needsToken
needsToken()
Returns the token type this module requires in order to execute.
Definition: ApiMove.php:270
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:117
ApiMove\moveSubpages
moveSubpages( $fromTitle, $toTitle, $reason, $noredirect, $changeTags=[])
Definition: ApiMove.php:205
ApiBase\dieWithError
dieWithError( $msg, $code=null, $data=null, $httpCode=null)
Abort execution with an error.
Definition: ApiBase.php:2014
true
return true
Definition: router.php:92
ApiBase\PARAM_TYPE
const PARAM_TYPE
(string|string[]) Either an array of allowed value strings, or a string type as described below.
Definition: ApiBase.php:94
ApiBase\getResult
getResult()
Get the result object.
Definition: ApiBase.php:640
NS_FILE
const NS_FILE
Definition: Defines.php:66
ContextSource\getUser
getUser()
Definition: ContextSource.php:120
ApiBase
This abstract class implements many basic API functions, and is the base of all API classes.
Definition: ApiBase.php:42
ApiMove\movePage
movePage(Title $from, Title $to, $reason, $createRedirect, $changeTags)
Definition: ApiMove.php:176
ApiMove\getAllowedParams
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
Definition: ApiMove.php:239
MovePage
Handles the backend logic of moving a page from one title to another.
Definition: MovePage.php:36
ApiMove\isWriteMode
isWriteMode()
Indicates whether this module requires write mode.
Definition: ApiMove.php:235
ApiMove\getExamplesMessages
getExamplesMessages()
Returns usage examples for this module.
Definition: ApiMove.php:274
ApiBase\extractRequestParams
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition: ApiBase.php:761
ApiMove
API Module to move pages.
Definition: ApiMove.php:29
ApiBase\setWatch
setWatch( $watch, $titleObj, $userOption=null)
Set a watch (or unwatch) based the based on a watchlist parameter.
Definition: ApiBase.php:1750
ApiResult\setIndexedTagName
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
Definition: ApiResult.php:616
ApiBase\getPermissionManager
getPermissionManager()
Obtain a PermissionManager instance that subclasses may use in their authorization checks.
Definition: ApiBase.php:710
ApiBase\useTransactionalTimeLimit
useTransactionalTimeLimit()
Call wfTransactionalTimeLimit() if this request was POSTed.
Definition: ApiBase.php:1871
wfEscapeWikiText
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
Definition: GlobalFunctions.php:1551
ApiBase\requireOnlyOneParameter
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
ApiMove\getHelpUrls
getHelpUrls()
Return links to more detailed help pages about the module.
Definition: ApiMove.php:282
ChangeTags\canAddTagsAccompanyingChange
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...
Definition: ChangeTags.php:521
Title
Represents a title within MediaWiki.
Definition: Title.php:42
$status
return $status
Definition: SyntaxHighlight.php:347
ApiMove\execute
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
Definition: ApiMove.php:31
ApiBase\PARAM_DFLT
const PARAM_DFLT
(null|boolean|integer|string) Default value of the parameter.
Definition: ApiBase.php:55
ApiBase\dieStatus
dieStatus(StatusValue $status)
Throw an ApiUsageException based on the Status object.
Definition: ApiBase.php:2086
ApiBase\getModuleName
getModuleName()
Get the name of the module being executed by this instance.
Definition: ApiBase.php:520
ApiBase\PARAM_ISMULTI
const PARAM_ISMULTI
(boolean) Accept multiple pipe-separated values for this parameter (e.g.
Definition: ApiBase.php:58
Title\newFromID
static newFromID( $id, $flags=0)
Create a new Title from an article ID.
Definition: Title.php:467
ApiMove\mustBePosted
mustBePosted()
Indicates whether this module must be called with a POST request.
Definition: ApiMove.php:231
ApiBase\getErrorFormatter
getErrorFormatter()
Get the error formatter.
Definition: ApiBase.php:654