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