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