MediaWiki REL1_37
DeleteAction.php
Go to the documentation of this file.
1<?php
26
33
36
39
42
46 public function __construct( Page $page, IContextSource $context = null ) {
47 parent::__construct( $page, $context );
48 $services = MediaWikiServices::getInstance();
49 $this->watchlistManager = $services->getWatchlistManager();
50 $this->linkRenderer = $services->getLinkRenderer();
51 $this->backlinkCacheFactory = $services->getBacklinkCacheFactory();
52 }
53
54 public function getName() {
55 return 'delete';
56 }
57
58 public function onView() {
59 return null;
60 }
61
62 public function show() {
64 $this->addHelpLink( 'Help:Sysop deleting and undeleting' );
65 if ( $this->getArticle() instanceof ImagePage ) {
66 $this->tempDeleteFile();
67 } else {
68 $this->tempDeleteArticle();
69 }
70 }
71
72 private function tempDeleteArticle() {
73 $article = $this->getArticle();
74 $title = $this->getTitle();
75 $context = $this->getContext();
76 $user = $context->getUser();
77 $request = $context->getRequest();
78
79 # Check permissions
80 $permissionStatus = PermissionStatus::newEmpty();
81 if ( !$context->getAuthority()->authorizeWrite( 'delete', $title, $permissionStatus ) ) {
82 throw new PermissionsError( 'delete', $permissionStatus );
83 }
84
85 # Read-only check...
86 if ( wfReadOnly() ) {
87 throw new ReadOnlyError;
88 }
89
90 # Better double-check that it hasn't been deleted yet!
91 $article->getPage()->loadPageData(
92 $request->wasPosted() ? WikiPage::READ_LATEST : WikiPage::READ_NORMAL
93 );
94 if ( !$article->getPage()->exists() ) {
95 $deleteLogPage = new LogPage( 'delete' );
96 $outputPage = $context->getOutput();
97 $outputPage->setPageTitle( $context->msg( 'cannotdelete-title', $title->getPrefixedText() ) );
98 $outputPage->wrapWikiMsg( "<div class=\"error mw-error-cannotdelete\">\n$1\n</div>",
99 [ 'cannotdelete', wfEscapeWikiText( $title->getPrefixedText() ) ]
100 );
101 $outputPage->addHTML(
102 Xml::element( 'h2', null, $deleteLogPage->getName()->text() )
103 );
104 LogEventsList::showLogExtract(
105 $outputPage,
106 'delete',
107 $title
108 );
109
110 return;
111 }
112
113 $deleteReasonList = $request->getText( 'wpDeleteReasonList', 'other' );
114 $deleteReason = $request->getText( 'wpReason' );
115
116 if ( $deleteReasonList == 'other' ) {
117 $reason = $deleteReason;
118 } elseif ( $deleteReason != '' ) {
119 // Entry from drop down menu + additional comment
120 $colonseparator = wfMessage( 'colon-separator' )->inContentLanguage()->text();
121 $reason = $deleteReasonList . $colonseparator . $deleteReason;
122 } else {
123 $reason = $deleteReasonList;
124 }
125
126 if ( $request->wasPosted() && $user->matchEditToken( $request->getVal( 'wpEditToken' ),
127 [ 'delete', $this->getTitle()->getPrefixedText() ] )
128 ) {
129 # Flag to hide all contents of the archived revisions
130
131 $suppress = $request->getCheck( 'wpSuppress' ) &&
132 $context->getAuthority()->isAllowed( 'suppressrevision' );
133
134 $article->doDelete( $reason, $suppress );
135
136 $this->watchlistManager->setWatch( $request->getCheck( 'wpWatch' ), $context->getAuthority(), $title );
137
138 return;
139 }
140
141 // Generate deletion reason
142 $hasHistory = false;
143 if ( !$reason ) {
144 try {
145 $reason = $article->getPage()
146 ->getAutoDeleteReason( $hasHistory );
147 } catch ( Exception $e ) {
148 # if a page is horribly broken, we still want to be able to
149 # delete it. So be lenient about errors here.
150 wfDebug( "Error while building auto delete summary: $e" );
151 $reason = '';
152 }
153 }
154
155 // If the page has a history, insert a warning
156 if ( $hasHistory ) {
157 $title = $this->getTitle();
158
159 // The following can use the real revision count as this is only being shown for users
160 // that can delete this page.
161 // This, as a side-effect, also makes sure that the following query isn't being run for
162 // pages with a larger history, unless the user has the 'bigdelete' right
163 // (and is about to delete this page).
165 $revisions = $edits = (int)$dbr->selectField(
166 'revision',
167 'COUNT(rev_page)',
168 [ 'rev_page' => $title->getArticleID() ],
169 __METHOD__
170 );
171
172 // @todo i18n issue/patchwork message
173 $context->getOutput()->addHTML(
174 '<strong class="mw-delete-warning-revisions">' .
175 $context->msg( 'historywarning' )->numParams( $revisions )->parse() .
176 $context->msg( 'word-separator' )->escaped() . $this->linkRenderer->makeKnownLink(
177 $title,
178 $context->msg( 'history' )->text(),
179 [],
180 [ 'action' => 'history' ] ) .
181 '</strong>'
182 );
183
184 if ( $title->isBigDeletion() ) {
186 $context->getOutput()->wrapWikiMsg( "<div class='error'>\n$1\n</div>\n",
187 [
188 'delete-warning-toobig',
190 ]
191 );
192 }
193 }
194
195 $this->tempConfirmDelete( $reason );
196 }
197
198 private function tempDeleteFile() {
199 $file = $this->getArticle()->getFile();
200 if ( !$file->exists() || !$file->isLocal() || $file->getRedirected() ) {
201 // Standard article deletion
202 $this->tempDeleteArticle();
203 return;
204 }
205 '@phan-var LocalFile $file';
206
207 $context = $this->getContext();
208 $services = MediaWikiServices::getInstance();
209 $deleter = new FileDeleteForm(
210 $file,
211 $context,
212 $services->getReadOnlyMode(),
213 $services->getRepoGroup(),
214 $services->getWatchlistManager(),
215 $this->linkRenderer,
216 $services->getUserOptionsLookup()
217 );
218 $deleter->execute();
219 }
220
224 private function tempConfirmDelete( string $reason ): void {
225 wfDebug( "Article::confirmDelete" );
226
227 $title = $this->getTitle();
228 $ctx = $this->getContext();
229 $outputPage = $ctx->getOutput();
230 $outputPage->setPageTitle( wfMessage( 'delete-confirm', $title->getPrefixedText() ) );
231 $outputPage->addBacklinkSubtitle( $title );
232 $outputPage->setRobotPolicy( 'noindex,nofollow' );
233 $outputPage->addModules( 'mediawiki.action.delete' );
234 $outputPage->addModuleStyles( 'mediawiki.action.styles' );
235
236 $backlinkCache = $this->backlinkCacheFactory->getBacklinkCache( $title );
237 if ( $backlinkCache->hasLinks( 'pagelinks' ) || $backlinkCache->hasLinks( 'templatelinks' ) ) {
238 $outputPage->addHtml(
239 Html::warningBox(
240 $outputPage->msg( 'deleting-backlinks-warning' )->parse(),
241 'plainlinks'
242 )
243 );
244 }
245
246 $subpageQueryLimit = 51;
247 $subpages = $title->getSubpages( $subpageQueryLimit );
248 $subpageCount = count( $subpages );
249 if ( $subpageCount > 0 ) {
250 $outputPage->addHtml(
251 Html::warningBox(
252 $outputPage->msg( 'deleting-subpages-warning', Message::numParam( $subpageCount ) )->parse(),
253 'plainlinks'
254 )
255 );
256 }
257 $outputPage->addWikiMsg( 'confirmdeletetext' );
258
259 $this->getHookRunner()->onArticleConfirmDelete( $this->getArticle(), $outputPage, $reason );
260
261 $user = $this->getContext()->getUser();
262 $services = MediaWikiServices::getInstance();
263 $checkWatch = $services->getUserOptionsLookup()->getBoolOption( $user, 'watchdeletion' ) ||
264 $this->watchlistManager->isWatched( $user, $title );
265
266 $outputPage->enableOOUI();
267
268 $fields = [];
269
270 $suppressAllowed = $this->getContext()->getAuthority()->isAllowed( 'suppressrevision' );
271 $dropDownReason = $ctx->msg( 'deletereason-dropdown' )->inContentLanguage()->text();
272 // Add additional specific reasons for suppress
273 if ( $suppressAllowed ) {
274 $dropDownReason .= "\n" . $ctx->msg( 'deletereason-dropdown-suppress' )
275 ->inContentLanguage()->text();
276 }
277
278 $options = Xml::listDropDownOptions(
279 $dropDownReason,
280 [ 'other' => $ctx->msg( 'deletereasonotherlist' )->inContentLanguage()->text() ]
281 );
282 $options = Xml::listDropDownOptionsOoui( $options );
283
284 $fields[] = new OOUI\FieldLayout(
285 new OOUI\DropdownInputWidget( [
286 'name' => 'wpDeleteReasonList',
287 'inputId' => 'wpDeleteReasonList',
288 'tabIndex' => 1,
289 'infusable' => true,
290 'value' => '',
291 'options' => $options
292 ] ),
293 [
294 'label' => $ctx->msg( 'deletecomment' )->text(),
295 'align' => 'top',
296 ]
297 );
298
299 // HTML maxlength uses "UTF-16 code units", which means that characters outside BMP
300 // (e.g. emojis) count for two each. This limit is overridden in JS to instead count
301 // Unicode codepoints.
302 $fields[] = new OOUI\FieldLayout(
303 new OOUI\TextInputWidget( [
304 'name' => 'wpReason',
305 'inputId' => 'wpReason',
306 'tabIndex' => 2,
307 'maxLength' => CommentStore::COMMENT_CHARACTER_LIMIT,
308 'infusable' => true,
309 'value' => $reason,
310 'autofocus' => true,
311 ] ),
312 [
313 'label' => $ctx->msg( 'deleteotherreason' )->text(),
314 'align' => 'top',
315 ]
316 );
317
318 if ( $user->isRegistered() ) {
319 $fields[] = new OOUI\FieldLayout(
320 new OOUI\CheckboxInputWidget( [
321 'name' => 'wpWatch',
322 'inputId' => 'wpWatch',
323 'tabIndex' => 3,
324 'selected' => $checkWatch,
325 ] ),
326 [
327 'label' => $ctx->msg( 'watchthis' )->text(),
328 'align' => 'inline',
329 'infusable' => true,
330 ]
331 );
332 }
333 if ( $suppressAllowed ) {
334 $fields[] = new OOUI\FieldLayout(
335 new OOUI\CheckboxInputWidget( [
336 'name' => 'wpSuppress',
337 'inputId' => 'wpSuppress',
338 'tabIndex' => 4,
339 ] ),
340 [
341 'label' => $ctx->msg( 'revdelete-suppress' )->text(),
342 'align' => 'inline',
343 'infusable' => true,
344 ]
345 );
346 }
347
348 $fields[] = new OOUI\FieldLayout(
349 new OOUI\ButtonInputWidget( [
350 'name' => 'wpConfirmB',
351 'inputId' => 'wpConfirmB',
352 'tabIndex' => 5,
353 'value' => $ctx->msg( 'deletepage' )->text(),
354 'label' => $ctx->msg( 'deletepage' )->text(),
355 'flags' => [ 'primary', 'destructive' ],
356 'type' => 'submit',
357 ] ),
358 [
359 'align' => 'top',
360 ]
361 );
362
363 $fieldset = new OOUI\FieldsetLayout( [
364 'label' => $ctx->msg( 'delete-legend' )->text(),
365 'id' => 'mw-delete-table',
366 'items' => $fields,
367 ] );
368
369 $form = new OOUI\FormLayout( [
370 'method' => 'post',
371 'action' => $title->getLocalURL( 'action=delete' ),
372 'id' => 'deleteconfirm',
373 ] );
374 $form->appendContent(
375 $fieldset,
376 new OOUI\HtmlSnippet(
377 Html::hidden( 'wpEditToken', $user->getEditToken( [ 'delete', $title->getPrefixedText() ] ) )
378 )
379 );
380
381 $outputPage->addHTML(
382 new OOUI\PanelLayout( [
383 'classes' => [ 'deletepage-wrapper' ],
384 'expanded' => false,
385 'padded' => true,
386 'framed' => true,
387 'content' => $form,
388 ] )
389 );
390
391 if ( $this->getContext()->getAuthority()->isAllowed( 'editinterface' ) ) {
392 $link = '';
393 if ( $suppressAllowed ) {
394 $link .= $this->linkRenderer->makeKnownLink(
395 $ctx->msg( 'deletereason-dropdown-suppress' )->inContentLanguage()->getTitle(),
396 $ctx->msg( 'delete-edit-reasonlist-suppress' )->text(),
397 [],
398 [ 'action' => 'edit' ]
399 );
400 $link .= $ctx->msg( 'pipe-separator' )->escaped();
401 }
402 $link .= $this->linkRenderer->makeKnownLink(
403 $ctx->msg( 'deletereason-dropdown' )->inContentLanguage()->getTitle(),
404 $ctx->msg( 'delete-edit-reasonlist' )->text(),
405 [],
406 [ 'action' => 'edit' ]
407 );
408 $outputPage->addHTML( '<p class="mw-delete-editreasons">' . $link . '</p>' );
409 }
410
411 $deleteLogPage = new LogPage( 'delete' );
412 $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
413 LogEventsList::showLogExtract( $outputPage, 'delete', $title );
414 }
415
416 public function doesWrites() {
417 return true;
418 }
419}
getAuthority()
$wgDeleteRevisionsLimit
Optional to restrict deletion of pages with higher revision counts to users with the 'bigdelete' perm...
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfReadOnly()
Check whether the wiki is in read-only mode.
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
getHookRunner()
Definition Action.php:247
WikiPage Article ImagePage CategoryPage Page $page
Page on which we're performing the action.
Definition Action.php:53
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
Definition Action.php:441
Article $article
Definition Action.php:59
getTitle()
Shortcut to get the Title object from the page.
Definition Action.php:216
getContext()
Get the IContextSource in use here.
Definition Action.php:132
IContextSource $context
IContextSource if specified; otherwise we'll use the Context from the Page.
Definition Action.php:66
getArticle()
Get a Article object.
Definition Action.php:206
useTransactionalTimeLimit()
Call wfTransactionalTimeLimit() if this request was POSTed.
Definition Action.php:470
array $fields
The fields used to create the HTMLForm.
Definition Action.php:73
doDelete( $reason, $suppress=false, $immediate=false)
Perform a deletion and output success or failure messages.
Definition Article.php:1802
getPage()
Get the WikiPage object of this instance.
Definition Article.php:234
Handle page deletion.
onView()
Show something on GET request.
getName()
Return the name of the action this object responds to.
LinkRenderer $linkRenderer
__construct(Page $page, IContextSource $context=null)
Only public since 1.21.to call
WatchlistManager $watchlistManager
doesWrites()
Indicates whether this action may perform database writes.
BacklinkCacheFactory $backlinkCacheFactory
tempConfirmDelete(string $reason)
File deletion user interface.
An action which just does something, without showing a form first.
Class for viewing MediaWiki file description pages.
Definition ImagePage.php:34
static showLogExtract(&$out, $types=[], $page='', $user='', $param=[])
Show log extract.
Class to simplify the use of log pages.
Definition LogPage.php:38
Class that generates HTML links for pages.
MediaWikiServices is the service locator for the application scope of MediaWiki.
A StatusValue for permission errors.
static numParam( $num)
Definition Message.php:1101
Show an error when a user tries to do something they do not have the necessary permissions for.
Show an error when the wiki is locked/read-only and the user tries to do something that requires writ...
static listDropDownOptions( $list, $params=[])
Build options for a drop-down box from a textual list.
Definition Xml.php:545
static listDropDownOptionsOoui( $options)
Convert options for a drop-down box into a format accepted by OOUI\DropdownInputWidget etc.
Definition Xml.php:595
Interface for objects which can provide a MediaWiki context on request.
msg( $key,... $params)
This is the method for getting translated interface messages.
Interface for type hinting (accepts WikiPage, Article, ImagePage, CategoryPage)
Definition Page.php:29
const DB_REPLICA
Definition defines.php:25
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
Definition router.php:42