MediaWiki master
SpecialRevisionDelete.php
Go to the documentation of this file.
1<?php
7namespace MediaWiki\Specials;
8
27
36 protected $wasSaved = false;
37
39 private $submitClicked;
40
42 private $ids;
43
45 private $archiveName;
46
48 private $token;
49
51 private $targetObj;
52
54 private $typeName;
55
57 private $checks;
58
60 private $typeLabels;
61
63 private $revDelList;
64
66 private $mIsAllowed;
67
69 private $otherReason;
70
74 private const UI_LABELS = [
75 'revision' => [
76 'check-label' => 'revdelete-hide-text',
77 'success' => 'revdelete-success',
78 'failure' => 'revdelete-failure',
79 'text' => 'revdelete-text-text',
80 'selected' => 'revdelete-selected-text',
81 ],
82 'archive' => [
83 'check-label' => 'revdelete-hide-text',
84 'success' => 'revdelete-success',
85 'failure' => 'revdelete-failure',
86 'text' => 'revdelete-text-text',
87 'selected' => 'revdelete-selected-text',
88 ],
89 'oldimage' => [
90 'check-label' => 'revdelete-hide-image',
91 'success' => 'revdelete-success',
92 'failure' => 'revdelete-failure',
93 'text' => 'revdelete-text-file',
94 'selected' => 'revdelete-selected-file',
95 ],
96 'filearchive' => [
97 'check-label' => 'revdelete-hide-image',
98 'success' => 'revdelete-success',
99 'failure' => 'revdelete-failure',
100 'text' => 'revdelete-text-file',
101 'selected' => 'revdelete-selected-file',
102 ],
103 'logging' => [
104 'check-label' => 'revdelete-hide-name',
105 'success' => 'logdelete-success',
106 'failure' => 'logdelete-failure',
107 'text' => 'logdelete-text',
108 'selected' => 'logdelete-selected',
109 ],
110 ];
111
112 public function __construct(
113 private readonly PermissionManager $permissionManager,
114 private readonly RepoGroup $repoGroup,
115 ) {
116 parent::__construct( 'Revisiondelete' );
117 }
118
120 public function doesWrites() {
121 return true;
122 }
123
125 public function execute( $par ) {
127
128 $this->checkPermissions();
129 $this->checkReadOnly();
130
131 $output = $this->getOutput();
132 $user = $this->getUser();
133
134 $this->setHeaders();
135 $this->outputHeader();
136 $request = $this->getRequest();
137 $this->submitClicked = $request->wasPosted() && $request->getBool( 'wpSubmit' );
138 # Handle our many different possible input types.
139 $ids = $request->getVal( 'ids' );
140 if ( $ids !== null ) {
141 # Allow CSV, for backwards compatibility, or a single ID for show/hide links
142 $this->ids = explode( ',', $ids );
143 } else {
144 # Array input
145 $this->ids = array_keys( $request->getArray( 'ids', [] ) );
146 }
147 // $this->ids = array_map( 'intval', $this->ids );
148 $this->ids = array_unique( array_filter( $this->ids ) );
149
150 $this->typeName = $request->getVal( 'type' ) ?? '';
151 $this->targetObj = Title::newFromText( $request->getText( 'target' ) );
152
153 # For reviewing deleted files...
154 $this->archiveName = $request->getVal( 'file' );
155 $this->token = $request->getVal( 'token' );
156 if ( $this->archiveName && $this->targetObj ) {
157 $this->tryShowFile( $this->archiveName );
158
159 return;
160 }
161
162 $this->typeName = RevisionDeleter::getCanonicalTypeName( $this->typeName );
163
164 # No targets?
165 if ( !$this->typeName || count( $this->ids ) == 0 ) {
166 throw new ErrorPageError( 'revdelete-nooldid-title', 'revdelete-nooldid-text' );
167 }
168
169 $restriction = RevisionDeleter::getRestriction( $this->typeName );
170
171 if ( !$this->getAuthority()->isAllowedAny( $restriction, 'deletedhistory' ) ) {
172 throw new PermissionsError( $restriction );
173 }
174
175 # Allow the list type to adjust the passed target
176 $this->targetObj = RevisionDeleter::suggestTarget(
177 $this->typeName,
178 $this->targetObj,
179 $this->ids
180 );
181
182 # We need a target page!
183 if ( $this->targetObj === null ||
184 ( $this->typeName !== 'logging' && !$this->targetObj->canExist() )
185 ) {
186 $output->addWikiMsg( 'undelete-header' );
187
188 return;
189 }
190
191 // Check blocks
192 $checkReplica = !$this->submitClicked;
193 if (
194 $this->permissionManager->isBlockedFrom(
195 $user,
196 $this->targetObj,
197 $checkReplica
198 )
199 ) {
200 throw new UserBlockedError(
201 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable Block is checked and not null
202 $user->getBlock(),
203 $user,
204 $this->getLanguage(),
205 $request->getIP()
206 );
207 }
208
209 $this->typeLabels = self::UI_LABELS[$this->typeName];
210 $list = $this->getList();
211 $list->reset();
212 $this->mIsAllowed = $this->permissionManager->userHasRight( $user, $restriction );
213 $canViewSuppressedOnly = $this->permissionManager->userHasRight( $user, 'viewsuppressed' ) &&
214 !$this->permissionManager->userHasRight( $user, 'suppressrevision' );
215 $pageIsSuppressed = $list->areAnySuppressed();
216 $this->mIsAllowed = $this->mIsAllowed && !( $canViewSuppressedOnly && $pageIsSuppressed );
217
218 $this->otherReason = $request->getVal( 'wpReason', '' );
219 # Give a link to the logs/hist for this page
220 $this->showConvenienceLinks();
221
222 # Initialise checkboxes
223 $this->checks = [
224 # Messages: revdelete-hide-text, revdelete-hide-image, revdelete-hide-name
225 [ $this->typeLabels['check-label'], 'wpHidePrimary',
226 RevisionDeleter::getRevdelConstant( $this->typeName )
227 ],
228 [ 'revdelete-hide-comment', 'wpHideComment', RevisionRecord::DELETED_COMMENT ],
229 [ 'revdelete-hide-user', 'wpHideUser', RevisionRecord::DELETED_USER ]
230 ];
231 if ( $this->permissionManager->userHasRight( $user, 'suppressrevision' ) ) {
232 $this->checks[] = [ 'revdelete-hide-restricted',
233 'wpHideRestricted', RevisionRecord::DELETED_RESTRICTED ];
234 }
235
236 # Either submit or create our form
237 if ( $this->mIsAllowed && $this->submitClicked ) {
238 $this->submit();
239 } else {
240 $this->showForm();
241 }
242
243 if ( $this->permissionManager->userHasRight( $user, 'deletedhistory' ) ) {
244 # Show relevant lines from the deletion log
245 $deleteLogPage = new LogPage( 'delete' );
246 $output->addHTML( "<h2>" . $deleteLogPage->getName()->escaped() . "</h2>\n" );
247 LogEventsList::showLogExtract(
248 $output,
249 'delete',
250 $this->targetObj,
251 '', /* user */
252 [ 'lim' => 25, 'conds' => $this->getLogQueryCond(), 'useMaster' => $this->wasSaved ]
253 );
254 }
255 # Show relevant lines from the suppression log
256 if ( $this->permissionManager->userHasRight( $user, 'suppressionlog' ) ) {
257 $suppressLogPage = new LogPage( 'suppress' );
258 $output->addHTML( "<h2>" . $suppressLogPage->getName()->escaped() . "</h2>\n" );
259 LogEventsList::showLogExtract(
260 $output,
261 'suppress',
262 $this->targetObj,
263 '',
264 [ 'lim' => 25, 'conds' => $this->getLogQueryCond(), 'useMaster' => $this->wasSaved ]
265 );
266 }
267 }
268
272 protected function showConvenienceLinks() {
273 $linkRenderer = $this->getLinkRenderer();
274 # Give a link to the logs/hist for this page
275 if ( $this->targetObj ) {
276 // Also set header tabs to be for the target.
277 $this->getSkin()->setRelevantTitle( $this->targetObj );
278
279 $links = [];
280 $links[] = $linkRenderer->makeKnownLink(
282 $this->msg( 'viewpagelogs' )->text(),
283 [],
284 [ 'page' => $this->targetObj->getPrefixedText() ]
285 );
286 if ( !$this->targetObj->isSpecialPage() ) {
287 # Give a link to the page history
288 $links[] = $linkRenderer->makeKnownLink(
289 $this->targetObj,
290 $this->msg( 'pagehist' )->text(),
291 [],
292 [ 'action' => 'history' ]
293 );
294 # Link to deleted edits
295 if ( $this->permissionManager->userHasRight( $this->getUser(), 'undelete' ) ) {
296 $undelete = SpecialPage::getTitleFor( 'Undelete' );
297 $links[] = $linkRenderer->makeKnownLink(
298 $undelete,
299 $this->msg( 'deletedhist' )->text(),
300 [],
301 [ 'target' => $this->targetObj->getPrefixedDBkey() ]
302 );
303 }
304 }
305 # Logs themselves don't have histories or archived revisions
306 $this->getOutput()->addSubtitle( $this->getLanguage()->pipeList( $links ) );
307 }
308 }
309
314 protected function getLogQueryCond() {
315 $conds = [];
316 // Revision delete logs for these item
317 $conds['log_type'] = [ 'delete', 'suppress' ];
318 $conds['log_action'] = $this->getList()->getLogAction();
319 $conds['ls_field'] = RevisionDeleter::getRelationType( $this->typeName );
320 // Convert IDs to strings, since ls_value is a text field. This avoids
321 // a fatal error in PostgreSQL: "operator does not exist: text = integer".
322 $conds['ls_value'] = array_map( 'strval', $this->ids );
323
324 return $conds;
325 }
326
332 protected function tryShowFile( $archiveName ) {
333 if ( $this->targetObj->getNamespace() !== NS_FILE ) {
334 $this->getOutput()->addWikiMsg( 'revdelete-no-file' );
335
336 return;
337 }
338 $repo = $this->repoGroup->getLocalRepo();
339 $oimage = $repo->newFromArchiveName( $this->targetObj, $archiveName );
340 $oimage->load();
341 // Check if user is allowed to see this file
342 if ( !$oimage->exists() ) {
343 $this->getOutput()->addWikiMsg( 'revdelete-no-file' );
344
345 return;
346 }
347 $user = $this->getUser();
348 if ( !$oimage->userCan( File::DELETED_FILE, $user ) ) {
349 if ( $oimage->isDeleted( File::DELETED_RESTRICTED ) ) {
350 throw new PermissionsError( 'suppressrevision' );
351 } else {
352 throw new PermissionsError( 'deletedtext' );
353 }
354 }
355 if ( !$user->matchEditToken( $this->token, $archiveName ) ) {
356 $lang = $this->getLanguage();
357 $this->getOutput()->addWikiMsg( 'revdelete-show-file-confirm',
358 $this->targetObj->getText(),
359 $lang->userDate( $oimage->getTimestamp(), $user ),
360 $lang->userTime( $oimage->getTimestamp(), $user ) );
361 $this->getOutput()->addHTML(
362 Html::rawElement( 'form', [
363 'method' => 'POST',
364 'action' => $this->getPageTitle()->getLocalURL( [
365 'target' => $this->targetObj->getPrefixedDBkey(),
366 'file' => $archiveName,
367 'token' => $user->getEditToken( $archiveName ),
368 ] )
369 ],
370 Html::submitButton( $this->msg( 'revdelete-show-file-submit' )->text() )
371 )
372 );
373
374 return;
375 }
376 $this->getOutput()->disable();
377 # We mustn't allow the output to be CDN cached, otherwise
378 # if an admin previews a deleted image, and it's cached, then
379 # a user without appropriate permissions can toddle off and
380 # nab the image, and CDN will serve it
381 $this->getRequest()->response()->header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', 0 ) . ' GMT' );
382 $this->getRequest()->response()->header(
383 'Cache-Control: no-cache, no-store, max-age=0, must-revalidate'
384 );
385
386 $key = $oimage->getStorageKey();
387 $path = $repo->getZonePath( 'deleted' ) . '/' . $repo->getDeletedHashPath( $key ) . $key;
388 $repo->streamFileWithStatus( $path );
389 }
390
395 protected function getList() {
396 if ( $this->revDelList === null ) {
397 $this->revDelList = RevisionDeleter::createList(
398 $this->typeName, $this->getContext(), $this->targetObj, $this->ids
399 );
400 }
401
402 return $this->revDelList;
403 }
404
409 protected function showForm() {
410 $userAllowed = true;
411
412 // Messages: revdelete-selected-text, revdelete-selected-file, logdelete-selected
413 $out = $this->getOutput();
414 $out->wrapWikiMsg( "<strong>$1</strong>", [ $this->typeLabels['selected'],
415 $this->getLanguage()->formatNum( count( $this->ids ) ), $this->targetObj->getPrefixedText() ] );
416
417 $this->addHelpLink( 'Help:RevisionDelete' );
418 $out->addHTML( "<ul>" );
419
420 $numRevisions = 0;
421 // Live revisions...
422 $list = $this->getList();
423 foreach ( $list as $item ) {
424 if ( !$item->canView() ) {
425 if ( !$this->submitClicked ) {
426 throw new PermissionsError( 'suppressrevision' );
427 }
428 $userAllowed = false;
429 }
430
431 $numRevisions++;
432 $out->addHTML( $item->getHTML() );
433 }
434
435 if ( !$numRevisions ) {
436 throw new ErrorPageError( 'revdelete-nooldid-title', 'revdelete-nooldid-text' );
437 }
438
439 $out->addHTML( "</ul>" );
440 // Explanation text
441 $this->addUsageText();
442
443 // Normal sysops can always see what they did, but can't always change it
444 if ( !$userAllowed ) {
445 return;
446 }
447
448 // Show form if the user can submit
449 if ( $this->mIsAllowed ) {
450 $suppressAllowed = $this->permissionManager
451 ->userHasRight( $this->getUser(), 'suppressrevision' );
452 $out->addModules( [ 'mediawiki.misc-authed-ooui' ] );
453 $out->addModuleStyles( [ 'mediawiki.special',
454 'mediawiki.interface.helpers.styles' ] );
455
456 $dropdownReason = $this->msg( 'revdelete-reason-dropdown' )
457 ->page( $this->targetObj )->inContentLanguage()->text();
458 // Add additional specific reasons for suppress
459 if ( $suppressAllowed ) {
460 $dropdownReason .= "\n" . $this->msg( 'revdelete-reason-dropdown-suppress' )
461 ->page( $this->targetObj )->inContentLanguage()->text();
462 }
463
464 $fields = $this->buildCheckBoxes();
465
466 $fields[] = [
467 'type' => 'select',
468 'label' => $this->msg( 'revdelete-log' )->text(),
469 'cssclass' => 'wpReasonDropDown',
470 'id' => 'wpRevDeleteReasonList',
471 'name' => 'wpRevDeleteReasonList',
472 'options' => Html::listDropdownOptions(
473 $dropdownReason,
474 [ 'other' => $this->msg( 'revdelete-reasonotherlist' )->text() ]
475 ),
476 'default' => $this->getRequest()->getText( 'wpRevDeleteReasonList', 'other' )
477 ];
478
479 $fields[] = [
480 'type' => 'text',
481 'label' => $this->msg( 'revdelete-otherreason' )->text(),
482 'name' => 'wpReason',
483 'id' => 'wpReason',
484 // HTML maxlength uses "UTF-16 code units", which means that characters outside BMP
485 // (e.g. emojis) count for two each. This limit is overridden in JS to instead count
486 // Unicode codepoints.
487 // "- 155" is to leave room for the 'wpRevDeleteReasonList' value.
488 'maxlength' => CommentStore::COMMENT_CHARACTER_LIMIT - 155,
489 ];
490
491 $fields[] = [
492 'type' => 'hidden',
493 'name' => 'wpEditToken',
494 'default' => $this->getUser()->getEditToken()
495 ];
496
497 $fields[] = [
498 'type' => 'hidden',
499 'name' => 'target',
500 'default' => $this->targetObj->getPrefixedText()
501 ];
502
503 $fields[] = [
504 'type' => 'hidden',
505 'name' => 'type',
506 'default' => $this->typeName
507 ];
508
509 $fields[] = [
510 'type' => 'hidden',
511 'name' => 'ids',
512 'default' => implode( ',', $this->ids )
513 ];
514
515 $htmlForm = HTMLForm::factory( 'ooui', $fields, $this->getContext() );
516 $htmlForm
517 ->setSubmitText( $this->msg( 'revdelete-submit', $numRevisions )->text() )
518 ->setSubmitName( 'wpSubmit' )
519 ->setWrapperLegend( $this->msg( 'revdelete-legend' )->text() )
520 ->setAction( $this->getPageTitle()->getLocalURL( [ 'action' => 'submit' ] ) )
521 ->loadData();
522 // Show link to edit the dropdown reasons
523 if ( $this->permissionManager->userHasRight( $this->getUser(), 'editinterface' ) ) {
524 $link = '';
525 $linkRenderer = $this->getLinkRenderer();
526 if ( $suppressAllowed ) {
527 $link .= $linkRenderer->makeKnownLink(
528 $this->msg( 'revdelete-reason-dropdown-suppress' )->inContentLanguage()->getTitle(),
529 $this->msg( 'revdelete-edit-reasonlist-suppress' )->text(),
530 [],
531 [ 'action' => 'edit' ]
532 );
533 $link .= $this->msg( 'pipe-separator' )->escaped();
534 }
535 $link .= $linkRenderer->makeKnownLink(
536 $this->msg( 'revdelete-reason-dropdown' )->inContentLanguage()->getTitle(),
537 $this->msg( 'revdelete-edit-reasonlist' )->text(),
538 [],
539 [ 'action' => 'edit' ]
540 );
541 $htmlForm->setPostHtml( Html::rawElement( 'p', [ 'class' => 'mw-revdel-editreasons' ], $link ) );
542 }
543 $out->addHTML( $htmlForm->getHTML( false ) );
544 }
545 }
546
551 protected function addUsageText() {
552 // Messages: revdelete-text-text, revdelete-text-file, logdelete-text
553 $this->getOutput()->wrapWikiMsg(
554 "<strong>$1</strong>\n$2", $this->typeLabels['text'],
555 'revdelete-text-others'
556 );
557
558 if ( $this->permissionManager->userHasRight( $this->getUser(), 'suppressrevision' ) ) {
559 $this->getOutput()->addWikiMsg( 'revdelete-suppress-text' );
560 }
561
562 if ( $this->mIsAllowed ) {
563 $this->getOutput()->addWikiMsg( 'revdelete-confirm' );
564 }
565 }
566
570 protected function buildCheckBoxes() {
571 $fields = [];
572
573 $type = 'radio';
574
575 $list = $this->getList();
576
577 // If there is just one item, use checkboxes
578 if ( $list->length() == 1 ) {
579 $list->reset();
580
581 $type = 'check';
582 }
583
584 foreach ( $this->checks as $item ) {
585 // Messages: revdelete-hide-text, revdelete-hide-image, revdelete-hide-name,
586 // revdelete-hide-comment, revdelete-hide-user, revdelete-hide-restricted
587 [ $message, $name, $bitField ] = $item;
588
589 $field = [
590 'type' => $type,
591 'label-raw' => $this->msg( $message )->escaped(),
592 'id' => $name,
593 'flatlist' => true,
594 'name' => $name,
595 'default' => $list->length() == 1 ? $list->current()->getBits() & $bitField : null
596 ];
597
598 if ( $bitField == RevisionRecord::DELETED_RESTRICTED ) {
599 $field['label-raw'] = "<b>" . $field['label-raw'] . "</b>";
600 if ( $type === 'radio' ) {
601 $field['options-messages'] = [
602 'revdelete-radio-same' => -1,
603 'revdelete-radio-unset-suppress' => 0,
604 'revdelete-radio-set-suppress' => 1
605 ];
606 }
607 } elseif ( $type === 'radio' ) {
608 $field['options-messages'] = [
609 'revdelete-radio-same' => -1,
610 'revdelete-radio-unset' => 0,
611 'revdelete-radio-set' => 1
612 ];
613 }
614
615 $fields[] = $field;
616 }
617
618 return $fields;
619 }
620
626 protected function submit() {
627 # Check edit token on submission
628 $token = $this->getRequest()->getVal( 'wpEditToken' );
629 if ( $this->submitClicked && !$this->getUser()->matchEditToken( $token ) ) {
630 $this->getOutput()->addWikiMsg( 'sessionfailure' );
631
632 return false;
633 }
634 $bitParams = $this->extractBitParams();
635 // from dropdown
636 $listReason = $this->getRequest()->getText( 'wpRevDeleteReasonList', 'other' );
637 $comment = $listReason;
638 if ( $comment === 'other' ) {
639 $comment = $this->otherReason;
640 } elseif ( $this->otherReason !== '' ) {
641 // Entry from drop down menu + additional comment
642 $comment .= $this->msg( 'colon-separator' )->inContentLanguage()->text()
643 . $this->otherReason;
644 }
645 # Can the user set this field?
646 if ( $bitParams[RevisionRecord::DELETED_RESTRICTED] == 1
647 && !$this->permissionManager->userHasRight( $this->getUser(), 'suppressrevision' )
648 ) {
649 throw new PermissionsError( 'suppressrevision' );
650 }
651 # If the save went through, go to success message...
652 $status = $this->save( $bitParams, $comment );
653 if ( $status->isGood() ) {
654 $this->success();
655
656 return true;
657 } else {
658 # ...otherwise, bounce back to form...
659 $this->failure( $status );
660 }
661
662 return false;
663 }
664
668 protected function success() {
669 // Messages: revdelete-success, logdelete-success
670 $out = $this->getOutput();
671 $out->setPageTitleMsg( $this->msg( 'actioncomplete' ) );
672 $out->addHTML(
673 Html::successBox(
674 $out->msg( $this->typeLabels['success'] )->parse()
675 )
676 );
677 $this->wasSaved = true;
678 $this->revDelList->reloadFromPrimary();
679 $this->showForm();
680 }
681
686 protected function failure( $status ) {
687 // Messages: revdelete-failure, logdelete-failure
688 $out = $this->getOutput();
689 $out->setPageTitleMsg( $this->msg( 'actionfailed' ) );
690 $out->addModuleStyles( 'mediawiki.codex.messagebox.styles' );
691 $out->addHTML(
692 Html::errorBox(
693 $out->parseAsContent(
694 $status->getWikiText( $this->typeLabels['failure'], false, $this->getLanguage() )
695 )
696 )
697 );
698 $this->showForm();
699 }
700
706 protected function extractBitParams() {
707 $bitfield = [];
708 foreach ( $this->checks as [ /* message */, $name, $field ] ) {
709 $val = $this->getRequest()->getInt( $name, 0 /* unchecked */ );
710 if ( $val < -1 || $val > 1 ) {
711 $val = -1; // -1 for existing value
712 }
713 $bitfield[$field] = $val;
714 }
715 if ( !isset( $bitfield[RevisionRecord::DELETED_RESTRICTED] ) ) {
716 $bitfield[RevisionRecord::DELETED_RESTRICTED] = 0;
717 }
718
719 return $bitfield;
720 }
721
728 protected function save( array $bitPars, $reason ) {
729 return $this->getList()->setVisibility(
730 [ 'value' => $bitPars, 'comment' => $reason ]
731 );
732 }
733
735 protected function getGroupName() {
736 return 'pagetools';
737 }
738}
739
744class_alias( SpecialRevisionDelete::class, 'SpecialRevisionDelete' );
const NS_FILE
Definition Defines.php:57
Handle database storage of comments such as edit summaries and log reasons.
An error page which can definitely be safely rendered using the OutputPage.
Show an error when a user tries to do something they do not have the necessary permissions for.
Show an error when the user tries to do something whilst blocked.
Implements some public methods and some protected utility functions which are required by multiple ch...
Definition File.php:80
Prioritized list of file repositories.
Definition RepoGroup.php:30
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition HTMLForm.php:207
This class is a collection of static functions that serve two purposes:
Definition Html.php:44
Class to simplify the use of log pages.
Definition LogPage.php:35
A service class for checking permissions To obtain an instance, use MediaWikiServices::getInstance()-...
General controller for RevDel, used by both SpecialRevisiondelete and ApiRevisionDelete.
Page revision base class.
Parent class for all special pages.
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!
getSkin()
Shortcut to get the skin being used for this instance.
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
getUser()
Shortcut to get the User executing this instance.
useTransactionalTimeLimit()
Call wfTransactionalTimeLimit() if this request was POSTed.
getPageTitle( $subpage=false)
Get a self-referential title object.
checkPermissions()
Checks if userCanExecute, and if not throws a PermissionsError.
checkReadOnly()
If the wiki is currently in readonly mode, throws a ReadOnlyError.
getContext()
Gets the context this SpecialPage is executed in.
getRequest()
Get the WebRequest being used for this instance.
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getOutput()
Get the OutputPage being used for this instance.
getAuthority()
Shortcut to get the Authority executing this instance.
getLanguage()
Shortcut to get user's language.
outputHeader( $summaryMessageKey='')
Outputs a summary message on top of special pages By default the message key is the canonical name of...
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
Shortcut to construct a special page which is unlisted by default.
Special page allowing users with the appropriate permissions to view and hide revisions.
doesWrites()
Indicates whether POST requests to this special page require write access to the wiki....
showForm()
Show a list of items that we will operate on, and show a form with checkboxes which will allow the us...
__construct(private readonly PermissionManager $permissionManager, private readonly RepoGroup $repoGroup,)
extractBitParams()
Put together an array that contains -1, 0, or the *_deleted const for each bit.
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
getLogQueryCond()
Get the condition used for fetching log snippets.
failure( $status)
Report that the submit operation failed.
save(array $bitPars, $reason)
Do the write operations.
execute( $par)
Default execute method Checks user permissions.This must be overridden by subclasses; it will be made...
tryShowFile( $archiveName)
Show a deleted file version requested by the visitor.
success()
Report that the submit operation succeeded.
submit()
UI entry point for form submission.
getList()
Get the list object for this request.
bool $wasSaved
Was the DB modified in this request.
showConvenienceLinks()
Show some useful links in the subtitle.
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