MediaWiki master
ApiDelete.php
Go to the documentation of this file.
1<?php
9namespace MediaWiki\Api;
10
24use StatusValue;
26
33class ApiDelete extends ApiBase {
34
36
37 private RepoGroup $repoGroup;
38 private DeletePageFactory $deletePageFactory;
39
40 public function __construct(
41 ApiMain $mainModule,
42 string $moduleName,
43 RepoGroup $repoGroup,
44 WatchlistManager $watchlistManager,
45 WatchedItemStoreInterface $watchedItemStore,
46 UserOptionsLookup $userOptionsLookup,
47 DeletePageFactory $deletePageFactory
48 ) {
49 parent::__construct( $mainModule, $moduleName );
50 $this->repoGroup = $repoGroup;
51 $this->deletePageFactory = $deletePageFactory;
52
53 // Variables needed in ApiWatchlistTrait trait
54 $this->watchlistExpiryEnabled = $this->getConfig()->get( MainConfigNames::WatchlistExpiry );
55 $this->watchlistMaxDuration =
57 $this->watchlistManager = $watchlistManager;
58 $this->watchedItemStore = $watchedItemStore;
59 $this->userOptionsLookup = $userOptionsLookup;
60 }
61
69 public function execute() {
71
72 $params = $this->extractRequestParams();
73
74 $pageObj = $this->getTitleOrPageId( $params, 'fromdbmaster' );
75 $titleObj = $pageObj->getTitle();
76 $this->getErrorFormatter()->setContextTitle( $titleObj );
77 if ( !$pageObj->exists() &&
78 // @phan-suppress-next-line PhanUndeclaredMethod
79 !( $titleObj->getNamespace() === NS_FILE && self::canDeleteFile( $pageObj->getFile() ) )
80 ) {
81 $this->dieWithError( 'apierror-missingtitle' );
82 }
83
84 $reason = $params['reason'];
85 $user = $this->getUser();
86
87 $tags = $params['tags'] ?: [];
88
89 if ( $titleObj->getNamespace() === NS_FILE ) {
90 $status = $this->deleteFile(
91 $pageObj,
92 $params['oldimage'],
93 $reason,
94 false,
95 $tags,
96 $params['deletetalk']
97 );
98 // TODO What kind of non-fatal errors should we expect here?
99 $wasScheduled = $status->isOK() && $status->getValue() === false;
100 } else {
101 $status = $this->delete( $pageObj, $reason, $tags, $params['deletetalk'] );
102 $wasScheduled = $status->isGood() && $status->getValue() === false;
103 }
104
105 if ( !$status->isOK() ) {
106 $this->dieStatus( $status );
107 }
108
109 if ( $wasScheduled ) {
110 $this->addWarning( [ 'delete-scheduled', $titleObj->getPrefixedText() ] );
111 }
112
113 // Deprecated parameters
114 if ( $params['watch'] ) {
115 $watch = 'watch';
116 } elseif ( $params['unwatch'] ) {
117 $watch = 'unwatch';
118 } else {
119 $watch = $params['watchlist'];
120 }
121
122 $watchlistExpiry = $this->getExpiryFromParams( $params, $titleObj, $user );
123 $this->setWatch( $watch, $titleObj, $user, 'watchdeletion', $watchlistExpiry );
124
125 $r = [
126 'title' => $titleObj->getPrefixedText(),
127 'reason' => $reason,
128 ];
129
130 // TODO: We could expose additional information (scheduled and log ID) about the status of the talk page
131 // deletion.
132 if ( $wasScheduled ) {
133 $r['scheduled'] = true;
134 } else {
135 // Scheduled deletions don't currently have a log entry available at this point
136 $r['logid'] = $status->value;
137 }
138 $this->getResult()->addValue( null, $this->getModuleName(), $r );
139 }
140
153 private function delete( WikiPage $page, &$reason, array $tags, bool $deleteTalk ): StatusValue {
154 $title = $page->getTitle();
155
156 // Auto-generate a summary, if necessary
157 if ( $reason === null ) {
158 $reason = $page->getAutoDeleteReason();
159 if ( $reason === false ) {
160 // Should be reachable only if the page has no revisions
161 return Status::newFatal( 'cannotdelete', $title->getPrefixedText() ); // @codeCoverageIgnore
162 }
163 }
164
165 $deletePage = $this->deletePageFactory->newDeletePage( $page, $this->getAuthority() );
166 if ( $deleteTalk ) {
167 $checkStatus = $deletePage->canProbablyDeleteAssociatedTalk();
168 if ( !$checkStatus->isGood() ) {
169 foreach ( $checkStatus->getMessages() as $msg ) {
170 $this->addWarning( $msg );
171 }
172 } else {
173 $deletePage->setDeleteAssociatedTalk( true );
174 }
175 }
176 $deletionStatus = $deletePage->setTags( $tags )->deleteIfAllowed( $reason );
177 if ( $deletionStatus->isGood() ) {
178 // @phan-suppress-next-line PhanTypeMismatchProperty Changing the type of the status parameter
179 $deletionStatus->value = $deletePage->deletionsWereScheduled()[DeletePage::PAGE_BASE]
180 ? false
181 : $deletePage->getSuccessfulDeletionsIDs()[DeletePage::PAGE_BASE];
182 }
183 return $deletionStatus;
184 }
185
190 protected static function canDeleteFile( File $file ) {
191 return $file->exists() && $file->isLocal() && !$file->getRedirected();
192 }
193
203 private function deleteFile(
204 WikiPage $page,
205 $oldimage,
206 &$reason,
207 bool $suppress,
208 array $tags,
209 bool $deleteTalk
210 ) {
211 $title = $page->getTitle();
212
213 // @phan-suppress-next-line PhanUndeclaredMethod There's no right typehint for it
214 $file = $page->getFile();
215 if ( !self::canDeleteFile( $file ) ) {
216 return $this->delete( $page, $reason, $tags, $deleteTalk );
217 }
218
219 // Check that the user is allowed to carry out the deletion
220 $this->checkTitleUserPermissions( $page->getTitle(), 'delete' );
221 if ( $tags ) {
222 // If change tagging was requested, check that the user is allowed to tag,
223 // and the tags are valid
224 $tagStatus = ChangeTags::canAddTagsAccompanyingChange( $tags, $this->getAuthority() );
225 if ( !$tagStatus->isOK() ) {
226 $this->dieStatus( $tagStatus );
227 }
228 }
229
230 if ( $oldimage ) {
231 if ( !FileDeleteForm::isValidOldSpec( $oldimage ) ) {
232 return Status::newFatal( 'invalidoldimage' );
233 }
234 $oldfile = $this->repoGroup->getLocalRepo()->newFromArchiveName( $title, $oldimage );
235 if ( !$oldfile->exists() || !$oldfile->isLocal() || $oldfile->getRedirected() ) {
236 return Status::newFatal( 'nodeleteablefile' );
237 }
238 }
239
240 return FileDeleteForm::doDelete(
241 $title,
242 $file,
243 $oldimage,
244 // Log and RC don't like null reasons
245 $reason ?? '',
246 $suppress,
247 $this->getUser(),
248 $tags,
249 $deleteTalk
250 );
251 }
252
254 public function mustBePosted() {
255 return true;
256 }
257
259 public function isWriteMode() {
260 return true;
261 }
262
264 public function getAllowedParams() {
265 $params = [
266 'title' => null,
267 'pageid' => [
268 ParamValidator::PARAM_TYPE => 'integer'
269 ],
270 'reason' => null,
271 'tags' => [
272 ParamValidator::PARAM_TYPE => 'tags',
273 ParamValidator::PARAM_ISMULTI => true,
274 ],
275 'deletetalk' => false,
276 'watch' => [
277 ParamValidator::PARAM_DEFAULT => false,
278 ParamValidator::PARAM_DEPRECATED => true,
279 ],
280 ];
281
282 // Params appear in the docs in the order they are defined,
283 // which is why this is here and not at the bottom.
284 $params += $this->getWatchlistParams();
285
286 return $params + [
287 'unwatch' => [
288 ParamValidator::PARAM_DEFAULT => false,
289 ParamValidator::PARAM_DEPRECATED => true,
290 ],
291 'oldimage' => null,
292 ];
293 }
294
296 public function needsToken() {
297 return 'csrf';
298 }
299
301 protected function getExamplesMessages() {
302 $title = Title::newMainPage()->getPrefixedText();
303 $mp = rawurlencode( $title );
304
305 return [
306 "action=delete&title={$mp}&token=123ABC"
307 => 'apihelp-delete-example-simple',
308 "action=delete&title={$mp}&token=123ABC&reason=Preparing%20for%20move"
309 => 'apihelp-delete-example-reason',
310 ];
311 }
312
314 public function getHelpUrls() {
315 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Delete';
316 }
317}
318
320class_alias( ApiDelete::class, 'ApiDelete' );
const NS_FILE
Definition Defines.php:57
This abstract class implements many basic API functions, and is the base of all API classes.
Definition ApiBase.php:61
dieWithError( $msg, $code=null, $data=null, $httpCode=0)
Abort execution with an error.
Definition ApiBase.php:1511
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:543
useTransactionalTimeLimit()
Call wfTransactionalTimeLimit() if this request was POSTed.
Definition ApiBase.php:1359
getResult()
Get the result object.
Definition ApiBase.php:682
addWarning( $msg, $code=null, $data=null)
Add a warning for this module.
Definition ApiBase.php:1429
dieStatus(StatusValue $status)
Throw an ApiUsageException based on the Status object.
Definition ApiBase.php:1562
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:823
getTitleOrPageId( $params, $load=false)
Attempts to load a WikiPage object from a title or pageid parameter, if possible.
Definition ApiBase.php:1147
API module that facilitates deleting pages.
Definition ApiDelete.php:33
__construct(ApiMain $mainModule, string $moduleName, RepoGroup $repoGroup, WatchlistManager $watchlistManager, WatchedItemStoreInterface $watchedItemStore, UserOptionsLookup $userOptionsLookup, DeletePageFactory $deletePageFactory)
Definition ApiDelete.php:40
getExamplesMessages()
Returns usage examples for this module.Return value has query strings as keys, with values being eith...
isWriteMode()
Indicates whether this module requires write access to the wiki.API modules must override this method...
execute()
Extracts the title and reason from the request parameters and invokes the local delete() function wit...
Definition ApiDelete.php:69
mustBePosted()
Indicates whether this module must be called with a POST request.Implementations of this method must ...
needsToken()
Returns the token type this module requires in order to execute.Modules are strongly encouraged to us...
static canDeleteFile(File $file)
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
getHelpUrls()
Return links to more detailed help pages about the module.1.25, returning boolean false is deprecated...
This is the main API class, used for both external and internal processing.
Definition ApiMain.php:65
Recent changes tagging.
Implements some public methods and some protected utility functions which are required by multiple ch...
Definition File.php:79
isLocal()
Returns true if the file comes from the local file repository.
Definition File.php:2099
exists()
Returns true if file exists in the repository.
Definition File.php:1036
Prioritized list of file repositories.
Definition RepoGroup.php:30
A class containing constants representing the names of configuration variables.
const WatchlistExpiry
Name constant for the WatchlistExpiry setting, for use with Config::get()
const WatchlistExpiryMaxDuration
Name constant for the WatchlistExpiryMaxDuration setting, for use with Config::get()
Backend logic for performing a page delete action.
File deletion user interface.
Base representation for an editable wiki page.
Definition WikiPage.php:82
getTitle()
Get the title object of the article.
Definition WikiPage.php:250
getAutoDeleteReason(&$hasHistory=false)
Auto-generates a deletion reason.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:44
Represents a title within MediaWiki.
Definition Title.php:69
Provides access to user options.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
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 delete actions.
setWatch(string $watch, PageIdentity $page, 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.
getExpiryFromParams(array $params, ?PageIdentity $page=null, ?UserIdentity $user=null, string $userOption='watchdefault-expiry')
Get formatted expiry from the given parameters.