81 'check-label' =>
'revdelete-hide-text',
82 'success' =>
'revdelete-success',
83 'failure' =>
'revdelete-failure',
84 'text' =>
'revdelete-text-text',
85 'selected' =>
'revdelete-selected-text',
88 'check-label' =>
'revdelete-hide-text',
89 'success' =>
'revdelete-success',
90 'failure' =>
'revdelete-failure',
91 'text' =>
'revdelete-text-text',
92 'selected' =>
'revdelete-selected-text',
95 'check-label' =>
'revdelete-hide-image',
96 'success' =>
'revdelete-success',
97 'failure' =>
'revdelete-failure',
98 'text' =>
'revdelete-text-file',
99 'selected' =>
'revdelete-selected-file',
102 'check-label' =>
'revdelete-hide-image',
103 'success' =>
'revdelete-success',
104 'failure' =>
'revdelete-failure',
105 'text' =>
'revdelete-text-file',
106 'selected' =>
'revdelete-selected-file',
109 'check-label' =>
'revdelete-hide-name',
110 'success' =>
'logdelete-success',
111 'failure' =>
'logdelete-failure',
112 'text' =>
'logdelete-text',
113 'selected' =>
'logdelete-selected',
124 parent::__construct(
'Revisiondelete',
'deleterevision' );
146 $this->submitClicked = $request->wasPosted() && $request->getBool(
'wpSubmit' );
147 # Handle our many different possible input types.
148 $ids = $request->getVal(
'ids' );
149 if (
$ids !==
null ) {
150 # Allow CSV, for backwards compatibility, or a single ID for show/hide links
151 $this->ids = explode(
',',
$ids );
154 $this->ids = array_keys( $request->getArray(
'ids', [] ) );
157 $this->ids = array_unique( array_filter( $this->ids ) );
159 $this->typeName = $request->getVal(
'type' );
160 $this->targetObj = Title::newFromText( $request->getText(
'target' ) );
162 # For reviewing deleted files...
163 $this->archiveName = $request->getVal(
'file' );
164 $this->token = $request->getVal(
'token' );
165 if ( $this->archiveName && $this->targetObj ) {
174 if ( !$this->typeName || count( $this->ids ) == 0 ) {
175 throw new ErrorPageError(
'revdelete-nooldid-title',
'revdelete-nooldid-text' );
178 # Allow the list type to adjust the passed target
185 # We need a target page!
186 if ( $this->targetObj ===
null ) {
187 $output->addWikiMsg(
'undelete-header' );
193 if ( $this->permissionManager->isBlockedFrom( $user, $this->targetObj ) ) {
197 $this->getLanguage(),
205 $this->mIsAllowed = $this->permissionManager->userHasRight( $user,
207 $canViewSuppressedOnly = $this->permissionManager->userHasRight( $user,
'viewsuppressed' ) &&
208 !$this->permissionManager->userHasRight( $user,
'suppressrevision' );
209 $pageIsSuppressed = $list->areAnySuppressed();
210 $this->mIsAllowed = $this->mIsAllowed && !( $canViewSuppressedOnly && $pageIsSuppressed );
212 $this->otherReason = $request->getVal(
'wpReason',
'' );
213 # Give a link to the logs/hist for this page
216 # Initialise checkboxes
218 # Messages: revdelete-hide-text, revdelete-hide-image, revdelete-hide-name
219 [ $this->typeLabels[
'check-label'],
'wpHidePrimary',
222 [
'revdelete-hide-comment',
'wpHideComment', RevisionRecord::DELETED_COMMENT ],
223 [
'revdelete-hide-user',
'wpHideUser', RevisionRecord::DELETED_USER ]
225 if ( $this->permissionManager->userHasRight( $user,
'suppressrevision' ) ) {
226 $this->checks[] = [
'revdelete-hide-restricted',
227 'wpHideRestricted', RevisionRecord::DELETED_RESTRICTED ];
230 # Either submit or create our form
231 if ( $this->mIsAllowed && $this->submitClicked ) {
237 if ( $this->permissionManager->userHasRight( $user,
'deletedhistory' ) ) {
239 # Show relevant lines from the deletion log
240 $deleteLogPage =
new LogPage(
'delete' );
241 $output->addHTML(
"<h2>" . $deleteLogPage->getName()->escaped() .
"</h2>\n" );
242 LogEventsList::showLogExtract(
247 [
'lim' => 25,
'conds' => $qc,
'useMaster' => $this->wasSaved ]
250 # Show relevant lines from the suppression log
251 if ( $this->permissionManager->userHasRight( $user,
'suppressionlog' ) ) {
252 $suppressLogPage =
new LogPage(
'suppress' );
253 $output->addHTML(
"<h2>" . $suppressLogPage->getName()->escaped() .
"</h2>\n" );
254 LogEventsList::showLogExtract(
259 [
'lim' => 25,
'conds' => $qc,
'useMaster' => $this->wasSaved ]
269 # Give a link to the logs/hist for this page
270 if ( $this->targetObj ) {
272 $this->
getSkin()->setRelevantTitle( $this->targetObj );
277 $this->
msg(
'viewpagelogs' )->text(),
279 [
'page' => $this->targetObj->getPrefixedText() ]
281 if ( !$this->targetObj->isSpecialPage() ) {
282 # Give a link to the page history
285 $this->
msg(
'pagehist' )->text(),
287 [
'action' =>
'history' ]
289 # Link to deleted edits
290 if ( $this->permissionManager->userHasRight( $this->getUser(),
'undelete' ) ) {
294 $this->
msg(
'deletedhist' )->text(),
296 [
'target' => $this->targetObj->getPrefixedDBkey() ]
300 # Logs themselves don't have histories or archived revisions
312 $conds[
'log_type'] = [
'delete',
'suppress' ];
313 $conds[
'log_action'] = $this->
getList()->getLogAction();
317 $conds[
'ls_value'] = array_map(
'strval', $this->ids );
330 $repo = $this->repoGroup->getLocalRepo();
331 $oimage = $repo->newFromArchiveName( $this->targetObj,
$archiveName );
334 if ( !$oimage->exists() ) {
335 $this->
getOutput()->addWikiMsg(
'revdelete-no-file' );
340 if ( !$oimage->userCan( File::DELETED_FILE, $user ) ) {
341 if ( $oimage->isDeleted( File::DELETED_RESTRICTED ) ) {
347 if ( !$user->matchEditToken( $this->token,
$archiveName ) ) {
349 $this->
getOutput()->addWikiMsg(
'revdelete-show-file-confirm',
350 $this->targetObj->getText(),
351 $lang->userDate( $oimage->getTimestamp(), $user ),
352 $lang->userTime( $oimage->getTimestamp(), $user ) );
354 Xml::openElement(
'form', [
357 'target' => $this->targetObj->getPrefixedDBkey(),
363 Xml::submitButton( $this->msg(
'revdelete-show-file-submit' )->text() ) .
370 # We mustn't allow the output to be CDN cached, otherwise
371 # if an admin previews a deleted image, and it's cached, then
372 # a user without appropriate permissions can toddle off and
373 # nab the image, and CDN will serve it
374 $this->
getRequest()->response()->header(
'Expires: ' . gmdate(
'D, d M Y H:i:s', 0 ) .
' GMT' );
376 'Cache-Control: no-cache, no-store, max-age=0, must-revalidate'
378 $this->
getRequest()->response()->header(
'Pragma: no-cache' );
380 $key = $oimage->getStorageKey();
381 $path = $repo->getZonePath(
'deleted' ) .
'/' . $repo->getDeletedHashPath( $key ) . $key;
382 $repo->streamFileWithStatus(
$path );
390 if ( $this->revDelList ===
null ) {
392 $this->typeName, $this->
getContext(), $this->targetObj, $this->ids
408 $out->wrapWikiMsg(
"<strong>$1</strong>", [ $this->typeLabels[
'selected'],
409 $this->
getLanguage()->formatNum( count( $this->ids ) ), $this->targetObj->getPrefixedText() ] );
412 $out->addHTML(
"<ul>" );
417 for ( $list->reset(); $list->current(); $list->next() ) {
418 $item = $list->current();
420 if ( !$item->canView() ) {
421 if ( !$this->submitClicked ) {
424 $userAllowed =
false;
428 $out->addHTML( $item->getHTML() );
431 if ( !$numRevisions ) {
432 throw new ErrorPageError(
'revdelete-nooldid-title',
'revdelete-nooldid-text' );
435 $out->addHTML(
"</ul>" );
440 if ( !$userAllowed ) {
445 if ( $this->mIsAllowed ) {
446 $suppressAllowed = $this->permissionManager
447 ->userHasRight( $this->
getUser(),
'suppressrevision' );
448 $out->addModules( [
'mediawiki.special.revisionDelete' ] );
449 $out->addModuleStyles( [
'mediawiki.special',
450 'mediawiki.interface.helpers.styles' ] );
452 $dropDownReason = $this->
msg(
'revdelete-reason-dropdown' )->inContentLanguage()->text();
454 if ( $suppressAllowed ) {
455 $dropDownReason .=
"\n" . $this->
msg(
'revdelete-reason-dropdown-suppress' )
456 ->inContentLanguage()->text();
459 $form = Xml::openElement(
'form', [
'method' =>
'post',
460 'action' => $this->
getPageTitle()->getLocalURL( [
'action' =>
'submit' ] ),
461 'id' =>
'mw-revdel-form-revisions' ] ) .
462 Xml::fieldset( $this->
msg(
'revdelete-legend' )->text() ) .
464 Xml::openElement(
'table' ) .
466 '<td class="mw-label">' .
467 Xml::label( $this->
msg(
'revdelete-log' )->text(),
'wpRevDeleteReasonList' ) .
469 '<td class="mw-input">' .
470 Xml::listDropDown(
'wpRevDeleteReasonList',
472 $this->
msg(
'revdelete-reasonotherlist' )->inContentLanguage()->text(),
473 $this->
getRequest()->getText(
'wpRevDeleteReasonList',
'other' ),
'wpReasonDropDown'
477 '<td class="mw-label">' .
478 Xml::label( $this->
msg(
'revdelete-otherreason' )->text(),
'wpReason' ) .
480 '<td class="mw-input">' .
481 Xml::input(
'wpReason', 60, $this->otherReason, [
487 'maxlength' => CommentStore::COMMENT_CHARACTER_LIMIT - 155,
492 '<td class="mw-submit">' .
493 Xml::submitButton( $this->
msg(
'revdelete-submit', $numRevisions )->text(),
494 [
'name' =>
'wpSubmit' ] ) .
497 Xml::closeElement(
'table' ) .
498 Html::hidden(
'wpEditToken', $this->
getUser()->getEditToken() ) .
499 Html::hidden(
'target', $this->targetObj->getPrefixedText() ) .
500 Html::hidden(
'type', $this->typeName ) .
501 Html::hidden(
'ids', implode(
',', $this->ids ) ) .
502 Xml::closeElement(
'fieldset' ) .
"\n" .
503 Xml::closeElement(
'form' ) .
"\n";
505 if ( $this->permissionManager->userHasRight( $this->getUser(),
'editinterface' ) ) {
508 if ( $suppressAllowed ) {
510 $this->
msg(
'revdelete-reason-dropdown-suppress' )->inContentLanguage()->
getTitle(),
511 $this->
msg(
'revdelete-edit-reasonlist-suppress' )->text(),
513 [
'action' =>
'edit' ]
515 $link .= $this->
msg(
'pipe-separator' )->escaped();
518 $this->
msg(
'revdelete-reason-dropdown' )->inContentLanguage()->
getTitle(),
519 $this->
msg(
'revdelete-edit-reasonlist' )->text(),
521 [
'action' =>
'edit' ]
523 $form .= Xml::tags(
'p', [
'class' =>
'mw-revdel-editreasons' ], $link ) .
"\n";
528 $out->addHTML( $form );
538 "<strong>$1</strong>\n$2", $this->typeLabels[
'text'],
539 'revdelete-text-others'
542 if ( $this->permissionManager->userHasRight( $this->getUser(),
'suppressrevision' ) ) {
543 $this->
getOutput()->addWikiMsg(
'revdelete-suppress-text' );
546 if ( $this->mIsAllowed ) {
547 $this->
getOutput()->addWikiMsg(
'revdelete-confirm' );
558 if ( $list->length() == 1 ) {
560 $bitfield = $list->current()->getBits();
562 if ( $this->submitClicked ) {
566 foreach ( $this->checks as $item ) {
569 list( $message, $name, $field ) = $item;
570 $innerHTML = Xml::checkLabel(
571 $this->
msg( $message )->text(),
577 if ( $field == RevisionRecord::DELETED_RESTRICTED ) {
578 $innerHTML =
"<b>$innerHTML</b>";
581 $line = Xml::tags(
'td', [
'class' =>
'mw-input' ], $innerHTML );
582 $html .=
"<tr>$line</tr>\n";
587 $html .=
'<th class="mw-revdel-checkbox">'
588 . $this->
msg(
'revdelete-radio-same' )->escaped() .
'</th>';
589 $html .=
'<th class="mw-revdel-checkbox">'
590 . $this->
msg(
'revdelete-radio-unset' )->escaped() .
'</th>';
591 $html .=
'<th class="mw-revdel-checkbox">'
592 . $this->
msg(
'revdelete-radio-set' )->escaped() .
'</th>';
593 $html .=
"<th></th></tr>\n";
594 foreach ( $this->checks as $item ) {
597 list( $message, $name, $field ) = $item;
599 if ( $this->submitClicked ) {
600 $selected = $this->
getRequest()->getInt( $name, 0 );
604 $line =
'<td class="mw-revdel-checkbox">' . Xml::radio( $name, -1, $selected == -1 ) .
'</td>';
605 $line .=
'<td class="mw-revdel-checkbox">' . Xml::radio( $name, 0, $selected == 0 ) .
'</td>';
606 $line .=
'<td class="mw-revdel-checkbox">' . Xml::radio( $name, 1, $selected == 1 ) .
'</td>';
607 $label = $this->
msg( $message )->escaped();
608 if ( $field == RevisionRecord::DELETED_RESTRICTED ) {
609 $label =
"<b>$label</b>";
611 $line .=
"<td>$label</td>";
612 $html .=
"<tr>$line</tr>\n";
627 # Check edit token on submission
629 if ( $this->submitClicked && !$this->
getUser()->matchEditToken(
$token ) ) {
630 $this->
getOutput()->addWikiMsg(
'sessionfailure' );
636 $listReason = $this->
getRequest()->getText(
'wpRevDeleteReasonList',
'other' );
637 $comment = $listReason;
638 if ( $comment ===
'other' ) {
640 } elseif ( $this->otherReason !==
'' ) {
642 $comment .= $this->
msg(
'colon-separator' )->inContentLanguage()->text()
645 # Can the user set this field?
646 if ( $bitParams[RevisionRecord::DELETED_RESTRICTED] == 1
647 && !$this->permissionManager->userHasRight( $this->getUser(),
'suppressrevision' )
651 # If the save went through, go to success message...
652 $status = $this->
save( $bitParams, $comment );
653 if ( $status->isGood() ) {
658 # ...otherwise, bounce back to form...
670 $this->
getOutput()->setPageTitle( $this->
msg(
'actioncomplete' ) );
672 "<div class=\"successbox\">\n$1\n</div>",
673 $this->typeLabels[
'success']
675 $this->wasSaved =
true;
676 $this->revDelList->reloadFromMaster();
686 $this->
getOutput()->setPageTitle( $this->
msg(
'actionfailed' ) );
687 $this->
getOutput()->wrapWikiTextAsInterface(
689 $status->getWikiText( $this->typeLabels[
'failure'],
false, $this->getLanguage() )
701 foreach ( $this->checks as $item ) {
702 list( , $name, $field ) = $item;
703 $val = $this->
getRequest()->getInt( $name, 0 );
704 if ( $val < -1 || $val > 1 ) {
707 $bitfield[$field] = $val;
709 if ( !isset( $bitfield[RevisionRecord::DELETED_RESTRICTED] ) ) {
710 $bitfield[RevisionRecord::DELETED_RESTRICTED] = 0;
722 protected function save( array $bitPars, $reason ) {
723 return $this->
getList()->setVisibility(
724 [
'value' => $bitPars,
'comment' => $reason ]
An error page which can definitely be safely rendered using the OutputPage.
Class to simplify the use of log pages.
Show an error when a user tries to do something they do not have the necessary permissions for.
Prioritized list of file repositories.
static getCanonicalTypeName( $typeName)
Gets the canonical type name, if any.
static createList( $typeName, IContextSource $context, Title $title, array $ids)
Instantiate the appropriate list class for a given list of IDs.
static getRelationType( $typeName)
Get DB field name for URL param... Future code for other things may also track other types of revisio...
static suggestTarget( $typeName, $target, array $ids)
Suggest a target for the revision deletion.
static extractBitfield(array $bitPars, $oldfield)
Put together a rev_deleted bitfield.
static getRevdelConstant( $typeName)
Get the revision deletion constant for the RevDel type.
static getRestriction( $typeName)
Get the user right required for the RevDel type.
outputHeader( $summaryMessageKey='')
Outputs a summary message on top of special pages Per default the message key is the canonical name o...
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!...
getOutput()
Get the OutputPage being used for this instance.
getUser()
Shortcut to get the User executing this instance.
getSkin()
Shortcut to get the skin being used for this instance.
checkPermissions()
Checks if userCanExecute, and if not throws a PermissionsError.
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,...
getContext()
Gets the context this SpecialPage is executed in.
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getRequest()
Get the WebRequest being used for this instance.
checkReadOnly()
If the wiki is currently in readonly mode, throws a ReadOnlyError.
getPageTitle( $subpage=false)
Get a self-referential title object.
useTransactionalTimeLimit()
Call wfTransactionalTimeLimit() if this request was POSTed.
getLanguage()
Shortcut to get user's language.
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
MediaWiki Linker LinkRenderer null $linkRenderer
Special page allowing users with the appropriate permissions to view and hide revisions.
string $token
Edit token for securing image views against XSS.
bool $mIsAllowed
Whether user is allowed to perform the action.
const UI_LABELS
UI labels for each type.
showForm()
Show a list of items that we will operate on, and show a form with checkboxes which will allow the us...
tryShowFile( $archiveName)
Show a deleted file version requested by the visitor.
string $archiveName
Archive name, for reviewing deleted files.
save(array $bitPars, $reason)
Do the write operations.
success()
Report that the submit operation succeeded.
addUsageText()
Show some introductory text.
array $typeLabels
UI Labels about the current type.
bool $wasSaved
Was the DB modified in this request.
getLogQueryCond()
Get the condition used for fetching log snippets.
PermissionManager $permissionManager
RevDelList $revDelList
RevDelList object, storing the list of items to be deleted/undeleted.
showConvenienceLinks()
Show some useful links in the subtitle.
doesWrites()
Indicates whether this special page may perform database writes.
execute( $par)
Default execute method Checks user permissions.
array $checks
Array of checkbox specs (message, name, deletion bits)
failure( $status)
Report that the submit operation failed.
__construct(PermissionManager $permissionManager, RepoGroup $repoGroup)
submit()
UI entry point for form submission.
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
Title $targetObj
Title object for target parameter.
extractBitParams()
Put together an array that contains -1, 0, or the *_deleted const for each bit.
bool $submitClicked
True if the submit button was clicked, and the form was posted.
string $typeName
Deletion type, may be revision, archive, oldimage, filearchive, logging.
getList()
Get the list object for this request.
array $ids
Target ID list.
Represents a title within MediaWiki.
Shortcut to construct a special page which is unlisted by default.
Show an error when the user tries to do something whilst blocked.
if(!isset( $args[0])) $lang