MediaWiki  master
SpecialUndelete.php
Go to the documentation of this file.
1 <?php
30 
38  private $mAction;
39  private $mTarget;
40  private $mTimestamp;
41  private $mRestore;
42  private $mRevdel;
43  private $mInvert;
44  private $mFilename;
46  private $mAllowed;
47  private $mCanView;
48  private $mComment;
49  private $mToken;
51  private $mPreview;
53  private $mDiff;
55  private $mDiffOnly;
57  private $mUnsuppress;
59  private $mFileVersions;
60 
62  private $mTargetObj;
66  private $mSearchPrefix;
67 
68  public function __construct() {
69  parent::__construct( 'Undelete', 'deletedhistory' );
70  }
71 
72  public function doesWrites() {
73  return true;
74  }
75 
76  private function loadRequest( $par ) {
77  $request = $this->getRequest();
78  $user = $this->getUser();
79 
80  $this->mAction = $request->getVal( 'action' );
81  if ( $par !== null && $par !== '' ) {
82  $this->mTarget = $par;
83  } else {
84  $this->mTarget = $request->getVal( 'target' );
85  }
86 
87  $this->mTargetObj = null;
88 
89  if ( $this->mTarget !== null && $this->mTarget !== '' ) {
90  $this->mTargetObj = Title::newFromText( $this->mTarget );
91  }
92 
93  $this->mSearchPrefix = $request->getText( 'prefix' );
94  $time = $request->getVal( 'timestamp' );
95  $this->mTimestamp = $time ? wfTimestamp( TS_MW, $time ) : '';
96  $this->mFilename = $request->getVal( 'file' );
97 
98  $posted = $request->wasPosted() &&
99  $user->matchEditToken( $request->getVal( 'wpEditToken' ) );
100  $this->mRestore = $request->getCheck( 'restore' ) && $posted;
101  $this->mRevdel = $request->getCheck( 'revdel' ) && $posted;
102  $this->mInvert = $request->getCheck( 'invert' ) && $posted;
103  $this->mPreview = $request->getCheck( 'preview' ) && $posted;
104  $this->mDiff = $request->getCheck( 'diff' );
105  $this->mDiffOnly = $request->getBool( 'diffonly', $this->getUser()->getOption( 'diffonly' ) );
106  $this->mComment = $request->getText( 'wpComment' );
107  $this->mUnsuppress = $request->getVal( 'wpUnsuppress' ) && MediaWikiServices::getInstance()
108  ->getPermissionManager()
109  ->userHasRight( $user, 'suppressrevision' );
110  $this->mToken = $request->getVal( 'token' );
111 
112  if ( $this->isAllowed( 'undelete' ) ) {
113  $this->mAllowed = true; // user can restore
114  $this->mCanView = true; // user can view content
115  } elseif ( $this->isAllowed( 'deletedtext' ) ) {
116  $this->mAllowed = false; // user cannot restore
117  $this->mCanView = true; // user can view content
118  $this->mRestore = false;
119  } else { // user can only view the list of revisions
120  $this->mAllowed = false;
121  $this->mCanView = false;
122  $this->mTimestamp = '';
123  $this->mRestore = false;
124  }
125 
126  if ( $this->mRestore || $this->mInvert ) {
127  $timestamps = [];
128  $this->mFileVersions = [];
129  foreach ( $request->getValues() as $key => $val ) {
130  $matches = [];
131  if ( preg_match( '/^ts(\d{14})$/', $key, $matches ) ) {
132  array_push( $timestamps, $matches[1] );
133  }
134 
135  if ( preg_match( '/^fileid(\d+)$/', $key, $matches ) ) {
136  $this->mFileVersions[] = intval( $matches[1] );
137  }
138  }
139  rsort( $timestamps );
140  $this->mTargetTimestamp = $timestamps;
141  }
142  }
143 
152  protected function isAllowed( $permission, User $user = null ) {
153  $user = $user ?: $this->getUser();
154  $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
155  $block = $user->getBlock();
156 
157  if ( $this->mTargetObj !== null ) {
158  return $permissionManager->userCan( $permission, $user, $this->mTargetObj );
159  } else {
160  $hasRight = $permissionManager->userHasRight( $user, $permission );
161  $sitewideBlock = $block && $block->isSitewide();
162  return $permission === 'undelete' ? ( $hasRight && !$sitewideBlock ) : $hasRight;
163  }
164  }
165 
166  public function userCanExecute( User $user ) {
167  return $this->isAllowed( $this->mRestriction, $user );
168  }
169 
173  public function checkPermissions() {
174  $user = $this->getUser();
175 
176  // First check if user has the right to use this page. If not,
177  // show a permissions error whether they are blocked or not.
178  if ( !parent::userCanExecute( $user ) ) {
179  $this->displayRestrictionError();
180  }
181 
182  // If a user has the right to use this page, but is blocked from
183  // the target, show a block error.
184  if (
185  $this->mTargetObj && MediaWikiServices::getInstance()
187  ->isBlockedFrom( $user, $this->mTargetObj )
188  ) {
189  throw new UserBlockedError( $user->getBlock() );
190  }
191 
192  // Finally, do the comprehensive permission check via isAllowed.
193  if ( !$this->userCanExecute( $user ) ) {
194  $this->displayRestrictionError();
195  }
196  }
197 
198  public function execute( $par ) {
199  $this->useTransactionalTimeLimit();
200 
201  $user = $this->getUser();
202 
203  $this->setHeaders();
204  $this->outputHeader();
205  $this->addHelpLink( 'Help:Deletion_and_undeletion' );
206 
207  $this->loadRequest( $par );
208  $this->checkPermissions(); // Needs to be after mTargetObj is set
209 
210  $out = $this->getOutput();
211 
212  if ( $this->mTargetObj === null ) {
213  $out->addWikiMsg( 'undelete-header' );
214 
215  # Not all users can just browse every deleted page from the list
216  if ( MediaWikiServices::getInstance()
218  ->userHasRight( $user, 'browsearchive' )
219  ) {
220  $this->showSearchForm();
221  }
222 
223  return;
224  }
225 
226  $this->addHelpLink( 'Help:Undelete' );
227  if ( $this->mAllowed ) {
228  $out->setPageTitle( $this->msg( 'undeletepage' ) );
229  } else {
230  $out->setPageTitle( $this->msg( 'viewdeletedpage' ) );
231  }
232 
233  $this->getSkin()->setRelevantTitle( $this->mTargetObj );
234 
235  if ( $this->mTimestamp !== '' ) {
236  $this->showRevision( $this->mTimestamp );
237  } elseif ( $this->mFilename !== null && $this->mTargetObj->inNamespace( NS_FILE ) ) {
238  $file = new ArchivedFile( $this->mTargetObj, 0, $this->mFilename );
239  // Check if user is allowed to see this file
240  if ( !$file->exists() ) {
241  $out->addWikiMsg( 'filedelete-nofile', $this->mFilename );
242  } elseif ( !$file->userCan( File::DELETED_FILE, $user ) ) {
243  if ( $file->isDeleted( File::DELETED_RESTRICTED ) ) {
244  throw new PermissionsError( 'suppressrevision' );
245  } else {
246  throw new PermissionsError( 'deletedtext' );
247  }
248  } elseif ( !$user->matchEditToken( $this->mToken, $this->mFilename ) ) {
249  $this->showFileConfirmationForm( $this->mFilename );
250  } else {
251  $this->showFile( $this->mFilename );
252  }
253  } elseif ( $this->mAction === 'submit' ) {
254  if ( $this->mRestore ) {
255  $this->undelete();
256  } elseif ( $this->mRevdel ) {
257  $this->redirectToRevDel();
258  }
259 
260  } else {
261  $this->showHistory();
262  }
263  }
264 
269  private function redirectToRevDel() {
270  $archive = new PageArchive( $this->mTargetObj );
271 
272  $revisions = [];
273 
274  foreach ( $this->getRequest()->getValues() as $key => $val ) {
275  $matches = [];
276  if ( preg_match( "/^ts(\d{14})$/", $key, $matches ) ) {
277  $revisions[$archive->getRevision( $matches[1] )->getId()] = 1;
278  }
279  }
280 
281  $query = [
282  'type' => 'revision',
283  'ids' => $revisions,
284  'target' => $this->mTargetObj->getPrefixedText()
285  ];
286  $url = SpecialPage::getTitleFor( 'Revisiondelete' )->getFullURL( $query );
287  $this->getOutput()->redirect( $url );
288  }
289 
290  private function showSearchForm() {
291  $out = $this->getOutput();
292  $out->setPageTitle( $this->msg( 'undelete-search-title' ) );
293  $fuzzySearch = $this->getRequest()->getVal( 'fuzzy', true );
294 
295  $out->enableOOUI();
296 
297  $fields = [];
298  $fields[] = new OOUI\ActionFieldLayout(
299  new OOUI\TextInputWidget( [
300  'name' => 'prefix',
301  'inputId' => 'prefix',
302  'infusable' => true,
303  'value' => $this->mSearchPrefix,
304  'autofocus' => true,
305  ] ),
306  new OOUI\ButtonInputWidget( [
307  'label' => $this->msg( 'undelete-search-submit' )->text(),
308  'flags' => [ 'primary', 'progressive' ],
309  'inputId' => 'searchUndelete',
310  'type' => 'submit',
311  ] ),
312  [
313  'label' => new OOUI\HtmlSnippet(
314  $this->msg(
315  $fuzzySearch ? 'undelete-search-full' : 'undelete-search-prefix'
316  )->parse()
317  ),
318  'align' => 'left',
319  ]
320  );
321 
322  $fieldset = new OOUI\FieldsetLayout( [
323  'label' => $this->msg( 'undelete-search-box' )->text(),
324  'items' => $fields,
325  ] );
326 
327  $form = new OOUI\FormLayout( [
328  'method' => 'get',
329  'action' => wfScript(),
330  ] );
331 
332  $form->appendContent(
333  $fieldset,
334  new OOUI\HtmlSnippet(
335  Html::hidden( 'title', $this->getPageTitle()->getPrefixedDBkey() ) .
336  Html::hidden( 'fuzzy', $fuzzySearch )
337  )
338  );
339 
340  $out->addHTML(
341  new OOUI\PanelLayout( [
342  'expanded' => false,
343  'padded' => true,
344  'framed' => true,
345  'content' => $form,
346  ] )
347  );
348 
349  # List undeletable articles
350  if ( $this->mSearchPrefix ) {
351  // For now, we enable search engine match only when specifically asked to
352  // by using fuzzy=1 parameter.
353  if ( $fuzzySearch ) {
354  $result = PageArchive::listPagesBySearch( $this->mSearchPrefix );
355  } else {
356  $result = PageArchive::listPagesByPrefix( $this->mSearchPrefix );
357  }
358  $this->showList( $result );
359  }
360  }
361 
368  private function showList( $result ) {
369  $out = $this->getOutput();
370 
371  if ( $result->numRows() == 0 ) {
372  $out->addWikiMsg( 'undelete-no-results' );
373 
374  return false;
375  }
376 
377  $out->addWikiMsg( 'undeletepagetext', $this->getLanguage()->formatNum( $result->numRows() ) );
378 
379  $linkRenderer = $this->getLinkRenderer();
380  $undelete = $this->getPageTitle();
381  $out->addHTML( "<ul id='undeleteResultsList'>\n" );
382  foreach ( $result as $row ) {
383  $title = Title::makeTitleSafe( $row->ar_namespace, $row->ar_title );
384  if ( $title !== null ) {
385  $item = $linkRenderer->makeKnownLink(
386  $undelete,
387  $title->getPrefixedText(),
388  [],
389  [ 'target' => $title->getPrefixedText() ]
390  );
391  } else {
392  // The title is no longer valid, show as text
393  $item = Html::element(
394  'span',
395  [ 'class' => 'mw-invalidtitle' ],
397  $this->getContext(),
398  $row->ar_namespace,
399  $row->ar_title
400  )
401  );
402  }
403  $revs = $this->msg( 'undeleterevisions' )->numParams( $row->count )->parse();
404  $out->addHTML(
406  'li',
407  [ 'class' => 'undeleteResult' ],
408  "{$item} ({$revs})"
409  )
410  );
411  }
412  $result->free();
413  $out->addHTML( "</ul>\n" );
414 
415  return true;
416  }
417 
418  private function showRevision( $timestamp ) {
419  if ( !preg_match( '/[0-9]{14}/', $timestamp ) ) {
420  return;
421  }
422 
423  $archive = new PageArchive( $this->mTargetObj, $this->getConfig() );
424  if ( !$this->getHookRunner()->onUndeleteForm__showRevision(
425  $archive, $this->mTargetObj )
426  ) {
427  return;
428  }
429  $rev = $archive->getRevision( $timestamp );
430 
431  $out = $this->getOutput();
432  $user = $this->getUser();
433 
434  if ( !$rev ) {
435  $out->addWikiMsg( 'undeleterevision-missing' );
436 
437  return;
438  }
439  $revRecord = $rev->getRevisionRecord();
440 
441  if ( $revRecord->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
442  if ( !RevisionRecord::userCanBitfield(
443  $revRecord->getVisibility(),
444  RevisionRecord::DELETED_TEXT,
445  $user
446  ) ) {
447  $out->wrapWikiMsg(
448  "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
449  $revRecord->isDeleted( RevisionRecord::DELETED_RESTRICTED ) ?
450  'rev-suppressed-text-permission' : 'rev-deleted-text-permission'
451  );
452 
453  return;
454  }
455 
456  $out->wrapWikiMsg(
457  "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
458  $revRecord->isDeleted( RevisionRecord::DELETED_RESTRICTED ) ?
459  'rev-suppressed-text-view' : 'rev-deleted-text-view'
460  );
461  $out->addHTML( '<br />' );
462  // and we are allowed to see...
463  }
464 
465  if ( $this->mDiff ) {
466  $previousRevRecord = $archive->getPreviousRevisionRecord( $timestamp );
467  if ( $previousRevRecord ) {
468  $this->showDiff( $previousRevRecord, $revRecord );
469  if ( $this->mDiffOnly ) {
470  return;
471  }
472 
473  $out->addHTML( '<hr />' );
474  } else {
475  $out->addWikiMsg( 'undelete-nodiff' );
476  }
477  }
478 
479  $link = $this->getLinkRenderer()->makeKnownLink(
480  $this->getPageTitle( $this->mTargetObj->getPrefixedDBkey() ),
481  $this->mTargetObj->getPrefixedText()
482  );
483 
484  $lang = $this->getLanguage();
485 
486  // date and time are separate parameters to facilitate localisation.
487  // $time is kept for backward compat reasons.
488  $time = $lang->userTimeAndDate( $timestamp, $user );
489  $d = $lang->userDate( $timestamp, $user );
490  $t = $lang->userTime( $timestamp, $user );
491  $userLink = Linker::revUserTools( $revRecord );
492 
493  $content = $revRecord->getContent(
494  SlotRecord::MAIN,
495  RevisionRecord::FOR_THIS_USER,
496  $user
497  );
498 
499  // TODO: MCR: this will have to become something like $hasTextSlots and $hasNonTextSlots
500  $isText = ( $content instanceof TextContent );
501 
502  if ( $this->mPreview || $isText ) {
503  $openDiv = '<div id="mw-undelete-revision" class="mw-warning">';
504  } else {
505  $openDiv = '<div id="mw-undelete-revision">';
506  }
507  $out->addHTML( $openDiv );
508 
509  // Revision delete links
510  if ( !$this->mDiff ) {
511  $revdel = Linker::getRevDeleteLink(
512  $user,
513  $revRecord,
514  $this->mTargetObj
515  );
516  if ( $revdel ) {
517  $out->addHTML( "$revdel " );
518  }
519  }
520 
521  $out->addWikiMsg(
522  'undelete-revision',
523  Message::rawParam( $link ), $time,
524  Message::rawParam( $userLink ), $d, $t
525  );
526  $out->addHTML( '</div>' );
527 
528  // Hook needs a Revision, but its deprecated, so that is fine
529  if ( !$this->getHookRunner()->onUndeleteShowRevision( $this->mTargetObj, $rev ) ) {
530  return;
531  }
532 
533  if ( $this->mPreview || !$isText ) {
534  // NOTE: non-text content has no source view, so always use rendered preview
535 
536  $popts = $out->parserOptions();
537  $renderer = MediaWikiServices::getInstance()->getRevisionRenderer();
538 
539  $rendered = $renderer->getRenderedRevision(
540  $revRecord,
541  $popts,
542  $user,
543  [ 'audience' => RevisionRecord::FOR_THIS_USER ]
544  );
545 
546  // Fail hard if the audience check fails, since we already checked
547  // at the beginning of this method.
548  $pout = $rendered->getRevisionParserOutput();
549 
550  $out->addParserOutput( $pout, [
551  'enableSectionEditLinks' => false,
552  ] );
553  }
554 
555  $out->enableOOUI();
556  $buttonFields = [];
557 
558  if ( $isText ) {
559  '@phan-var TextContent $content';
560  // TODO: MCR: make this work for multiple slots
561  // source view for textual content
562  $sourceView = Xml::element( 'textarea', [
563  'readonly' => 'readonly',
564  'cols' => 80,
565  'rows' => 25
566  ], $content->getText() . "\n" );
567 
568  $buttonFields[] = new OOUI\ButtonInputWidget( [
569  'type' => 'submit',
570  'name' => 'preview',
571  'label' => $this->msg( 'showpreview' )->text()
572  ] );
573  } else {
574  $sourceView = '';
575  }
576 
577  $buttonFields[] = new OOUI\ButtonInputWidget( [
578  'name' => 'diff',
579  'type' => 'submit',
580  'label' => $this->msg( 'showdiff' )->text()
581  ] );
582 
583  $out->addHTML(
584  $sourceView .
585  Xml::openElement( 'div', [
586  'style' => 'clear: both' ] ) .
587  Xml::openElement( 'form', [
588  'method' => 'post',
589  'action' => $this->getPageTitle()->getLocalURL( [ 'action' => 'submit' ] ) ] ) .
590  Xml::element( 'input', [
591  'type' => 'hidden',
592  'name' => 'target',
593  'value' => $this->mTargetObj->getPrefixedDBkey() ] ) .
594  Xml::element( 'input', [
595  'type' => 'hidden',
596  'name' => 'timestamp',
597  'value' => $timestamp ] ) .
598  Xml::element( 'input', [
599  'type' => 'hidden',
600  'name' => 'wpEditToken',
601  'value' => $user->getEditToken() ] ) .
602  new OOUI\FieldLayout(
603  new OOUI\Widget( [
604  'content' => new OOUI\HorizontalLayout( [
605  'items' => $buttonFields
606  ] )
607  ] )
608  ) .
609  Xml::closeElement( 'form' ) .
610  Xml::closeElement( 'div' )
611  );
612  }
613 
621  private function showDiff(
622  RevisionRecord $previousRevRecord,
623  RevisionRecord $currentRevRecord
624  ) {
625  $currentTitle = Title::newFromLinkTarget( $currentRevRecord->getPageAsLinkTarget() );
626 
627  $diffContext = clone $this->getContext();
628  $diffContext->setTitle( $currentTitle );
629  $diffContext->setWikiPage( WikiPage::factory( $currentTitle ) );
630 
631  $contentModel = $currentRevRecord->getSlot(
632  SlotRecord::MAIN,
633  RevisionRecord::RAW
634  )->getModel();
635 
636  $diffEngine = MediaWikiServices::getInstance()
637  ->getContentHandlerFactory()
638  ->getContentHandler( $contentModel )
639  ->createDifferenceEngine( $diffContext );
640 
641  $diffEngine->setRevisions( $previousRevRecord, $currentRevRecord );
642  $diffEngine->showDiffStyle();
643  $formattedDiff = $diffEngine->getDiff(
644  $this->diffHeader( $previousRevRecord, 'o' ),
645  $this->diffHeader( $currentRevRecord, 'n' )
646  );
647 
648  $this->getOutput()->addHTML( "<div>$formattedDiff</div>\n" );
649  }
650 
656  private function diffHeader( RevisionRecord $revRecord, $prefix ) {
657  $isDeleted = !( $revRecord->getId() && $revRecord->getPageAsLinkTarget() );
658  if ( $isDeleted ) {
660  $targetPage = $this->getPageTitle();
661  $targetQuery = [
662  'target' => $this->mTargetObj->getPrefixedText(),
663  'timestamp' => wfTimestamp( TS_MW, $revRecord->getTimestamp() )
664  ];
665  } else {
667  $targetPage = $revRecord->getPageAsLinkTarget();
668  $targetQuery = [ 'oldid' => $revRecord->getId() ];
669  }
670 
671  // Add show/hide deletion links if available
672  $user = $this->getUser();
673  $lang = $this->getLanguage();
674  $rdel = Linker::getRevDeleteLink( $user, $revRecord, $this->mTargetObj );
675 
676  if ( $rdel ) {
677  $rdel = " $rdel";
678  }
679 
680  $minor = $revRecord->isMinor() ? ChangesList::flag( 'minor' ) : '';
681 
682  $tagIds = wfGetDB( DB_REPLICA )->selectFieldValues(
683  'change_tag',
684  'ct_tag_id',
685  [ 'ct_rev_id' => $revRecord->getId() ],
686  __METHOD__
687  );
688  $tags = [];
689  $changeTagDefStore = MediaWikiServices::getInstance()->getChangeTagDefStore();
690  foreach ( $tagIds as $tagId ) {
691  try {
692  $tags[] = $changeTagDefStore->getName( (int)$tagId );
693  } catch ( NameTableAccessException $exception ) {
694  continue;
695  }
696  }
697  $tags = implode( ',', $tags );
698  $tagSummary = ChangeTags::formatSummaryRow( $tags, 'deleteddiff', $this->getContext() );
699 
700  // FIXME This is reimplementing DifferenceEngine#getRevisionHeader
701  // and partially #showDiffPage, but worse
702  return '<div id="mw-diff-' . $prefix . 'title1"><strong>' .
703  $this->getLinkRenderer()->makeLink(
704  $targetPage,
705  $this->msg(
706  'revisionasof',
707  $lang->userTimeAndDate( $revRecord->getTimestamp(), $user ),
708  $lang->userDate( $revRecord->getTimestamp(), $user ),
709  $lang->userTime( $revRecord->getTimestamp(), $user )
710  )->text(),
711  [],
712  $targetQuery
713  ) .
714  '</strong></div>' .
715  '<div id="mw-diff-' . $prefix . 'title2">' .
716  Linker::revUserTools( $revRecord ) . '<br />' .
717  '</div>' .
718  '<div id="mw-diff-' . $prefix . 'title3">' .
719  $minor . Linker::revComment( $revRecord ) . $rdel . '<br />' .
720  '</div>' .
721  '<div id="mw-diff-' . $prefix . 'title5">' .
722  $tagSummary[0] . '<br />' .
723  '</div>';
724  }
725 
730  private function showFileConfirmationForm( $key ) {
731  $out = $this->getOutput();
732  $lang = $this->getLanguage();
733  $user = $this->getUser();
734  $file = new ArchivedFile( $this->mTargetObj, 0, $this->mFilename );
735  $out->addWikiMsg( 'undelete-show-file-confirm',
736  $this->mTargetObj->getText(),
737  $lang->userDate( $file->getTimestamp(), $user ),
738  $lang->userTime( $file->getTimestamp(), $user ) );
739  $out->addHTML(
740  Xml::openElement( 'form', [
741  'method' => 'POST',
742  'action' => $this->getPageTitle()->getLocalURL( [
743  'target' => $this->mTarget,
744  'file' => $key,
745  'token' => $user->getEditToken( $key ),
746  ] ),
747  ]
748  ) .
749  Xml::submitButton( $this->msg( 'undelete-show-file-submit' )->text() ) .
750  '</form>'
751  );
752  }
753 
758  private function showFile( $key ) {
759  $this->getOutput()->disable();
760 
761  # We mustn't allow the output to be CDN cached, otherwise
762  # if an admin previews a deleted image, and it's cached, then
763  # a user without appropriate permissions can toddle off and
764  # nab the image, and CDN will serve it
765  $response = $this->getRequest()->response();
766  $response->header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', 0 ) . ' GMT' );
767  $response->header( 'Cache-Control: no-cache, no-store, max-age=0, must-revalidate' );
768  $response->header( 'Pragma: no-cache' );
769 
770  $repo = MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo();
771  $path = $repo->getZonePath( 'deleted' ) . '/' . $repo->getDeletedHashPath( $key ) . $key;
772  $repo->streamFileWithStatus( $path );
773  }
774 
775  protected function showHistory() {
776  $this->checkReadOnly();
777 
778  $out = $this->getOutput();
779  if ( $this->mAllowed ) {
780  $out->addModules( 'mediawiki.special.undelete' );
781  }
782  $out->wrapWikiMsg(
783  "<div class='mw-undelete-pagetitle'>\n$1\n</div>\n",
784  [ 'undeletepagetitle', wfEscapeWikiText( $this->mTargetObj->getPrefixedText() ) ]
785  );
786 
787  $archive = new PageArchive( $this->mTargetObj, $this->getConfig() );
788  $this->getHookRunner()->onUndeleteForm__showHistory( $archive, $this->mTargetObj );
789 
790  $out->addHTML( '<div class="mw-undelete-history">' );
791  if ( $this->mAllowed ) {
792  $out->addWikiMsg( 'undeletehistory' );
793  $out->addWikiMsg( 'undeleterevdel' );
794  } else {
795  $out->addWikiMsg( 'undeletehistorynoadmin' );
796  }
797  $out->addHTML( '</div>' );
798 
799  # List all stored revisions
800  $revisions = $archive->listRevisions();
801  $files = $archive->listFiles();
802 
803  $haveRevisions = $revisions && $revisions->numRows() > 0;
804  $haveFiles = $files && $files->numRows() > 0;
805 
806  # Batch existence check on user and talk pages
807  if ( $haveRevisions ) {
808  $batch = new LinkBatch();
809  foreach ( $revisions as $row ) {
810  $batch->addObj( Title::makeTitleSafe( NS_USER, $row->ar_user_text ) );
811  $batch->addObj( Title::makeTitleSafe( NS_USER_TALK, $row->ar_user_text ) );
812  }
813  $batch->execute();
814  $revisions->seek( 0 );
815  }
816  if ( $haveFiles ) {
817  $batch = new LinkBatch();
818  foreach ( $files as $row ) {
819  $batch->addObj( Title::makeTitleSafe( NS_USER, $row->fa_user_text ) );
820  $batch->addObj( Title::makeTitleSafe( NS_USER_TALK, $row->fa_user_text ) );
821  }
822  $batch->execute();
823  $files->seek( 0 );
824  }
825 
826  if ( $this->mAllowed ) {
827  $out->enableOOUI();
828 
829  $action = $this->getPageTitle()->getLocalURL( [ 'action' => 'submit' ] );
830  # Start the form here
831  $form = new OOUI\FormLayout( [
832  'method' => 'post',
833  'action' => $action,
834  'id' => 'undelete',
835  ] );
836  }
837 
838  # Show relevant lines from the deletion log:
839  $deleteLogPage = new LogPage( 'delete' );
840  $out->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) . "\n" );
841  LogEventsList::showLogExtract( $out, 'delete', $this->mTargetObj );
842  # Show relevant lines from the suppression log:
843  $suppressLogPage = new LogPage( 'suppress' );
844  $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
845  if ( $permissionManager->userHasRight( $this->getUser(), 'suppressionlog' ) ) {
846  $out->addHTML( Xml::element( 'h2', null, $suppressLogPage->getName()->text() ) . "\n" );
847  LogEventsList::showLogExtract( $out, 'suppress', $this->mTargetObj );
848  }
849 
850  if ( $this->mAllowed && ( $haveRevisions || $haveFiles ) ) {
851  $fields = [];
852  $fields[] = new OOUI\Layout( [
853  'content' => new OOUI\HtmlSnippet( $this->msg( 'undeleteextrahelp' )->parseAsBlock() )
854  ] );
855 
856  $fields[] = new OOUI\FieldLayout(
857  new OOUI\TextInputWidget( [
858  'name' => 'wpComment',
859  'inputId' => 'wpComment',
860  'infusable' => true,
861  'value' => $this->mComment,
862  'autofocus' => true,
863  // HTML maxlength uses "UTF-16 code units", which means that characters outside BMP
864  // (e.g. emojis) count for two each. This limit is overridden in JS to instead count
865  // Unicode codepoints.
867  ] ),
868  [
869  'label' => $this->msg( 'undeletecomment' )->text(),
870  'align' => 'top',
871  ]
872  );
873 
874  $fields[] = new OOUI\FieldLayout(
875  new OOUI\Widget( [
876  'content' => new OOUI\HorizontalLayout( [
877  'items' => [
878  new OOUI\ButtonInputWidget( [
879  'name' => 'restore',
880  'inputId' => 'mw-undelete-submit',
881  'value' => '1',
882  'label' => $this->msg( 'undeletebtn' )->text(),
883  'flags' => [ 'primary', 'progressive' ],
884  'type' => 'submit',
885  ] ),
886  new OOUI\ButtonInputWidget( [
887  'name' => 'invert',
888  'inputId' => 'mw-undelete-invert',
889  'value' => '1',
890  'label' => $this->msg( 'undeleteinvert' )->text()
891  ] ),
892  ]
893  ] )
894  ] )
895  );
896 
897  if ( $permissionManager->userHasRight( $this->getUser(), 'suppressrevision' ) ) {
898  $fields[] = new OOUI\FieldLayout(
899  new OOUI\CheckboxInputWidget( [
900  'name' => 'wpUnsuppress',
901  'inputId' => 'mw-undelete-unsuppress',
902  'value' => '1',
903  ] ),
904  [
905  'label' => $this->msg( 'revdelete-unsuppress' )->text(),
906  'align' => 'inline',
907  ]
908  );
909  }
910 
911  $fieldset = new OOUI\FieldsetLayout( [
912  'label' => $this->msg( 'undelete-fieldset-title' )->text(),
913  'id' => 'mw-undelete-table',
914  'items' => $fields,
915  ] );
916 
917  $form->appendContent(
918  new OOUI\PanelLayout( [
919  'expanded' => false,
920  'padded' => true,
921  'framed' => true,
922  'content' => $fieldset,
923  ] ),
924  new OOUI\HtmlSnippet(
925  Html::hidden( 'target', $this->mTarget ) .
926  Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() )
927  )
928  );
929  }
930 
931  $history = '';
932  $history .= Xml::element( 'h2', null, $this->msg( 'history' )->text() ) . "\n";
933 
934  if ( $haveRevisions ) {
935  # Show the page's stored (deleted) history
936 
937  if ( $permissionManager->userHasRight( $this->getUser(), 'deleterevision' ) ) {
938  $history .= Html::element(
939  'button',
940  [
941  'name' => 'revdel',
942  'type' => 'submit',
943  'class' => 'deleterevision-log-submit mw-log-deleterevision-button'
944  ],
945  $this->msg( 'showhideselectedversions' )->text()
946  ) . "\n";
947  }
948 
949  $history .= '<ul class="mw-undelete-revlist">';
950  $remaining = $revisions->numRows();
951  $earliestLiveTime = $this->mTargetObj->getEarliestRevTime();
952 
953  foreach ( $revisions as $row ) {
954  $remaining--;
955  $history .= $this->formatRevisionRow( $row, $earliestLiveTime, $remaining );
956  }
957  $revisions->free();
958  $history .= '</ul>';
959  } else {
960  $out->addWikiMsg( 'nohistory' );
961  }
962 
963  if ( $haveFiles ) {
964  $history .= Xml::element( 'h2', null, $this->msg( 'filehist' )->text() ) . "\n";
965  $history .= '<ul class="mw-undelete-revlist">';
966  foreach ( $files as $row ) {
967  $history .= $this->formatFileRow( $row );
968  }
969  $files->free();
970  $history .= '</ul>';
971  }
972 
973  if ( $this->mAllowed ) {
974  # Slip in the hidden controls here
975  $misc = Html::hidden( 'target', $this->mTarget );
976  $misc .= Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() );
977  $history .= $misc;
978 
979  $form->appendContent( new OOUI\HtmlSnippet( $history ) );
980  $out->addHTML( $form );
981  } else {
982  $out->addHTML( $history );
983  }
984 
985  return true;
986  }
987 
988  protected function formatRevisionRow( $row, $earliestLiveTime, $remaining ) {
989  $revRecord = MediaWikiServices::getInstance()
990  ->getRevisionFactory()
991  ->newRevisionFromArchiveRow(
992  $row,
993  RevisionFactory::READ_NORMAL,
994  $this->mTargetObj
995  );
996 
997  $revTextSize = '';
998  $ts = wfTimestamp( TS_MW, $row->ar_timestamp );
999  // Build checkboxen...
1000  if ( $this->mAllowed ) {
1001  if ( $this->mInvert ) {
1002  if ( in_array( $ts, $this->mTargetTimestamp ) ) {
1003  $checkBox = Xml::check( "ts$ts" );
1004  } else {
1005  $checkBox = Xml::check( "ts$ts", true );
1006  }
1007  } else {
1008  $checkBox = Xml::check( "ts$ts" );
1009  }
1010  } else {
1011  $checkBox = '';
1012  }
1013 
1014  // Build page & diff links...
1015  $user = $this->getUser();
1016  if ( $this->mCanView ) {
1017  $titleObj = $this->getPageTitle();
1018  # Last link
1019  if ( !RevisionRecord::userCanBitfield(
1020  $revRecord->getVisibility(),
1021  RevisionRecord::DELETED_TEXT,
1022  $this->getUser()
1023  ) ) {
1024  $pageLink = htmlspecialchars( $this->getLanguage()->userTimeAndDate( $ts, $user ) );
1025  $last = $this->msg( 'diff' )->escaped();
1026  } elseif ( $remaining > 0 || ( $earliestLiveTime && $ts > $earliestLiveTime ) ) {
1027  $pageLink = $this->getPageLink( $revRecord, $titleObj, $ts );
1028  $last = $this->getLinkRenderer()->makeKnownLink(
1029  $titleObj,
1030  $this->msg( 'diff' )->text(),
1031  [],
1032  [
1033  'target' => $this->mTargetObj->getPrefixedText(),
1034  'timestamp' => $ts,
1035  'diff' => 'prev'
1036  ]
1037  );
1038  } else {
1039  $pageLink = $this->getPageLink( $revRecord, $titleObj, $ts );
1040  $last = $this->msg( 'diff' )->escaped();
1041  }
1042  } else {
1043  $pageLink = htmlspecialchars( $this->getLanguage()->userTimeAndDate( $ts, $user ) );
1044  $last = $this->msg( 'diff' )->escaped();
1045  }
1046 
1047  // User links
1048  $userLink = Linker::revUserTools( $revRecord );
1049 
1050  // Minor edit
1051  $minor = $revRecord->isMinor() ? ChangesList::flag( 'minor' ) : '';
1052 
1053  // Revision text size
1054  $size = $row->ar_len;
1055  if ( $size !== null ) {
1056  $revTextSize = Linker::formatRevisionSize( $size );
1057  }
1058 
1059  // Edit summary
1060  $comment = Linker::revComment( $revRecord );
1061 
1062  // Tags
1063  $attribs = [];
1064  list( $tagSummary, $classes ) = ChangeTags::formatSummaryRow(
1065  $row->ts_tags,
1066  'deletedhistory',
1067  $this->getContext()
1068  );
1069  if ( $classes ) {
1070  $attribs['class'] = implode( ' ', $classes );
1071  }
1072 
1073  $revisionRow = $this->msg( 'undelete-revision-row2' )
1074  ->rawParams(
1075  $checkBox,
1076  $last,
1077  $pageLink,
1078  $userLink,
1079  $minor,
1080  $revTextSize,
1081  $comment,
1082  $tagSummary
1083  )
1084  ->escaped();
1085 
1086  return Xml::tags( 'li', $attribs, $revisionRow ) . "\n";
1087  }
1088 
1089  private function formatFileRow( $row ) {
1090  $file = ArchivedFile::newFromRow( $row );
1091  $ts = wfTimestamp( TS_MW, $row->fa_timestamp );
1092  $user = $this->getUser();
1093 
1094  $checkBox = '';
1095  if ( $this->mCanView && $row->fa_storage_key ) {
1096  if ( $this->mAllowed ) {
1097  $checkBox = Xml::check( 'fileid' . $row->fa_id );
1098  }
1099  $key = urlencode( $row->fa_storage_key );
1100  $pageLink = $this->getFileLink( $file, $this->getPageTitle(), $ts, $key );
1101  } else {
1102  $pageLink = htmlspecialchars( $this->getLanguage()->userTimeAndDate( $ts, $user ) );
1103  }
1104  $userLink = $this->getFileUser( $file );
1105  $data = $this->msg( 'widthheight' )->numParams( $row->fa_width, $row->fa_height )->text();
1106  $bytes = $this->msg( 'parentheses' )
1107  ->plaintextParams( $this->msg( 'nbytes' )->numParams( $row->fa_size )->text() )
1108  ->plain();
1109  $data = htmlspecialchars( $data . ' ' . $bytes );
1110  $comment = $this->getFileComment( $file );
1111 
1112  // Add show/hide deletion links if available
1113  $canHide = $this->isAllowed( 'deleterevision' );
1114  if ( $canHide || ( $file->getVisibility() && $this->isAllowed( 'deletedhistory' ) ) ) {
1115  if ( !$file->userCan( File::DELETED_RESTRICTED, $user ) ) {
1116  // Revision was hidden from sysops
1117  $revdlink = Linker::revDeleteLinkDisabled( $canHide );
1118  } else {
1119  $query = [
1120  'type' => 'filearchive',
1121  'target' => $this->mTargetObj->getPrefixedDBkey(),
1122  'ids' => $row->fa_id
1123  ];
1124  $revdlink = Linker::revDeleteLink( $query,
1125  $file->isDeleted( File::DELETED_RESTRICTED ), $canHide );
1126  }
1127  } else {
1128  $revdlink = '';
1129  }
1130 
1131  return "<li>$checkBox $revdlink $pageLink . . $userLink $data $comment</li>\n";
1132  }
1133 
1142  private function getPageLink( RevisionRecord $revRecord, $titleObj, $ts ) {
1143  $user = $this->getUser();
1144  $time = $this->getLanguage()->userTimeAndDate( $ts, $user );
1145 
1146  if ( !RevisionRecord::userCanBitfield(
1147  $revRecord->getVisibility(),
1148  RevisionRecord::DELETED_TEXT,
1149  $user
1150  ) ) {
1151  return '<span class="history-deleted">' . $time . '</span>';
1152  }
1153 
1154  $link = $this->getLinkRenderer()->makeKnownLink(
1155  $titleObj,
1156  $time,
1157  [],
1158  [
1159  'target' => $this->mTargetObj->getPrefixedText(),
1160  'timestamp' => $ts
1161  ]
1162  );
1163 
1164  if ( $revRecord->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
1165  $link = '<span class="history-deleted">' . $link . '</span>';
1166  }
1167 
1168  return $link;
1169  }
1170 
1181  private function getFileLink( $file, $titleObj, $ts, $key ) {
1182  $user = $this->getUser();
1183  $time = $this->getLanguage()->userTimeAndDate( $ts, $user );
1184 
1185  if ( !$file->userCan( File::DELETED_FILE, $user ) ) {
1186  return '<span class="history-deleted">' . htmlspecialchars( $time ) . '</span>';
1187  }
1188 
1189  $link = $this->getLinkRenderer()->makeKnownLink(
1190  $titleObj,
1191  $time,
1192  [],
1193  [
1194  'target' => $this->mTargetObj->getPrefixedText(),
1195  'file' => $key,
1196  'token' => $user->getEditToken( $key )
1197  ]
1198  );
1199 
1200  if ( $file->isDeleted( File::DELETED_FILE ) ) {
1201  $link = '<span class="history-deleted">' . $link . '</span>';
1202  }
1203 
1204  return $link;
1205  }
1206 
1213  private function getFileUser( $file ) {
1214  if ( !$file->userCan( File::DELETED_USER, $this->getUser() ) ) {
1215  return '<span class="history-deleted">' .
1216  $this->msg( 'rev-deleted-user' )->escaped() .
1217  '</span>';
1218  }
1219 
1220  $link = Linker::userLink( $file->getRawUser(), $file->getRawUserText() ) .
1221  Linker::userToolLinks( $file->getRawUser(), $file->getRawUserText() );
1222 
1223  if ( $file->isDeleted( File::DELETED_USER ) ) {
1224  $link = '<span class="history-deleted">' . $link . '</span>';
1225  }
1226 
1227  return $link;
1228  }
1229 
1236  private function getFileComment( $file ) {
1237  if ( !$file->userCan( File::DELETED_COMMENT, $this->getUser() ) ) {
1238  return '<span class="history-deleted"><span class="comment">' .
1239  $this->msg( 'rev-deleted-comment' )->escaped() . '</span></span>';
1240  }
1241 
1242  $link = Linker::commentBlock( $file->getRawDescription() );
1243 
1244  if ( $file->isDeleted( File::DELETED_COMMENT ) ) {
1245  $link = '<span class="history-deleted">' . $link . '</span>';
1246  }
1247 
1248  return $link;
1249  }
1250 
1251  private function undelete() {
1252  if ( $this->getConfig()->get( 'UploadMaintenance' )
1253  && $this->mTargetObj->getNamespace() == NS_FILE
1254  ) {
1255  throw new ErrorPageError( 'undelete-error', 'filedelete-maintenance' );
1256  }
1257 
1258  $this->checkReadOnly();
1259 
1260  $out = $this->getOutput();
1261  $archive = new PageArchive( $this->mTargetObj, $this->getConfig() );
1262  $this->getHookRunner()->onUndeleteForm__undelete( $archive, $this->mTargetObj );
1263  $ok = $archive->undeleteAsUser(
1264  $this->mTargetTimestamp,
1265  $this->getUser(),
1266  $this->mComment,
1267  $this->mFileVersions,
1268  $this->mUnsuppress
1269  );
1270 
1271  if ( is_array( $ok ) ) {
1272  if ( $ok[1] ) { // Undeleted file count
1273  $this->getHookRunner()->onFileUndeleteComplete(
1274  $this->mTargetObj, $this->mFileVersions, $this->getUser(), $this->mComment );
1275  }
1276 
1277  $link = $this->getLinkRenderer()->makeKnownLink( $this->mTargetObj );
1278  $out->addWikiMsg( 'undeletedpage', Message::rawParam( $link ) );
1279  } else {
1280  $out->setPageTitle( $this->msg( 'undelete-error' ) );
1281  }
1282 
1283  // Show revision undeletion warnings and errors
1284  $status = $archive->getRevisionStatus();
1285  if ( $status && !$status->isGood() ) {
1286  $out->wrapWikiTextAsInterface(
1287  'error',
1288  '<div id="mw-error-cannotundelete">' .
1289  $status->getWikiText(
1290  'cannotundelete',
1291  'cannotundelete',
1292  $this->getLanguage()
1293  ) . '</div>'
1294  );
1295  }
1296 
1297  // Show file undeletion warnings and errors
1298  $status = $archive->getFileStatus();
1299  if ( $status && !$status->isGood() ) {
1300  $out->wrapWikiTextAsInterface(
1301  'error',
1302  $status->getWikiText(
1303  'undelete-error-short',
1304  'undelete-error-long',
1305  $this->getLanguage()
1306  )
1307  );
1308  }
1309  }
1310 
1319  public function prefixSearchSubpages( $search, $limit, $offset ) {
1320  return $this->prefixSearchString( $search, $limit, $offset );
1321  }
1322 
1323  protected function getGroupName() {
1324  return 'pagetools';
1325  }
1326 }
SpecialPage\getPageTitle
getPageTitle( $subpage=false)
Get a self-referential title object.
Definition: SpecialPage.php:669
SpecialUndelete\formatRevisionRow
formatRevisionRow( $row, $earliestLiveTime, $remaining)
Definition: SpecialUndelete.php:988
SpecialPage\msg
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
Definition: SpecialPage.php:800
Title\newFromText
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:332
Revision\RevisionRecord
Page revision base class.
Definition: RevisionRecord.php:46
SpecialUndelete\$mPreview
bool null $mPreview
Definition: SpecialUndelete.php:51
SpecialUndelete\$mAllowed
$mAllowed
Definition: SpecialUndelete.php:46
File\DELETED_USER
const DELETED_USER
Definition: File.php:68
UserBlockedError
Show an error when the user tries to do something whilst blocked.
Definition: UserBlockedError.php:30
LinkBatch
Class representing a list of titles The execute() method checks them all for existence and adds them ...
Definition: LinkBatch.php:35
PageArchive
Used to show archived pages and eventually restore them.
Definition: PageArchive.php:32
$response
$response
Definition: opensearch_desc.php:44
Linker\userLink
static userLink( $userId, $userName, $altUserName=false)
Make user link (or user contributions for unregistered users)
Definition: Linker.php:896
SpecialPage\getOutput
getOutput()
Get the OutputPage being used for this instance.
Definition: SpecialPage.php:716
File\DELETED_RESTRICTED
const DELETED_RESTRICTED
Definition: File.php:69
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:146
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:37
SpecialUndelete\getGroupName
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
Definition: SpecialUndelete.php:1323
Revision\RevisionRecord\isDeleted
isDeleted( $field)
MCR migration note: this replaces Revision::isDeleted.
Definition: RevisionRecord.php:420
SpecialUndelete
Special page allowing users with the appropriate permissions to view and restore deleted content.
Definition: SpecialUndelete.php:37
SpecialUndelete\isAllowed
isAllowed( $permission, User $user=null)
Checks whether a user is allowed the permission for the specific title if one is set.
Definition: SpecialUndelete.php:152
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:1806
SpecialUndelete\execute
execute( $par)
Default execute method Checks user permissions.
Definition: SpecialUndelete.php:198
SpecialUndelete\$mFileVersions
int[] null $mFileVersions
Definition: SpecialUndelete.php:59
Linker\userToolLinks
static userToolLinks( $userId, $userText, $redContribsWhenNoEdits=false, $flags=0, $edits=null, $useParentheses=true)
Generate standard user tool links (talk, contributions, block link, etc.)
Definition: Linker.php:941
SpecialUndelete\$mAction
$mAction
Definition: SpecialUndelete.php:38
SpecialUndelete\showRevision
showRevision( $timestamp)
Definition: SpecialUndelete.php:418
Revision\RevisionRecord\getTimestamp
getTimestamp()
MCR migration note: this replaces Revision::getTimestamp.
Definition: RevisionRecord.php:442
SpecialUndelete\showHistory
showHistory()
Definition: SpecialUndelete.php:775
SpecialUndelete\$mDiff
bool null $mDiff
Definition: SpecialUndelete.php:53
SpecialUndelete\showList
showList( $result)
Generic list of deleted pages.
Definition: SpecialUndelete.php:368
SpecialUndelete\showSearchForm
showSearchForm()
Definition: SpecialUndelete.php:290
Linker\revComment
static revComment( $rev, $local=false, $isPublic=false, $useParentheses=true)
Wrap and format the given revision's comment block, if the current user is allowed to view it.
Definition: Linker.php:1606
Linker\getRevDeleteLink
static getRevDeleteLink(User $user, $rev, LinkTarget $title)
Get a revision-deletion link, or disabled link, or nothing, depending on user permissions & the setti...
Definition: Linker.php:2196
PageArchive\listPagesByPrefix
static listPagesByPrefix( $prefix)
List deleted pages recorded in the archive table matching the given title prefix.
Definition: PageArchive.php:132
NS_FILE
const NS_FILE
Definition: Defines.php:75
SpecialPage\displayRestrictionError
displayRestrictionError()
Output an error message telling the user what access level they have to have.
Definition: SpecialPage.php:317
SpecialUndelete\checkPermissions
checkPermissions()
Checks if userCanExecute, and if not throws a PermissionsError.1.19 void
Definition: SpecialUndelete.php:173
$file
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
Definition: router.php:42
Revision\RevisionRecord\getSlot
getSlot( $role, $audience=self::FOR_PUBLIC, User $user=null)
Returns meta-data for the given slot.
Definition: RevisionRecord.php:191
Revision\RevisionFactory
Service for constructing revision objects.
Definition: RevisionFactory.php:38
SpecialPage\getTitleFor
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,...
Definition: SpecialPage.php:90
SpecialPage\getSkin
getSkin()
Shortcut to get the skin being used for this instance.
Definition: SpecialPage.php:736
SpecialPage\useTransactionalTimeLimit
useTransactionalTimeLimit()
Call wfTransactionalTimeLimit() if this request was POSTed.
Definition: SpecialPage.php:902
SpecialUndelete\$mTargetTimestamp
$mTargetTimestamp
Definition: SpecialUndelete.php:45
PermissionsError
Show an error when a user tries to do something they do not have the necessary permissions for.
Definition: PermissionsError.php:30
SpecialPage\getLanguage
getLanguage()
Shortcut to get user's language.
Definition: SpecialPage.php:746
Linker\getInvalidTitleDescription
static getInvalidTitleDescription(IContextSource $context, $namespace, $title)
Get a message saying that an invalid title was encountered.
Definition: Linker.php:188
SpecialUndelete\getFileUser
getFileUser( $file)
Fetch file's user id if it's available to this user.
Definition: SpecialUndelete.php:1213
PageArchive\listPagesBySearch
static listPagesBySearch( $term)
List deleted pages recorded in the archive matching the given term, using search engine archive.
Definition: PageArchive.php:82
Xml\openElement
static openElement( $element, $attribs=null)
This opens an XML element.
Definition: Xml.php:108
SpecialUndelete\$mFilename
$mFilename
Definition: SpecialUndelete.php:44
SpecialUndelete\$mRestore
$mRestore
Definition: SpecialUndelete.php:41
SpecialUndelete\$mTargetObj
Title $mTargetObj
Definition: SpecialUndelete.php:62
SpecialPage\addHelpLink
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
Definition: SpecialPage.php:836
SpecialPage\prefixSearchString
prefixSearchString( $search, $limit, $offset)
Perform a regular substring search for prefixSearchSubpages.
Definition: SpecialPage.php:515
SpecialUndelete\$mTimestamp
$mTimestamp
Definition: SpecialUndelete.php:40
SpecialPage\getHookRunner
getHookRunner()
Definition: SpecialPage.php:974
SpecialUndelete\$mCanView
$mCanView
Definition: SpecialUndelete.php:47
SpecialPage\getConfig
getConfig()
Shortcut to get main config object.
Definition: SpecialPage.php:766
File\DELETED_COMMENT
const DELETED_COMMENT
Definition: File.php:67
WikiPage\factory
static factory(Title $title)
Create a WikiPage object of the appropriate class for the given title.
Definition: WikiPage.php:146
SpecialUndelete\redirectToRevDel
redirectToRevDel()
Convert submitted form data to format expected by RevisionDelete and redirect the request.
Definition: SpecialUndelete.php:269
wfScript
wfScript( $script='index')
Get the path to a specified script file, respecting file extensions; this is a wrapper around $wgScri...
Definition: GlobalFunctions.php:2530
Wikimedia\Rdbms\IResultWrapper
Result wrapper for grabbing data queried from an IDatabase object.
Definition: IResultWrapper.php:24
SpecialUndelete\userCanExecute
userCanExecute(User $user)
Checks if the given user (identified by an object) can execute this special page (as defined by $mRes...
Definition: SpecialUndelete.php:166
getPermissionManager
getPermissionManager()
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2463
$matches
$matches
Definition: NoLocalSettings.php:24
Xml\check
static check( $name, $checked=false, $attribs=[])
Convenience function to build an HTML checkbox.
Definition: Xml.php:327
SpecialUndelete\doesWrites
doesWrites()
Indicates whether this special page may perform database writes.
Definition: SpecialUndelete.php:72
SpecialUndelete\showDiff
showDiff(RevisionRecord $previousRevRecord, RevisionRecord $currentRevRecord)
Build a diff display between this and the previous either deleted or non-deleted edit.
Definition: SpecialUndelete.php:621
LogPage
Class to simplify the use of log pages.
Definition: LogPage.php:33
SpecialUndelete\$mUnsuppress
bool null $mUnsuppress
Definition: SpecialUndelete.php:57
Revision\RevisionRecord\isMinor
isMinor()
MCR migration note: this replaces Revision::isMinor.
Definition: RevisionRecord.php:409
Xml\element
static element( $element, $attribs=null, $contents='', $allowShortTag=true)
Format an XML element with given attributes and, optionally, text content.
Definition: Xml.php:41
ChangesList\flag
static flag( $flag, IContextSource $context=null)
Make an "<abbr>" element for a given change flag.
Definition: ChangesList.php:268
Linker\revDeleteLinkDisabled
static revDeleteLinkDisabled( $delete=true)
Creates a dead (show/hide) link for deleting revisions/log entries.
Definition: Linker.php:2276
SpecialUndelete\$mToken
$mToken
Definition: SpecialUndelete.php:49
$title
$title
Definition: testCompression.php:38
SpecialPage\setHeaders
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!
Definition: SpecialPage.php:551
SpecialPage\getUser
getUser()
Shortcut to get the User executing this instance.
Definition: SpecialPage.php:726
LogEventsList\showLogExtract
static showLogExtract(&$out, $types=[], $page='', $user='', $param=[])
Show log extract.
Definition: LogEventsList.php:639
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
SpecialUndelete\$mRevdel
$mRevdel
Definition: SpecialUndelete.php:42
Linker\revUserTools
static revUserTools( $rev, $isPublic=false, $useParentheses=true)
Generate a user tool link cluster if the current user is allowed to view it.
Definition: Linker.php:1142
SpecialPage\getContext
getContext()
Gets the context this SpecialPage is executed in.
Definition: SpecialPage.php:689
SpecialUndelete\diffHeader
diffHeader(RevisionRecord $revRecord, $prefix)
Definition: SpecialUndelete.php:656
Revision\RevisionRecord\getId
getId()
Get revision ID.
Definition: RevisionRecord.php:279
Html\hidden
static hidden( $name, $value, array $attribs=[])
Convenience function to produce an input element with type=hidden.
Definition: Html.php:802
SpecialUndelete\prefixSearchSubpages
prefixSearchSubpages( $search, $limit, $offset)
Return an array of subpages beginning with $search that this special page will accept.
Definition: SpecialUndelete.php:1319
Title\makeTitleSafe
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:621
SpecialUndelete\formatFileRow
formatFileRow( $row)
Definition: SpecialUndelete.php:1089
$content
$content
Definition: router.php:76
NS_USER_TALK
const NS_USER_TALK
Definition: Defines.php:72
SpecialUndelete\$mSearchPrefix
string $mSearchPrefix
Search prefix.
Definition: SpecialUndelete.php:66
Linker\commentBlock
static commentBlock( $comment, $title=null, $local=false, $wikiId=null, $useParentheses=true)
Wrap a comment in standard punctuation and formatting if it's non-empty, otherwise return empty strin...
Definition: Linker.php:1575
ArchivedFile
Class representing a row of the 'filearchive' table.
Definition: ArchivedFile.php:32
Message\rawParam
static rawParam( $raw)
Definition: Message.php:1035
SpecialPage
Parent class for all special pages.
Definition: SpecialPage.php:39
SpecialUndelete\$mDiffOnly
bool null $mDiffOnly
Definition: SpecialUndelete.php:55
Xml\tags
static tags( $element, $attribs, $contents)
Same as Xml::element(), but does not escape contents.
Definition: Xml.php:130
SpecialUndelete\$mComment
$mComment
Definition: SpecialUndelete.php:48
SpecialUndelete\$mInvert
$mInvert
Definition: SpecialUndelete.php:43
SpecialPage\getRequest
getRequest()
Get the WebRequest being used for this instance.
Definition: SpecialPage.php:706
Linker\formatRevisionSize
static formatRevisionSize( $size)
Definition: Linker.php:1651
wfEscapeWikiText
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
Definition: GlobalFunctions.php:1485
SpecialUndelete\getFileComment
getFileComment( $file)
Fetch file upload comment if it's available to this user.
Definition: SpecialUndelete.php:1236
Title\newFromLinkTarget
static newFromLinkTarget(LinkTarget $linkTarget, $forceClone='')
Returns a Title given a LinkTarget.
Definition: Title.php:284
TextContent
Content object implementation for representing flat text.
Definition: TextContent.php:37
SpecialUndelete\getPageLink
getPageLink(RevisionRecord $revRecord, $titleObj, $ts)
Fetch revision text link if it's available to all users.
Definition: SpecialUndelete.php:1142
CommentStore\COMMENT_CHARACTER_LIMIT
const COMMENT_CHARACTER_LIMIT
Maximum length of a comment in UTF-8 characters.
Definition: CommentStore.php:37
SpecialPage\getLinkRenderer
getLinkRenderer()
Definition: SpecialPage.php:912
SpecialUndelete\getFileLink
getFileLink( $file, $titleObj, $ts, $key)
Fetch image view link if it's available to all users.
Definition: SpecialUndelete.php:1181
Revision\RevisionRecord\getVisibility
getVisibility()
Get the deletion bitfield of the revision.
Definition: RevisionRecord.php:431
Revision\RevisionRecord\getPageAsLinkTarget
getPageAsLinkTarget()
Returns the title of the page this revision is associated with as a LinkTarget object.
Definition: RevisionRecord.php:351
Title
Represents a title within MediaWiki.
Definition: Title.php:42
Xml\closeElement
static closeElement( $element)
Shortcut to close an XML element.
Definition: Xml.php:117
SpecialUndelete\showFile
showFile( $key)
Show a deleted file version requested by the visitor.
Definition: SpecialUndelete.php:758
$path
$path
Definition: NoLocalSettings.php:25
SpecialUndelete\__construct
__construct()
Definition: SpecialUndelete.php:68
Html\rawElement
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:209
MediaWiki\Storage\NameTableAccessException
Exception representing a failure to look up a row from a name table.
Definition: NameTableAccessException.php:32
Linker\revDeleteLink
static revDeleteLink( $query=[], $restricted=false, $delete=true)
Creates a (show/hide) link for deleting revisions/log entries.
Definition: Linker.php:2254
NS_USER
const NS_USER
Definition: Defines.php:71
File\DELETED_FILE
const DELETED_FILE
Definition: File.php:66
SpecialPage\checkReadOnly
checkReadOnly()
If the wiki is currently in readonly mode, throws a ReadOnlyError.
Definition: SpecialPage.php:341
$t
$t
Definition: testCompression.php:74
SpecialUndelete\$mTarget
$mTarget
Definition: SpecialUndelete.php:39
SpecialPage\$linkRenderer
MediaWiki Linker LinkRenderer null $linkRenderer
Definition: SpecialPage.php:69
SpecialUndelete\undelete
undelete()
Definition: SpecialUndelete.php:1251
Html\element
static element( $element, $attribs=[], $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition: Html.php:231
SpecialUndelete\showFileConfirmationForm
showFileConfirmationForm( $key)
Show a form confirming whether a tokenless user really wants to see a file.
Definition: SpecialUndelete.php:730
ErrorPageError
An error page which can definitely be safely rendered using the OutputPage.
Definition: ErrorPageError.php:27
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:55
SpecialPage\outputHeader
outputHeader( $summaryMessageKey='')
Outputs a summary message on top of special pages Per default the message key is the canonical name o...
Definition: SpecialPage.php:636
ChangeTags\formatSummaryRow
static formatSummaryRow( $tags, $page, IContextSource $context=null)
Creates HTML for the given tags.
Definition: ChangeTags.php:100
ArchivedFile\newFromRow
static newFromRow( $row)
Loads a file object from the filearchive table.
Definition: ArchivedFile.php:213
Revision\SlotRecord
Value object representing a content slot associated with a page revision.
Definition: SlotRecord.php:39
SpecialUndelete\loadRequest
loadRequest( $par)
Definition: SpecialUndelete.php:76
Xml\submitButton
static submitButton( $value, $attribs=[])
Convenience function to build an HTML submit button When $wgUseMediaWikiUIEverywhere is true it will ...
Definition: Xml.php:463