MediaWiki  master
SpecialRevisionDelete.php
Go to the documentation of this file.
1 <?php
26 
35  protected $wasSaved = false;
36 
38  private $submitClicked;
39 
41  private $ids;
42 
44  private $archiveName;
45 
47  private $token;
48 
50  private $targetObj;
51 
53  private $typeName;
54 
56  private $checks;
57 
59  private $typeLabels;
60 
62  private $revDelList;
63 
65  private $mIsAllowed;
66 
68  private $otherReason;
69 
72 
74  private $repoGroup;
75 
79  private const UI_LABELS = [
80  'revision' => [
81  'check-label' => 'revdelete-hide-text',
82  'success' => 'revdelete-success',
83  'failure' => 'revdelete-failure',
84  'text' => 'revdelete-text-text',
85  'selected' => 'revdelete-selected-text',
86  ],
87  'archive' => [
88  'check-label' => 'revdelete-hide-text',
89  'success' => 'revdelete-success',
90  'failure' => 'revdelete-failure',
91  'text' => 'revdelete-text-text',
92  'selected' => 'revdelete-selected-text',
93  ],
94  'oldimage' => [
95  'check-label' => 'revdelete-hide-image',
96  'success' => 'revdelete-success',
97  'failure' => 'revdelete-failure',
98  'text' => 'revdelete-text-file',
99  'selected' => 'revdelete-selected-file',
100  ],
101  'filearchive' => [
102  'check-label' => 'revdelete-hide-image',
103  'success' => 'revdelete-success',
104  'failure' => 'revdelete-failure',
105  'text' => 'revdelete-text-file',
106  'selected' => 'revdelete-selected-file',
107  ],
108  'logging' => [
109  'check-label' => 'revdelete-hide-name',
110  'success' => 'logdelete-success',
111  'failure' => 'logdelete-failure',
112  'text' => 'logdelete-text',
113  'selected' => 'logdelete-selected',
114  ],
115  ];
116 
124  parent::__construct( 'Revisiondelete' );
125 
126  $this->permissionManager = $permissionManager;
127  $this->repoGroup = $repoGroup;
128  }
129 
130  public function doesWrites() {
131  return true;
132  }
133 
134  public function execute( $par ) {
135  $this->useTransactionalTimeLimit();
136 
137  $this->checkPermissions();
138  $this->checkReadOnly();
139 
140  $output = $this->getOutput();
141  $user = $this->getUser();
142 
143  $this->setHeaders();
144  $this->outputHeader();
145  $request = $this->getRequest();
146  $this->submitClicked = $request->wasPosted() && $request->getBool( 'wpSubmit' );
147  # Handle our many different possible input types.
148  $ids = $request->getVal( 'ids' );
149  if ( $ids !== null ) {
150  # Allow CSV, for backwards compatibility, or a single ID for show/hide links
151  $this->ids = explode( ',', $ids );
152  } else {
153  # Array input
154  $this->ids = array_keys( $request->getArray( 'ids', [] ) );
155  }
156  // $this->ids = array_map( 'intval', $this->ids );
157  $this->ids = array_unique( array_filter( $this->ids ) );
158 
159  $this->typeName = $request->getVal( 'type' );
160  $this->targetObj = Title::newFromText( $request->getText( 'target' ) );
161 
162  # For reviewing deleted files...
163  $this->archiveName = $request->getVal( 'file' );
164  $this->token = $request->getVal( 'token' );
165  if ( $this->archiveName && $this->targetObj ) {
166  $this->tryShowFile( $this->archiveName );
167 
168  return;
169  }
170 
171  $this->typeName = RevisionDeleter::getCanonicalTypeName( $this->typeName );
172 
173  # No targets?
174  if ( !$this->typeName || count( $this->ids ) == 0 ) {
175  throw new ErrorPageError( 'revdelete-nooldid-title', 'revdelete-nooldid-text' );
176  }
177 
178  $restriction = RevisionDeleter::getRestriction( $this->typeName );
179 
180  if ( !$this->getAuthority()->isAllowedAny( $restriction, 'deletedhistory' ) ) {
181  throw new PermissionsError( $restriction );
182  }
183 
184  # Allow the list type to adjust the passed target
185  $this->targetObj = RevisionDeleter::suggestTarget(
186  $this->typeName,
187  $this->targetObj,
188  $this->ids
189  );
190 
191  # We need a target page!
192  if ( $this->targetObj === null ) {
193  $output->addWikiMsg( 'undelete-header' );
194 
195  return;
196  }
197 
198  // Check blocks
199  $checkReplica = !$this->submitClicked;
200  if (
201  $this->permissionManager->isBlockedFrom(
202  $user,
203  $this->targetObj,
204  $checkReplica
205  )
206  ) {
207  throw new UserBlockedError(
208  // @phan-suppress-next-line PhanTypeMismatchArgumentNullable Block is checked and not null
209  $user->getBlock(),
210  $user,
211  $this->getLanguage(),
212  $request->getIP()
213  );
214  }
215 
216  $this->typeLabels = self::UI_LABELS[$this->typeName];
217  $list = $this->getList();
218  $list->reset();
219  $this->mIsAllowed = $this->permissionManager->userHasRight( $user, $restriction );
220  $canViewSuppressedOnly = $this->permissionManager->userHasRight( $user, 'viewsuppressed' ) &&
221  !$this->permissionManager->userHasRight( $user, 'suppressrevision' );
222  $pageIsSuppressed = $list->areAnySuppressed();
223  $this->mIsAllowed = $this->mIsAllowed && !( $canViewSuppressedOnly && $pageIsSuppressed );
224 
225  $this->otherReason = $request->getVal( 'wpReason' );
226  # Give a link to the logs/hist for this page
227  $this->showConvenienceLinks();
228 
229  # Initialise checkboxes
230  $this->checks = [
231  # Messages: revdelete-hide-text, revdelete-hide-image, revdelete-hide-name
232  [ $this->typeLabels['check-label'], 'wpHidePrimary',
233  RevisionDeleter::getRevdelConstant( $this->typeName )
234  ],
235  [ 'revdelete-hide-comment', 'wpHideComment', RevisionRecord::DELETED_COMMENT ],
236  [ 'revdelete-hide-user', 'wpHideUser', RevisionRecord::DELETED_USER ]
237  ];
238  if ( $this->permissionManager->userHasRight( $user, 'suppressrevision' ) ) {
239  $this->checks[] = [ 'revdelete-hide-restricted',
240  'wpHideRestricted', RevisionRecord::DELETED_RESTRICTED ];
241  }
242 
243  # Either submit or create our form
244  if ( $this->mIsAllowed && $this->submitClicked ) {
245  $this->submit();
246  } else {
247  $this->showForm();
248  }
249 
250  if ( $this->permissionManager->userHasRight( $user, 'deletedhistory' ) ) {
251  # Show relevant lines from the deletion log
252  $deleteLogPage = new LogPage( 'delete' );
253  $output->addHTML( "<h2>" . $deleteLogPage->getName()->escaped() . "</h2>\n" );
255  $output,
256  'delete',
257  $this->targetObj,
258  '', /* user */
259  [ 'lim' => 25, 'conds' => $this->getLogQueryCond(), 'useMaster' => $this->wasSaved ]
260  );
261  }
262  # Show relevant lines from the suppression log
263  if ( $this->permissionManager->userHasRight( $user, 'suppressionlog' ) ) {
264  $suppressLogPage = new LogPage( 'suppress' );
265  $output->addHTML( "<h2>" . $suppressLogPage->getName()->escaped() . "</h2>\n" );
267  $output,
268  'suppress',
269  $this->targetObj,
270  '',
271  [ 'lim' => 25, 'conds' => $this->getLogQueryCond(), 'useMaster' => $this->wasSaved ]
272  );
273  }
274  }
275 
279  protected function showConvenienceLinks() {
280  $linkRenderer = $this->getLinkRenderer();
281  # Give a link to the logs/hist for this page
282  if ( $this->targetObj ) {
283  // Also set header tabs to be for the target.
284  $this->getSkin()->setRelevantTitle( $this->targetObj );
285 
286  $links = [];
287  $links[] = $linkRenderer->makeKnownLink(
288  SpecialPage::getTitleFor( 'Log' ),
289  $this->msg( 'viewpagelogs' )->text(),
290  [],
291  [ 'page' => $this->targetObj->getPrefixedText() ]
292  );
293  if ( !$this->targetObj->isSpecialPage() ) {
294  # Give a link to the page history
295  $links[] = $linkRenderer->makeKnownLink(
296  $this->targetObj,
297  $this->msg( 'pagehist' )->text(),
298  [],
299  [ 'action' => 'history' ]
300  );
301  # Link to deleted edits
302  if ( $this->permissionManager->userHasRight( $this->getUser(), 'undelete' ) ) {
303  $undelete = SpecialPage::getTitleFor( 'Undelete' );
304  $links[] = $linkRenderer->makeKnownLink(
305  $undelete,
306  $this->msg( 'deletedhist' )->text(),
307  [],
308  [ 'target' => $this->targetObj->getPrefixedDBkey() ]
309  );
310  }
311  }
312  # Logs themselves don't have histories or archived revisions
313  $this->getOutput()->addSubtitle( $this->getLanguage()->pipeList( $links ) );
314  }
315  }
316 
321  protected function getLogQueryCond() {
322  $conds = [];
323  // Revision delete logs for these item
324  $conds['log_type'] = [ 'delete', 'suppress' ];
325  $conds['log_action'] = $this->getList()->getLogAction();
326  $conds['ls_field'] = RevisionDeleter::getRelationType( $this->typeName );
327  // Convert IDs to strings, since ls_value is a text field. This avoids
328  // a fatal error in PostgreSQL: "operator does not exist: text = integer".
329  $conds['ls_value'] = array_map( 'strval', $this->ids );
330 
331  return $conds;
332  }
333 
341  protected function tryShowFile( $archiveName ) {
342  $repo = $this->repoGroup->getLocalRepo();
343  $oimage = $repo->newFromArchiveName( $this->targetObj, $archiveName );
344  $oimage->load();
345  // Check if user is allowed to see this file
346  if ( !$oimage->exists() ) {
347  $this->getOutput()->addWikiMsg( 'revdelete-no-file' );
348 
349  return;
350  }
351  $user = $this->getUser();
352  if ( !$oimage->userCan( File::DELETED_FILE, $user ) ) {
353  if ( $oimage->isDeleted( File::DELETED_RESTRICTED ) ) {
354  throw new PermissionsError( 'suppressrevision' );
355  } else {
356  throw new PermissionsError( 'deletedtext' );
357  }
358  }
359  if ( !$user->matchEditToken( $this->token, $archiveName ) ) {
360  $lang = $this->getLanguage();
361  $this->getOutput()->addWikiMsg( 'revdelete-show-file-confirm',
362  $this->targetObj->getText(),
363  $lang->userDate( $oimage->getTimestamp(), $user ),
364  $lang->userTime( $oimage->getTimestamp(), $user ) );
365  $this->getOutput()->addHTML(
366  Xml::openElement( 'form', [
367  'method' => 'POST',
368  'action' => $this->getPageTitle()->getLocalURL( [
369  'target' => $this->targetObj->getPrefixedDBkey(),
370  'file' => $archiveName,
371  'token' => $user->getEditToken( $archiveName ),
372  ] )
373  ]
374  ) .
375  Xml::submitButton( $this->msg( 'revdelete-show-file-submit' )->text() ) .
376  '</form>'
377  );
378 
379  return;
380  }
381  $this->getOutput()->disable();
382  # We mustn't allow the output to be CDN cached, otherwise
383  # if an admin previews a deleted image, and it's cached, then
384  # a user without appropriate permissions can toddle off and
385  # nab the image, and CDN will serve it
386  $this->getRequest()->response()->header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', 0 ) . ' GMT' );
387  $this->getRequest()->response()->header(
388  'Cache-Control: no-cache, no-store, max-age=0, must-revalidate'
389  );
390  $this->getRequest()->response()->header( 'Pragma: no-cache' );
391 
392  $key = $oimage->getStorageKey();
393  $path = $repo->getZonePath( 'deleted' ) . '/' . $repo->getDeletedHashPath( $key ) . $key;
394  $repo->streamFileWithStatus( $path );
395  }
396 
401  protected function getList() {
402  if ( $this->revDelList === null ) {
403  $this->revDelList = RevisionDeleter::createList(
404  $this->typeName, $this->getContext(), $this->targetObj, $this->ids
405  );
406  }
407 
408  return $this->revDelList;
409  }
410 
415  protected function showForm() {
416  $userAllowed = true;
417 
418  // Messages: revdelete-selected-text, revdelete-selected-file, logdelete-selected
419  $out = $this->getOutput();
420  $out->wrapWikiMsg( "<strong>$1</strong>", [ $this->typeLabels['selected'],
421  $this->getLanguage()->formatNum( count( $this->ids ) ), $this->targetObj->getPrefixedText() ] );
422 
423  $this->addHelpLink( 'Help:RevisionDelete' );
424  $out->addHTML( "<ul>" );
425 
426  $numRevisions = 0;
427  // Live revisions...
428  $list = $this->getList();
429  for ( $list->reset(); $list->current(); $list->next() ) {
430  $item = $list->current();
431 
432  if ( !$item->canView() ) {
433  if ( !$this->submitClicked ) {
434  throw new PermissionsError( 'suppressrevision' );
435  }
436  $userAllowed = false;
437  }
438 
439  $numRevisions++;
440  $out->addHTML( $item->getHTML() );
441  }
442 
443  if ( !$numRevisions ) {
444  throw new ErrorPageError( 'revdelete-nooldid-title', 'revdelete-nooldid-text' );
445  }
446 
447  $out->addHTML( "</ul>" );
448  // Explanation text
449  $this->addUsageText();
450 
451  // Normal sysops can always see what they did, but can't always change it
452  if ( !$userAllowed ) {
453  return;
454  }
455 
456  // Show form if the user can submit
457  if ( $this->mIsAllowed ) {
458  $suppressAllowed = $this->permissionManager
459  ->userHasRight( $this->getUser(), 'suppressrevision' );
460  $out->addModules( [ 'mediawiki.special.revisionDelete' ] );
461  $out->addModuleStyles( [ 'mediawiki.special',
462  'mediawiki.interface.helpers.styles' ] );
463 
464  $dropDownReason = $this->msg( 'revdelete-reason-dropdown' )->inContentLanguage()->text();
465  // Add additional specific reasons for suppress
466  if ( $suppressAllowed ) {
467  $dropDownReason .= "\n" . $this->msg( 'revdelete-reason-dropdown-suppress' )
468  ->inContentLanguage()->text();
469  }
470 
471  $form = Xml::openElement( 'form', [ 'method' => 'post',
472  'action' => $this->getPageTitle()->getLocalURL( [ 'action' => 'submit' ] ),
473  'id' => 'mw-revdel-form-revisions' ] ) .
474  Xml::fieldset( $this->msg( 'revdelete-legend' )->text() ) .
475  $this->buildCheckBoxes() .
476  Xml::openElement( 'table' ) .
477  "<tr>\n" .
478  '<td class="mw-label">' .
479  Xml::label( $this->msg( 'revdelete-log' )->text(), 'wpRevDeleteReasonList' ) .
480  '</td>' .
481  '<td class="mw-input">' .
482  Xml::listDropDown( 'wpRevDeleteReasonList',
483  $dropDownReason,
484  $this->msg( 'revdelete-reasonotherlist' )->inContentLanguage()->text(),
485  $this->getRequest()->getText( 'wpRevDeleteReasonList', 'other' ), 'wpReasonDropDown'
486  ) .
487  '</td>' .
488  "</tr><tr>\n" .
489  '<td class="mw-label">' .
490  Xml::label( $this->msg( 'revdelete-otherreason' )->text(), 'wpReason' ) .
491  '</td>' .
492  '<td class="mw-input">' .
493  Xml::input( 'wpReason', 60, $this->otherReason, [
494  'id' => 'wpReason',
495  // HTML maxlength uses "UTF-16 code units", which means that characters outside BMP
496  // (e.g. emojis) count for two each. This limit is overridden in JS to instead count
497  // Unicode codepoints.
498  // "- 155" is to leave room for the 'wpRevDeleteReasonList' value.
499  'maxlength' => CommentStore::COMMENT_CHARACTER_LIMIT - 155,
500  ] ) .
501  '</td>' .
502  "</tr><tr>\n" .
503  '<td></td>' .
504  '<td class="mw-submit">' .
505  Xml::submitButton( $this->msg( 'revdelete-submit', $numRevisions )->text(),
506  [ 'name' => 'wpSubmit' ] ) .
507  '</td>' .
508  "</tr>\n" .
509  Xml::closeElement( 'table' ) .
510  Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() ) .
511  Html::hidden( 'target', $this->targetObj->getPrefixedText() ) .
512  Html::hidden( 'type', $this->typeName ) .
513  Html::hidden( 'ids', implode( ',', $this->ids ) ) .
514  Xml::closeElement( 'fieldset' ) . "\n" .
515  Xml::closeElement( 'form' ) . "\n";
516  // Show link to edit the dropdown reasons
517  if ( $this->permissionManager->userHasRight( $this->getUser(), 'editinterface' ) ) {
518  $link = '';
519  $linkRenderer = $this->getLinkRenderer();
520  if ( $suppressAllowed ) {
521  $link .= $linkRenderer->makeKnownLink(
522  $this->msg( 'revdelete-reason-dropdown-suppress' )->inContentLanguage()->getTitle(),
523  $this->msg( 'revdelete-edit-reasonlist-suppress' )->text(),
524  [],
525  [ 'action' => 'edit' ]
526  );
527  $link .= $this->msg( 'pipe-separator' )->escaped();
528  }
529  $link .= $linkRenderer->makeKnownLink(
530  $this->msg( 'revdelete-reason-dropdown' )->inContentLanguage()->getTitle(),
531  $this->msg( 'revdelete-edit-reasonlist' )->text(),
532  [],
533  [ 'action' => 'edit' ]
534  );
535  $form .= Xml::tags( 'p', [ 'class' => 'mw-revdel-editreasons' ], $link ) . "\n";
536  }
537  } else {
538  $form = '';
539  }
540  $out->addHTML( $form );
541  }
542 
547  protected function addUsageText() {
548  // Messages: revdelete-text-text, revdelete-text-file, logdelete-text
549  $this->getOutput()->wrapWikiMsg(
550  "<strong>$1</strong>\n$2", $this->typeLabels['text'],
551  'revdelete-text-others'
552  );
553 
554  if ( $this->permissionManager->userHasRight( $this->getUser(), 'suppressrevision' ) ) {
555  $this->getOutput()->addWikiMsg( 'revdelete-suppress-text' );
556  }
557 
558  if ( $this->mIsAllowed ) {
559  $this->getOutput()->addWikiMsg( 'revdelete-confirm' );
560  }
561  }
562 
566  protected function buildCheckBoxes() {
567  $html = '<table>';
568  // If there is just one item, use checkboxes
569  $list = $this->getList();
570  if ( $list->length() == 1 ) {
571  $list->reset();
572  $bitfield = $list->current()->getBits(); // existing field
573 
574  if ( $this->submitClicked ) {
575  $bitfield = RevisionDeleter::extractBitfield( $this->extractBitParams(), $bitfield );
576  }
577 
578  foreach ( $this->checks as $item ) {
579  // Messages: revdelete-hide-text, revdelete-hide-image, revdelete-hide-name,
580  // revdelete-hide-comment, revdelete-hide-user, revdelete-hide-restricted
581  list( $message, $name, $field ) = $item;
582  $innerHTML = Xml::checkLabel(
583  $this->msg( $message )->text(),
584  $name,
585  $name,
586  (bool)( $bitfield & $field )
587  );
588 
589  if ( $field == RevisionRecord::DELETED_RESTRICTED ) {
590  $innerHTML = "<b>$innerHTML</b>";
591  }
592 
593  $line = Xml::tags( 'td', [ 'class' => 'mw-input' ], $innerHTML );
594  $html .= "<tr>$line</tr>\n";
595  }
596  } else {
597  // Otherwise, use tri-state radios
598  $html .= '<tr>';
599  $html .= '<th class="mw-revdel-checkbox">'
600  . $this->msg( 'revdelete-radio-same' )->escaped() . '</th>';
601  $html .= '<th class="mw-revdel-checkbox">'
602  . $this->msg( 'revdelete-radio-unset' )->escaped() . '</th>';
603  $html .= '<th class="mw-revdel-checkbox">'
604  . $this->msg( 'revdelete-radio-set' )->escaped() . '</th>';
605  $html .= "<th></th></tr>\n";
606  foreach ( $this->checks as $item ) {
607  // Messages: revdelete-hide-text, revdelete-hide-image, revdelete-hide-name,
608  // revdelete-hide-comment, revdelete-hide-user, revdelete-hide-restricted
609  list( $message, $name, $field ) = $item;
610  // If there are several items, use third state by default...
611  if ( $this->submitClicked ) {
612  $selected = $this->getRequest()->getInt( $name, 0 /* unchecked */ );
613  } else {
614  $selected = -1; // use existing field
615  }
616  $line = '<td class="mw-revdel-checkbox">' . Xml::radio( $name, '-1', $selected == -1 ) . '</td>';
617  $line .= '<td class="mw-revdel-checkbox">' . Xml::radio( $name, '0', $selected == 0 ) . '</td>';
618  $line .= '<td class="mw-revdel-checkbox">' . Xml::radio( $name, '1', $selected == 1 ) . '</td>';
619  $label = $this->msg( $message )->escaped();
620  if ( $field == RevisionRecord::DELETED_RESTRICTED ) {
621  $label = "<b>$label</b>";
622  }
623  $line .= "<td>$label</td>";
624  $html .= "<tr>$line</tr>\n";
625  }
626  }
627 
628  $html .= '</table>';
629 
630  return $html;
631  }
632 
638  protected function submit() {
639  # Check edit token on submission
640  $token = $this->getRequest()->getVal( 'wpEditToken' );
641  if ( $this->submitClicked && !$this->getUser()->matchEditToken( $token ) ) {
642  $this->getOutput()->addWikiMsg( 'sessionfailure' );
643 
644  return false;
645  }
646  $bitParams = $this->extractBitParams();
647  // from dropdown
648  $listReason = $this->getRequest()->getText( 'wpRevDeleteReasonList', 'other' );
649  $comment = $listReason;
650  if ( $comment === 'other' ) {
651  $comment = $this->otherReason;
652  } elseif ( $this->otherReason !== '' ) {
653  // Entry from drop down menu + additional comment
654  $comment .= $this->msg( 'colon-separator' )->inContentLanguage()->text()
656  }
657  # Can the user set this field?
658  if ( $bitParams[RevisionRecord::DELETED_RESTRICTED] == 1
659  && !$this->permissionManager->userHasRight( $this->getUser(), 'suppressrevision' )
660  ) {
661  throw new PermissionsError( 'suppressrevision' );
662  }
663  # If the save went through, go to success message...
664  $status = $this->save( $bitParams, $comment );
665  if ( $status->isGood() ) {
666  $this->success();
667 
668  return true;
669  } else {
670  # ...otherwise, bounce back to form...
671  $this->failure( $status );
672  }
673 
674  return false;
675  }
676 
680  protected function success() {
681  // Messages: revdelete-success, logdelete-success
682  $out = $this->getOutput();
683  $out->setPageTitle( $this->msg( 'actioncomplete' ) );
684  $out->addHTML(
686  $out->msg( $this->typeLabels['success'] )->parse()
687  )
688  );
689  $this->wasSaved = true;
690  $this->revDelList->reloadFromPrimary();
691  $this->showForm();
692  }
693 
698  protected function failure( $status ) {
699  // Messages: revdelete-failure, logdelete-failure
700  $out = $this->getOutput();
701  $out->setPageTitle( $this->msg( 'actionfailed' ) );
702  $out->addHTML(
704  $out->parseAsContent(
705  $status->getWikiText( $this->typeLabels['failure'], false, $this->getLanguage() )
706  )
707  )
708  );
709  $this->showForm();
710  }
711 
717  protected function extractBitParams() {
718  $bitfield = [];
719  foreach ( $this->checks as $item ) {
720  list( /* message */, $name, $field ) = $item;
721  $val = $this->getRequest()->getInt( $name, 0 /* unchecked */ );
722  if ( $val < -1 || $val > 1 ) {
723  $val = -1; // -1 for existing value
724  }
725  $bitfield[$field] = $val;
726  }
727  if ( !isset( $bitfield[RevisionRecord::DELETED_RESTRICTED] ) ) {
728  $bitfield[RevisionRecord::DELETED_RESTRICTED] = 0;
729  }
730 
731  return $bitfield;
732  }
733 
740  protected function save( array $bitPars, $reason ) {
741  return $this->getList()->setVisibility(
742  [ 'value' => $bitPars, 'comment' => $reason ]
743  );
744  }
745 
746  protected function getGroupName() {
747  return 'pagetools';
748  }
749 }
const COMMENT_CHARACTER_LIMIT
Maximum length of a comment in UTF-8 characters.
An error page which can definitely be safely rendered using the OutputPage.
const DELETED_RESTRICTED
Definition: File.php:75
const DELETED_FILE
Definition: File.php:72
static successBox( $html, $className='')
Return a success box.
Definition: Html.php:800
static errorBox( $html, $heading='', $className='')
Return an error box.
Definition: Html.php:788
static hidden( $name, $value, array $attribs=[])
Convenience function to produce an input element with type=hidden.
Definition: Html.php:851
static showLogExtract(&$out, $types=[], $page='', $user='', $param=[])
Show log extract.
Class to simplify the use of log pages.
Definition: LogPage.php:39
A service class for checking permissions To obtain an instance, use MediaWikiServices::getInstance()-...
Page revision base class.
Show an error when a user tries to do something they do not have the necessary permissions for.
Prioritized list of file repositories.
Definition: RepoGroup.php:29
static getCanonicalTypeName( $typeName)
Gets the canonical type name, if any.
static getRelationType( $typeName)
Get DB field name for URL param...
static suggestTarget( $typeName, $target, array $ids)
Suggest a target for the revision deletion.
static extractBitfield(array $bitPars, $oldfield)
Put together a rev_deleted bitfield.
static getRevdelConstant( $typeName)
Get the revision deletion constant for the RevDel type.
static getRestriction( $typeName)
Get the user right required for the RevDel type.
static createList( $typeName, IContextSource $context, PageIdentity $page, array $ids)
Instantiate the appropriate list class for a given list of IDs.
outputHeader( $summaryMessageKey='')
Outputs a summary message on top of special pages Per default the message key is the canonical name o...
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!
getOutput()
Get the OutputPage being used for this instance.
getUser()
Shortcut to get the User executing this instance.
getSkin()
Shortcut to get the skin being used for this instance.
checkPermissions()
Checks if userCanExecute, and if not throws a PermissionsError.
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
getContext()
Gets the context this SpecialPage is executed in.
LinkRenderer null $linkRenderer
Definition: SpecialPage.php:81
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getAuthority()
Shortcut to get the Authority executing this instance.
getRequest()
Get the WebRequest being used for this instance.
checkReadOnly()
If the wiki is currently in readonly mode, throws a ReadOnlyError.
getPageTitle( $subpage=false)
Get a self-referential title object.
useTransactionalTimeLimit()
Call wfTransactionalTimeLimit() if this request was POSTed.
getLanguage()
Shortcut to get user's language.
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
Special page allowing users with the appropriate permissions to view and hide revisions.
string $token
Edit token for securing image views against XSS.
bool $mIsAllowed
Whether user is allowed to perform the action.
const UI_LABELS
UI labels for each type.
showForm()
Show a list of items that we will operate on, and show a form with checkboxes which will allow the us...
tryShowFile( $archiveName)
Show a deleted file version requested by the visitor.
string $archiveName
Archive name, for reviewing deleted files.
save(array $bitPars, $reason)
Do the write operations.
success()
Report that the submit operation succeeded.
addUsageText()
Show some introductory text.
array $typeLabels
UI Labels about the current type.
bool $wasSaved
Was the DB modified in this request.
getLogQueryCond()
Get the condition used for fetching log snippets.
PermissionManager $permissionManager
RevDelList $revDelList
RevDelList object, storing the list of items to be deleted/undeleted.
showConvenienceLinks()
Show some useful links in the subtitle.
doesWrites()
Indicates whether this special page may perform database writes.
execute( $par)
Default execute method Checks user permissions.
array $checks
Array of checkbox specs (message, name, deletion bits)
failure( $status)
Report that the submit operation failed.
__construct(PermissionManager $permissionManager, RepoGroup $repoGroup)
submit()
UI entry point for form submission.
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
Title $targetObj
Title object for target parameter.
extractBitParams()
Put together an array that contains -1, 0, or the *_deleted const for each bit.
bool $submitClicked
True if the submit button was clicked, and the form was posted.
string $typeName
Deletion type, may be revision, archive, oldimage, filearchive, logging.
getList()
Get the list object for this request.
array $ids
Target ID list.
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:369
Shortcut to construct a special page which is unlisted by default.
Show an error when the user tries to do something whilst blocked.
static closeElement( $element)
Shortcut to close an XML element.
Definition: Xml.php:121
static label( $label, $id, $attribs=[])
Convenience function to build an HTML form label.
Definition: Xml.php:367
static openElement( $element, $attribs=null)
This opens an XML element.
Definition: Xml.php:112
static input( $name, $size=false, $value=false, $attribs=[])
Convenience function to build an HTML text input field.
Definition: Xml.php:283
static submitButton( $value, $attribs=[])
Convenience function to build an HTML submit button When $wgUseMediaWikiUIEverywhere is true it will ...
Definition: Xml.php:469
static checkLabel( $label, $name, $id, $checked=false, $attribs=[])
Convenience function to build an HTML checkbox with a label.
Definition: Xml.php:428
static tags( $element, $attribs, $contents)
Same as Xml::element(), but does not escape contents.
Definition: Xml.php:134
static radio( $name, $value, $checked=false, $attribs=[])
Convenience function to build an HTML radio button.
Definition: Xml.php:350
static listDropDown( $name='', $list='', $other='', $selected='', $class='', $tabindex=null)
Build a drop-down box from a textual list.
Definition: Xml.php:519
static fieldset( $legend=false, $content=false, $attribs=[])
Shortcut for creating fieldsets.
Definition: Xml.php:628
$line
Definition: mcc.php:119
if(!isset( $args[0])) $lang