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