MediaWiki REL1_39
ApiMove.php
Go to the documentation of this file.
1<?php
28
33class ApiMove extends ApiBase {
34
36
38 private $movePageFactory;
39
41 private $repoGroup;
42
43 public function __construct(
44 ApiMain $mainModule,
45 $moduleName,
46 MovePageFactory $movePageFactory,
47 RepoGroup $repoGroup,
48 WatchlistManager $watchlistManager,
49 UserOptionsLookup $userOptionsLookup
50 ) {
51 parent::__construct( $mainModule, $moduleName );
52
53 $this->movePageFactory = $movePageFactory;
54 $this->repoGroup = $repoGroup;
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() {
66
67 $user = $this->getUser();
68 $params = $this->extractRequestParams();
69
70 $this->requireOnlyOneParameter( $params, 'from', 'fromid' );
71
72 if ( isset( $params['from'] ) ) {
73 $fromTitle = Title::newFromText( $params['from'] );
74 if ( !$fromTitle || $fromTitle->isExternal() ) {
75 $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $params['from'] ) ] );
76 }
77 } elseif ( isset( $params['fromid'] ) ) {
78 $fromTitle = Title::newFromID( $params['fromid'] );
79 if ( !$fromTitle ) {
80 $this->dieWithError( [ 'apierror-nosuchpageid', $params['fromid'] ] );
81 }
82 } else {
83 throw new LogicException( 'Unreachable due to requireOnlyOneParameter' );
84 }
85
86 if ( !$fromTitle->exists() ) {
87 $this->dieWithError( 'apierror-missingtitle' );
88 }
89 $fromTalk = $fromTitle->getTalkPage();
90
91 $toTitle = Title::newFromText( $params['to'] );
92 if ( !$toTitle || $toTitle->isExternal() ) {
93 $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $params['to'] ) ] );
94 }
95 $toTalk = $toTitle->getTalkPageIfDefined();
96
97 if ( $toTitle->getNamespace() === NS_FILE
98 && !$this->repoGroup->getLocalRepo()->findFile( $toTitle )
99 && $this->repoGroup->findFile( $toTitle )
100 ) {
101 if ( !$params['ignorewarnings'] &&
102 $this->getAuthority()->isAllowed( 'reupload-shared' ) ) {
103 $this->dieWithError( 'apierror-fileexists-sharedrepo-perm' );
104 } elseif ( !$this->getAuthority()->isAllowed( 'reupload-shared' ) ) {
105 $this->dieWithError( 'apierror-cantoverwrite-sharedfile' );
106 }
107 }
108
109 // Rate limit
110 if ( $user->pingLimiter( 'move' ) ) {
111 $this->dieWithError( 'apierror-ratelimited' );
112 }
113
114 // Move the page
115 $toTitleExists = $toTitle->exists();
116 $mp = $this->movePageFactory->newMovePage( $fromTitle, $toTitle );
117 $status = $mp->moveIfAllowed(
118 $this->getAuthority(),
119 $params['reason'],
120 !$params['noredirect'],
121 $params['tags'] ?: []
122 );
123 if ( !$status->isOK() ) {
124 $this->dieStatus( $status );
125 }
126
127 $r = [
128 'from' => $fromTitle->getPrefixedText(),
129 'to' => $toTitle->getPrefixedText(),
130 'reason' => $params['reason']
131 ];
132
133 // NOTE: we assume that if the old title exists, it's because it was re-created as
134 // a redirect to the new title. This is not safe, but what we did before was
135 // even worse: we just determined whether a redirect should have been created,
136 // and reported that it was created if it should have, without any checks.
137 $r['redirectcreated'] = $fromTitle->exists();
138
139 $r['moveoverredirect'] = $toTitleExists;
140
141 // Move the talk page
142 if ( $params['movetalk'] && $toTalk && $fromTalk->exists() && !$fromTitle->isTalkPage() ) {
143 $toTalkExists = $toTalk->exists();
144 $mp = $this->movePageFactory->newMovePage( $fromTalk, $toTalk );
145 $status = $mp->moveIfAllowed(
146 $this->getAuthority(),
147 $params['reason'],
148 !$params['noredirect'],
149 $params['tags'] ?: []
150 );
151 if ( $status->isOK() ) {
152 $r['talkfrom'] = $fromTalk->getPrefixedText();
153 $r['talkto'] = $toTalk->getPrefixedText();
154 $r['talkmoveoverredirect'] = $toTalkExists;
155 } else {
156 // We're not going to dieWithError() on failure, since we already changed something
157 $r['talkmove-errors'] = $this->getErrorFormatter()->arrayFromStatus( $status );
158 }
159 }
160
161 $result = $this->getResult();
162
163 // Move subpages
164 if ( $params['movesubpages'] ) {
165 $r['subpages'] = $this->moveSubpages(
166 $fromTitle,
167 $toTitle,
168 $params['reason'],
169 $params['noredirect'],
170 $params['tags'] ?: []
171 );
172 ApiResult::setIndexedTagName( $r['subpages'], 'subpage' );
173
174 if ( $params['movetalk'] && $toTalk ) {
175 $r['subpages-talk'] = $this->moveSubpages(
176 $fromTalk,
177 $toTalk,
178 $params['reason'],
179 $params['noredirect'],
180 $params['tags'] ?: []
181 );
182 ApiResult::setIndexedTagName( $r['subpages-talk'], 'subpage' );
183 }
184 }
185
186 $watch = $params['watchlist'] ?? 'preferences';
187 $watchlistExpiry = $this->getExpiryFromParams( $params );
188
189 // Watch pages
190 $this->setWatch( $watch, $fromTitle, $user, 'watchmoves', $watchlistExpiry );
191 $this->setWatch( $watch, $toTitle, $user, 'watchmoves', $watchlistExpiry );
192
193 $result->addValue( null, $this->getModuleName(), $r );
194 }
195
204 public function moveSubpages( $fromTitle, $toTitle, $reason, $noredirect, $changeTags = [] ) {
205 $retval = [];
206
207 $mp = $this->movePageFactory->newMovePage( $fromTitle, $toTitle );
208 $result =
209 $mp->moveSubpagesIfAllowed( $this->getAuthority(), $reason, !$noredirect, $changeTags );
210 if ( !$result->isOK() ) {
211 // This means the whole thing failed
212 return [ 'errors' => $this->getErrorFormatter()->arrayFromStatus( $result ) ];
213 }
214
215 // At least some pages could be moved
216 // Report each of them separately
217 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 $params = [
241 'from' => null,
242 'fromid' => [
243 ParamValidator::PARAM_TYPE => 'integer'
244 ],
245 'to' => [
246 ParamValidator::PARAM_TYPE => 'string',
247 ParamValidator::PARAM_REQUIRED => true
248 ],
249 'reason' => '',
250 'movetalk' => false,
251 'movesubpages' => false,
252 'noredirect' => false,
253 ];
254
255 // Params appear in the docs in the order they are defined,
256 // which is why this is here and not at the bottom.
257 $params += $this->getWatchlistParams();
258
259 return $params + [
260 'ignorewarnings' => false,
261 'tags' => [
262 ParamValidator::PARAM_TYPE => 'tags',
263 ParamValidator::PARAM_ISMULTI => true,
264 ],
265 ];
266 }
267
268 public function needsToken() {
269 return 'csrf';
270 }
271
272 protected function getExamplesMessages() {
273 return [
274 'action=move&from=Badtitle&to=Goodtitle&token=123ABC&' .
275 'reason=Misspelled%20title&movetalk=&noredirect='
276 => 'apihelp-move-example-move',
277 ];
278 }
279
280 public function getHelpUrls() {
281 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Move';
282 }
283}
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.
const NS_FILE
Definition Defines.php:70
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:56
dieWithError( $msg, $code=null, $data=null, $httpCode=0)
Abort execution with an error.
Definition ApiBase.php:1454
getErrorFormatter()
Definition ApiBase.php:640
requireOnlyOneParameter( $params,... $required)
Die if none or more than one of a certain set of parameters is set and not false.
Definition ApiBase.php:903
getResult()
Get the result object.
Definition ApiBase.php:629
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:765
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:498
dieStatus(StatusValue $status)
Throw an ApiUsageException based on the Status object.
Definition ApiBase.php:1515
useTransactionalTimeLimit()
Call wfTransactionalTimeLimit() if this request was POSTed.
Definition ApiBase.php:1299
This is the main API class, used for both external and internal processing.
Definition ApiMain.php:52
API Module to move pages.
Definition ApiMove.php:33
needsToken()
Returns the token type this module requires in order to execute.
Definition ApiMove.php:268
isWriteMode()
Indicates whether this module requires write mode.
Definition ApiMove.php:235
__construct(ApiMain $mainModule, $moduleName, MovePageFactory $movePageFactory, RepoGroup $repoGroup, WatchlistManager $watchlistManager, UserOptionsLookup $userOptionsLookup)
Definition ApiMove.php:43
moveSubpages( $fromTitle, $toTitle, $reason, $noredirect, $changeTags=[])
Definition ApiMove.php:204
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
Definition ApiMove.php:64
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:280
getExamplesMessages()
Returns usage examples for this module.
Definition ApiMove.php:272
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
Definition ApiMove.php:239
A class containing constants representing the names of configuration variables.
Provides access to user options.
Prioritized list of file repositories.
Definition RepoGroup.php:29
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:44
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...
Service for page rename actions.
return true
Definition router.php:92