MediaWiki master
ApiDelete.php
Go to the documentation of this file.
1<?php
23namespace MediaWiki\Api;
24
25use ChangeTags;
26use File;
35use RepoGroup;
36use StatusValue;
38use WikiPage;
39
46class ApiDelete extends ApiBase {
47
49
50 private RepoGroup $repoGroup;
51 private DeletePageFactory $deletePageFactory;
52
53 public function __construct(
54 ApiMain $mainModule,
55 string $moduleName,
56 RepoGroup $repoGroup,
57 WatchlistManager $watchlistManager,
58 UserOptionsLookup $userOptionsLookup,
59 DeletePageFactory $deletePageFactory
60 ) {
61 parent::__construct( $mainModule, $moduleName );
62 $this->repoGroup = $repoGroup;
63 $this->deletePageFactory = $deletePageFactory;
64
65 // Variables needed in ApiWatchlistTrait trait
66 $this->watchlistExpiryEnabled = $this->getConfig()->get( MainConfigNames::WatchlistExpiry );
67 $this->watchlistMaxDuration =
69 $this->watchlistManager = $watchlistManager;
70 $this->userOptionsLookup = $userOptionsLookup;
71 }
72
80 public function execute() {
82
84
85 $pageObj = $this->getTitleOrPageId( $params, 'fromdbmaster' );
86 $titleObj = $pageObj->getTitle();
87 $this->getErrorFormatter()->setContextTitle( $titleObj );
88 if ( !$pageObj->exists() &&
89 // @phan-suppress-next-line PhanUndeclaredMethod
90 !( $titleObj->getNamespace() === NS_FILE && self::canDeleteFile( $pageObj->getFile() ) )
91 ) {
92 $this->dieWithError( 'apierror-missingtitle' );
93 }
94
95 $reason = $params['reason'];
96 $user = $this->getUser();
97
98 $tags = $params['tags'] ?: [];
99
100 if ( $titleObj->getNamespace() === NS_FILE ) {
101 $status = $this->deleteFile(
102 $pageObj,
103 $params['oldimage'],
104 $reason,
105 false,
106 $tags,
107 $params['deletetalk']
108 );
109 // TODO What kind of non-fatal errors should we expect here?
110 $wasScheduled = $status->isOK() && $status->getValue() === false;
111 } else {
112 $status = $this->delete( $pageObj, $reason, $tags, $params['deletetalk'] );
113 $wasScheduled = $status->isGood() && $status->getValue() === false;
114 }
115
116 if ( !$status->isOK() ) {
117 $this->dieStatus( $status );
118 }
119
120 if ( $wasScheduled ) {
121 $this->addWarning( [ 'delete-scheduled', $titleObj->getPrefixedText() ] );
122 }
123
124 // Deprecated parameters
125 if ( $params['watch'] ) {
126 $watch = 'watch';
127 } elseif ( $params['unwatch'] ) {
128 $watch = 'unwatch';
129 } else {
130 $watch = $params['watchlist'];
131 }
132
133 $watchlistExpiry = $this->getExpiryFromParams( $params );
134 $this->setWatch( $watch, $titleObj, $user, 'watchdeletion', $watchlistExpiry );
135
136 $r = [
137 'title' => $titleObj->getPrefixedText(),
138 'reason' => $reason,
139 ];
140
141 // TODO: We could expose additional information (scheduled and log ID) about the status of the talk page
142 // deletion.
143 if ( $wasScheduled ) {
144 $r['scheduled'] = true;
145 } else {
146 // Scheduled deletions don't currently have a log entry available at this point
147 $r['logid'] = $status->value;
148 }
149 $this->getResult()->addValue( null, $this->getModuleName(), $r );
150 }
151
164 private function delete( WikiPage $page, &$reason, array $tags, bool $deleteTalk ): StatusValue {
165 $title = $page->getTitle();
166
167 // Auto-generate a summary, if necessary
168 if ( $reason === null ) {
169 $reason = $page->getAutoDeleteReason();
170 if ( $reason === false ) {
171 // Should be reachable only if the page has no revisions
172 return Status::newFatal( 'cannotdelete', $title->getPrefixedText() ); // @codeCoverageIgnore
173 }
174 }
175
176 $deletePage = $this->deletePageFactory->newDeletePage( $page, $this->getAuthority() );
177 if ( $deleteTalk ) {
178 $checkStatus = $deletePage->canProbablyDeleteAssociatedTalk();
179 if ( !$checkStatus->isGood() ) {
180 foreach ( $checkStatus->getMessages() as $msg ) {
181 $this->addWarning( $msg );
182 }
183 } else {
184 $deletePage->setDeleteAssociatedTalk( true );
185 }
186 }
187 $deletionStatus = $deletePage->setTags( $tags )->deleteIfAllowed( $reason );
188 if ( $deletionStatus->isGood() ) {
189 $deletionStatus->value = $deletePage->deletionsWereScheduled()[DeletePage::PAGE_BASE]
190 ? false
191 : $deletePage->getSuccessfulDeletionsIDs()[DeletePage::PAGE_BASE];
192 }
193 return $deletionStatus;
194 }
195
200 protected static function canDeleteFile( File $file ) {
201 return $file->exists() && $file->isLocal() && !$file->getRedirected();
202 }
203
213 private function deleteFile(
214 WikiPage $page,
215 $oldimage,
216 &$reason,
217 bool $suppress,
218 array $tags,
219 bool $deleteTalk
220 ) {
221 $title = $page->getTitle();
222
223 // @phan-suppress-next-line PhanUndeclaredMethod There's no right typehint for it
224 $file = $page->getFile();
225 if ( !self::canDeleteFile( $file ) ) {
226 return $this->delete( $page, $reason, $tags, $deleteTalk );
227 }
228
229 // Check that the user is allowed to carry out the deletion
230 $this->checkTitleUserPermissions( $page->getTitle(), 'delete' );
231 if ( $tags ) {
232 // If change tagging was requested, check that the user is allowed to tag,
233 // and the tags are valid
234 $tagStatus = ChangeTags::canAddTagsAccompanyingChange( $tags, $this->getAuthority() );
235 if ( !$tagStatus->isOK() ) {
236 $this->dieStatus( $tagStatus );
237 }
238 }
239
240 if ( $oldimage ) {
241 if ( !FileDeleteForm::isValidOldSpec( $oldimage ) ) {
242 return Status::newFatal( 'invalidoldimage' );
243 }
244 $oldfile = $this->repoGroup->getLocalRepo()->newFromArchiveName( $title, $oldimage );
245 if ( !$oldfile->exists() || !$oldfile->isLocal() || $oldfile->getRedirected() ) {
246 return Status::newFatal( 'nodeleteablefile' );
247 }
248 }
249
250 return FileDeleteForm::doDelete(
251 $title,
252 $file,
253 $oldimage,
254 // Log and RC don't like null reasons
255 $reason ?? '',
256 $suppress,
257 $this->getUser(),
258 $tags,
259 $deleteTalk
260 );
261 }
262
263 public function mustBePosted() {
264 return true;
265 }
266
267 public function isWriteMode() {
268 return true;
269 }
270
271 public function getAllowedParams() {
272 $params = [
273 'title' => null,
274 'pageid' => [
275 ParamValidator::PARAM_TYPE => 'integer'
276 ],
277 'reason' => null,
278 'tags' => [
279 ParamValidator::PARAM_TYPE => 'tags',
280 ParamValidator::PARAM_ISMULTI => true,
281 ],
282 'deletetalk' => false,
283 'watch' => [
284 ParamValidator::PARAM_DEFAULT => false,
285 ParamValidator::PARAM_DEPRECATED => true,
286 ],
287 ];
288
289 // Params appear in the docs in the order they are defined,
290 // which is why this is here and not at the bottom.
291 $params += $this->getWatchlistParams();
292
293 return $params + [
294 'unwatch' => [
295 ParamValidator::PARAM_DEFAULT => false,
296 ParamValidator::PARAM_DEPRECATED => true,
297 ],
298 'oldimage' => null,
299 ];
300 }
301
302 public function needsToken() {
303 return 'csrf';
304 }
305
306 protected function getExamplesMessages() {
307 $title = Title::newMainPage()->getPrefixedText();
308 $mp = rawurlencode( $title );
309
310 return [
311 "action=delete&title={$mp}&token=123ABC"
312 => 'apihelp-delete-example-simple',
313 "action=delete&title={$mp}&token=123ABC&reason=Preparing%20for%20move"
314 => 'apihelp-delete-example-reason',
315 ];
316 }
317
318 public function getHelpUrls() {
319 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Delete';
320 }
321}
322
324class_alias( ApiDelete::class, 'ApiDelete' );
const NS_FILE
Definition Defines.php:71
array $params
The job parameters.
Recent changes tagging.
static canAddTagsAccompanyingChange(array $tags, ?Authority $performer=null, $checkBlock=true)
Is it OK to allow the user to apply all the specified tags at the same time as they edit/make the cha...
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:2052
exists()
Returns true if file exists in the repository.
Definition File.php:1035
getRedirected()
Definition File.php:2440
This abstract class implements many basic API functions, and is the base of all API classes.
Definition ApiBase.php:76
dieWithError( $msg, $code=null, $data=null, $httpCode=0)
Abort execution with an error.
Definition ApiBase.php:1531
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:571
useTransactionalTimeLimit()
Call wfTransactionalTimeLimit() if this request was POSTed.
Definition ApiBase.php:1379
getResult()
Get the result object.
Definition ApiBase.php:710
addWarning( $msg, $code=null, $data=null)
Add a warning for this module.
Definition ApiBase.php:1449
dieStatus(StatusValue $status)
Throw an ApiUsageException based on the Status object.
Definition ApiBase.php:1586
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:851
getTitleOrPageId( $params, $load=false)
Attempts to load a WikiPage object from a title or pageid parameter, if possible.
Definition ApiBase.php:1138
API module that facilitates deleting pages.
Definition ApiDelete.php:46
getExamplesMessages()
Returns usage examples for this module.
isWriteMode()
Indicates whether this module requires write access to the wiki.
execute()
Extracts the title and reason from the request parameters and invokes the local delete() function wit...
Definition ApiDelete.php:80
mustBePosted()
Indicates whether this module must be called with a POST request.
needsToken()
Returns the token type this module requires in order to execute.
static canDeleteFile(File $file)
__construct(ApiMain $mainModule, string $moduleName, RepoGroup $repoGroup, WatchlistManager $watchlistManager, UserOptionsLookup $userOptionsLookup, DeletePageFactory $deletePageFactory)
Definition ApiDelete.php:53
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.
This is the main API class, used for both external and internal processing.
Definition ApiMain.php:78
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.
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:32
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Base representation for an editable wiki page.
Definition WikiPage.php:85
getAutoDeleteReason(&$hasHistory=false)
Auto-generates a deletion reason.
getTitle()
Get the title object of the article.
Definition WikiPage.php:259
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.
getExpiryFromParams(array $params)
Get formatted expiry from the given parameters, or null if no expiry was provided.
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.