MediaWiki  master
ApiMove.php
Go to the documentation of this file.
1 <?php
25 
30 class ApiMove extends ApiBase {
31 
33 
36 
37  public function __construct(
38  ApiMain $mainModule,
39  $moduleName,
41  ) {
42  parent::__construct( $mainModule, $moduleName );
43 
44  $this->movePageFactory = $movePageFactory;
45 
46  $this->watchlistExpiryEnabled = $this->getConfig()->get( 'WatchlistExpiry' );
47  $this->watchlistMaxDuration = $this->getConfig()->get( 'WatchlistExpiryMaxDuration' );
48  }
49 
50  public function execute() {
52 
53  $user = $this->getUser();
54  $params = $this->extractRequestParams();
55 
56  $this->requireOnlyOneParameter( $params, 'from', 'fromid' );
57 
58  if ( isset( $params['from'] ) ) {
59  $fromTitle = Title::newFromText( $params['from'] );
60  if ( !$fromTitle || $fromTitle->isExternal() ) {
61  $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $params['from'] ) ] );
62  }
63  } elseif ( isset( $params['fromid'] ) ) {
64  $fromTitle = Title::newFromID( $params['fromid'] );
65  if ( !$fromTitle ) {
66  $this->dieWithError( [ 'apierror-nosuchpageid', $params['fromid'] ] );
67  }
68  }
69 
70  if ( !$fromTitle->exists() ) {
71  $this->dieWithError( 'apierror-missingtitle' );
72  }
73  $fromTalk = $fromTitle->getTalkPage();
74 
75  $toTitle = Title::newFromText( $params['to'] );
76  if ( !$toTitle || $toTitle->isExternal() ) {
77  $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $params['to'] ) ] );
78  }
79  $toTalk = $toTitle->getTalkPageIfDefined();
80 
81  $repoGroup = MediaWikiServices::getInstance()->getRepoGroup();
82  if ( $toTitle->getNamespace() === NS_FILE
83  && !$repoGroup->getLocalRepo()->findFile( $toTitle )
84  && $repoGroup->findFile( $toTitle )
85  ) {
86  if ( !$params['ignorewarnings'] &&
87  $this->getPermissionManager()->userHasRight( $user, 'reupload-shared' ) ) {
88  $this->dieWithError( 'apierror-fileexists-sharedrepo-perm' );
89  } elseif ( !$this->getPermissionManager()->userHasRight( $user, 'reupload-shared' ) ) {
90  $this->dieWithError( 'apierror-cantoverwrite-sharedfile' );
91  }
92  }
93 
94  // Rate limit
95  if ( $user->pingLimiter( 'move' ) ) {
96  $this->dieWithError( 'apierror-ratelimited' );
97  }
98 
99  // Check if the user is allowed to add the specified changetags
100  if ( $params['tags'] ) {
101  $ableToTag = ChangeTags::canAddTagsAccompanyingChange( $params['tags'], $user );
102  if ( !$ableToTag->isOK() ) {
103  $this->dieStatus( $ableToTag );
104  }
105  }
106 
107  // Move the page
108  $toTitleExists = $toTitle->exists();
109  $status = $this->movePage( $fromTitle, $toTitle, $params['reason'], !$params['noredirect'],
110  $params['tags'] ?: [] );
111  if ( !$status->isOK() ) {
112  $user->spreadAnyEditBlock();
113  $this->dieStatus( $status );
114  }
115 
116  $r = [
117  'from' => $fromTitle->getPrefixedText(),
118  'to' => $toTitle->getPrefixedText(),
119  'reason' => $params['reason']
120  ];
121 
122  // NOTE: we assume that if the old title exists, it's because it was re-created as
123  // a redirect to the new title. This is not safe, but what we did before was
124  // even worse: we just determined whether a redirect should have been created,
125  // and reported that it was created if it should have, without any checks.
126  $r['redirectcreated'] = $fromTitle->exists();
127 
128  $r['moveoverredirect'] = $toTitleExists;
129 
130  // Move the talk page
131  if ( $params['movetalk'] && $toTalk && $fromTalk->exists() && !$fromTitle->isTalkPage() ) {
132  $toTalkExists = $toTalk->exists();
133  $status = $this->movePage(
134  $fromTalk,
135  $toTalk,
136  $params['reason'],
137  !$params['noredirect'],
138  $params['tags'] ?: []
139  );
140  if ( $status->isOK() ) {
141  $r['talkfrom'] = $fromTalk->getPrefixedText();
142  $r['talkto'] = $toTalk->getPrefixedText();
143  $r['talkmoveoverredirect'] = $toTalkExists;
144  } else {
145  // We're not going to dieWithError() on failure, since we already changed something
146  $r['talkmove-errors'] = $this->getErrorFormatter()->arrayFromStatus( $status );
147  }
148  }
149 
150  $result = $this->getResult();
151 
152  // Move subpages
153  if ( $params['movesubpages'] ) {
154  $r['subpages'] = $this->moveSubpages(
155  $fromTitle,
156  $toTitle,
157  $params['reason'],
158  $params['noredirect'],
159  $params['tags'] ?: []
160  );
161  ApiResult::setIndexedTagName( $r['subpages'], 'subpage' );
162 
163  if ( $params['movetalk'] ) {
164  $r['subpages-talk'] = $this->moveSubpages(
165  $fromTalk,
166  $toTalk,
167  $params['reason'],
168  $params['noredirect'],
169  $params['tags'] ?: []
170  );
171  ApiResult::setIndexedTagName( $r['subpages-talk'], 'subpage' );
172  }
173  }
174 
175  $watch = 'preferences';
176  if ( isset( $params['watchlist'] ) ) {
177  $watch = $params['watchlist'];
178  }
179  $watchlistExpiry = $this->getExpiryFromParams( $params );
180 
181  // Watch pages
182  $this->setWatch( $watch, $fromTitle, $user, 'watchmoves', $watchlistExpiry );
183  $this->setWatch( $watch, $toTitle, $user, 'watchmoves', $watchlistExpiry );
184 
185  $result->addValue( null, $this->getModuleName(), $r );
186  }
187 
196  protected function movePage( Title $from, Title $to, $reason, $createRedirect, $changeTags ) {
197  $mp = $this->movePageFactory->newMovePage( $from, $to );
198  $valid = $mp->isValidMove();
199  if ( !$valid->isOK() ) {
200  return $valid;
201  }
202 
203  $user = $this->getUser();
204  $permStatus = $mp->checkPermissions( $user, $reason );
205  if ( !$permStatus->isOK() ) {
206  return $permStatus;
207  }
208 
209  // Check suppressredirect permission
210  if ( !$this->getPermissionManager()->userHasRight( $user, 'suppressredirect' ) ) {
211  $createRedirect = true;
212  }
213 
214  return $mp->move( $user, $reason, $createRedirect, $changeTags );
215  }
216 
225  public function moveSubpages( $fromTitle, $toTitle, $reason, $noredirect, $changeTags = [] ) {
226  $retval = [];
227 
228  $mp = $this->movePageFactory->newMovePage( $fromTitle, $toTitle );
229  $result =
230  $mp->moveSubpagesIfAllowed( $this->getUser(), $reason, !$noredirect, $changeTags );
231  if ( !$result->isOK() ) {
232  // This means the whole thing failed
233  return [ 'errors' => $this->getErrorFormatter()->arrayFromStatus( $result ) ];
234  }
235 
236  // At least some pages could be moved
237  // Report each of them separately
238  foreach ( $result->getValue() as $oldTitle => $status ) {
240  $r = [ 'from' => $oldTitle ];
241  if ( $status->isOK() ) {
242  $r['to'] = $status->getValue();
243  } else {
244  $r['errors'] = $this->getErrorFormatter()->arrayFromStatus( $status );
245  }
246  $retval[] = $r;
247  }
248 
249  return $retval;
250  }
251 
252  public function mustBePosted() {
253  return true;
254  }
255 
256  public function isWriteMode() {
257  return true;
258  }
259 
260  public function getAllowedParams() {
261  $params = [
262  'from' => null,
263  'fromid' => [
264  ApiBase::PARAM_TYPE => 'integer'
265  ],
266  'to' => [
267  ApiBase::PARAM_TYPE => 'string',
269  ],
270  'reason' => '',
271  'movetalk' => false,
272  'movesubpages' => false,
273  'noredirect' => false,
274  ];
275 
276  // Params appear in the docs in the order they are defined,
277  // which is why this is here and not at the bottom.
278  $params += $this->getWatchlistParams();
279 
280  return $params + [
281  'ignorewarnings' => false,
282  'tags' => [
283  ApiBase::PARAM_TYPE => 'tags',
284  ApiBase::PARAM_ISMULTI => true,
285  ],
286  ];
287  }
288 
289  public function needsToken() {
290  return 'csrf';
291  }
292 
293  protected function getExamplesMessages() {
294  return [
295  'action=move&from=Badtitle&to=Goodtitle&token=123ABC&' .
296  'reason=Misspelled%20title&movetalk=&noredirect='
297  => 'apihelp-move-example-move',
298  ];
299  }
300 
301  public function getHelpUrls() {
302  return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Move';
303  }
304 }
ApiMain
This is the main API class, used for both external and internal processing.
Definition: ApiMain.php:48
ContextSource\getConfig
getConfig()
Definition: ContextSource.php:70
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:361
getExpiryFromParams
getExpiryFromParams(array $params)
Get formatted expiry from the given parameters, or null if no expiry was provided.
Definition: ApiWatchlistTrait.php:139
ApiBase\PARAM_REQUIRED
const PARAM_REQUIRED
Definition: ApiBase.php:75
ApiMove\needsToken
needsToken()
Returns the token type this module requires in order to execute.
Definition: ApiMove.php:289
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:166
ApiMove\$movePageFactory
MovePageFactory $movePageFactory
Definition: ApiMove.php:35
ApiMove\moveSubpages
moveSubpages( $fromTitle, $toTitle, $reason, $noredirect, $changeTags=[])
Definition: ApiMove.php:225
ApiBase\dieWithError
dieWithError( $msg, $code=null, $data=null, $httpCode=null)
Abort execution with an error.
Definition: ApiBase.php:1370
true
return true
Definition: router.php:90
ApiBase\PARAM_TYPE
const PARAM_TYPE
Definition: ApiBase.php:69
ApiBase\getResult
getResult()
Get the result object.
Definition: ApiBase.php:558
ContextSource\getUser
getUser()
Stable to override.
Definition: ContextSource.php:134
ApiMove\__construct
__construct(ApiMain $mainModule, $moduleName, MovePageFactory $movePageFactory)
Definition: ApiMove.php:37
ApiBase
This abstract class implements many basic API functions, and is the base of all API classes.
Definition: ApiBase.php:52
ApiMove\movePage
movePage(Title $from, Title $to, $reason, $createRedirect, $changeTags)
Definition: ApiMove.php:196
ApiMove\getAllowedParams
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
Definition: ApiMove.php:260
Status
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition: Status.php:44
StatusValue\getValue
getValue()
Definition: StatusValue.php:138
ApiMove\isWriteMode
isWriteMode()
Indicates whether this module requires write mode.
Definition: ApiMove.php:256
setWatch
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.
Definition: ApiWatchlistTrait.php:72
ApiMove\getExamplesMessages
getExamplesMessages()
Returns usage examples for this module.
Definition: ApiMove.php:293
ApiBase\extractRequestParams
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition: ApiBase.php:706
ApiMove
API Module to move pages.
Definition: ApiMove.php:30
ApiResult\setIndexedTagName
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
Definition: ApiResult.php:601
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:843
ApiBase\getPermissionManager
getPermissionManager()
Obtain a PermissionManager instance that subclasses may use in their authorization checks.
Definition: ApiBase.php:627
ApiBase\useTransactionalTimeLimit
useTransactionalTimeLimit()
Call wfTransactionalTimeLimit() if this request was POSTed.
Definition: ApiBase.php:1228
wfEscapeWikiText
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
Definition: GlobalFunctions.php:1504
ApiMove\getHelpUrls
getHelpUrls()
Return links to more detailed help pages about the module.
Definition: ApiMove.php:301
Page\MovePageFactory
Definition: MovePageFactory.php:30
ApiWatchlistTrait
trait ApiWatchlistTrait
An ApiWatchlistTrait adds class properties and convenience methods for APIs that allow you to watch a...
Definition: ApiWatchlistTrait.php:17
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:598
Title
Represents a title within MediaWiki.
Definition: Title.php:46
getWatchlistParams
getWatchlistParams(array $watchOptions=[])
Get additional allow params specific to watchlisting.
Definition: ApiWatchlistTrait.php:35
ApiMove\execute
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
Definition: ApiMove.php:50
ApiBase\dieStatus
dieStatus(StatusValue $status)
Throw an ApiUsageException based on the Status object.
Definition: ApiBase.php:1428
ApiBase\getModuleName
getModuleName()
Get the name of the module being executed by this instance.
Definition: ApiBase.php:437
ApiBase\PARAM_ISMULTI
const PARAM_ISMULTI
Definition: ApiBase.php:68
NS_FILE
const NS_FILE
Definition: Defines.php:69
Title\newFromID
static newFromID( $id, $flags=0)
Create a new Title from an article ID.
Definition: Title.php:505
ApiMove\mustBePosted
mustBePosted()
Indicates whether this module must be called with a POST request Stable to override.
Definition: ApiMove.php:252
ApiBase\getErrorFormatter
getErrorFormatter()
Stable to override.
Definition: ApiBase.php:572