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 public function __construct(
38 ApiMain $mainModule,
39 string $moduleName,
40 private readonly RepoGroup $repoGroup,
41 WatchlistManager $watchlistManager,
42 WatchedItemStoreInterface $watchedItemStore,
43 UserOptionsLookup $userOptionsLookup,
44 private readonly DeletePageFactory $deletePageFactory,
45 ) {
46 parent::__construct( $mainModule, $moduleName );
47
48 // Variables needed in ApiWatchlistTrait trait
49 $this->watchlistExpiryEnabled = $this->getConfig()->get( MainConfigNames::WatchlistExpiry );
50 $this->watchlistMaxDuration =
52 $this->watchlistManager = $watchlistManager;
53 $this->watchedItemStore = $watchedItemStore;
54 $this->userOptionsLookup = $userOptionsLookup;
55 }
56
64 public function execute() {
66
67 $params = $this->extractRequestParams();
68
69 $pageObj = $this->getTitleOrPageId( $params, 'fromdbmaster' );
70 $titleObj = $pageObj->getTitle();
71 $this->getErrorFormatter()->setContextTitle( $titleObj );
72 if ( !$pageObj->exists() &&
73 // @phan-suppress-next-line PhanUndeclaredMethod
74 !( $titleObj->getNamespace() === NS_FILE && self::canDeleteFile( $pageObj->getFile() ) )
75 ) {
76 $this->dieWithError( 'apierror-missingtitle' );
77 }
78
79 $reason = $params['reason'];
80 $user = $this->getUser();
81
82 $tags = $params['tags'] ?: [];
83
84 if ( $titleObj->getNamespace() === NS_FILE ) {
85 $status = $this->deleteFile(
86 $pageObj,
87 $params['oldimage'],
88 $reason,
89 false,
90 $tags,
91 $params['deletetalk']
92 );
93 // TODO What kind of non-fatal errors should we expect here?
94 $wasScheduled = $status->isOK() && $status->getValue() === false;
95 } else {
96 $status = $this->delete( $pageObj, $reason, $tags, $params['deletetalk'] );
97 $wasScheduled = $status->isGood() && $status->getValue() === false;
98 }
99
100 if ( !$status->isOK() ) {
101 $this->dieStatus( $status );
102 }
103
104 if ( $wasScheduled ) {
105 $this->addWarning( [ 'delete-scheduled', $titleObj->getPrefixedText() ] );
106 }
107
108 // Deprecated parameters
109 if ( $params['watch'] ) {
110 $watch = 'watch';
111 } elseif ( $params['unwatch'] ) {
112 $watch = 'unwatch';
113 } else {
114 $watch = $params['watchlist'];
115 }
116
117 $watchlistExpiry = $this->getExpiryFromParams( $params, $titleObj, $user );
118 $this->setWatch( $watch, $titleObj, $user, 'watchdeletion', $watchlistExpiry );
119
120 $r = [
121 'title' => $titleObj->getPrefixedText(),
122 'reason' => $reason,
123 ];
124
125 // TODO: We could expose additional information (scheduled and log ID) about the status of the talk page
126 // deletion.
127 if ( $wasScheduled ) {
128 $r['scheduled'] = true;
129 } else {
130 // Scheduled deletions don't currently have a log entry available at this point
131 $r['logid'] = $status->value;
132 }
133 $this->getResult()->addValue( null, $this->getModuleName(), $r );
134 }
135
148 private function delete( WikiPage $page, &$reason, array $tags, bool $deleteTalk ): StatusValue {
149 $title = $page->getTitle();
150
151 // Auto-generate a summary, if necessary
152 if ( $reason === null ) {
153 $reason = $page->getAutoDeleteReason();
154 if ( $reason === false ) {
155 // Should be reachable only if the page has no revisions
156 return Status::newFatal( 'cannotdelete', $title->getPrefixedText() ); // @codeCoverageIgnore
157 }
158 }
159
160 $deletePage = $this->deletePageFactory->newDeletePage( $page, $this->getAuthority() );
161 if ( $deleteTalk ) {
162 $checkStatus = $deletePage->canProbablyDeleteAssociatedTalk();
163 if ( !$checkStatus->isGood() ) {
164 foreach ( $checkStatus->getMessages() as $msg ) {
165 $this->addWarning( $msg );
166 }
167 } else {
168 $deletePage->setDeleteAssociatedTalk( true );
169 }
170 }
171 $deletionStatus = $deletePage->setTags( $tags )->deleteIfAllowed( $reason );
172 if ( $deletionStatus->isGood() ) {
173 // @phan-suppress-next-line PhanTypeMismatchProperty Changing the type of the status parameter
174 $deletionStatus->value = $deletePage->deletionsWereScheduled()[DeletePage::PAGE_BASE]
175 ? false
176 : $deletePage->getSuccessfulDeletionsIDs()[DeletePage::PAGE_BASE];
177 }
178 return $deletionStatus;
179 }
180
185 protected static function canDeleteFile( File $file ) {
186 return $file->exists() && $file->isLocal() && !$file->getRedirected();
187 }
188
198 private function deleteFile(
199 WikiPage $page,
200 $oldimage,
201 &$reason,
202 bool $suppress,
203 array $tags,
204 bool $deleteTalk
205 ) {
206 $title = $page->getTitle();
207
208 // @phan-suppress-next-line PhanUndeclaredMethod There's no right typehint for it
209 $file = $page->getFile();
210 if ( !self::canDeleteFile( $file ) ) {
211 return $this->delete( $page, $reason, $tags, $deleteTalk );
212 }
213
214 // Check that the user is allowed to carry out the deletion
215 $this->checkTitleUserPermissions( $page->getTitle(), 'delete' );
216 if ( $tags ) {
217 // If change tagging was requested, check that the user is allowed to tag,
218 // and the tags are valid
219 $tagStatus = ChangeTags::canAddTagsAccompanyingChange( $tags, $this->getAuthority() );
220 if ( !$tagStatus->isOK() ) {
221 $this->dieStatus( $tagStatus );
222 }
223 }
224
225 if ( $oldimage ) {
226 if ( !FileDeleteForm::isValidOldSpec( $oldimage ) ) {
227 return Status::newFatal( 'invalidoldimage' );
228 }
229 $oldfile = $this->repoGroup->getLocalRepo()->newFromArchiveName( $title, $oldimage );
230 if ( !$oldfile->exists() || !$oldfile->isLocal() || $oldfile->getRedirected() ) {
231 return Status::newFatal( 'nodeleteablefile' );
232 }
233 }
234
235 return FileDeleteForm::doDelete(
236 $title,
237 $file,
238 $oldimage,
239 // Log and RC don't like null reasons
240 $reason ?? '',
241 $suppress,
242 $this->getUser(),
243 $tags,
244 $deleteTalk
245 );
246 }
247
249 public function mustBePosted() {
250 return true;
251 }
252
254 public function isWriteMode() {
255 return true;
256 }
257
259 public function getAllowedParams() {
260 $params = [
261 'title' => null,
262 'pageid' => [
263 ParamValidator::PARAM_TYPE => 'integer'
264 ],
265 'reason' => null,
266 'tags' => [
267 ParamValidator::PARAM_TYPE => 'tags',
268 ParamValidator::PARAM_ISMULTI => true,
269 ],
270 'deletetalk' => false,
271 'watch' => [
272 ParamValidator::PARAM_DEFAULT => false,
273 ParamValidator::PARAM_DEPRECATED => true,
274 ],
275 ];
276
277 // Params appear in the docs in the order they are defined,
278 // which is why this is here and not at the bottom.
279 $params += $this->getWatchlistParams();
280
281 return $params + [
282 'unwatch' => [
283 ParamValidator::PARAM_DEFAULT => false,
284 ParamValidator::PARAM_DEPRECATED => true,
285 ],
286 'oldimage' => null,
287 ];
288 }
289
291 public function needsToken() {
292 return 'csrf';
293 }
294
296 protected function getExamplesMessages() {
297 $title = Title::newMainPage()->getPrefixedText();
298 $mp = rawurlencode( $title );
299
300 return [
301 "action=delete&title={$mp}&token=123ABC"
302 => 'apihelp-delete-example-simple',
303 "action=delete&title={$mp}&token=123ABC&reason=Preparing%20for%20move"
304 => 'apihelp-delete-example-reason',
305 ];
306 }
307
309 public function getHelpUrls() {
310 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Delete';
311 }
312}
313
315class_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:60
dieWithError( $msg, $code=null, $data=null, $httpCode=0)
Abort execution with an error.
Definition ApiBase.php:1506
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:542
useTransactionalTimeLimit()
Call wfTransactionalTimeLimit() if this request was POSTed.
Definition ApiBase.php:1354
getResult()
Get the result object.
Definition ApiBase.php:681
addWarning( $msg, $code=null, $data=null)
Add a warning for this module.
Definition ApiBase.php:1424
dieStatus(StatusValue $status)
Throw an ApiUsageException based on the Status object.
Definition ApiBase.php:1557
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:822
getTitleOrPageId( $params, $load=false)
Attempts to load a WikiPage object from a title or pageid parameter, if possible.
Definition ApiBase.php:1146
API module that facilitates deleting pages.
Definition ApiDelete.php:33
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:64
mustBePosted()
Indicates whether this module must be called with a POST request.Implementations of this method must ...
__construct(ApiMain $mainModule, string $moduleName, private readonly RepoGroup $repoGroup, WatchlistManager $watchlistManager, WatchedItemStoreInterface $watchedItemStore, UserOptionsLookup $userOptionsLookup, private readonly DeletePageFactory $deletePageFactory,)
Definition ApiDelete.php:37
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:66
Recent changes tagging.
Implements some public methods and some protected utility functions which are required by multiple ch...
Definition File.php:80
isLocal()
Returns true if the file comes from the local file repository.
Definition File.php:2098
exists()
Returns true if file exists in the repository.
Definition File.php:1087
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.