63 private $mTargetTimestamp = [];
67 private $mComment =
'';
78 private $mFileVersions = [];
80 private $mUndeleteTalk;
87 private $mSearchPrefix;
90 private $permissionManager;
93 private $revisionStore;
96 private $revisionRenderer;
99 private $contentHandlerFactory;
102 private $changeTagDefStore;
105 private $linkBatchFactory;
111 private $loadBalancer;
114 private $userOptionsLookup;
117 private $wikiPageFactory;
120 private $searchEngineFactory;
123 private $undeletePageFactory;
126 private $archivedRevisionLookup;
129 private $commentFormatter;
163 parent::__construct(
'Undelete',
'deletedhistory' );
164 $this->permissionManager = $permissionManager;
165 $this->revisionStore = $revisionStore;
166 $this->revisionRenderer = $revisionRenderer;
167 $this->contentHandlerFactory = $contentHandlerFactory;
168 $this->changeTagDefStore = $changeTagDefStore;
169 $this->linkBatchFactory = $linkBatchFactory;
171 $this->loadBalancer = $loadBalancer;
172 $this->userOptionsLookup = $userOptionsLookup;
173 $this->wikiPageFactory = $wikiPageFactory;
174 $this->searchEngineFactory = $searchEngineFactory;
175 $this->undeletePageFactory = $undeletePageFactory;
176 $this->archivedRevisionLookup = $archivedRevisionLookup;
177 $this->commentFormatter = $commentFormatter;
184 private function loadRequest( $par ) {
188 $this->mAction = $request->getRawVal(
'action' );
189 if ( $par !==
null && $par !==
'' ) {
190 $this->mTarget = $par;
192 $this->mTarget = $request->getVal(
'target' );
195 $this->mTargetObj =
null;
197 if ( $this->mTarget !==
null && $this->mTarget !==
'' ) {
198 $this->mTargetObj = Title::newFromText( $this->mTarget );
201 $this->mSearchPrefix = $request->getText(
'prefix' );
202 $time = $request->getVal(
'timestamp' );
203 $this->mTimestamp = $time ?
wfTimestamp( TS_MW, $time ) :
'';
204 $this->mFilename = $request->getVal(
'file' );
206 $posted = $request->wasPosted() &&
207 $user->matchEditToken( $request->getVal(
'wpEditToken' ) );
208 $this->mRestore = $request->getCheck(
'restore' ) && $posted;
209 $this->mRevdel = $request->getCheck(
'revdel' ) && $posted;
210 $this->mInvert = $request->getCheck(
'invert' ) && $posted;
211 $this->mPreview = $request->getCheck(
'preview' ) && $posted;
212 $this->mDiff = $request->getCheck(
'diff' );
213 $this->mDiffOnly = $request->getBool(
'diffonly',
214 $this->userOptionsLookup->getOption( $this->getUser(),
'diffonly' ) );
215 $this->mComment = $request->getText(
'wpComment' );
216 $this->mUnsuppress = $request->getVal(
'wpUnsuppress' ) &&
217 $this->permissionManager->userHasRight( $user,
'suppressrevision' );
218 $this->mToken = $request->getVal(
'token' );
219 $this->mUndeleteTalk = $request->getCheck(
'undeletetalk' );
222 $this->mAllowed =
true;
223 $this->mCanView =
true;
224 } elseif ( $this->
isAllowed(
'deletedtext' ) ) {
225 $this->mAllowed =
false;
226 $this->mCanView =
true;
227 $this->mRestore =
false;
229 $this->mAllowed =
false;
230 $this->mCanView =
false;
231 $this->mTimestamp =
'';
232 $this->mRestore =
false;
235 if ( $this->mRestore || $this->mInvert ) {
237 $this->mFileVersions = [];
238 foreach ( $request->getValues() as $key => $val ) {
240 if ( preg_match(
'/^ts(\d{14})$/', $key,
$matches ) ) {
241 array_push( $timestamps,
$matches[1] );
244 if ( preg_match(
'/^fileid(\d+)$/', $key,
$matches ) ) {
245 $this->mFileVersions[] = intval(
$matches[1] );
248 rsort( $timestamps );
249 $this->mTargetTimestamp = $timestamps;
262 $user = $user ?: $this->
getUser();
263 $block = $user->getBlock();
265 if ( $this->mTargetObj !==
null ) {
266 return $this->permissionManager->userCan( $permission, $user, $this->mTargetObj );
268 $hasRight = $this->permissionManager->userHasRight( $user, $permission );
269 $sitewideBlock = $block && $block->isSitewide();
270 return $permission ===
'undelete' ? ( $hasRight && !$sitewideBlock ) : $hasRight;
275 return $this->
isAllowed( $this->mRestriction, $user );
286 if ( !parent::userCanExecute( $user ) ) {
293 $this->mTargetObj && $this->permissionManager->isBlockedFrom( $user, $this->mTargetObj ) ) {
311 $this->
addHelpLink(
'Help:Deletion_and_undeletion' );
313 $this->loadRequest( $par );
318 if ( $this->mTargetObj ===
null ) {
319 $out->addWikiMsg(
'undelete-header' );
321 # Not all users can just browse every deleted page from the list
322 if ( $this->permissionManager->userHasRight( $user,
'browsearchive' ) ) {
323 $this->showSearchForm();
330 if ( $this->mAllowed ) {
331 $out->setPageTitle( $this->
msg(
'undeletepage' ) );
333 $out->setPageTitle( $this->
msg(
'viewdeletedpage' ) );
336 $this->
getSkin()->setRelevantTitle( $this->mTargetObj );
338 if ( $this->mTimestamp !==
'' ) {
339 $this->showRevision( $this->mTimestamp );
340 } elseif ( $this->mFilename !==
null && $this->mTargetObj->inNamespace(
NS_FILE ) ) {
343 if ( !
$file->exists() ) {
344 $out->addWikiMsg(
'filedelete-nofile', $this->mFilename );
351 } elseif ( !$user->matchEditToken( $this->mToken, $this->mFilename ) ) {
352 $this->showFileConfirmationForm( $this->mFilename );
354 $this->showFile( $this->mFilename );
356 } elseif ( $this->mAction ===
'submit' ) {
357 if ( $this->mRestore ) {
359 } elseif ( $this->mRevdel ) {
360 $this->redirectToRevDel();
372 private function redirectToRevDel() {
375 foreach ( $this->
getRequest()->getValues() as $key => $val ) {
377 if ( preg_match(
"/^ts(\d{14})$/", $key,
$matches ) ) {
378 $revisionRecord = $this->archivedRevisionLookup
379 ->getRevisionRecordByTimestamp( $this->mTargetObj,
$matches[1] );
380 if ( $revisionRecord ) {
382 $revisions[ $revisionRecord->getId() ] = 1;
388 'type' =>
'revision',
390 'target' => $this->mTargetObj->getPrefixedText()
396 private function showSearchForm() {
398 $out->setPageTitle( $this->
msg(
'undelete-search-title' ) );
399 $fuzzySearch = $this->
getRequest()->getVal(
'fuzzy',
'1' );
404 $fields[] =
new OOUI\ActionFieldLayout(
405 new OOUI\TextInputWidget( [
407 'inputId' =>
'prefix',
409 'value' => $this->mSearchPrefix,
412 new OOUI\ButtonInputWidget( [
413 'label' => $this->
msg(
'undelete-search-submit' )->text(),
414 'flags' => [
'primary',
'progressive' ],
415 'inputId' =>
'searchUndelete',
419 'label' =>
new OOUI\HtmlSnippet(
421 $fuzzySearch ?
'undelete-search-full' :
'undelete-search-prefix'
428 $fieldset =
new OOUI\FieldsetLayout( [
429 'label' => $this->
msg(
'undelete-search-box' )->text(),
433 $form =
new OOUI\FormLayout( [
438 $form->appendContent(
440 new OOUI\HtmlSnippet(
441 Html::hidden(
'title', $this->
getPageTitle()->getPrefixedDBkey() ) .
442 Html::hidden(
'fuzzy', $fuzzySearch )
447 new OOUI\PanelLayout( [
455 # List undeletable articles
456 if ( $this->mSearchPrefix ) {
459 if ( $fuzzySearch ) {
464 $this->showList( $result );
474 private function showList( $result ) {
477 if ( $result->numRows() == 0 ) {
478 $out->addWikiMsg(
'undelete-no-results' );
483 $out->addWikiMsg(
'undeletepagetext', $this->
getLanguage()->formatNum( $result->numRows() ) );
487 $out->addHTML(
"<ul id='undeleteResultsList'>\n" );
488 foreach ( $result as $row ) {
489 $title = Title::makeTitleSafe( $row->ar_namespace, $row->ar_title );
491 $item = $linkRenderer->makeKnownLink(
493 $title->getPrefixedText(),
495 [
'target' =>
$title->getPrefixedText() ]
499 $item = Html::element(
501 [
'class' =>
'mw-invalidtitle' ],
502 Linker::getInvalidTitleDescription(
509 $revs = $this->
msg(
'undeleterevisions' )->numParams( $row->count )->parse();
513 [
'class' =>
'undeleteResult' ],
514 $item . $this->
msg(
'word-separator' )->escaped() .
515 $this->
msg(
'parentheses' )->rawParams( $revs )->escaped()
520 $out->addHTML(
"</ul>\n" );
525 private function showRevision( $timestamp ) {
526 if ( !preg_match(
'/[0-9]{14}/', $timestamp ) ) {
530 $out->addModuleStyles(
'mediawiki.interface.helpers.styles' );
536 $this->
msg(
'undelete-back-to-list' )->text(),
538 [
'target' => $this->mTargetObj->getPrefixedText() ]
541 $subtitle =
"< $listLink";
542 $out->setSubtitle( $subtitle );
547 $archive, $this->mTargetObj )
551 $revRecord = $this->archivedRevisionLookup->getRevisionRecordByTimestamp( $this->mTargetObj, $timestamp );
556 $out->addWikiMsg(
'undeleterevision-missing' );
560 if ( $revRecord->
isDeleted( RevisionRecord::DELETED_TEXT ) ) {
562 $titleText = $this->mTargetObj->getPrefixedDBkey();
563 if ( !$revRecord->
userCan( RevisionRecord::DELETED_TEXT, $this->getAuthority() ) ) {
564 $msg = $revRecord->
isDeleted( RevisionRecord::DELETED_RESTRICTED )
565 ? [
'rev-suppressed-text-permission', $titleText ]
566 : [
'rev-deleted-text-permission', $titleText ];
569 $this->
msg( $msg[0], $msg[1] )->parse(),
576 $msg = $revRecord->
isDeleted( RevisionRecord::DELETED_RESTRICTED )
577 ? [
'rev-suppressed-text-view', $titleText ]
578 : [
'rev-deleted-text-view', $titleText ];
581 $this->
msg( $msg[0], $msg[1] )->parse(),
588 if ( $this->mDiff ) {
589 $previousRevRecord = $this->archivedRevisionLookup
590 ->getPreviousRevisionRecord( $this->mTargetObj, $timestamp );
591 if ( $previousRevRecord ) {
592 $this->showDiff( $previousRevRecord, $revRecord );
593 if ( $this->mDiffOnly ) {
597 $out->addHTML(
'<hr />' );
599 $out->addWikiMsg(
'undelete-nodiff' );
604 $this->
getPageTitle( $this->mTargetObj->getPrefixedDBkey() ),
605 $this->mTargetObj->getPrefixedText()
612 $time =
$lang->userTimeAndDate( $timestamp, $user );
613 $d =
$lang->userDate( $timestamp, $user );
614 $t =
$lang->userTime( $timestamp, $user );
615 $userLink = Linker::revUserTools( $revRecord );
619 RevisionRecord::FOR_THIS_USER,
626 $undeleteRevisionContent =
'';
628 if ( !$this->mDiff ) {
629 $revdel = Linker::getRevDeleteLink(
635 $undeleteRevisionContent = $revdel .
' ';
639 $undeleteRevisionContent .= $out->msg(
648 if ( $this->mPreview || $isText ) {
651 $undeleteRevisionContent,
652 'mw-undelete-revision'
659 [
'class' =>
'mw-undelete-revision', ],
660 $undeleteRevisionContent
665 if ( $this->mPreview || !$isText ) {
668 $popts = $out->parserOptions();
670 $rendered = $this->revisionRenderer->getRenderedRevision(
674 [
'audience' => RevisionRecord::FOR_THIS_USER,
'causeAction' =>
'undelete-preview' ]
679 $pout = $rendered->getRevisionParserOutput();
681 $out->addParserOutput( $pout, [
682 'enableSectionEditLinks' =>
false,
690 '@phan-var TextContent $content';
694 'readonly' =>
'readonly',
699 $buttonFields[] =
new OOUI\ButtonInputWidget( [
702 'label' => $this->
msg(
'showpreview' )->text()
708 $buttonFields[] =
new OOUI\ButtonInputWidget( [
711 'label' => $this->
msg(
'showdiff' )->text()
717 'style' =>
'clear: both' ] ) .
720 'action' => $this->
getPageTitle()->getLocalURL( [
'action' =>
'submit' ] ) ] ) .
724 'value' => $this->mTargetObj->getPrefixedDBkey() ] ) .
727 'name' =>
'timestamp',
728 'value' => $timestamp ] ) .
731 'name' =>
'wpEditToken',
732 'value' => $user->getEditToken() ] ) .
733 new OOUI\FieldLayout(
735 'content' =>
new OOUI\HorizontalLayout( [
736 'items' => $buttonFields
752 private function showDiff(
759 $diffContext->setTitle( $currentTitle );
760 $diffContext->setWikiPage( $this->wikiPageFactory->newFromTitle( $currentTitle ) );
762 $contentModel = $currentRevRecord->
getSlot(
767 $diffEngine = $this->contentHandlerFactory->getContentHandler( $contentModel )
768 ->createDifferenceEngine( $diffContext );
770 $diffEngine->setRevisions( $previousRevRecord, $currentRevRecord );
771 $diffEngine->showDiffStyle();
772 $formattedDiff = $diffEngine->getDiff(
773 $this->diffHeader( $previousRevRecord,
'o' ),
774 $this->diffHeader( $currentRevRecord,
'n' )
777 $this->
getOutput()->addHTML(
"<div>$formattedDiff</div>\n" );
785 private function diffHeader(
RevisionRecord $revRecord, $prefix ) {
790 'target' => $this->mTargetObj->getPrefixedText(),
796 $targetQuery = [
'oldid' => $revRecord->
getId() ];
802 $rdel = Linker::getRevDeleteLink( $user, $revRecord, $this->mTargetObj );
811 $tagIds =
$dbr->selectFieldValues(
814 [
'ct_rev_id' => $revRecord->
getId() ],
818 foreach ( $tagIds as $tagId ) {
820 $tags[] = $this->changeTagDefStore->getName( (
int)$tagId );
825 $tags = implode(
',', $tags );
838 if ( $revRecord->
isDeleted( RevisionRecord::DELETED_TEXT ) ) {
839 $asof = Html::rawElement(
841 [
'class' => Linker::getRevisionDeletedClass( $revRecord ) ],
848 return '<div id="mw-diff-' . $prefix .
'title1"><strong>' .
851 '<div id="mw-diff-' . $prefix .
'title2">' .
852 Linker::revUserTools( $revRecord ) .
'<br />' .
854 '<div id="mw-diff-' . $prefix .
'title3">' .
855 $minor . $this->commentFormatter->formatRevision( $revRecord, $user ) . $rdel .
'<br />' .
857 '<div id="mw-diff-' . $prefix .
'title5">' .
858 $tagSummary[0] .
'<br />' .
866 private function showFileConfirmationForm( $key ) {
871 $out->addWikiMsg(
'undelete-show-file-confirm',
872 $this->mTargetObj->getText(),
873 $lang->userDate(
$file->getTimestamp(), $user ),
874 $lang->userTime(
$file->getTimestamp(), $user ) );
879 'target' => $this->mTarget,
881 'token' => $user->getEditToken( $key ),
894 private function showFile( $key ) {
897 # We mustn't allow the output to be CDN cached, otherwise
898 # if an admin previews a deleted image, and it's cached, then
899 # a user without appropriate permissions can toddle off and
900 # nab the image, and CDN will serve it
902 $response->header(
'Expires: ' . gmdate(
'D, d M Y H:i:s', 0 ) .
' GMT' );
903 $response->header(
'Cache-Control: no-cache, no-store, max-age=0, must-revalidate' );
904 $response->header(
'Pragma: no-cache' );
906 $path = $this->localRepo->getZonePath(
'deleted' ) .
'/' . $this->localRepo->getDeletedHashPath( $key ) . $key;
907 $this->localRepo->streamFileWithStatus(
$path );
914 if ( $this->mAllowed ) {
915 $out->addModules(
'mediawiki.misc-authed-ooui' );
917 $out->addModuleStyles(
'mediawiki.interface.helpers.styles' );
919 "<div class='mw-undelete-pagetitle'>\n$1\n</div>\n",
920 [
'undeletepagetitle',
wfEscapeWikiText( $this->mTargetObj->getPrefixedText() ) ]
925 $this->
getHookRunner()->onUndeleteForm__showHistory( $archive, $this->mTargetObj );
927 $out->addHTML( Html::openElement(
'div', [
'class' =>
'mw-undelete-history' ] ) );
928 if ( $this->mAllowed ) {
929 $out->addWikiMsg(
'undeletehistory' );
930 $out->addWikiMsg(
'undeleterevdel' );
932 $out->addWikiMsg(
'undeletehistorynoadmin' );
934 $out->addHTML( Html::closeElement(
'div' ) );
936 # List all stored revisions
937 $revisions = $this->archivedRevisionLookup->listRevisions( $this->mTargetObj );
938 $files = $archive->listFiles();
940 $haveRevisions = $revisions && $revisions->numRows() > 0;
941 $haveFiles = $files && $files->numRows() > 0;
943 # Batch existence check on user and talk pages
944 if ( $haveRevisions || $haveFiles ) {
945 $batch = $this->linkBatchFactory->newLinkBatch();
946 if ( $haveRevisions ) {
947 foreach ( $revisions as $row ) {
948 $batch->add(
NS_USER, $row->ar_user_text );
951 $revisions->seek( 0 );
954 foreach ( $files as $row ) {
955 $batch->add(
NS_USER, $row->fa_user_text );
963 if ( $this->mAllowed ) {
966 $action = $this->
getPageTitle()->getLocalURL( [
'action' =>
'submit' ] );
967 # Start the form here
968 $form =
new OOUI\FormLayout( [
975 # Show relevant lines from the deletion log:
976 $deleteLogPage =
new LogPage(
'delete' );
977 $out->addHTML(
Xml::element(
'h2',
null, $deleteLogPage->getName()->text() ) .
"\n" );
979 # Show relevant lines from the suppression log:
980 $suppressLogPage =
new LogPage(
'suppress' );
981 if ( $this->permissionManager->userHasRight( $this->getUser(),
'suppressionlog' ) ) {
982 $out->addHTML(
Xml::element(
'h2',
null, $suppressLogPage->getName()->text() ) .
"\n" );
986 if ( $this->mAllowed && ( $haveRevisions || $haveFiles ) ) {
988 $fields[] =
new OOUI\Layout( [
989 'content' =>
new OOUI\HtmlSnippet( $this->
msg(
'undeleteextrahelp' )->parseAsBlock() )
992 $fields[] =
new OOUI\FieldLayout(
993 new OOUI\TextInputWidget( [
994 'name' =>
'wpComment',
995 'inputId' =>
'wpComment',
997 'value' => $this->mComment,
1002 'maxLength' => CommentStore::COMMENT_CHARACTER_LIMIT,
1005 'label' => $this->
msg(
'undeletecomment' )->text(),
1010 if ( $this->permissionManager->userHasRight( $this->getUser(),
'suppressrevision' ) ) {
1011 $fields[] =
new OOUI\FieldLayout(
1012 new OOUI\CheckboxInputWidget( [
1013 'name' =>
'wpUnsuppress',
1014 'inputId' =>
'mw-undelete-unsuppress',
1018 'label' => $this->
msg(
'revdelete-unsuppress' )->text(),
1019 'align' =>
'inline',
1024 $undelPage = $this->undeletePageFactory->newUndeletePage(
1025 $this->wikiPageFactory->newFromTitle( $this->mTargetObj ),
1026 $this->getContext()->getAuthority()
1028 if ( $undelPage->canProbablyUndeleteAssociatedTalk()->isGood() ) {
1029 $fields[] =
new OOUI\FieldLayout(
1030 new OOUI\CheckboxInputWidget( [
1031 'name' =>
'undeletetalk',
1032 'inputId' =>
'mw-undelete-undeletetalk',
1033 'selected' =>
false,
1036 'label' => $this->
msg(
'undelete-undeletetalk' )->text(),
1037 'align' =>
'inline',
1042 $fields[] =
new OOUI\FieldLayout(
1044 'content' =>
new OOUI\HorizontalLayout( [
1046 new OOUI\ButtonInputWidget( [
1047 'name' =>
'restore',
1048 'inputId' =>
'mw-undelete-submit',
1050 'label' => $this->
msg(
'undeletebtn' )->text(),
1051 'flags' => [
'primary',
'progressive' ],
1054 new OOUI\ButtonInputWidget( [
1056 'inputId' =>
'mw-undelete-invert',
1058 'label' => $this->
msg(
'undeleteinvert' )->text()
1065 $fieldset =
new OOUI\FieldsetLayout( [
1066 'label' => $this->
msg(
'undelete-fieldset-title' )->text(),
1067 'id' =>
'mw-undelete-table',
1072 $form->appendContent(
1073 new OOUI\PanelLayout( [
1074 'expanded' =>
false,
1077 'content' => $fieldset,
1079 new OOUI\HtmlSnippet(
1080 Html::hidden(
'target', $this->mTarget ) .
1081 Html::hidden(
'wpEditToken', $this->
getUser()->getEditToken() )
1087 $history .=
Xml::element(
'h2',
null, $this->
msg(
'history' )->text() ) .
"\n";
1089 if ( $haveRevisions ) {
1090 # Show the page's stored (deleted) history
1092 if ( $this->permissionManager->userHasRight( $this->getUser(),
'deleterevision' ) ) {
1093 $history .= Html::element(
1098 'class' =>
'deleterevision-log-submit mw-log-deleterevision-button'
1100 $this->
msg(
'showhideselectedversions' )->text()
1104 $history .= Html::openElement(
'ul', [
'class' =>
'mw-undelete-revlist' ] );
1105 $remaining = $revisions->numRows();
1106 $firstRev = $this->revisionStore->getFirstRevision( $this->mTargetObj );
1107 $earliestLiveTime = $firstRev ? $firstRev->getTimestamp() :
null;
1109 foreach ( $revisions as $row ) {
1114 $history .= Html::closeElement(
'ul' );
1116 $out->addWikiMsg(
'nohistory' );
1120 $history .=
Xml::element(
'h2',
null, $this->
msg(
'filehist' )->text() ) .
"\n";
1121 $history .= Html::openElement(
'ul', [
'class' =>
'mw-undelete-revlist' ] );
1122 foreach ( $files as $row ) {
1123 $history .= $this->formatFileRow( $row );
1126 $history .= Html::closeElement(
'ul' );
1129 if ( $this->mAllowed ) {
1130 # Slip in the hidden controls here
1131 $misc = Html::hidden(
'target', $this->mTarget );
1132 $misc .= Html::hidden(
'wpEditToken', $this->
getUser()->getEditToken() );
1136 $form->appendContent(
new OOUI\HtmlSnippet( $history ) );
1138 $out->addHTML( (
string)$form );
1140 $out->addHTML( $history );
1147 $revRecord = $this->revisionStore->newRevisionFromArchiveRow(
1149 RevisionStore::READ_NORMAL,
1156 if ( $this->mAllowed ) {
1157 if ( $this->mInvert ) {
1158 if ( in_array( $ts, $this->mTargetTimestamp ) ) {
1172 if ( $this->mCanView ) {
1175 if ( !$revRecord->
userCan( RevisionRecord::DELETED_TEXT, $this->getAuthority() ) ) {
1176 $pageLink = htmlspecialchars( $this->
getLanguage()->userTimeAndDate( $ts, $user ) );
1177 $last = $this->
msg(
'diff' )->escaped();
1178 } elseif ( $remaining > 0 || ( $earliestLiveTime && $ts > $earliestLiveTime ) ) {
1179 $pageLink = $this->getPageLink( $revRecord, $titleObj, $ts );
1182 $this->
msg(
'diff' )->text(),
1185 'target' => $this->mTargetObj->getPrefixedText(),
1191 $pageLink = $this->getPageLink( $revRecord, $titleObj, $ts );
1192 $last = $this->
msg(
'diff' )->escaped();
1195 $pageLink = htmlspecialchars( $this->
getLanguage()->userTimeAndDate( $ts, $user ) );
1196 $last = $this->
msg(
'diff' )->escaped();
1200 $userLink = Linker::revUserTools( $revRecord );
1206 $size = $row->ar_len;
1207 if ( $size !==
null ) {
1208 $revTextSize = Linker::formatRevisionSize( $size );
1212 $comment = $this->commentFormatter->formatRevision( $revRecord, $user );
1222 $attribs[
'class'] = implode(
' ', $classes );
1225 $revisionRow = $this->
msg(
'undelete-revision-row2' )
1238 return Xml::tags(
'li', $attribs, $revisionRow ) .
"\n";
1241 private function formatFileRow( $row ) {
1247 if ( $this->mCanView && $row->fa_storage_key ) {
1248 if ( $this->mAllowed ) {
1249 $checkBox =
Xml::check(
'fileid' . $row->fa_id );
1251 $key = urlencode( $row->fa_storage_key );
1254 $pageLink = htmlspecialchars( $this->
getLanguage()->userTimeAndDate( $ts, $user ) );
1256 $userLink = $this->getFileUser(
$file );
1257 $data = $this->
msg(
'widthheight' )->numParams( $row->fa_width, $row->fa_height )->text();
1258 $bytes = $this->
msg(
'parentheses' )
1259 ->plaintextParams( $this->
msg(
'nbytes' )->numParams( $row->fa_size )->text() )
1261 $data = htmlspecialchars( $data .
' ' . $bytes );
1262 $comment = $this->getFileComment(
$file );
1265 $canHide = $this->
isAllowed(
'deleterevision' );
1266 if ( $canHide || (
$file->getVisibility() && $this->isAllowed(
'deletedhistory' ) ) ) {
1269 $revdlink = Linker::revDeleteLinkDisabled( $canHide );
1272 'type' =>
'filearchive',
1273 'target' => $this->mTargetObj->getPrefixedDBkey(),
1274 'ids' => $row->fa_id
1276 $revdlink = Linker::revDeleteLink( $query,
1283 return "<li>$checkBox $revdlink $pageLink . . $userLink $data $comment</li>\n";
1294 private function getPageLink(
RevisionRecord $revRecord, $titleObj, $ts ) {
1296 $time = $this->
getLanguage()->userTimeAndDate( $ts, $user );
1298 if ( !$revRecord->
userCan( RevisionRecord::DELETED_TEXT, $this->getAuthority() ) ) {
1300 return Html::element(
1302 [
'class' =>
'history-deleted' ],
1312 'target' => $this->mTargetObj->getPrefixedText(),
1317 if ( $revRecord->
isDeleted( RevisionRecord::DELETED_TEXT ) ) {
1318 $class = Linker::getRevisionDeletedClass( $revRecord );
1319 $link =
'<span class="' . $class .
'">' . $link .
'</span>';
1335 private function getFileLink(
$file, $titleObj, $ts, $key ) {
1337 $time = $this->
getLanguage()->userTimeAndDate( $ts, $user );
1340 return Html::element(
1342 [
'class' =>
'history-deleted' ],
1352 'target' => $this->mTargetObj->getPrefixedText(),
1354 'token' => $user->getEditToken( $key )
1359 $link =
'<span class="history-deleted">' . $link .
'</span>';
1371 private function getFileUser(
$file ) {
1374 return Html::rawElement(
1376 [
'class' =>
'history-deleted' ],
1377 $this->
msg(
'rev-deleted-user' )->escaped()
1381 $link = Linker::userLink( $uploader->getId(), $uploader->getName() ) .
1382 Linker::userToolLinks( $uploader->getId(), $uploader->getName() );
1385 $link = Html::rawElement(
1387 [
'class' =>
'history-deleted' ],
1401 private function getFileComment(
$file ) {
1403 return Html::rawElement(
1405 [
'class' =>
'history-deleted' ],
1408 [
'class' =>
'comment' ],
1409 $this->
msg(
'rev-deleted-comment' )->escaped()
1415 $link = $this->commentFormatter->formatBlock( $comment );
1418 $link = Html::rawElement(
1420 [
'class' =>
'history-deleted' ],
1428 private function undelete() {
1429 if ( $this->
getConfig()->
get( MainConfigNames::UploadMaintenance )
1430 && $this->mTargetObj->getNamespace() ===
NS_FILE
1432 throw new ErrorPageError(
'undelete-error',
'filedelete-maintenance' );
1438 $undeletePage = $this->undeletePageFactory->newUndeletePage(
1439 $this->wikiPageFactory->newFromTitle( $this->mTargetObj ),
1440 $this->getAuthority()
1442 if ( $this->mUndeleteTalk && $undeletePage->canProbablyUndeleteAssociatedTalk()->isGood() ) {
1443 $undeletePage->setUndeleteAssociatedTalk(
true );
1445 $status = $undeletePage
1446 ->setUndeleteOnlyTimestamps( $this->mTargetTimestamp )
1447 ->setUndeleteOnlyFileVersions( $this->mFileVersions )
1448 ->setUnsuppress( $this->mUnsuppress )
1450 ->undeleteIfAllowed( $this->mComment );
1452 if ( !$status->isGood() ) {
1453 $out->setPageTitle( $this->
msg(
'undelete-error' ) );
1454 $out->wrapWikiTextAsInterface(
1465 $restoredRevs = $status->getValue()[UndeletePage::REVISIONS_RESTORED];
1466 $restoredFiles = $status->getValue()[UndeletePage::FILES_RESTORED];
1468 if ( $restoredRevs === 0 && $restoredFiles === 0 ) {
1470 $out->setPageTitle( $this->
msg(
'undelete-error' ) );
1472 if ( $status->getValue()[UndeletePage::FILES_RESTORED] !== 0 ) {
1474 $this->mTargetObj, $this->mFileVersions, $this->
getUser(), $this->mComment );
1491 return $this->
prefixSearchString( $search, $limit, $offset, $this->searchEngineFactory );
wfScript( $script='index')
Get the path to a specified script file, respecting file extensions; this is a wrapper around $wgScri...
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
Deleted file in the 'filearchive' table.
static newFromRow( $row)
Loads a file object from the filearchive table.
static flag( $flag, IContextSource $context=null)
Make an "<abbr>" element for a given change flag.
An error page which can definitely be safely rendered using the OutputPage.
static showLogExtract(&$out, $types=[], $page='', $user='', $param=[])
Show log extract.
Class to simplify the use of log pages.
A class containing constants representing the names of configuration variables.
Backend logic for performing a page undelete action.
Service for creating WikiPage objects.
Used to show archived pages and eventually restore them.
static listPagesBySearch( $term)
List deleted pages recorded in the archive matching the given term, using search engine archive.
static listPagesByPrefix( $prefix)
List deleted pages recorded in the archive table matching the given title prefix.
Show an error when a user tries to do something they do not have the necessary permissions for.
Prioritized list of file repositories.
getLocalRepo()
Get the local repository, i.e.
Factory class for SearchEngine.
Parent class for all special pages.
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.
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.
getAuthority()
Shortcut to get the Authority executing this instance.
getConfig()
Shortcut to get main config object.
getRequest()
Get the WebRequest being used for this instance.
checkReadOnly()
If the wiki is currently in readonly mode, throws a ReadOnlyError.
displayRestrictionError()
Output an error message telling the user what access level they have to have.
prefixSearchString( $search, $limit, $offset, SearchEngineFactory $searchEngineFactory=null)
Perform a regular substring search for prefixSearchSubpages.
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.
Special page allowing users with the appropriate permissions to view and restore deleted content.
prefixSearchSubpages( $search, $limit, $offset)
Return an array of subpages beginning with $search that this special page will accept.
__construct(PermissionManager $permissionManager, RevisionStore $revisionStore, RevisionRenderer $revisionRenderer, IContentHandlerFactory $contentHandlerFactory, NameTableStore $changeTagDefStore, LinkBatchFactory $linkBatchFactory, RepoGroup $repoGroup, ILoadBalancer $loadBalancer, UserOptionsLookup $userOptionsLookup, WikiPageFactory $wikiPageFactory, SearchEngineFactory $searchEngineFactory, UndeletePageFactory $undeletePageFactory, ArchivedRevisionLookup $archivedRevisionLookup, CommentFormatter $commentFormatter)
execute( $par)
Default execute method Checks user permissions.
checkPermissions()
Checks if userCanExecute, and if not throws a PermissionsError.Stability: stableto override 1....
doesWrites()
Indicates whether this special page may perform database writes.
isAllowed( $permission, User $user=null)
Checks whether a user is allowed the permission for the specific title if one is set.
userCanExecute(User $user)
Checks if the given user (identified by an object) can execute this special page (as defined by $mRes...
formatRevisionRow( $row, $earliestLiveTime, $remaining)
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
static wrap( $sv)
Succinct helper method to wrap a StatusValue.
Content object implementation for representing flat text.
Show an error when the user tries to do something whilst blocked.
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
static closeElement( $element)
Shortcut to close an XML element.
static check( $name, $checked=false, $attribs=[])
Convenience function to build an HTML checkbox.
static openElement( $element, $attribs=null)
This opens an XML element.
static submitButton( $value, $attribs=[])
Convenience function to build an HTML submit button When $wgUseMediaWikiUIEverywhere is true it will ...
static tags( $element, $attribs, $contents)
Same as Xml::element(), but does not escape contents.
static element( $element, $attribs=null, $contents='', $allowShortTag=true)
Format an XML element with given attributes and, optionally, text content.
Service for page undelete actions.
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
if(!isset( $args[0])) $lang