MediaWiki master
SpecialUndelete.php
Go to the documentation of this file.
1<?php
7namespace MediaWiki\Specials;
8
15use MediaWiki\EditPage\DataStashTrait;
33use MediaWiki\Page\UndeletePage;
56use OOUI\ActionFieldLayout;
57use OOUI\ButtonInputWidget;
58use OOUI\CheckboxInputWidget;
59use OOUI\DropdownInputWidget;
60use OOUI\FieldLayout;
61use OOUI\FieldsetLayout;
62use OOUI\FormLayout;
63use OOUI\HorizontalLayout;
64use OOUI\HtmlSnippet;
65use OOUI\Layout;
66use OOUI\PanelLayout;
67use OOUI\TextInputWidget;
68use OOUI\Widget;
72use Wikimedia\Timestamp\TimestampFormat as TS;
73
81 use DataStashTrait;
82
87 private const REVISION_HISTORY_LIMIT = 500;
88
90 private $mAction;
92 private $mTarget;
94 private $mTimestamp;
96 private $mRestore;
98 private $mRevdel;
100 private $mInvert;
102 private $mFilename;
104 private $mTargetTimestamp = [];
106 private $mAllowed;
108 private $mCanView;
110 private $mComment = '';
112 private $mToken;
114 private $mPreview;
116 private $mDiff;
118 private $mDiffOnly;
120 private $mUnsuppress;
122 private $mFileVersions = [];
124 private $mUndeleteTalk;
126 private $mHistoryOffset;
127
129 private $mTargetObj;
133 private $mSearchPrefix;
134
135 private bool $reauthInProgress = false;
136
137 private LocalRepo $localRepo;
138
139 public function __construct(
140 private readonly PermissionManager $permissionManager,
141 private readonly RevisionStore $revisionStore,
142 private readonly RevisionRenderer $revisionRenderer,
143 private readonly IContentHandlerFactory $contentHandlerFactory,
144 private readonly NameTableStore $changeTagDefStore,
145 private readonly LinkBatchFactory $linkBatchFactory,
146 RepoGroup $repoGroup,
147 private readonly IConnectionProvider $dbProvider,
148 private readonly UserOptionsLookup $userOptionsLookup,
149 private readonly WikiPageFactory $wikiPageFactory,
150 private readonly SearchEngineFactory $searchEngineFactory,
151 private readonly UndeletePageFactory $undeletePageFactory,
152 private readonly ArchivedRevisionLookup $archivedRevisionLookup,
153 private readonly CommentFormatter $commentFormatter,
154 private readonly WatchlistManager $watchlistManager,
155 private readonly ChangeTagsFormatter $changeTagsFormatter,
156 ) {
157 parent::__construct( 'Undelete' );
158 $this->localRepo = $repoGroup->getLocalRepo();
159 }
160
162 public function getRestriction(): string {
163 return 'deletedhistory';
164 }
165
167 public function doesWrites() {
168 return true;
169 }
170
171 public function getTitle(): Title {
172 return $this->mTargetObj
173 ? $this->getPageTitle( $this->mTargetObj->getPrefixedText() )
174 : $this->getPageTitle();
175 }
176
177 private function getStashKeyForTitle( Title $title ): string {
178 return $this->getName() . ':' . $title->getPrefixedDBkey();
179 }
180
185 private function parseSubmittedFormFields( array $values ): void {
186 $commentList = $values['wpCommentList'] ?? 'other';
187 $comment = $values['wpComment'] ?? '';
188 if ( $commentList === 'other' ) {
189 $this->mComment = $comment;
190 } elseif ( $comment !== '' ) {
191 $this->mComment = $commentList
192 . $this->msg( 'colon-separator' )->inContentLanguage()->text()
193 . $comment;
194 } else {
195 $this->mComment = $commentList;
196 }
197
198 $this->mUnsuppress = !empty( $values['wpUnsuppress'] )
199 && $this->permissionManager->userHasRight( $this->getUser(), 'suppressrevision' );
200 $this->mUndeleteTalk = !empty( $values['undeletetalk'] );
201
202 $timestamps = [];
203 $this->mFileVersions = [];
204 foreach ( $values as $key => $val ) {
205 $matches = [];
206 if ( preg_match( '/^ts(\d{14})$/', $key, $matches ) ) {
207 $timestamps[] = $matches[1];
208 } elseif ( preg_match( '/^fileid(\d+)$/', $key, $matches ) ) {
209 $this->mFileVersions[] = (int)$matches[1];
210 }
211 }
212 rsort( $timestamps );
213 $this->mTargetTimestamp = $timestamps;
214 }
215
219 protected function handleRetrievedData( array $data ): void {
220 $this->parseSubmittedFormFields( $data );
221 if ( $this->mAllowed ) {
222 $this->mAction = 'submit';
223 $this->mRestore = true;
224 }
225 }
226
227 private function loadRequest( ?string $par ) {
228 $request = $this->getRequest();
229 $user = $this->getUser();
230
231 $this->mAction = $request->getRawVal( 'action' );
232 if ( $par !== null && $par !== '' ) {
233 $this->mTarget = $par;
234 } else {
235 $this->mTarget = $request->getVal( 'target' );
236 }
237
238 $this->mTargetObj = null;
239
240 if ( $this->mTarget !== null && $this->mTarget !== '' ) {
241 $this->mTargetObj = Title::newFromText( $this->mTarget );
242 }
243
244 $this->mSearchPrefix = $request->getText( 'prefix' );
245 $time = $request->getVal( 'timestamp' );
246 $this->mTimestamp = $time ? wfTimestamp( TS::MW, $time ) : '';
247 $this->mFilename = $request->getVal( 'file' );
248
249 $posted = $request->wasPosted() &&
250 $user->matchEditToken( $request->getVal( 'wpEditToken' ) );
251 $this->mRestore = $request->getCheck( 'restore' ) && $posted;
252 $this->mRevdel = $request->getCheck( 'revdel' ) && $posted;
253 $this->mInvert = $request->getCheck( 'invert' ) && $posted;
254 $this->mPreview = $request->getCheck( 'preview' ) && $posted;
255 $this->mDiff = $request->getCheck( 'diff' );
256 $this->mDiffOnly = $request->getBool( 'diffonly',
257 $this->userOptionsLookup->getOption( $this->getUser(), 'diffonly' ) );
258 $this->mToken = $request->getVal( 'token' );
259 $this->mHistoryOffset = $request->getVal( 'historyoffset' );
260
261 $this->parseSubmittedFormFields( $request->getValues() );
262
263 if ( $this->isAllowed( 'undelete' ) ) {
264 $this->mAllowed = true; // user can restore
265 $this->mCanView = true; // user can view content
266 } elseif ( $this->isAllowed( 'deletedtext' ) ) {
267 $this->mAllowed = false; // user cannot restore
268 $this->mCanView = true; // user can view content
269 $this->mRestore = false;
270 } else { // user can only view the list of revisions
271 $this->mAllowed = false;
272 $this->mCanView = false;
273 $this->mTimestamp = '';
274 $this->mRestore = false;
275 }
276 }
277
286 protected function isAllowed( $permission, ?User $user = null ) {
287 $user ??= $this->getUser();
288 $block = $user->getBlock();
289
290 if ( $this->mTargetObj !== null ) {
291 return $this->permissionManager->userCan( $permission, $user, $this->mTargetObj );
292 } else {
293 $hasRight = $this->permissionManager->userHasRight( $user, $permission );
294 $sitewideBlock = $block && $block->isSitewide();
295 return $permission === 'undelete' ? ( $hasRight && !$sitewideBlock ) : $hasRight;
296 }
297 }
298
300 public function userCanExecute( User $user ) {
301 return $this->isAllowed( $this->getRestriction(), $user );
302 }
303
307 public function checkPermissions() {
308 $user = $this->getUser();
309
310 // First check if user has the right to use this page. If not,
311 // show a permissions error whether they are blocked or not.
312 if ( !parent::userCanExecute( $user ) ) {
313 $this->displayRestrictionError();
314 }
315
316 // If a user has the right to use this page, but is blocked from
317 // the target, show a block error.
318 if (
319 $this->mTargetObj && $this->permissionManager->isBlockedFrom( $user, $this->mTargetObj ) ) {
320 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable Block is checked and not null
321 throw new UserBlockedError( $user->getBlock() );
322 }
323
324 // Finally, do the comprehensive permission check via isAllowed.
325 if ( !$this->userCanExecute( $user ) ) {
326 $this->displayRestrictionError();
327 }
328 }
329
331 public function execute( $par ) {
332 $this->useTransactionalTimeLimit();
333
334 $user = $this->getUser();
335
336 $this->setHeaders();
337 $this->outputHeader();
338 $this->addHelpLink( 'Help:Deletion_and_undeletion' );
339
340 $this->loadRequest( $par );
341 $this->checkPermissions(); // Needs to be after mTargetObj is set
342
343 $out = $this->getOutput();
344 // This page uses Html::warningBox and Html::errorBox
345 $out->addModuleStyles( 'mediawiki.codex.messagebox.styles' );
346
347 if ( $this->mTargetObj === null ) {
348 $out->addWikiMsg( 'undelete-header' );
349
350 # Not all users can just browse every deleted page from the list
351 if ( $this->permissionManager->userHasRight( $user, 'browsearchive' ) ) {
352 $this->showSearchForm();
353 }
354
355 return;
356 }
357
358 $this->addHelpLink( 'Help:Undelete' );
359 if ( $this->mAllowed ) {
360 $out->setPageTitleMsg( $this->msg( 'undeletepage' ) );
361 } else {
362 $out->setPageTitleMsg( $this->msg( 'viewdeletedpage' ) );
363 }
364
365 $this->getSkin()->setRelevantTitle( $this->mTargetObj );
366
367 // Resume from a stashed submit after reauth. Must come before the
368 // dispatch below, since the return trip is a GET with no form fields.
369 $this->setStashKey( $this->getStashKeyForTitle( $this->mTargetObj ) );
370 if ( $this->retrieveStashedData() ) {
371 $this->undelete();
372 if ( !$this->reauthInProgress ) {
373 $this->destroyStashedData();
374 }
375 return;
376 }
377
378 if ( $this->mTimestamp !== '' ) {
379 $this->showRevision( $this->mTimestamp );
380 } elseif ( $this->mFilename !== null && $this->mTargetObj->inNamespace( NS_FILE ) ) {
381 $file = new ArchivedFile( $this->mTargetObj, 0, $this->mFilename );
382 // Check if user is allowed to see this file
383 if ( !$file->exists() ) {
384 $out->addWikiMsg( 'filedelete-nofile', $this->mFilename );
385 } elseif ( !$file->userCan( File::DELETED_FILE, $user ) ) {
386 if ( $file->isDeleted( File::DELETED_RESTRICTED ) ) {
387 throw new PermissionsError( 'suppressrevision' );
388 } else {
389 throw new PermissionsError( 'deletedtext' );
390 }
391 } elseif ( !$user->matchEditToken( $this->mToken, $this->mFilename ) ) {
392 $this->showFileConfirmationForm( $this->mFilename );
393 } else {
394 $this->showFile( $this->mFilename );
395 }
396 } elseif ( $this->mAction === 'submit' ) {
397 if ( $this->mRestore ) {
398 $this->undelete();
399 } elseif ( $this->mRevdel ) {
400 $this->redirectToRevDel();
401 }
402 } elseif ( $this->mAction === 'render' ) {
403 $this->showMoreHistory();
404 } else {
405 $this->showHistory();
406 }
407 }
408
413 private function redirectToRevDel() {
414 $revisionIds = [];
415 $fileArchiveIds = [];
416
417 foreach ( $this->getRequest()->getValues() as $key => $val ) {
418 $matches = [];
419 if ( preg_match( "/^ts(\d{14})$/", $key, $matches ) ) {
420 $revisionRecord = $this->archivedRevisionLookup
421 ->getRevisionRecordByTimestamp( $this->mTargetObj, $matches[1] );
422 if ( $revisionRecord ) {
423 $revisionIds[] = (int)$revisionRecord->getId();
424 }
425 } elseif ( preg_match( '/^fileid(\d+)$/', $key, $matches ) ) {
426 $fileArchiveIds[] = (int)$matches[1];
427 }
428 }
429
430 $hasRevisions = count( $revisionIds ) > 0;
431 $hasFiles = count( $fileArchiveIds ) > 0;
432
433 // Check selection validity, see if mixed or nothing selected
434 if ( $hasRevisions && $hasFiles ) {
435 $this->renderUndeleteSelectionError( 'mixed' );
436 return;
437 } elseif ( !$hasRevisions && !$hasFiles ) {
438 $this->renderUndeleteSelectionError( 'none' );
439 return;
440 }
441
442 // Exp. assoc array of id => 1 (ids[123]=1)
443 $idsForQuery = $hasFiles
444 ? array_fill_keys( $fileArchiveIds, 1 )
445 : array_fill_keys( $revisionIds, 1 );
446
447 $query = [
448 'type' => $hasFiles ? 'filearchive' : 'revision',
449 'ids' => $idsForQuery,
450 'target' => $this->mTargetObj->getPrefixedText()
451 ];
452
453 $url = SpecialPage::getTitleFor( 'Revisiondelete' )->getFullURL( $query );
454 $this->getOutput()->redirect( $url );
455 }
456
462 private function renderUndeleteSelectionError( string $case ): void {
463 $msg = $case === 'mixed'
464 ? $this->msg( 'undelete-error-mixed' )
465 : $this->msg( 'undelete-error-none' );
466
467 $this->getOutput()->addHTML( Html::errorBox( $msg->parse() ) );
468 }
469
470 private function showSearchForm() {
471 $out = $this->getOutput();
472 $out->setPageTitleMsg( $this->msg( 'undelete-search-title' ) );
473 $fuzzySearch = $this->getRequest()->getVal( 'fuzzy', '1' );
474
475 $out->enableOOUI();
476
477 $fields = [];
478 $fields[] = new ActionFieldLayout(
479 new TextInputWidget( [
480 'name' => 'prefix',
481 'inputId' => 'prefix',
482 'infusable' => true,
483 'value' => $this->mSearchPrefix,
484 'autofocus' => true,
485 ] ),
486 new ButtonInputWidget( [
487 'label' => $this->msg( 'undelete-search-submit' )->text(),
488 'flags' => [ 'primary', 'progressive' ],
489 'inputId' => 'searchUndelete',
490 'type' => 'submit',
491 ] ),
492 [
493 'label' => new HtmlSnippet(
494 $this->msg(
495 $fuzzySearch ? 'undelete-search-full' : 'undelete-search-prefix'
496 )->parse()
497 ),
498 'align' => 'left',
499 ]
500 );
501
502 $fieldset = new FieldsetLayout( [
503 'label' => $this->msg( 'undelete-search-box' )->text(),
504 'items' => $fields,
505 ] );
506
507 $form = new FormLayout( [
508 'method' => 'get',
509 'action' => wfScript(),
510 ] );
511
512 $form->appendContent(
513 $fieldset,
514 new HtmlSnippet(
515 Html::hidden( 'title', $this->getPageTitle()->getPrefixedDBkey() ) .
516 Html::hidden( 'fuzzy', $fuzzySearch )
517 )
518 );
519
520 $out->addHTML(
521 ( new PanelLayout( [
522 'expanded' => false,
523 'padded' => true,
524 'framed' => true,
525 'content' => $form,
526 ] ) )->toString()
527 );
528
529 # List undeletable articles
530 if ( $this->mSearchPrefix ) {
531 // For now, we enable search engine match only when specifically asked to
532 // by using fuzzy=1 parameter.
533 if ( $fuzzySearch ) {
534 $result = PageArchive::listPagesBySearch( $this->mSearchPrefix );
535 } else {
536 $result = PageArchive::listPagesByPrefix( $this->mSearchPrefix );
537 }
538 $this->showList( $result );
539 }
540 }
541
548 private function showList( $result ) {
549 $out = $this->getOutput();
550
551 if ( $result->numRows() == 0 ) {
552 $out->addWikiMsg( 'undelete-no-results' );
553
554 return false;
555 }
556
557 $out->addWikiMsg( 'undeletepagetext', $this->getLanguage()->formatNum( $result->numRows() ) );
558
559 $linkRenderer = $this->getLinkRenderer();
560 $undelete = $this->getPageTitle();
561 $out->addHTML( "<ul id='undeleteResultsList'>\n" );
562 foreach ( $result as $row ) {
563 $title = Title::makeTitleSafe( $row->ar_namespace, $row->ar_title );
564 if ( $title !== null ) {
565 $item = $linkRenderer->makeKnownLink(
566 $undelete,
567 $title->getPrefixedText(),
568 [],
569 [ 'target' => $title->getPrefixedText() ]
570 );
571 } else {
572 // The title is no longer valid, show as text
573 $item = Html::element(
574 'span',
575 [ 'class' => 'mw-invalidtitle' ],
576 Linker::getInvalidTitleDescription(
577 $this->getContext(),
578 $row->ar_namespace,
579 $row->ar_title
580 )
581 );
582 }
583 $revs = $this->msg( 'undeleterevisions' )->numParams( $row->count )->parse();
584 $out->addHTML(
585 Html::rawElement(
586 'li',
587 [ 'class' => 'undeleteResult' ],
588 $item . $this->msg( 'word-separator' )->escaped() .
589 $this->msg( 'parentheses' )->rawParams( $revs )->escaped()
590 )
591 );
592 }
593 $result->free();
594 $out->addHTML( "</ul>\n" );
595
596 return true;
597 }
598
599 private function showRevision( string $timestamp ) {
600 if ( !preg_match( '/[0-9]{14}/', $timestamp ) ) {
601 return;
602 }
603 $out = $this->getOutput();
604 $out->addModuleStyles( 'mediawiki.interface.helpers.styles' );
605
606 // When viewing a specific revision, add a subtitle link back to the overall
607 // history, see T284114
608 $listLink = $this->getLinkRenderer()->makeKnownLink(
609 $this->getPageTitle(),
610 $this->msg( 'undelete-back-to-list' )->text(),
611 [],
612 [ 'target' => $this->mTargetObj->getPrefixedText() ]
613 );
614 // same < arrow as with subpages
615 $subtitle = "&lt; $listLink";
616 $out->setSubtitle( $subtitle );
617
618 $archive = new PageArchive( $this->mTargetObj );
619 // FIXME: This hook must be deprecated, passing PageArchive by ref is awful.
620 if ( !$this->getHookRunner()->onUndeleteForm__showRevision(
621 $archive, $this->mTargetObj )
622 ) {
623 return;
624 }
625 $revRecord = $this->archivedRevisionLookup->getRevisionRecordByTimestamp( $this->mTargetObj, $timestamp );
626
627 $user = $this->getUser();
628
629 if ( !$revRecord ) {
630 $out->addWikiMsg( 'undeleterevision-missing' );
631 return;
632 }
633
634 if ( $revRecord->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
635 // Used in wikilinks, should not contain whitespaces
636 $titleText = $this->mTargetObj->getPrefixedURL();
637 if ( !$revRecord->userCan( RevisionRecord::DELETED_TEXT, $this->getAuthority() ) ) {
638 $msg = $revRecord->isDeleted( RevisionRecord::DELETED_RESTRICTED )
639 ? [ 'rev-suppressed-text-permission', $titleText ]
640 : [ 'rev-deleted-text-permission', $titleText ];
641 $out->addHTML(
642 Html::warningBox(
643 $this->msg( $msg[0], $msg[1] )->parse(),
644 'plainlinks'
645 )
646 );
647 return;
648 }
649
650 $msg = $revRecord->isDeleted( RevisionRecord::DELETED_RESTRICTED )
651 ? [ 'rev-suppressed-text-view', $titleText ]
652 : [ 'rev-deleted-text-view', $titleText ];
653 $out->addHTML(
654 Html::warningBox(
655 $this->msg( $msg[0], $msg[1] )->parse(),
656 'plainlinks'
657 )
658 );
659 // and we are allowed to see...
660 }
661
662 if ( $this->mDiff ) {
663 $previousRevRecord = $this->archivedRevisionLookup
664 ->getPreviousRevisionRecord( $this->mTargetObj, $timestamp );
665 if ( $previousRevRecord ) {
666 $this->showDiff( $previousRevRecord, $revRecord );
667 if ( $this->mDiffOnly ) {
668 return;
669 }
670
671 $out->addHTML( '<hr />' );
672 } else {
673 $out->addWikiMsg( 'undelete-nodiff' );
674 }
675 }
676
677 $link = $this->getLinkRenderer()->makeKnownLink(
678 $this->getPageTitle( $this->mTargetObj->getPrefixedDBkey() ),
679 $this->mTargetObj->getPrefixedText()
680 );
681
682 $lang = $this->getLanguage();
683
684 // date and time are separate parameters to facilitate localisation.
685 // $time is kept for backward compat reasons.
686 $time = $lang->userTimeAndDate( $timestamp, $user );
687 $d = $lang->userDate( $timestamp, $user );
688 $t = $lang->userTime( $timestamp, $user );
689 $userLink = Linker::revUserTools( $revRecord );
690
691 try {
692 $content = $revRecord->getContent(
693 SlotRecord::MAIN,
694 RevisionRecord::FOR_THIS_USER,
695 $user
696 );
697 } catch ( RevisionAccessException ) {
698 $content = null;
699 }
700
701 // TODO: MCR: this will have to become something like $hasTextSlots and $hasNonTextSlots
702 $isText = ( $content instanceof TextContent );
703
704 $undeleteRevisionContent = '';
705 // Revision delete links
706 if ( !$this->mDiff ) {
707 $revdel = Linker::getRevDeleteLink(
708 $user,
709 $revRecord,
710 $this->mTargetObj
711 );
712 if ( $revdel ) {
713 $undeleteRevisionContent = $revdel . ' ';
714 }
715 }
716
717 $undeleteRevisionContent .= $out->msg(
718 'undelete-revision',
719 Message::rawParam( $link ),
720 $time,
721 Message::rawParam( $userLink ),
722 $d,
723 $t
724 )->parseAsBlock();
725
726 if ( $this->mPreview || $isText ) {
727 $out->addHTML(
728 Html::warningBox(
729 $undeleteRevisionContent,
730 'mw-undelete-revision'
731 )
732 );
733 } else {
734 $out->addHTML(
735 Html::rawElement(
736 'div',
737 [ 'class' => 'mw-undelete-revision', ],
738 $undeleteRevisionContent
739 )
740 );
741 }
742
743 if ( $this->mPreview || !$isText ) {
744 // NOTE: non-text content has no source view, so always use rendered preview
745
746 $popts = ParserOptions::newFromContext( $this->getContext() );
747
748 try {
749 $rendered = $this->revisionRenderer->getRenderedRevision(
750 $revRecord,
751 $popts,
752 $user,
753 [ 'audience' => RevisionRecord::FOR_THIS_USER, 'causeAction' => 'undelete-preview' ]
754 );
755
756 // Fail hard if the audience check fails, since we already checked
757 // at the beginning of this method.
758 $pout = $rendered->getRevisionParserOutput();
759
760 $popts->setSuppressSectionEditLinks();
761 $out->addParserOutput( $pout, $popts );
762 } catch ( RevisionAccessException ) {
763 }
764 }
765
766 $out->enableOOUI();
767 $buttonFields = [];
768
769 if ( $isText ) {
770 '@phan-var TextContent $content';
771 // TODO: MCR: make this work for multiple slots
772 // source view for textual content
773 $sourceView = Html::element( 'textarea', [
774 'class' => 'mw-undelete-textarea',
775 'readonly' => 'readonly',
776 'cols' => 80,
777 'rows' => 25
778 ], $content->getText() . "\n" );
779
780 $buttonFields[] = new ButtonInputWidget( [
781 'type' => 'submit',
782 'name' => 'preview',
783 'label' => $this->msg( 'showpreview' )->text()
784 ] );
785 } else {
786 $sourceView = '';
787 }
788
789 $buttonFields[] = new ButtonInputWidget( [
790 'name' => 'diff',
791 'type' => 'submit',
792 'label' => $this->msg( 'showdiff' )->text()
793 ] );
794
795 $out->addHTML(
796 $sourceView .
797 Html::openElement( 'div', [
798 'style' => 'clear: both' ] ) .
799 Html::openElement( 'form', [
800 'method' => 'post',
801 'action' => $this->getPageTitle()->getLocalURL( [ 'action' => 'submit' ] ) ] ) .
802 Html::element( 'input', [
803 'type' => 'hidden',
804 'name' => 'target',
805 'value' => $this->mTargetObj->getPrefixedDBkey() ] ) .
806 Html::element( 'input', [
807 'type' => 'hidden',
808 'name' => 'timestamp',
809 'value' => $timestamp ] ) .
810 Html::element( 'input', [
811 'type' => 'hidden',
812 'name' => 'wpEditToken',
813 'value' => $user->getEditToken() ] ) .
814 new FieldLayout(
815 new Widget( [
816 'content' => new HorizontalLayout( [
817 'items' => $buttonFields
818 ] )
819 ] )
820 ) .
821 Html::closeElement( 'form' ) .
822 Html::closeElement( 'div' )
823 );
824 }
825
833 private function showDiff(
834 RevisionRecord $previousRevRecord,
835 RevisionRecord $currentRevRecord
836 ) {
837 $currentTitle = Title::newFromPageIdentity( $currentRevRecord->getPage() );
838
839 $diffContext = new DerivativeContext( $this->getContext() );
840 $diffContext->setTitle( $currentTitle );
841 $diffContext->setWikiPage( $this->wikiPageFactory->newFromTitle( $currentTitle ) );
842
843 $contentModel = $currentRevRecord->getSlot(
844 SlotRecord::MAIN,
845 RevisionRecord::RAW
846 )->getModel();
847
848 $diffEngine = $this->contentHandlerFactory->getContentHandler( $contentModel )
849 ->createDifferenceEngine( $diffContext );
850
851 $diffEngine->setRevisions( $previousRevRecord, $currentRevRecord );
852 $diffEngine->showDiffStyle();
853 $formattedDiff = $diffEngine->getDiff(
854 $this->diffHeader( $previousRevRecord, 'o' ),
855 $this->diffHeader( $currentRevRecord, 'n' )
856 );
857
858 if ( $formattedDiff === false ) {
859 if ( $diffEngine->hasSuppressedRevision() ) {
860 $error = 'rev-suppressed-no-diff';
861 } elseif ( $diffEngine->hasDeletedRevision() ) {
862 $error = 'rev-deleted-no-diff';
863 } else {
864 // Something else went wrong when loading the diff - at least explain that something was wrong ...
865 $error = 'undelete-error-loading-diff';
866 }
867 $this->getOutput()->addHTML( $this->msg( $error )->parse() );
868 } else {
869 $this->getOutput()->addHTML( "<div>$formattedDiff</div>\n" );
870 }
871 }
872
878 private function diffHeader( RevisionRecord $revRecord, $prefix ) {
879 if ( $revRecord instanceof RevisionArchiveRecord ) {
880 // Revision in the archive table, only viewable via this special page
881 $targetPage = $this->getPageTitle();
882 $targetQuery = [
883 'target' => $this->mTargetObj->getPrefixedText(),
884 'timestamp' => wfTimestamp( TS::MW, $revRecord->getTimestamp() )
885 ];
886 } else {
887 // Revision in the revision table, viewable by oldid
888 $targetPage = $revRecord->getPageAsLinkTarget();
889 $targetQuery = [ 'oldid' => $revRecord->getId() ];
890 }
891
892 // Add show/hide deletion links if available
893 $user = $this->getUser();
894 $lang = $this->getLanguage();
895 $rdel = Linker::getRevDeleteLink( $user, $revRecord, $this->mTargetObj );
896
897 if ( $rdel ) {
898 $rdel = " $rdel";
899 }
900
901 $minor = $revRecord->isMinor() ? ChangesList::flag( 'minor', $this->getContext() ) : '';
902
903 $dbr = $this->dbProvider->getReplicaDatabase();
904 $tagIds = $dbr->newSelectQueryBuilder()
905 ->select( 'ct_tag_id' )
906 ->from( 'change_tag' )
907 ->where( [ 'ct_rev_id' => $revRecord->getId() ] )
908 ->caller( __METHOD__ )->fetchFieldValues();
909 $tags = [];
910 foreach ( $tagIds as $tagId ) {
911 try {
912 $tags[] = $this->changeTagDefStore->getName( (int)$tagId );
913 } catch ( NameTableAccessException ) {
914 continue;
915 }
916 }
917 $tags = implode( ',', $tags );
918 $tagSummary = $this->changeTagsFormatter->formatTagsAsSummaryList(
919 $tags,
920 $this->getContext(),
921 $this->getAuthority()
922 );
923 $asof = $this->getLinkRenderer()->makeLink(
924 $targetPage,
925 $this->msg(
926 'revisionasof',
927 $lang->userTimeAndDate( $revRecord->getTimestamp(), $user ),
928 $lang->userDate( $revRecord->getTimestamp(), $user ),
929 $lang->userTime( $revRecord->getTimestamp(), $user )
930 )->text(),
931 [],
932 $targetQuery
933 );
934 if ( $revRecord->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
935 $asof = Html::rawElement(
936 'span',
937 [ 'class' => Linker::getRevisionDeletedClass( $revRecord ) ],
938 $asof
939 );
940 }
941
942 // FIXME This is reimplementing DifferenceEngine#getRevisionHeader
943 // and partially #showDiffPage, but worse
944 return '<div id="mw-diff-' . $prefix . 'title1"><strong>' .
945 $asof .
946 '</strong></div>' .
947 '<div id="mw-diff-' . $prefix . 'title2">' .
948 Linker::revUserTools( $revRecord ) . '<br />' .
949 '</div>' .
950 '<div id="mw-diff-' . $prefix . 'title3">' .
951 $minor . $this->commentFormatter->formatRevision( $revRecord, $user ) . $rdel . '<br />' .
952 '</div>' .
953 '<div id="mw-diff-' . $prefix . 'title5">' .
954 $tagSummary[0] . '<br />' .
955 '</div>';
956 }
957
962 private function showFileConfirmationForm( $key ) {
963 $out = $this->getOutput();
964 $lang = $this->getLanguage();
965 $user = $this->getUser();
966 $file = new ArchivedFile( $this->mTargetObj, 0, $this->mFilename );
967 $out->addWikiMsg( 'undelete-show-file-confirm',
968 $this->mTargetObj->getText(),
969 $lang->userDate( $file->getTimestamp(), $user ),
970 $lang->userTime( $file->getTimestamp(), $user ) );
971 $out->addHTML(
972 Html::rawElement( 'form', [
973 'method' => 'POST',
974 'action' => $this->getPageTitle()->getLocalURL( [
975 'target' => $this->mTarget,
976 'file' => $key,
977 'token' => $user->getEditToken( $key ),
978 ] ),
979 ],
980 Html::submitButton( $this->msg( 'undelete-show-file-submit' )->text() )
981 )
982 );
983 }
984
989 private function showFile( $key ) {
990 $this->getOutput()->disable();
991
992 # We mustn't allow the output to be CDN cached, otherwise
993 # if an admin previews a deleted image, and it's cached, then
994 # a user without appropriate permissions can toddle off and
995 # nab the image, and CDN will serve it
996 $response = $this->getRequest()->response();
997 $response->header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', 0 ) . ' GMT' );
998 $response->header( 'Cache-Control: no-cache, no-store, max-age=0, must-revalidate' );
999
1000 $path = $this->localRepo->getZonePath( 'deleted' ) . '/' . $this->localRepo->getDeletedHashPath( $key ) . $key;
1001 $this->localRepo->streamFileWithStatus( $path );
1002 }
1003
1004 private function addRevisionsToBatch( LinkBatch $batch, IResultWrapper $revisions ) {
1005 foreach ( $revisions as $row ) {
1006 $batch->addUser( new UserIdentityValue( (int)$row->ar_user, $row->ar_user_text ) );
1007 }
1008 }
1009
1010 private function addFilesToBatch( LinkBatch $batch, IResultWrapper $files ) {
1011 foreach ( $files as $row ) {
1012 $batch->add( NS_USER, $row->fa_user_text );
1013 $batch->add( NS_USER_TALK, $row->fa_user_text );
1014 }
1015 }
1016
1020 protected function showMoreHistory() {
1021 $out = $this->getOutput();
1022 $out->setArticleBodyOnly( true );
1023 $dbr = $this->dbProvider->getReplicaDatabase();
1024 if ( $this->mHistoryOffset ) {
1025 $extraConds = [ $dbr->expr( 'ar_timestamp', '<', $dbr->timestamp( $this->mHistoryOffset ) ) ];
1026 } else {
1027 $extraConds = [];
1028 }
1029 $revisions = $this->archivedRevisionLookup->listArchivedRevisions(
1030 $this->mTargetObj,
1031 $this->getAuthority(),
1032 $extraConds,
1033 self::REVISION_HISTORY_LIMIT + 1
1034 );
1035 $batch = $this->linkBatchFactory->newLinkBatch()->setCaller( __METHOD__ );
1036 $this->addRevisionsToBatch( $batch, $revisions );
1037 $batch->execute();
1038 $out->addHTML( $this->formatRevisionHistory( $revisions ) );
1039
1040 if ( $revisions->numRows() > self::REVISION_HISTORY_LIMIT ) {
1041 // Indicate to JS that the "show more" button should remain active
1042 $out->setStatusCode( 206 );
1043 }
1044 }
1045
1052 protected function formatRevisionHistory( IResultWrapper $revisions ) {
1053 $history = Html::openElement( 'ul', [ 'class' => 'mw-undelete-revlist' ] );
1054
1055 // Exclude the last data row if there is more data than history limit amount
1056 $numRevisions = $revisions->numRows();
1057 $displayCount = min( $numRevisions, self::REVISION_HISTORY_LIMIT );
1058 $firstRev = $this->revisionStore->getFirstRevision( $this->mTargetObj );
1059 $earliestLiveTime = $firstRev ? $firstRev->getTimestamp() : null;
1060
1061 $sizes = [];
1062 foreach ( $revisions as $rev ) {
1063 $sizes[$rev->ar_rev_id] = $rev->ar_len;
1064 }
1065
1066 $revisions->rewind();
1067 for ( $i = 0; $i < $displayCount; $i++ ) {
1068 $row = $revisions->fetchObject();
1069 // The $remaining parameter controls diff links and so must
1070 // include the undisplayed row beyond the display limit.
1071 $history .= $this->formatRevisionRow( $row, $earliestLiveTime, $numRevisions - $i, $sizes );
1072 }
1073 $history .= Html::closeElement( 'ul' );
1074 return $history;
1075 }
1076
1077 protected function showHistory() {
1078 $this->checkReadOnly();
1079
1080 $out = $this->getOutput();
1081 if ( $this->mAllowed ) {
1082 $out->addModules( 'mediawiki.misc-authed-ooui' );
1083 $out->addModuleStyles( 'mediawiki.special' );
1084 }
1085 $out->addModuleStyles( 'mediawiki.interface.helpers.styles' );
1086 $out->addModuleStyles( 'mediawiki.special.changeslist' );
1087
1088 $out->wrapWikiMsg(
1089 "<div class='mw-undelete-pagetitle'>\n$1\n</div>\n",
1090 [ 'undeletepagetitle', wfEscapeWikiText( $this->mTargetObj->getPrefixedText() ) ]
1091 );
1092
1093 $archive = new PageArchive( $this->mTargetObj );
1094 // FIXME: This hook must be deprecated, passing PageArchive by ref is awful.
1095 $this->getHookRunner()->onUndeleteForm__showHistory( $archive, $this->mTargetObj );
1096
1097 $out->addHTML( Html::openElement( 'div', [ 'class' => 'mw-undelete-history' ] ) );
1098 if ( $this->mAllowed ) {
1099 $out->addWikiMsg( 'undeletehistory' );
1100 $out->addWikiMsg( 'undeleterevdel' );
1101 } else {
1102 $out->addWikiMsg( 'undeletehistorynoadmin' );
1103 }
1104 $out->addHTML( Html::closeElement( 'div' ) );
1105
1106 # List all stored revisions
1107 $revisions = $this->archivedRevisionLookup->listArchivedRevisions(
1108 $this->mTargetObj,
1109 $this->getAuthority(),
1110 limit: self::REVISION_HISTORY_LIMIT + 1
1111 );
1112 $files = $archive->listFiles();
1113 $numRevisions = $revisions->numRows();
1114 $showLoadMore = $numRevisions > self::REVISION_HISTORY_LIMIT;
1115 $haveRevisions = $numRevisions > 0;
1116 $haveFiles = $files && $files->numRows() > 0;
1117
1118 # Batch existence check on user and talk pages
1119 if ( $haveRevisions || $haveFiles ) {
1120 $batch = $this->linkBatchFactory->newLinkBatch()->setCaller( __METHOD__ );
1121 $this->addRevisionsToBatch( $batch, $revisions );
1122 if ( $haveFiles ) {
1123 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable -- $files is non-null
1124 $this->addFilesToBatch( $batch, $files );
1125 }
1126 $batch->execute();
1127 }
1128
1129 if ( $this->mAllowed ) {
1130 $out->enableOOUI();
1131
1132 $action = $this->getPageTitle()->getLocalURL( [ 'action' => 'submit' ] );
1133 # Start the form here
1134 $form = new FormLayout( [
1135 'method' => 'post',
1136 'action' => $action,
1137 'id' => 'undelete',
1138 ] );
1139 }
1140
1141 # Show relevant lines from the deletion log:
1142 $deleteLogPage = new LogPage( 'delete' );
1143 $out->addHTML( Html::element( 'h2', [], $deleteLogPage->getName()->text() ) . "\n" );
1144 LogEventsList::showLogExtract( $out, 'delete', $this->mTargetObj );
1145 # Show relevant lines from the suppression log:
1146 $suppressLogPage = new LogPage( 'suppress' );
1147 if ( $this->permissionManager->userHasRight( $this->getUser(), 'suppressionlog' ) ) {
1148 $out->addHTML( Html::element( 'h2', [], $suppressLogPage->getName()->text() ) . "\n" );
1149 LogEventsList::showLogExtract( $out, 'suppress', $this->mTargetObj );
1150 }
1151
1152 if ( $this->mAllowed && ( $haveRevisions || $haveFiles ) ) {
1153 $unsuppressAllowed = $this->permissionManager->userHasRight( $this->getUser(), 'suppressrevision' );
1154 $fields = [];
1155 $fields[] = new Layout( [
1156 'content' => new HtmlSnippet( $this->msg( 'undeleteextrahelp' )->parseAsBlock() )
1157 ] );
1158
1159 $dropdownComment = $this->msg( 'undelete-comment-dropdown' )
1160 ->page( $this->mTargetObj )->inContentLanguage()->text();
1161 // Add additional specific reasons for unsuppress
1162 if ( $unsuppressAllowed ) {
1163 $dropdownComment .= "\n" . $this->msg( 'undelete-comment-dropdown-unsuppress' )
1164 ->page( $this->mTargetObj )->inContentLanguage()->text();
1165 }
1166 $options = Html::listDropdownOptions(
1167 $dropdownComment,
1168 [ 'other' => $this->msg( 'undeletecommentotherlist' )->text() ]
1169 );
1170 $options = Html::listDropdownOptionsOoui( $options );
1171
1172 $fields[] = new FieldLayout(
1173 new DropdownInputWidget( [
1174 'name' => 'wpCommentList',
1175 'inputId' => 'wpCommentList',
1176 'infusable' => true,
1177 'value' => $this->getRequest()->getText( 'wpCommentList', 'other' ),
1178 'options' => $options,
1179 ] ),
1180 [
1181 'label' => $this->msg( 'undeletecomment' )->text(),
1182 'align' => 'top',
1183 ]
1184 );
1185
1186 $fields[] = new FieldLayout(
1187 new TextInputWidget( [
1188 'name' => 'wpComment',
1189 'inputId' => 'wpComment',
1190 'infusable' => true,
1191 'value' => $this->getRequest()->getText( 'wpComment' ),
1192 'autofocus' => true,
1193 // HTML maxlength uses "UTF-16 code units", which means that characters outside BMP
1194 // (e.g. emojis) count for two each. This limit is overridden in JS to instead count
1195 // Unicode codepoints.
1196 'maxLength' => CommentStore::COMMENT_CHARACTER_LIMIT,
1197 ] ),
1198 [
1199 'label' => $this->msg( 'undeleteothercomment' )->text(),
1200 'align' => 'top',
1201 ]
1202 );
1203
1204 if ( $this->getUser()->isRegistered() ) {
1205 $checkWatch = $this->watchlistManager->isWatched( $this->getUser(), $this->mTargetObj )
1206 || $this->getRequest()->getText( 'wpWatch' );
1207 $fields[] = new FieldLayout(
1208 new CheckboxInputWidget( [
1209 'name' => 'wpWatch',
1210 'inputId' => 'mw-undelete-watch',
1211 'value' => '1',
1212 'selected' => $checkWatch,
1213 ] ),
1214 [
1215 'label' => $this->msg( 'watchthis' )->text(),
1216 'align' => 'inline',
1217 ]
1218 );
1219 }
1220
1221 if ( $unsuppressAllowed ) {
1222 $fields[] = new FieldLayout(
1223 new CheckboxInputWidget( [
1224 'name' => 'wpUnsuppress',
1225 'inputId' => 'mw-undelete-unsuppress',
1226 'value' => '1',
1227 ] ),
1228 [
1229 'label' => $this->msg( 'revdelete-unsuppress' )->text(),
1230 'align' => 'inline',
1231 ]
1232 );
1233 }
1234
1235 $undelPage = $this->undeletePageFactory->newUndeletePage(
1236 $this->wikiPageFactory->newFromTitle( $this->mTargetObj ),
1237 $this->getContext()->getAuthority()
1238 );
1239 if ( $undelPage->canProbablyUndeleteAssociatedTalk()->isGood() ) {
1240 $fields[] = new FieldLayout(
1241 new CheckboxInputWidget( [
1242 'name' => 'undeletetalk',
1243 'inputId' => 'mw-undelete-undeletetalk',
1244 'selected' => false,
1245 ] ),
1246 [
1247 'label' => $this->msg( 'undelete-undeletetalk' )->text(),
1248 'align' => 'inline',
1249 ]
1250 );
1251 }
1252
1253 $fields[] = new FieldLayout(
1254 new Widget( [
1255 'content' => new HorizontalLayout( [
1256 'items' => [
1257 new ButtonInputWidget( [
1258 'name' => 'restore',
1259 'inputId' => 'mw-undelete-submit',
1260 'value' => '1',
1261 'label' => $this->msg( 'undeletebtn' )->text(),
1262 'flags' => [ 'primary', 'progressive' ],
1263 'type' => 'submit',
1264 ] ),
1265 new ButtonInputWidget( [
1266 'name' => 'invert',
1267 'inputId' => 'mw-undelete-invert',
1268 'value' => '1',
1269 'label' => $this->msg( 'undeleteinvert' )->text()
1270 ] ),
1271 ]
1272 ] )
1273 ] )
1274 );
1275
1276 $fieldset = new FieldsetLayout( [
1277 'label' => $this->msg( 'undelete-fieldset-title' )->text(),
1278 'id' => 'mw-undelete-table',
1279 'items' => $fields,
1280 ] );
1281
1282 $link = '';
1283 if ( $this->getAuthority()->isAllowed( 'editinterface' ) ) {
1284 if ( $unsuppressAllowed ) {
1285 $link .= $this->getLinkRenderer()->makeKnownLink(
1286 $this->msg( 'undelete-comment-dropdown-unsuppress' )->inContentLanguage()->getTitle(),
1287 $this->msg( 'undelete-edit-commentlist-unsuppress' )->text(),
1288 [],
1289 [ 'action' => 'edit' ]
1290 );
1291 $link .= $this->msg( 'pipe-separator' )->escaped();
1292 }
1293 $link .= $this->getLinkRenderer()->makeKnownLink(
1294 $this->msg( 'undelete-comment-dropdown' )->inContentLanguage()->getTitle(),
1295 $this->msg( 'undelete-edit-commentlist' )->text(),
1296 [],
1297 [ 'action' => 'edit' ]
1298 );
1299
1300 $link = Html::rawElement( 'p', [ 'class' => 'mw-undelete-editcomments' ], $link );
1301 }
1302
1303 // @phan-suppress-next-line PhanPossiblyUndeclaredVariable form is set, when used here
1304 $form->appendContent(
1305 new PanelLayout( [
1306 'expanded' => false,
1307 'padded' => true,
1308 'framed' => true,
1309 'content' => $fieldset,
1310 ] ),
1311 new HtmlSnippet(
1312 $link .
1313 Html::hidden( 'target', $this->mTarget ) .
1314 Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() )
1315 )
1316 );
1317 }
1318
1319 $history = '';
1320 $history .= Html::element( 'h2', [], $this->msg( 'history' )->text() ) . "\n";
1321
1322 if ( $haveRevisions ) {
1323 # Show the page's stored (deleted) history
1324
1325 if ( $this->mAllowed && $this->permissionManager->userHasRight( $this->getUser(), 'deleterevision' ) ) {
1326 $history .= Html::element(
1327 'button',
1328 [
1329 'name' => 'revdel',
1330 'type' => 'submit',
1331 'class' => [ 'deleterevision-log-submit', 'mw-log-deleterevision-button' ]
1332 ],
1333 $this->msg( 'showhideselectedversions' )->text()
1334 ) . "\n";
1335 }
1336
1337 $history .= $this->formatRevisionHistory( $revisions );
1338
1339 if ( $showLoadMore ) {
1340 $history .= Html::rawElement( 'div', [],
1341 Html::element( 'span', [ 'id' => 'mw-load-more-revisions' ],
1342 $this->msg( 'undelete-load-more-revisions' )->text()
1343 )
1344 ) . "\n";
1345 }
1346 } else {
1347 $out->addWikiMsg( 'nohistory' );
1348 }
1349
1350 if ( $haveFiles ) {
1351 $history .= Html::element( 'h2', [], $this->msg( 'filehist' )->text() ) . "\n";
1352 $history .= Html::openElement( 'ul', [ 'class' => 'mw-undelete-revlist' ] );
1353 foreach ( $files as $row ) {
1354 $history .= $this->formatFileRow( $row );
1355 }
1356 $files->free();
1357 $history .= Html::closeElement( 'ul' );
1358 }
1359
1360 if ( $this->mAllowed ) {
1361 # Slip in the hidden controls here
1362 $misc = Html::hidden( 'target', $this->mTarget );
1363 $misc .= Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() );
1364 $history .= $misc;
1365
1366 // @phan-suppress-next-line PhanPossiblyUndeclaredVariable form is set, when used here
1367 $form->appendContent( new HtmlSnippet( $history ) );
1368 // @phan-suppress-next-line PhanPossiblyUndeclaredVariable form is set, when used here
1369 $out->addHTML( (string)$form );
1370 } else {
1371 $out->addHTML( $history );
1372 }
1373 }
1374
1382 protected function formatRevisionRow( $row, $earliestLiveTime, $remaining, $sizes ) {
1383 $revRecord = $this->revisionStore->newRevisionFromArchiveRow(
1384 $row,
1385 IDBAccessObject::READ_NORMAL,
1386 $this->mTargetObj
1387 );
1388
1389 $revTextSize = '';
1390 $ts = wfTimestamp( TS::MW, $row->ar_timestamp );
1391 // Build checkboxen...
1392 if ( $this->mAllowed ) {
1393 if ( $this->mInvert ) {
1394 if ( in_array( $ts, $this->mTargetTimestamp ) ) {
1395 $checkBox = Html::check( "ts$ts" );
1396 } else {
1397 $checkBox = Html::check( "ts$ts", true );
1398 }
1399 } else {
1400 $checkBox = Html::check( "ts$ts" );
1401 }
1402 } else {
1403 $checkBox = '';
1404 }
1405
1406 // Build page & diff links...
1407 $user = $this->getUser();
1408 if ( $this->mCanView ) {
1409 $titleObj = $this->getPageTitle();
1410 # Last link
1411 if ( !$revRecord->userCan( RevisionRecord::DELETED_TEXT, $this->getAuthority() ) ) {
1412 $pageLink = htmlspecialchars( $this->getLanguage()->userTimeAndDate( $ts, $user ) );
1413 $last = $this->msg( 'diff' )->escaped();
1414 } elseif ( $remaining > 0 || ( $earliestLiveTime && $ts > $earliestLiveTime ) ) {
1415 $pageLink = $this->getPageLink( $revRecord, $titleObj, $ts );
1416 $last = $this->getLinkRenderer()->makeKnownLink(
1417 $titleObj,
1418 $this->msg( 'diff' )->text(),
1419 [],
1420 [
1421 'target' => $this->mTargetObj->getPrefixedText(),
1422 'timestamp' => $ts,
1423 'diff' => 'prev'
1424 ]
1425 );
1426 } else {
1427 $pageLink = $this->getPageLink( $revRecord, $titleObj, $ts );
1428 $last = $this->msg( 'diff' )->escaped();
1429 }
1430 } else {
1431 $pageLink = htmlspecialchars( $this->getLanguage()->userTimeAndDate( $ts, $user ) );
1432 $last = $this->msg( 'diff' )->escaped();
1433 }
1434
1435 // User links
1436 $userLink = Linker::revUserTools( $revRecord );
1437
1438 // Minor edit
1439 $minor = $revRecord->isMinor() ? ChangesList::flag( 'minor', $this->getContext() ) : '';
1440
1441 // Revision text size
1442 $size = $row->ar_len;
1443 if ( $size !== null ) {
1444 $revTextSize = Linker::formatRevisionSize( $size );
1445 $prevSize = $sizes[$row->ar_parent_id ?? 0] ?? 0;
1446 $sDiff = ChangesList::showCharacterDifference( $prevSize, $size, $this->getContext() );
1447 $revTextSize = "$revTextSize $sDiff";
1448 }
1449
1450 // Edit summary
1451 $comment = $this->commentFormatter->formatRevision( $revRecord, $user );
1452
1453 // Tags
1454 $attribs = [];
1455 [ $tagSummary, $classes ] = $this->changeTagsFormatter->formatTagsAsSummaryList(
1456 $row->ts_tags,
1457 $this->getContext(),
1458 $this->getAuthority()
1459 );
1460 $attribs['class'] = $classes;
1461
1462 $revisionRow = $this->msg( 'undelete-revision-row2' )
1463 ->rawParams(
1464 $checkBox,
1465 $last,
1466 $pageLink,
1467 $userLink,
1468 $minor,
1469 $revTextSize,
1470 $comment,
1471 $tagSummary
1472 )
1473 ->escaped();
1474
1475 return Html::rawElement( 'li', $attribs, $revisionRow ) . "\n";
1476 }
1477
1478 private function formatFileRow( \stdClass $row ): string {
1479 $file = ArchivedFile::newFromRow( $row );
1480 $ts = wfTimestamp( TS::MW, $row->fa_timestamp );
1481 $user = $this->getUser();
1482
1483 $checkBox = '';
1484 if ( $this->mCanView && $row->fa_storage_key ) {
1485 if ( $this->mAllowed ) {
1486 $checkBox = Html::check( 'fileid' . $row->fa_id );
1487 }
1488 $key = urlencode( $row->fa_storage_key );
1489 $pageLink = $this->getFileLink( $file, $this->getPageTitle(), $ts, $key );
1490 } else {
1491 $pageLink = htmlspecialchars( $this->getLanguage()->userTimeAndDate( $ts, $user ) );
1492 }
1493 $userLink = $this->getFileUser( $file );
1494 $data = $this->msg( 'widthheight' )->numParams( $row->fa_width, $row->fa_height )->text();
1495 $bytes = $this->msg( 'parentheses' )
1496 ->plaintextParams( $this->msg( 'nbytes' )->numParams( $row->fa_size )->text() )
1497 ->plain();
1498 $data = htmlspecialchars( $data . ' ' . $bytes );
1499 $comment = $this->getFileComment( $file );
1500
1501 // Add show/hide deletion links if available
1502 $canHide = $this->isAllowed( 'deleterevision' );
1503 if ( $canHide || ( $file->getVisibility() && $this->isAllowed( 'deletedhistory' ) ) ) {
1504 if ( !$file->userCan( File::DELETED_RESTRICTED, $user ) ) {
1505 // Revision was hidden from sysops
1506 $revdlink = Linker::revDeleteLinkDisabled( $canHide );
1507 } else {
1508 $query = [
1509 'type' => 'filearchive',
1510 'target' => $this->mTargetObj->getPrefixedDBkey(),
1511 'ids' => $row->fa_id
1512 ];
1513 $revdlink = Linker::revDeleteLink( $query,
1514 $file->isDeleted( File::DELETED_RESTRICTED ), $canHide );
1515 }
1516 } else {
1517 $revdlink = '';
1518 }
1519
1520 return "<li>$checkBox $revdlink $pageLink . . $userLink $data $comment</li>\n";
1521 }
1522
1531 private function getPageLink( RevisionRecord $revRecord, LinkTarget $target, $ts ) {
1532 $user = $this->getUser();
1533 $time = $this->getLanguage()->userTimeAndDate( $ts, $user );
1534
1535 if ( !$revRecord->userCan( RevisionRecord::DELETED_TEXT, $this->getAuthority() ) ) {
1536 // TODO The condition cannot be true when the function is called
1537 return Html::element(
1538 'span',
1539 [ 'class' => 'history-deleted' ],
1540 $time
1541 );
1542 }
1543
1544 $link = $this->getLinkRenderer()->makeKnownLink(
1545 $target,
1546 $time,
1547 [],
1548 [
1549 'target' => $this->mTargetObj->getPrefixedText(),
1550 'timestamp' => $ts
1551 ]
1552 );
1553
1554 if ( $revRecord->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
1555 $class = Linker::getRevisionDeletedClass( $revRecord );
1556 $link = '<span class="' . $class . '">' . $link . '</span>';
1557 }
1558
1559 return $link;
1560 }
1561
1572 private function getFileLink( $file, LinkTarget $target, $ts, $key ) {
1573 $user = $this->getUser();
1574 $time = $this->getLanguage()->userTimeAndDate( $ts, $user );
1575
1576 if ( !$file->userCan( File::DELETED_FILE, $user ) ) {
1577 return Html::element(
1578 'span',
1579 [ 'class' => 'history-deleted' ],
1580 $time
1581 );
1582 }
1583
1584 if ( $file->exists() ) {
1585 $link = $this->getLinkRenderer()->makeKnownLink(
1586 $target,
1587 $time,
1588 [],
1589 [
1590 'target' => $this->mTargetObj->getPrefixedText(),
1591 'file' => $key,
1592 'token' => $user->getEditToken( $key )
1593 ]
1594 );
1595 } else {
1596 $link = htmlspecialchars( $time );
1597 }
1598
1599 if ( $file->isDeleted( File::DELETED_FILE ) ) {
1600 $link = '<span class="history-deleted">' . $link . '</span>';
1601 }
1602
1603 return $link;
1604 }
1605
1612 private function getFileUser( $file ) {
1613 $uploader = $file->getUploader( File::FOR_THIS_USER, $this->getAuthority() );
1614 if ( !$uploader ) {
1615 return Html::element( 'span',
1616 [ 'class' => 'history-deleted' ],
1617 $this->msg( 'rev-deleted-user' )->text()
1618 );
1619 }
1620
1621 $link = Linker::userLink( $uploader->getId(), $uploader->getName() ) .
1622 Linker::userToolLinks( $uploader->getId(), $uploader->getName() );
1623
1624 if ( $file->isDeleted( File::DELETED_USER ) ) {
1625 $link = Html::rawElement(
1626 'span',
1627 [ 'class' => 'history-deleted' ],
1628 $link
1629 );
1630 }
1631
1632 return $link;
1633 }
1634
1641 private function getFileComment( $file ) {
1642 if ( !$file->userCan( File::DELETED_COMMENT, $this->getAuthority() ) ) {
1643 return Html::rawElement(
1644 'span',
1645 [ 'class' => 'history-deleted' ],
1646 Html::element( 'span',
1647 [ 'class' => 'comment' ],
1648 $this->msg( 'rev-deleted-comment' )->text()
1649 )
1650 );
1651 }
1652
1653 $comment = $file->getDescription( File::FOR_THIS_USER, $this->getAuthority() );
1654 $link = $this->commentFormatter->formatBlock( $comment );
1655
1656 if ( $file->isDeleted( File::DELETED_COMMENT ) ) {
1657 $link = Html::rawElement(
1658 'span',
1659 [ 'class' => 'history-deleted' ],
1660 $link
1661 );
1662 }
1663
1664 return $link;
1665 }
1666
1667 private function undelete() {
1668 $this->reauthInProgress = false;
1669
1670 if ( $this->getConfig()->get( MainConfigNames::UploadMaintenance )
1671 && $this->mTargetObj->getNamespace() === NS_FILE
1672 ) {
1673 throw new ErrorPageError( 'undelete-error', 'filedelete-maintenance' );
1674 }
1675
1676 $this->checkReadOnly();
1677
1678 $out = $this->getOutput();
1679 $undeletePage = $this->undeletePageFactory->newUndeletePage(
1680 $this->wikiPageFactory->newFromTitle( $this->mTargetObj ),
1681 $this->getAuthority()
1682 );
1683 if ( $this->mUndeleteTalk && $undeletePage->canProbablyUndeleteAssociatedTalk()->isGood() ) {
1684 $undeletePage->setUndeleteAssociatedTalk( true );
1685 }
1686 $status = $undeletePage
1687 ->setUndeleteOnlyTimestamps( $this->mTargetTimestamp )
1688 ->setUndeleteOnlyFileVersions( $this->mFileVersions )
1689 ->setUnsuppress( $this->mUnsuppress )
1690 // TODO This is currently duplicating some permission checks, but we do need it (T305680)
1691 ->undeleteIfAllowed( $this->mComment );
1692
1693 if ( !$status->isGood() ) {
1694 if ( $status instanceof PermissionStatus && $status->getReauthOperation() !== null ) {
1695 $this->setStashKey( $this->getStashKeyForTitle( $this->mTargetObj ) );
1696 $queryParams = $this->stashDataOnPost();
1697 $this->doReauthRedirect( $status, $queryParams );
1698 $this->reauthInProgress = true;
1699 return;
1700 }
1701 $out->setPageTitleMsg( $this->msg( 'undelete-error' ) );
1702 foreach ( $status->getMessages() as $msg ) {
1703 $out->addHTML( Html::errorBox(
1704 $this->msg( $msg )->parse()
1705 ) );
1706 }
1707 return;
1708 }
1709
1710 $restoredRevs = $status->getValue()[UndeletePage::REVISIONS_RESTORED];
1711 $restoredFiles = $status->getValue()[UndeletePage::FILES_RESTORED];
1712
1713 if ( $restoredRevs === 0 && $restoredFiles === 0 ) {
1714 // TODO Should use a different message here
1715 $out->setPageTitleMsg( $this->msg( 'undelete-error' ) );
1716 } else {
1717 if ( $status->getValue()[UndeletePage::FILES_RESTORED] !== 0 ) {
1718 $this->getHookRunner()->onFileUndeleteComplete(
1719 $this->mTargetObj, $this->mFileVersions, $this->getUser(), $this->mComment );
1720 }
1721
1722 $link = $this->getLinkRenderer()->makeKnownLink( $this->mTargetObj );
1723 $out->addWikiMsg( 'undeletedpage', Message::rawParam( $link ) );
1724
1725 $this->watchlistManager->setWatch(
1726 $this->getRequest()->getCheck( 'wpWatch' ),
1727 $this->getAuthority(),
1728 $this->mTargetObj
1729 );
1730 }
1731 }
1732
1741 public function prefixSearchSubpages( $search, $limit, $offset ) {
1742 return $this->prefixSearchString( $search, $limit, $offset, $this->searchEngineFactory );
1743 }
1744
1746 protected function getGroupName() {
1747 return 'pagetools';
1748 }
1749}
1750
1751// @codeCoverageIgnoreStart
1756class_alias( SpecialUndelete::class, 'SpecialUndelete' );
1757// @codeCoverageIgnoreEnd
const NS_USER
Definition Defines.php:53
const NS_FILE
Definition Defines.php:57
const NS_USER_TALK
Definition Defines.php:54
wfEscapeWikiText( $input)
Escapes the given text so that it may be output using addWikiText() without any linking,...
wfTimestamp( $outputtype=TS::UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfScript( $script='index')
Get the URL path to a MediaWiki entry point.
Formats change tags for display in HTML and use filter dropdown menus.
This is the main service interface for converting single-line comments from various DB comment fields...
Handle database storage of comments such as edit summaries and log reasons.
Content object implementation for representing flat text.
An IContextSource implementation which will inherit context from another source but allow individual ...
An error page which can definitely be safely rendered using the OutputPage.
Show an error when a user tries to do something they do not have the necessary permissions for.
Show an error when the user tries to do something whilst blocked.
Deleted file in the 'filearchive' table.
Implements some public methods and some protected utility functions which are required by multiple ch...
Definition File.php:80
Local repository that stores files in the local filesystem and registers them in the wiki's own datab...
Definition LocalRepo.php:44
Prioritized list of file repositories.
Definition RepoGroup.php:30
getLocalRepo()
Get the local repository, i.e.
This class is a collection of static functions that serve two purposes:
Definition Html.php:44
Some internal bits split of from Skin.php.
Definition Linker.php:48
Class to simplify the use of log pages.
Definition LogPage.php:34
A class containing constants representing the names of configuration variables.
The Message class deals with fetching and processing of interface message into a variety of formats.
Definition Message.php:144
Factory for LinkBatch objects to batch query page metadata.
Batch query for page metadata and feed to LinkCache.
Definition LinkBatch.php:36
Used to show archived pages and eventually restore them.
Service for creating WikiPage objects.
Set options of the Parser.
A service class for checking permissions To obtain an instance, use MediaWikiServices::getInstance()-...
A StatusValue for permission errors.
Base class for lists of recent changes shown on special pages.
Exception representing a failure to look up a revision.
A RevisionRecord representing a revision of a deleted page persisted in the archive table.
Page revision base class.
The RevisionRenderer service provides access to rendered output for revisions.
Service for looking up page revisions.
Value object representing a content slot associated with a page revision.
Factory class for SearchEngine.
Parent class for all special pages.
Special page allowing users with the appropriate permissions to view and restore deleted content.
formatRevisionRow( $row, $earliestLiveTime, $remaining, $sizes)
doesWrites()
Indicates whether POST requests to this special page require write access to the wiki....
checkPermissions()
Checks if userCanExecute, and if not throws a PermissionsError.to override 1.19 void
handleRetrievedData(array $data)
Restore submitted undelete form state from the stash on the reauth return trip.
isAllowed( $permission, ?User $user=null)
Checks whether a user is allowed the permission for the specific title if one is set.
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
prefixSearchSubpages( $search, $limit, $offset)
Return an array of subpages beginning with $search that this special page will accept.
formatRevisionHistory(IResultWrapper $revisions)
Generate the.
__construct(private readonly PermissionManager $permissionManager, private readonly RevisionStore $revisionStore, private readonly RevisionRenderer $revisionRenderer, private readonly IContentHandlerFactory $contentHandlerFactory, private readonly NameTableStore $changeTagDefStore, private readonly LinkBatchFactory $linkBatchFactory, RepoGroup $repoGroup, private readonly IConnectionProvider $dbProvider, private readonly UserOptionsLookup $userOptionsLookup, private readonly WikiPageFactory $wikiPageFactory, private readonly SearchEngineFactory $searchEngineFactory, private readonly UndeletePageFactory $undeletePageFactory, private readonly ArchivedRevisionLookup $archivedRevisionLookup, private readonly CommentFormatter $commentFormatter, private readonly WatchlistManager $watchlistManager, private readonly ChangeTagsFormatter $changeTagsFormatter,)
execute( $par)
Default execute method Checks user permissions.This must be overridden by subclasses; it will be made...
userCanExecute(User $user)
Checks if the given user (identified by an object) can execute this special page (as defined by getRe...
getRestriction()
Get the permission that a user must have to execute this page.to override
showMoreHistory()
Handle XHR "show more history" requests (T249977)
Exception representing a failure to look up a row from a name table.
Represents a title within MediaWiki.
Definition Title.php:69
Provides access to user options.
Value object representing a user's identity.
User class for the MediaWiki software.
Definition User.php:129
return[ 'config-schema-inverse'=>['default'=>['ConfigRegistry'=>['main'=> 'MediaWiki\\Config\\GlobalVarConfig::newInstance',], 'Sitename'=> 'MediaWiki', 'Server'=> false, 'CanonicalServer'=> false, 'ServerName'=> false, 'AssumeProxiesUseDefaultProtocolPorts'=> true, 'HttpsPort'=> 443, 'ForceHTTPS'=> false, 'ScriptPath'=> '/wiki', 'UsePathInfo'=> null, 'Script'=> false, 'LoadScript'=> false, 'RestPath'=> false, 'StylePath'=> false, 'LocalStylePath'=> false, 'ExtensionAssetsPath'=> false, 'ExtensionDirectory'=> null, 'StyleDirectory'=> null, 'ArticlePath'=> false, 'UploadPath'=> false, 'ImgAuthPath'=> false, 'ThumbPath'=> false, 'UploadDirectory'=> false, 'FileCacheDirectory'=> false, 'Logo'=> false, 'Logos'=> false, 'Favicon'=> '/favicon.ico', 'AppleTouchIcon'=> false, 'ReferrerPolicy'=> false, 'TmpDirectory'=> false, 'UploadBaseUrl'=> '', 'UploadStashScalerBaseUrl'=> false, 'ActionPaths'=>[], 'MainPageIsDomainRoot'=> false, 'EnableUploads'=> false, 'UploadStashMaxAge'=> 21600, 'EnableAsyncUploads'=> false, 'EnableAsyncUploadsByURL'=> false, 'UploadMaintenance'=> false, 'IllegalFileChars'=> ':\\/\\\\', 'DeletedDirectory'=> false, 'ImgAuthDetails'=> false, 'ImgAuthUrlPathMap'=>[], 'LocalFileRepo'=>['class'=> 'MediaWiki\\FileRepo\\LocalRepo', 'name'=> 'local', 'directory'=> null, 'scriptDirUrl'=> null, 'favicon'=> null, 'url'=> null, 'hashLevels'=> null, 'thumbScriptUrl'=> null, 'transformVia404'=> null, 'deletedDir'=> null, 'deletedHashLevels'=> null, 'updateCompatibleMetadata'=> null, 'reserializeMetadata'=> null,], 'ForeignFileRepos'=>[], 'UseInstantCommons'=> false, 'UseSharedUploads'=> false, 'SharedUploadDirectory'=> null, 'SharedUploadPath'=> null, 'HashedSharedUploadDirectory'=> true, 'RepositoryBaseUrl'=> 'https:'FetchCommonsDescriptions'=> false, 'SharedUploadDBname'=> false, 'SharedUploadDBprefix'=> '', 'CacheSharedUploads'=> true, 'ForeignUploadTargets'=>['local',], 'UploadDialog'=>['fields'=>['description'=> true, 'date'=> false, 'categories'=> false,], 'licensemessages'=>['local'=> 'generic-local', 'foreign'=> 'generic-foreign',], 'comment'=>['local'=> '', 'foreign'=> '',], 'format'=>['filepage'=> ' $DESCRIPTION', 'description'=> ' $TEXT', 'ownwork'=> '', 'license'=> '', 'uncategorized'=> '',],], 'FileBackends'=>[], 'LockManagers'=>[], 'DefaultLockManager'=> null, 'ShowEXIF'=> null, 'UpdateCompatibleMetadata'=> false, 'AllowCopyUploads'=> false, 'CopyUploadsDomains'=>[], 'CopyUploadsFromSpecialUpload'=> false, 'CopyUploadProxy'=> false, 'CopyUploadTimeout'=> false, 'CopyUploadAllowOnWikiDomainConfig'=> false, 'MaxUploadSize'=> 104857600, 'MinUploadChunkSize'=> 1024, 'UploadNavigationUrl'=> false, 'UploadMissingFileUrl'=> false, 'ThumbnailScriptPath'=> false, 'SharedThumbnailScriptPath'=> false, 'HashedUploadDirectory'=> true, 'CSPUploadEntryPoint'=> true, 'FileExtensions'=>['png', 'gif', 'jpg', 'jpeg', 'webp',], 'ProhibitedFileExtensions'=>['html', 'htm', 'js', 'jsb', 'mhtml', 'mht', 'xhtml', 'xht', 'php', 'phtml', 'php3', 'php4', 'php5', 'phps', 'phar', 'shtml', 'jhtml', 'pl', 'py', 'cgi', 'exe', 'scr', 'dll', 'msi', 'vbs', 'bat', 'com', 'pif', 'cmd', 'vxd', 'cpl', 'xml',], 'MimeTypeExclusions'=>['text/html', 'application/javascript', 'text/javascript', 'text/x-javascript', 'application/x-shellscript', 'application/x-php', 'text/x-php', 'text/x-python', 'text/x-perl', 'text/x-bash', 'text/x-sh', 'text/x-csh', 'text/scriptlet', 'application/x-msdownload', 'application/x-msmetafile', 'application/java', 'application/xml', 'text/xml',], 'CheckFileExtensions'=> true, 'StrictFileExtensions'=> true, 'DisableUploadScriptChecks'=> false, 'UploadSizeWarning'=> false, 'TrustedMediaFormats'=>['BITMAP', 'AUDIO', 'VIDEO', 'image/svg+xml', 'application/pdf',], 'MediaHandlers'=>[], 'NativeImageLazyLoading'=> false, 'ParserTestMediaHandlers'=>['image/jpeg'=> 'MockBitmapHandler', 'image/png'=> 'MockBitmapHandler', 'image/gif'=> 'MockBitmapHandler', 'image/tiff'=> 'MockBitmapHandler', 'image/webp'=> 'MockBitmapHandler', 'image/x-ms-bmp'=> 'MockBitmapHandler', 'image/x-bmp'=> 'MockBitmapHandler', 'image/x-xcf'=> 'MockBitmapHandler', 'image/svg+xml'=> 'MockSvgHandler', 'image/vnd.djvu'=> 'MockDjVuHandler',], 'UseImageResize'=> true, 'UseImageMagick'=> false, 'ImageMagickConvertCommand'=> '/usr/bin/convert', 'MaxInterlacingAreas'=>[], 'SharpenParameter'=> '0x0.4', 'SharpenReductionThreshold'=> 0.85, 'ImageMagickTempDir'=> false, 'CustomConvertCommand'=> false, 'JpegTran'=> '/usr/bin/jpegtran', 'JpegPixelFormat'=> 'yuv420', 'JpegQuality'=> 80, 'Exiv2Command'=> '/usr/bin/exiv2', 'Exiftool'=> '/usr/bin/exiftool', 'SVGConverters'=>['ImageMagick'=> ' $path/convert -background "#ffffff00" -thumbnail $widthx$height\\! $input PNG:$output', 'inkscape'=> ' $path/inkscape -w $width -o $output $input', 'batik'=> 'java -Djava.awt.headless=true -jar $path/batik-rasterizer.jar -w $width -d $output $input', 'rsvg'=> ' $path/rsvg-convert -w $width -h $height -o $output $input', 'ImagickExt'=>['SvgHandler::rasterizeImagickExt',],], 'SVGConverter'=> 'ImageMagick', 'SVGConverterPath'=> '', 'SVGMaxSize'=> 5120, 'SVGMetadataCutoff'=> 5242880, 'SVGNativeRendering'=> true, 'SVGNativeRenderingSizeLimit'=> 51200, 'MediaInTargetLanguage'=> true, 'MaxImageArea'=> 12500000, 'MaxAnimatedGifArea'=> 12500000, 'TiffThumbnailType'=>[], 'ThumbnailEpoch'=> '20030516000000', 'AttemptFailureEpoch'=> 1, 'IgnoreImageErrors'=> false, 'GenerateThumbnailOnParse'=> true, 'ShowArchiveThumbnails'=> true, 'EnableAutoRotation'=> null, 'Antivirus'=> null, 'AntivirusSetup'=>['clamav'=>['command'=> 'clamscan --no-summary ', 'codemap'=>[0=> 0, 1=> 1, 52=> -1, ' *'=> false,], 'messagepattern'=> '/.*?:(.*)/sim',],], 'AntivirusRequired'=> true, 'VerifyMimeType'=> true, 'MimeTypeFile'=> 'internal', 'MimeInfoFile'=> 'internal', 'MimeDetectorCommand'=> null, 'TrivialMimeDetection'=> false, 'XMLMimeTypes'=>['http:'svg'=> 'image/svg+xml', 'http:'http:'html'=> 'text/html',], 'ImageLimits'=>[[320, 240,], [640, 480,], [800, 600,], [1024, 768,], [1280, 1024,], [2560, 2048,],], 'ThumbLimits'=>[120, 150, 180, 200, 220, 250, 300, 400,], 'ThumbnailNamespaces'=>[6,], 'ThumbnailSteps'=> null, 'ThumbnailBuckets'=> null, 'ThumbnailMinimumBucketDistance'=> 50, 'UploadThumbnailRenderMap'=>[], 'UploadThumbnailRenderMethod'=> 'jobqueue', 'UploadThumbnailRenderHttpCustomHost'=> false, 'UploadThumbnailRenderHttpCustomDomain'=> false, 'UseTinyRGBForJPGThumbnails'=> false, 'GalleryOptions'=>[], 'ThumbUpright'=> 0.75, 'DirectoryMode'=> 511, 'ResponsiveImages'=> true, 'ImagePreconnect'=> false, 'TrackMediaRequestProvenance'=> false, 'DjvuUseBoxedCommand'=> false, 'DjvuDump'=> null, 'DjvuRenderer'=> null, 'DjvuTxt'=> null, 'DjvuPostProcessor'=> 'pnmtojpeg', 'DjvuOutputExtension'=> 'jpg', 'EmergencyContact'=> false, 'RestTermsOfServiceUrl'=> null, 'PasswordSender'=> false, 'NoReplyAddress'=> false, 'EnableEmail'=> true, 'EnableUserEmail'=> true, 'UserEmailUseReplyTo'=> true, 'PasswordReminderResendTime'=> 24, 'NewPasswordExpiry'=> 604800, 'UserEmailConfirmationTokenExpiry'=> 604800, 'PasswordExpirationDays'=> false, 'PasswordExpireGrace'=> 604800, 'SMTP'=> false, 'AdditionalMailParams'=> null, 'AllowHTMLEmail'=> false, 'EnotifFromEditor'=> false, 'EmailAuthentication'=> true, 'EmailConfirmationBanner'=> false, 'EnotifWatchlist'=> false, 'EnotifUserTalk'=> false, 'EnotifRevealEditorAddress'=> false, 'EnotifMinorEdits'=> true, 'EnotifUseRealName'=> false, 'UsersNotifiedOnAllChanges'=>[], 'DBname'=> 'my_wiki', 'DBmwschema'=> null, 'DBprefix'=> '', 'DBserver'=> 'localhost', 'DBport'=> 5432, 'DBuser'=> 'wikiuser', 'DBpassword'=> '', 'DBtype'=> 'mysql', 'DBssl'=> false, 'DBcompress'=> false, 'DBStrictWarnings'=> false, 'DBadminuser'=> null, 'DBadminpassword'=> null, 'SearchType'=> null, 'SearchTypeAlternatives'=> null, 'DBTableOptions'=> 'ENGINE=InnoDB, DEFAULT CHARSET=binary', 'SQLMode'=> '', 'SQLiteDataDir'=> '', 'SharedDB'=> null, 'SharedPrefix'=> false, 'SharedTables'=>['user', 'user_properties', 'user_autocreate_serial',], 'SharedSchema'=> false, 'DBservers'=> false, 'LBFactoryConf'=>['class'=> 'Wikimedia\\Rdbms\\LBFactorySimple',], 'DataCenterUpdateStickTTL'=> 10, 'DBerrorLog'=> false, 'DBerrorLogTZ'=> false, 'LocalDatabases'=>[], 'DatabaseReplicaLagWarning'=> 10, 'DatabaseReplicaLagCritical'=> 30, 'MaxExecutionTimeForExpensiveQueries'=> 0, 'VirtualDomainsMapping'=>[], 'FileSchemaMigrationStage'=> 3, 'ExternalLinksDomainGaps'=>[], 'ContentHandlers'=>['wikitext'=>['class'=> 'MediaWiki\\Content\\WikitextContentHandler', 'services'=>['TitleFactory', 'ParserFactory', 'GlobalIdGenerator', 'LanguageNameUtils', 'LinkRenderer', 'MagicWordFactory', 'ParsoidParserFactory',],], 'javascript'=>['class'=> 'MediaWiki\\Content\\JavaScriptContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'UserOptionsLookup', 'CodeHighlighter',],], 'json'=>['class'=> 'MediaWiki\\Content\\JsonContentHandler', 'services'=>['ParsoidParserFactory', 'TitleFactory',],], 'css'=>['class'=> 'MediaWiki\\Content\\CssContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'UserOptionsLookup', 'CodeHighlighter',],], 'vue'=>['class'=> 'MediaWiki\\Content\\VueContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'CodeHighlighter',],], 'text'=> 'MediaWiki\\Content\\TextContentHandler', 'unknown'=> 'MediaWiki\\Content\\FallbackContentHandler',], 'NamespaceContentModels'=>[], 'TextModelsToParse'=>['wikitext', 'javascript', 'css',], 'CompressRevisions'=> false, 'ExternalStores'=>[], 'ExternalServers'=>[], 'DefaultExternalStore'=> false, 'RevisionCacheExpiry'=> 604800, 'PageLanguageUseDB'=> false, 'DiffEngine'=> null, 'ExternalDiffEngine'=> false, 'Wikidiff2Options'=>[], 'RequestTimeLimit'=> null, 'TransactionalTimeLimit'=> 120, 'CriticalSectionTimeLimit'=> 180.0, 'MiserMode'=> false, 'DisableQueryPages'=> false, 'QueryCacheLimit'=> 1000, 'WantedPagesThreshold'=> 1, 'AllowSlowParserFunctions'=> false, 'AllowSchemaUpdates'=> true, 'MaxArticleSize'=> 2048, 'MemoryLimit'=> '50M', 'PoolCounterConf'=> null, 'PoolCountClientConf'=>['servers'=>['127.0.0.1',], 'timeout'=> 0.1,], 'MaxUserDBWriteDuration'=> false, 'MaxJobDBWriteDuration'=> false, 'LinkHolderBatchSize'=> 1000, 'MaximumMovedPages'=> 100, 'ForceDeferredUpdatesPreSend'=> false, 'MultiShardSiteStats'=> false, 'CacheDirectory'=> false, 'MainCacheType'=> 0, 'MessageCacheType'=> -1, 'ParserCacheType'=> -1, 'SessionCacheType'=> -1, 'AnonSessionCacheType'=> false, 'LanguageConverterCacheType'=> -1, 'ObjectCaches'=>[0=>['class'=> 'Wikimedia\\ObjectCache\\EmptyBagOStuff', 'reportDupes'=> false,], 1=>['class'=> 'MediaWiki\\ObjectCache\\SqlBagOStuff', 'loggroup'=> 'SQLBagOStuff',], 'memcached-php'=>['class'=> 'Wikimedia\\ObjectCache\\MemcachedPhpBagOStuff', 'loggroup'=> 'memcached',], 'memcached-pecl'=>['class'=> 'Wikimedia\\ObjectCache\\MemcachedPeclBagOStuff', 'loggroup'=> 'memcached',], 'hash'=>['class'=> 'Wikimedia\\ObjectCache\\HashBagOStuff', 'reportDupes'=> false,], 'apc'=>['class'=> 'Wikimedia\\ObjectCache\\APCUBagOStuff', 'reportDupes'=> false,], 'apcu'=>['class'=> 'Wikimedia\\ObjectCache\\APCUBagOStuff', 'reportDupes'=> false,],], 'WANObjectCache'=>[], 'MicroStashType'=> -1, 'MainStash'=> 1, 'ParsoidCacheConfig'=>['StashType'=> null, 'StashDuration'=> 86400, 'WarmParsoidParserCache'=> false,], 'ParsoidSelectiveUpdateSampleRate'=> 0, 'ParserCacheFilterConfig'=>['pcache'=>['default'=>['minCpuTime'=> 0,],], 'postproc-pcache'=>['default'=>['minCpuTime'=> 9223372036854775807,],], 'parsoid-pcache'=>['default'=>['minCpuTime'=> 0,],], 'postproc-parsoid-pcache'=>['default'=>['minCpuTime'=> 0,],],], 'ChronologyProtectorSecret'=> '', 'ParserCacheExpireTime'=> 86400, 'ParserCacheAsyncExpireTime'=> 60, 'ParserCacheAsyncRefreshJobs'=> true, 'OldRevisionParserCacheExpireTime'=> 3600, 'ObjectCacheSessionExpiry'=> 3600, 'SuspiciousIpExpiry'=> false, 'SessionPbkdf2Iterations'=> 10001, 'UseSessionCookieJwt'=> false, 'JwtSessionCookieIssuer'=> null, 'MemCachedServers'=>['127.0.0.1:11211',], 'MemCachedPersistent'=> false, 'MemCachedTimeout'=> 500000, 'UseLocalMessageCache'=> false, 'AdaptiveMessageCache'=> false, 'LocalisationCacheConf'=>['class'=> 'MediaWiki\\Language\\LocalisationCache', 'store'=> 'detect', 'storeClass'=> false, 'storeDirectory'=> false, 'storeServer'=>[], 'forceRecache'=> false, 'manualRecache'=> false,], 'CachePages'=> true, 'CacheEpoch'=> '20030516000000', 'GitInfoCacheDirectory'=> false, 'UseFileCache'=> false, 'FileCacheDepth'=> 2, 'RenderHashAppend'=> '', 'EnableSidebarCache'=> false, 'SidebarCacheExpiry'=> 86400, 'UseGzip'=> false, 'InvalidateCacheOnLocalSettingsChange'=> true, 'ExtensionInfoMTime'=> false, 'EnableRemoteBagOStuffTests'=> false, 'UseCdn'=> false, 'VaryOnXFP'=> false, 'InternalServer'=> false, 'CdnMaxAge'=> 18000, 'CdnMaxageLagged'=> 30, 'CdnMaxageStale'=> 10, 'CdnReboundPurgeDelay'=> 0, 'CdnMaxageSubstitute'=> 60, 'ForcedRawSMaxage'=> 300, 'CdnServers'=>[], 'CdnServersNoPurge'=>[], 'HTCPRouting'=>[], 'HTCPMulticastTTL'=> 1, 'UsePrivateIPs'=> false, 'CdnMatchParameterOrder'=> true, 'LanguageCode'=> 'en', 'GrammarForms'=>[], 'InterwikiMagic'=> true, 'HideInterlanguageLinks'=> false, 'ExtraInterlanguageLinkPrefixes'=>[], 'InterlanguageLinkCodeMap'=>[], 'ExtraLanguageNames'=>[], 'ExtraLanguageCodes'=>['bh'=> 'bho', 'no'=> 'nb', 'simple'=> 'en',], 'DummyLanguageCodes'=>[], 'AllUnicodeFixes'=> false, 'LegacyEncoding'=> false, 'AmericanDates'=> false, 'TranslateNumerals'=> true, 'UseDatabaseMessages'=> true, 'MaxMsgCacheEntrySize'=> 10000, 'DisableLangConversion'=> false, 'DisableTitleConversion'=> false, 'DefaultLanguageVariant'=> false, 'UsePigLatinVariant'=> false, 'DisabledVariants'=>[], 'VariantArticlePath'=> false, 'UseXssLanguage'=> false, 'LoginLanguageSelector'=> false, 'ForceUIMsgAsContentMsg'=>[], 'RawHtmlMessages'=>[], 'Localtimezone'=> null, 'LocalTZoffset'=> null, 'OverrideUcfirstCharacters'=>[], 'MimeType'=> 'text/html', 'Html5Version'=> null, 'EditSubmitButtonLabelPublish'=> false, 'XhtmlNamespaces'=>[], 'SiteNotice'=> '', 'BrowserFormatDetection'=> 'telephone=no', 'SkinMetaTags'=>[], 'DefaultSkin'=> 'vector-2022', 'FallbackSkin'=> 'fallback', 'SkipSkins'=>[], 'DisableOutputCompression'=> false, 'FragmentMode'=>['html5', 'legacy',], 'ExternalInterwikiFragmentMode'=> 'legacy', 'FooterIcons'=>['copyright'=>['copyright'=>[],], 'poweredby'=>['mediawiki'=>['src'=> null, 'url'=> 'https:'alt'=> 'Powered by MediaWiki', 'lang'=> 'en',],],], 'EnableSectionShare'=> false, 'UseCombinedLoginLink'=> false, 'Edititis'=> false, 'Send404Code'=> true, 'ShowRollbackEditCount'=> 10, 'EnableCanonicalServerLink'=> false, 'InterwikiLogoOverride'=>[], 'ResourceModules'=>[], 'ResourceModuleSkinStyles'=>[], 'ResourceLoaderSources'=>[], 'ResourceBasePath'=> null, 'ResourceLoaderMaxage'=>[], 'ResourceLoaderDebug'=> false, 'ResourceLoaderMaxQueryLength'=> false, 'ResourceLoaderValidateJS'=> true, 'ResourceLoaderEnableJSProfiler'=> false, 'ResourceLoaderStorageEnabled'=> true, 'ResourceLoaderStorageVersion'=> 1, 'ResourceLoaderEnableSourceMapLinks'=> true, 'AllowSiteCSSOnRestrictedPages'=> false, 'VueDevelopmentMode'=> false, 'CodexDevelopmentDir'=> null, 'MetaNamespace'=> false, 'MetaNamespaceTalk'=> false, 'CanonicalNamespaceNames'=>[-2=> 'Media', -1=> 'Special', 0=> '', 1=> 'Talk', 2=> 'User', 3=> 'User_talk', 4=> 'Project', 5=> 'Project_talk', 6=> 'File', 7=> 'File_talk', 8=> 'MediaWiki', 9=> 'MediaWiki_talk', 10=> 'Template', 11=> 'Template_talk', 12=> 'Help', 13=> 'Help_talk', 14=> 'Category', 15=> 'Category_talk',], 'ExtraNamespaces'=>[], 'ExtraGenderNamespaces'=>[], 'NamespaceAliases'=>[], 'LegalTitleChars'=> ' %!"$&\'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF+', 'CapitalLinks' => true, 'CapitalLinkOverrides' => [ ], 'NamespacesWithSubpages' => [ 1 => true, 2 => true, 3 => true, 4 => true, 5 => true, 7 => true, 8 => true, 9 => true, 10 => true, 11 => true, 12 => true, 13 => true, 15 => true, ], 'NamespacesWithoutAutoSummaries' => [ ], 'ContentNamespaces' => [ 0, ], 'ShortPagesNamespaceExclusions' => [ ], 'ExtraSignatureNamespaces' => [ ], 'InvalidRedirectTargets' => [ 'Filepath', 'Mypage', 'Mytalk', 'Redirect', 'Mylog', ], 'DisableHardRedirects' => false, 'FixDoubleRedirects' => false, 'LocalInterwikis' => [ ], 'InterwikiExpiry' => 10800, 'InterwikiCache' => false, 'InterwikiScopes' => 3, 'InterwikiFallbackSite' => 'wiki', 'RedirectSources' => false, 'SiteTypes' => [ 'mediawiki' => 'MediaWiki\\Site\\MediaWikiSite', ], 'MaxTocLevel' => 999, 'MaxPPNodeCount' => 1000000, 'MaxTemplateDepth' => 100, 'MaxPPExpandDepth' => 100, 'UrlProtocols' => [ 'bitcoin:', 'ftp: 'ftps: 'geo:', 'git: 'gopher: 'http: 'https: 'irc: 'ircs: 'magnet:', 'mailto:', 'matrix:', 'mms: 'news:', 'nntp: 'redis: 'sftp: 'sip:', 'sips:', 'sms:', 'ssh: 'svn: 'tel:', 'telnet: 'urn:', 'wikipedia: 'worldwind: 'xmpp:', ' ], 'CleanSignatures' => true, 'AllowExternalImages' => false, 'AllowExternalImagesFrom' => '', 'EnableImageWhitelist' => false, 'TidyConfig' => [ ], 'ParsoidSettings' => [ 'useSelser' => true, ], 'ParsoidExperimentalParserFunctionOutput' => false, 'RawHtml' => false, 'ExternalLinkTarget' => false, 'NoFollowLinks' => true, 'NoFollowNsExceptions' => [ ], 'NoFollowDomainExceptions' => [ 'mediawiki.org', ], 'RegisterInternalExternals' => false, 'ExternalLinksIgnoreDomains' => [ ], 'AllowDisplayTitle' => true, 'RestrictDisplayTitle' => true, 'ExpensiveParserFunctionLimit' => 100, 'PreprocessorCacheThreshold' => 1000, 'EnableScaryTranscluding' => false, 'TranscludeCacheExpiry' => 3600, 'EnableMagicLinks' => [ 'ISBN' => false, 'PMID' => false, 'RFC' => false, ], 'ParserEnableUserLanguage' => false, 'ArticleCountMethod' => 'link', 'ActiveUserDays' => 30, 'LearnerEdits' => 10, 'LearnerMemberSince' => 4, 'ExperiencedUserEdits' => 500, 'ExperiencedUserMemberSince' => 30, 'ManualRevertSearchRadius' => 15, 'RevertedTagMaxDepth' => 15, 'CentralIdLookupProviders' => [ 'local' => [ 'class' => 'MediaWiki\\User\\CentralId\\LocalIdLookup', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', 'HideUserUtils', ], ], ], 'CentralIdLookupProvider' => 'local', 'UserRegistrationProviders' => [ 'local' => [ 'class' => 'MediaWiki\\User\\Registration\\LocalUserRegistrationProvider', 'services' => [ 'ConnectionProvider', ], ], ], 'PasswordPolicy' => [ 'policies' => [ 'bureaucrat' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'sysop' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'interface-admin' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'bot' => [ 'MinimalPasswordLength' => 10, 'MinimumPasswordLengthToLogin' => 1, ], 'default' => [ 'MinimalPasswordLength' => [ 'value' => 8, 'suggestChangeOnLogin' => true, ], 'PasswordCannotBeSubstringInUsername' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], 'PasswordCannotMatchDefaults' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], 'MaximalPasswordLength' => [ 'value' => 4096, 'suggestChangeOnLogin' => true, ], 'PasswordNotInCommonList' => [ 'value' => true, 'suggestChangeOnLogin' => true, ], ], ], 'checks' => [ 'MinimalPasswordLength' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMinimalPasswordLength', ], 'MinimumPasswordLengthToLogin' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMinimumPasswordLengthToLogin', ], 'PasswordCannotBeSubstringInUsername' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordCannotBeSubstringInUsername', ], 'PasswordCannotMatchDefaults' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordCannotMatchDefaults', ], 'MaximalPasswordLength' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkMaximalPasswordLength', ], 'PasswordNotInCommonList' => [ 'MediaWiki\\Password\\PasswordPolicyChecks', 'checkPasswordNotInCommonList', ], ], ], 'AuthManagerConfig' => null, 'AuthManagerAutoConfig' => [ 'preauth' => [ 'MediaWiki\\Auth\\ThrottlePreAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\ThrottlePreAuthenticationProvider', 'sort' => 0, ], 'MediaWiki\\Auth\\PreviouslyRenamedAccountPreAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\PreviouslyRenamedAccountPreAuthenticationProvider', 'services' => [ 'ConnectionProvider', 'UserFactory', ], 'sort' => 0, ], ], 'primaryauth' => [ 'MediaWiki\\Auth\\TemporaryPasswordPrimaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\TemporaryPasswordPrimaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', 'UserOptionsLookup', ], 'args' => [ [ 'authoritative' => false, ], ], 'sort' => 0, ], 'MediaWiki\\Auth\\LocalPasswordPrimaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\LocalPasswordPrimaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', ], 'args' => [ [ 'authoritative' => true, ], ], 'sort' => 100, ], ], 'secondaryauth' => [ 'MediaWiki\\Auth\\CheckBlocksSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\CheckBlocksSecondaryAuthenticationProvider', 'sort' => 0, ], 'MediaWiki\\Auth\\ResetPasswordSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\ResetPasswordSecondaryAuthenticationProvider', 'sort' => 100, ], 'MediaWiki\\Auth\\EmailNotificationSecondaryAuthenticationProvider' => [ 'class' => 'MediaWiki\\Auth\\EmailNotificationSecondaryAuthenticationProvider', 'services' => [ 'DBLoadBalancerFactory', ], 'sort' => 200, ], ], ], 'RememberMe' => 'choose', 'ReauthenticateTime' => [ 'default' => 3600, ], 'ChangeCredentialsBlacklist' => [ 'MediaWiki\\Auth\\TemporaryPasswordAuthenticationRequest', ], 'RemoveCredentialsBlacklist' => [ 'MediaWiki\\Auth\\PasswordAuthenticationRequest', ], 'InvalidPasswordReset' => true, 'PasswordDefault' => 'pbkdf2', 'PasswordConfig' => [ 'A' => [ 'class' => 'MediaWiki\\Password\\MWOldPassword', ], 'B' => [ 'class' => 'MediaWiki\\Password\\MWSaltedPassword', ], 'pbkdf2-legacyA' => [ 'class' => 'MediaWiki\\Password\\LayeredParameterizedPassword', 'types' => [ 'A', 'pbkdf2', ], ], 'pbkdf2-legacyB' => [ 'class' => 'MediaWiki\\Password\\LayeredParameterizedPassword', 'types' => [ 'B', 'pbkdf2', ], ], 'bcrypt' => [ 'class' => 'MediaWiki\\Password\\BcryptPassword', 'cost' => 9, ], 'pbkdf2' => [ 'class' => 'MediaWiki\\Password\\Pbkdf2PasswordUsingOpenSSL', 'algo' => 'sha512', 'cost' => '30000', 'length' => '64', ], 'argon2' => [ 'class' => 'MediaWiki\\Password\\Argon2Password', 'algo' => 'auto', ], ], 'PasswordResetRoutes' => [ 'username' => true, 'email' => true, ], 'MaxSigChars' => 255, 'SignatureValidation' => 'warning', 'SignatureAllowedLintErrors' => [ 'obsolete-tag', ], 'MaxNameChars' => 255, 'ReservedUsernames' => [ 'MediaWiki default', 'Conversion script', 'Maintenance script', 'Template namespace initialisation script', 'ScriptImporter', 'Delete page script', 'Move page script', 'Command line script', 'Unknown user', 'msg:double-redirect-fixer', 'msg:usermessage-editor', 'msg:proxyblocker', 'msg:sorbs', 'msg:spambot_username', 'msg:autochange-username', ], 'DefaultUserOptions' => [ 'ccmeonemails' => 0, 'date' => 'default', 'diffonly' => 0, 'diff-type' => 'table', 'disablemail' => 0, 'editfont' => 'monospace', 'editondblclick' => 0, 'editrecovery' => 0, 'editsectiononrightclick' => 0, 'email-allow-new-users' => 1, 'enotifminoredits' => 0, 'enotifrevealaddr' => 0, 'enotifusertalkpages' => 1, 'enotifwatchlistpages' => 1, 'extendwatchlist' => 1, 'fancysig' => 0, 'forceeditsummary' => 0, 'forcesafemode' => 0, 'gender' => 'unknown', 'hidecategorization' => 1, 'hideminor' => 0, 'hidepatrolled' => 0, 'imagesize' => 2, 'minordefault' => 0, 'newpageshidepatrolled' => 0, 'nickname' => '', 'norollbackdiff' => 0, 'prefershttps' => 1, 'previewonfirst' => 0, 'previewontop' => 1, 'pst-cssjs' => 1, 'rcdays' => 7, 'rcenhancedfilters-disable' => 0, 'rclimit' => 50, 'requireemail' => 0, 'search-match-redirect' => true, 'search-special-page' => 'Search', 'search-thumbnail-extra-namespaces' => true, 'searchlimit' => 20, 'showhiddencats' => 0, 'shownumberswatching' => 1, 'showrollbackconfirmation' => 0, 'skin' => false, 'skin-responsive' => 1, 'thumbsize' => 5, 'underline' => 2, 'useeditwarning' => 1, 'uselivepreview' => 0, 'usenewrc' => 1, 'watchcreations' => 1, 'watchcreations-expiry' => 'infinite', 'watchdefault' => 1, 'watchdefault-expiry' => 'infinite', 'watchdeletion' => 0, 'watchlistdays' => 7, 'watchlisthideanons' => 0, 'watchlisthidebots' => 0, 'watchlisthidecategorization' => 1, 'watchlisthideliu' => 0, 'watchlisthideminor' => 0, 'watchlisthideown' => 0, 'watchlisthidepatrolled' => 0, 'watchlistreloadautomatically' => 0, 'watchlistunwatchlinks' => 0, 'watchmoves' => 0, 'watchrollback' => 0, 'watchuploads' => 1, 'watchrollback-expiry' => 'infinite', 'watchstar-expiry' => 'infinite', 'wlenhancedfilters-disable' => 0, 'wllimit' => 250, ], 'ConditionalUserOptions' => [ ], 'HiddenPrefs' => [ ], 'UserJsPrefLimit' => 100, 'InvalidUsernameCharacters' => '@:>=', 'UserrightsInterwikiDelimiter' => '@', 'SecureLogin' => false, 'AuthenticationTokenVersion' => null, 'SessionProviders' => [ 'MediaWiki\\Session\\CookieSessionProvider' => [ 'class' => 'MediaWiki\\Session\\CookieSessionProvider', 'args' => [ [ 'priority' => 30, ], ], 'services' => [ 'JwtCodec', 'UrlUtils', ], ], 'MediaWiki\\Session\\BotPasswordSessionProvider' => [ 'class' => 'MediaWiki\\Session\\BotPasswordSessionProvider', 'args' => [ [ 'priority' => 75, ], ], 'services' => [ 'GrantsInfo', ], ], ], 'AutoCreateTempUser' => [ 'known' => false, 'enabled' => false, 'actions' => [ 'edit', ], 'genPattern' => '~$1', 'matchPattern' => null, 'reservedPattern' => '~$1', 'serialProvider' => [ 'type' => 'local', 'useYear' => true, ], 'serialMapping' => [ 'type' => 'readable-numeric', ], 'expireAfterDays' => 90, 'notifyBeforeExpirationDays' => 10, ], 'AutoblockExemptions' => [ ], 'AutoblockExpiry' => 86400, 'BlockAllowsUTEdit' => true, 'BlockCIDRLimit' => [ 'IPv4' => 16, 'IPv6' => 19, ], 'BlockDisablesLogin' => false, 'EnableMultiBlocks' => false, 'WhitelistRead' => false, 'WhitelistReadRegexp' => false, 'EmailConfirmToEdit' => false, 'HideIdentifiableRedirects' => true, 'GroupPermissions' => [ '*' => [ 'createaccount' => true, 'autocreateaccount' => true, 'read' => true, 'edit' => true, 'createpage' => true, 'createtalk' => true, 'viewmyprivateinfo' => true, 'editmyprivateinfo' => true, 'editmyoptions' => true, ], 'user' => [ 'move' => true, 'move-subpages' => true, 'move-rootuserpages' => true, 'move-categorypages' => true, 'movefile' => true, 'read' => true, 'edit' => true, 'createpage' => true, 'createtalk' => true, 'upload' => true, 'reupload' => true, 'reupload-shared' => true, 'minoredit' => true, 'editmyusercss' => true, 'editmyuserjson' => true, 'editmyuserjs' => true, 'editmyuserjsredirect' => true, 'sendemail' => true, 'applychangetags' => true, 'changetags' => true, 'viewmywatchlist' => true, 'editmywatchlist' => true, 'createwithcontentmodel' => true, 'logout' => true, ], 'autoconfirmed' => [ 'autoconfirmed' => true, 'editsemiprotected' => true, ], 'bot' => [ 'bot' => true, 'autoconfirmed' => true, 'editsemiprotected' => true, 'nominornewtalk' => true, 'autopatrol' => true, 'suppressredirect' => true, 'apihighlimits' => true, ], 'sysop' => [ 'block' => true, 'createaccount' => true, 'createpreviouslyrenamedaccount' => true, 'delete' => true, 'bigdelete' => true, 'deletedhistory' => true, 'deletedtext' => true, 'undelete' => true, 'editcontentmodel' => true, 'editinterface' => true, 'editsitejson' => true, 'edituserjson' => true, 'import' => true, 'importupload' => true, 'move' => true, 'move-subpages' => true, 'move-rootuserpages' => true, 'move-categorypages' => true, 'patrol' => true, 'autopatrol' => true, 'protect' => true, 'editprotected' => true, 'rollback' => true, 'upload' => true, 'reupload' => true, 'reupload-shared' => true, 'unwatchedpages' => true, 'autoconfirmed' => true, 'editsemiprotected' => true, 'ipblock-exempt' => true, 'blockemail' => true, 'markbotedits' => true, 'apihighlimits' => true, 'browsearchive' => true, 'noratelimit' => true, 'movefile' => true, 'unblockself' => true, 'suppressredirect' => true, 'mergehistory' => true, 'managechangetags' => true, 'deletechangetags' => true, ], 'interface-admin' => [ 'editinterface' => true, 'editsitecss' => true, 'editsitejson' => true, 'editsitejs' => true, 'editusercss' => true, 'edituserjson' => true, 'edituserjs' => true, ], 'bureaucrat' => [ 'userrights' => true, 'noratelimit' => true, 'renameuser' => true, ], 'suppress' => [ 'hideuser' => true, 'suppressrevision' => true, 'viewsuppressed' => true, 'suppressionlog' => true, 'deleterevision' => true, 'deletelogentry' => true, ], ], 'PrivilegedGroups' => [ 'bureaucrat', 'interface-admin', 'suppress', 'sysop', ], 'RevokePermissions' => [ ], 'GroupInheritsPermissions' => [ ], 'ImplicitGroups' => [ '*', 'user', 'autoconfirmed', ], 'GroupsAddToSelf' => [ ], 'GroupsRemoveFromSelf' => [ ], 'RestrictedGroups' => [ ], 'UserRequirementsPrivateConditions' => [ ], 'RestrictionTypes' => [ 'create', 'edit', 'move', 'upload', ], 'RestrictionLevels' => [ '', 'autoconfirmed', 'sysop', ], 'CascadingRestrictionLevels' => [ 'sysop', ], 'SemiprotectedRestrictionLevels' => [ 'autoconfirmed', ], 'NamespaceProtection' => [ ], 'RestrictUserPageEditing' => false, 'NonincludableNamespaces' => [ ], 'AutoConfirmAge' => 0, 'AutoConfirmCount' => 0, 'Autopromote' => [ 'autoconfirmed' => [ '&', [ 1, null, ], [ 2, null, ], ], ], 'AutopromoteOnce' => [ 'onEdit' => [ ], ], 'AutopromoteOnceLogInRC' => true, 'AutopromoteOnceRCExcludedGroups' => [ ], 'AddGroups' => [ ], 'RemoveGroups' => [ ], 'AvailableRights' => [ ], 'ImplicitRights' => [ ], 'DeleteRevisionsLimit' => 0, 'DeleteRevisionsBatchSize' => 1000, 'HideUserContribLimit' => 1000, 'AccountCreationThrottle' => [ [ 'count' => 0, 'seconds' => 86400, ], ], 'TempAccountCreationThrottle' => [ [ 'count' => 1, 'seconds' => 600, ], [ 'count' => 6, 'seconds' => 86400, ], ], 'TempAccountNameAcquisitionThrottle' => [ [ 'count' => 60, 'seconds' => 86400, ], ], 'SpamRegex' => [ ], 'SummarySpamRegex' => [ ], 'EnableDnsBlacklist' => false, 'DnsBlacklistUrls' => [ ], 'ProxyList' => [ ], 'ProxyWhitelist' => [ ], 'SoftBlockRanges' => [ ], 'ApplyIpBlocksToXff' => false, 'RateLimits' => [ 'edit' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], 'user' => [ 90, 60, ], ], 'move' => [ 'newbie' => [ 2, 120, ], 'user' => [ 8, 60, ], ], 'upload' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], ], 'rollback' => [ 'user' => [ 10, 60, ], 'newbie' => [ 5, 120, ], ], 'mailpassword' => [ 'ip' => [ 5, 3600, ], ], 'sendemail' => [ 'ip' => [ 5, 86400, ], 'newbie' => [ 5, 86400, ], 'user' => [ 20, 86400, ], ], 'changeemail' => [ 'ip-all' => [ 10, 3600, ], 'user' => [ 4, 86400, ], ], 'confirmemail' => [ 'ip-all' => [ 10, 3600, ], 'user' => [ 4, 86400, ], ], 'purge' => [ 'ip' => [ 30, 60, ], 'user' => [ 30, 60, ], ], 'linkpurge' => [ 'ip' => [ 30, 60, ], 'user' => [ 30, 60, ], ], 'renderfile' => [ 'ip' => [ 700, 30, ], 'user' => [ 700, 30, ], ], 'renderfile-nonstandard' => [ 'ip' => [ 70, 30, ], 'user' => [ 70, 30, ], ], 'stashedit' => [ 'ip' => [ 30, 60, ], 'newbie' => [ 30, 60, ], ], 'stashbasehtml' => [ 'ip' => [ 5, 60, ], 'newbie' => [ 5, 60, ], ], 'changetags' => [ 'ip' => [ 8, 60, ], 'newbie' => [ 8, 60, ], ], 'editcontentmodel' => [ 'newbie' => [ 2, 120, ], 'user' => [ 8, 60, ], ], ], 'RateLimitsExcludedIPs' => [ ], 'PutIPinRC' => true, 'QueryPageDefaultLimit' => 50, 'ExternalQuerySources' => [ ], 'PasswordAttemptThrottle' => [ [ 'count' => 5, 'seconds' => 300, ], [ 'count' => 150, 'seconds' => 172800, ], ], 'GrantPermissions' => [ 'basic' => [ 'autocreateaccount' => true, 'autoconfirmed' => true, 'autopatrol' => true, 'editsemiprotected' => true, 'ipblock-exempt' => true, 'nominornewtalk' => true, 'patrolmarks' => true, 'read' => true, 'unwatchedpages' => true, ], 'highvolume' => [ 'bot' => true, 'apihighlimits' => true, 'noratelimit' => true, 'markbotedits' => true, ], 'import' => [ 'import' => true, 'importupload' => true, ], 'editpage' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'pagelang' => true, ], 'editprotected' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editprotected' => true, ], 'editmycssjs' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editmyusercss' => true, 'editmyuserjson' => true, 'editmyuserjs' => true, ], 'editmyoptions' => [ 'editmyoptions' => true, 'editmyuserjson' => true, ], 'editinterface' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editinterface' => true, 'edituserjson' => true, 'editsitejson' => true, ], 'editsiteconfig' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editinterface' => true, 'edituserjson' => true, 'editsitejson' => true, 'editusercss' => true, 'edituserjs' => true, 'editsitecss' => true, 'editsitejs' => true, ], 'createeditmovepage' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'createpage' => true, 'createtalk' => true, 'delete-redirect' => true, 'move' => true, 'move-rootuserpages' => true, 'move-subpages' => true, 'move-categorypages' => true, 'suppressredirect' => true, ], 'uploadfile' => [ 'upload' => true, 'reupload-own' => true, ], 'uploadeditmovefile' => [ 'upload' => true, 'reupload-own' => true, 'reupload' => true, 'reupload-shared' => true, 'upload_by_url' => true, 'movefile' => true, 'suppressredirect' => true, ], 'patrol' => [ 'patrol' => true, ], 'rollback' => [ 'rollback' => true, ], 'blockusers' => [ 'block' => true, 'blockemail' => true, ], 'viewdeleted' => [ 'browsearchive' => true, 'deletedhistory' => true, 'deletedtext' => true, ], 'viewrestrictedlogs' => [ 'suppressionlog' => true, ], 'delete' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'browsearchive' => true, 'deletedhistory' => true, 'deletedtext' => true, 'delete' => true, 'bigdelete' => true, 'deletelogentry' => true, 'deleterevision' => true, 'undelete' => true, ], 'oversight' => [ 'suppressrevision' => true, 'viewsuppressed' => true, ], 'protect' => [ 'edit' => true, 'minoredit' => true, 'applychangetags' => true, 'changetags' => true, 'editcontentmodel' => true, 'createwithcontentmodel' => true, 'editprotected' => true, 'protect' => true, ], 'viewmywatchlist' => [ 'viewmywatchlist' => true, ], 'editmywatchlist' => [ 'editmywatchlist' => true, ], 'sendemail' => [ 'sendemail' => true, ], 'createaccount' => [ 'createaccount' => true, ], 'privateinfo' => [ 'viewmyprivateinfo' => true, ], 'mergehistory' => [ 'mergehistory' => true, ], 'managesessions' => [ 'logout' => true, ], ], 'GrantPermissionGroups' => [ 'basic' => 'hidden', 'editpage' => 'page-interaction', 'createeditmovepage' => 'page-interaction', 'editprotected' => 'page-interaction', 'patrol' => 'page-interaction', 'uploadfile' => 'file-interaction', 'uploadeditmovefile' => 'file-interaction', 'sendemail' => 'email', 'viewmywatchlist' => 'watchlist-interaction', 'editviewmywatchlist' => 'watchlist-interaction', 'editmycssjs' => 'customization', 'editmyoptions' => 'customization', 'editinterface' => 'administration', 'editsiteconfig' => 'administration', 'rollback' => 'administration', 'blockusers' => 'administration', 'delete' => 'administration', 'viewdeleted' => 'administration', 'viewrestrictedlogs' => 'administration', 'protect' => 'administration', 'oversight' => 'administration', 'createaccount' => 'administration', 'mergehistory' => 'administration', 'import' => 'administration', 'highvolume' => 'high-volume', 'privateinfo' => 'private-information', 'managesessions' => 'private-information', ], 'GrantRiskGroups' => [ 'basic' => 'low', 'editpage' => 'low', 'createeditmovepage' => 'low', 'editprotected' => 'vandalism', 'patrol' => 'low', 'uploadfile' => 'low', 'uploadeditmovefile' => 'low', 'sendemail' => 'security', 'viewmywatchlist' => 'low', 'editviewmywatchlist' => 'low', 'editmycssjs' => 'security', 'editmyoptions' => 'security', 'editinterface' => 'vandalism', 'editsiteconfig' => 'security', 'rollback' => 'low', 'blockusers' => 'vandalism', 'delete' => 'vandalism', 'viewdeleted' => 'vandalism', 'viewrestrictedlogs' => 'security', 'protect' => 'vandalism', 'oversight' => 'security', 'createaccount' => 'low', 'mergehistory' => 'vandalism', 'import' => 'security', 'highvolume' => 'low', 'privateinfo' => 'low', ], 'EnableBotPasswords' => true, 'BotPasswordsCluster' => false, 'BotPasswordsDatabase' => false, 'BotPasswordsLimit' => 100, 'SecretKey' => false, 'JwtPrivateKey' => false, 'JwtPublicKey' => false, 'AllowUserJs' => false, 'ReauthenticateForActions' => [ 'edituserjs' => 'edituserjscss', 'editusercss' => 'edituserjscss', 'editsitejs' => 'editsitejscss', 'editsitecss' => 'editsitejscss', ], 'AllowUserCss' => false, 'AllowUserCssPrefs' => true, 'UseSiteJs' => true, 'UseSiteCss' => true, 'BreakFrames' => false, 'EditPageFrameOptions' => 'DENY', 'ApiFrameOptions' => 'DENY', 'CSPHeader' => false, 'CSPReportOnlyHeader' => false, 'CSPUseReportURIDirective' => false, 'CSPFalsePositiveUrls' => [ 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'https: 'chrome-extension' => true, ], 'AllowCrossOrigin' => false, 'RestAllowCrossOriginCookieAuth' => false, 'SessionSecret' => false, 'CookieExpiration' => 2592000, 'ExtendedLoginCookieExpiration' => 15552000, 'SessionCookieJwtExpiration' => 14400, 'CookieDomain' => '', 'CookiePath' => '/', 'CookieSecure' => 'detect', 'CookiePrefix' => false, 'CookieHttpOnly' => true, 'CookieSameSite' => null, 'CacheVaryCookies' => [ ], 'SessionName' => false, 'CookieSetOnAutoblock' => true, 'CookieSetOnIpBlock' => true, 'DebugLogFile' => '', 'DebugLogPrefix' => '', 'DebugRedirects' => false, 'DebugRawPage' => false, 'DebugComments' => false, 'DebugDumpSql' => false, 'TrxProfilerLimits' => [ 'GET' => [ 'masterConns' => 0, 'writes' => 0, 'readQueryTime' => 5, 'readQueryRows' => 10000, ], 'POST' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 100000, 'maxAffected' => 1000, ], 'POST-nonwrite' => [ 'writes' => 0, 'readQueryTime' => 5, 'readQueryRows' => 10000, ], 'PostSend-GET' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 10000, 'maxAffected' => 1000, 'masterConns' => 0, 'writes' => 0, ], 'PostSend-POST' => [ 'readQueryTime' => 5, 'writeQueryTime' => 1, 'readQueryRows' => 100000, 'maxAffected' => 1000, ], 'JobRunner' => [ 'readQueryTime' => 30, 'writeQueryTime' => 5, 'readQueryRows' => 100000, 'maxAffected' => 500, ], 'Maintenance' => [ 'writeQueryTime' => 5, 'maxAffected' => 1000, ], ], 'DebugLogGroups' => [ ], 'MWLoggerDefaultSpi' => [ 'class' => 'MediaWiki\\Logger\\LegacySpi', ], 'ShowDebug' => false, 'SpecialVersionShowHooks' => false, 'ShowExceptionDetails' => false, 'LogExceptionBacktrace' => true, 'PropagateErrors' => true, 'ShowHostnames' => false, 'OverrideHostname' => false, 'DevelopmentWarnings' => false, 'DeprecationReleaseLimit' => false, 'Profiler' => [ ], 'StatsdServer' => false, 'StatsdMetricPrefix' => 'MediaWiki', 'StatsTarget' => null, 'StatsFormat' => null, 'StatsPrefix' => 'mediawiki', 'OpenTelemetryConfig' => null, 'PageInfoTransclusionLimit' => 50, 'EnableJavaScriptTest' => false, 'CachePrefix' => false, 'DebugToolbar' => false, 'ApiClientErrorSampleRate' => 1.0, 'DisableTextSearch' => false, 'AdvancedSearchHighlighting' => false, 'SearchHighlightBoundaries' => '[\\p{Z}\\p{P}\\p{C}]', 'OpenSearchTemplates' => [ 'application/x-suggestions+json' => false, 'application/x-suggestions+xml' => false, ], 'OpenSearchDefaultLimit' => 10, 'OpenSearchDescriptionLength' => 100, 'SearchSuggestCacheExpiry' => 1200, 'DisableSearchUpdate' => false, 'NamespacesToBeSearchedDefault' => [ true, ], 'DisableInternalSearch' => false, 'SearchForwardUrl' => null, 'SitemapNamespaces' => false, 'SitemapNamespacesPriorities' => false, 'SitemapApiConfig' => [ ], 'SpecialSearchFormOptions' => [ ], 'SearchMatchRedirectPreference' => false, 'SearchRunSuggestedQuery' => true, 'Diff3' => '/usr/bin/diff3', 'Diff' => '/usr/bin/diff', 'PreviewOnOpenNamespaces' => [ 14 => true, ], 'UniversalEditButton' => true, 'UseAutomaticEditSummaries' => true, 'CommandLineDarkBg' => false, 'ReadOnly' => null, 'ReadOnlyWatchedItemStore' => false, 'ReadOnlyFile' => false, 'UpgradeKey' => false, 'GitBin' => '/usr/bin/git', 'GitRepositoryViewers' => [ 'https: 'ssh: 'https: 'git@github\\.com:(.*?)(\\.git)?' => 'https: ], 'InstallerInitialPages' => [ [ 'titlemsg' => 'mainpage', 'text' => '{{subst:int:mainpagetext}}{{subst:int:mainpagedocfooter}}', ], ], 'RCMaxAge' => 7776000, 'WatchersMaxAge' => 15552000, 'UnwatchedPageSecret' => 1, 'RCFilterByAge' => false, 'RCLinkLimits' => [ 50, 100, 250, 500, ], 'RCLinkDays' => [ 1, 3, 7, 14, 30, ], 'RCFeeds' => [ ], 'RCWatchCategoryMembership' => false, 'UseRCPatrol' => true, 'StructuredChangeFiltersLiveUpdatePollingRate' => 3, 'UseNPPatrol' => true, 'UseFilePatrol' => true, 'Feed' => true, 'FeedLimit' => 50, 'FeedCacheTimeout' => 60, 'FeedDiffCutoff' => 32768, 'OverrideSiteFeed' => [ ], 'FeedClasses' => [ 'rss' => 'MediaWiki\\Feed\\RSSFeed', 'atom' => 'MediaWiki\\Feed\\AtomFeed', ], 'AdvertisedFeedTypes' => [ 'atom', ], 'RCShowWatchingUsers' => false, 'RCShowChangedSize' => true, 'RCChangedSizeThreshold' => 500, 'ShowUpdatedMarker' => true, 'DisableAnonTalk' => false, 'UseTagFilter' => true, 'SoftwareTags' => [ 'mw-contentmodelchange' => true, 'mw-new-redirect' => true, 'mw-removed-redirect' => true, 'mw-changed-redirect-target' => true, 'mw-blank' => true, 'mw-replace' => true, 'mw-recreated' => true, 'mw-rollback' => true, 'mw-undo' => true, 'mw-manual-revert' => true, 'mw-reverted' => true, 'mw-server-side-upload' => true, 'mw-ipblock-appeal' => true, 'mw-edited-other-users-js' => true, 'mw-edited-other-users-css' => true, ], 'RestrictedTagViewRights' => [ ], 'UnwatchedPageThreshold' => false, 'RecentChangesFlags' => [ 'newpage' => [ 'letter' => 'newpageletter', 'title' => 'recentchanges-label-newpage', 'legend' => 'recentchanges-legend-newpage', 'grouping' => 'any', ], 'minor' => [ 'letter' => 'minoreditletter', 'title' => 'recentchanges-label-minor', 'legend' => 'recentchanges-legend-minor', 'class' => 'minoredit', 'grouping' => 'all', ], 'bot' => [ 'letter' => 'boteditletter', 'title' => 'recentchanges-label-bot', 'legend' => 'recentchanges-legend-bot', 'class' => 'botedit', 'grouping' => 'all', ], 'unpatrolled' => [ 'letter' => 'unpatrolledletter', 'title' => 'recentchanges-label-unpatrolled', 'legend' => 'recentchanges-legend-unpatrolled', 'grouping' => 'any', ], ], 'WatchlistExpiry' => false, 'EnableWatchstarPopover' => false, 'EnableWatchlistLabels' => false, 'WatchlistLabelsMaxPerUser' => 100, 'WatchlistPurgeRate' => 0.1, 'WatchlistExpiryMaxDuration' => '1 year', 'EnableChangesListQueryPartitioning' => false, 'RightsPage' => null, 'RightsUrl' => null, 'RightsText' => null, 'RightsIcon' => null, 'UseCopyrightUpload' => false, 'MaxCredits' => 0, 'ShowCreditsIfMax' => true, 'ImportSources' => [ ], 'ImportTargetNamespace' => null, 'ExportAllowHistory' => true, 'ExportMaxHistory' => 0, 'ExportAllowListContributors' => false, 'ExportMaxLinkDepth' => 0, 'ExportFromNamespaces' => false, 'ExportAllowAll' => false, 'ExportPagelistLimit' => 5000, 'XmlDumpSchemaVersion' => '0.11', 'WikiFarmSettingsDirectory' => null, 'WikiFarmSettingsExtension' => 'yaml', 'ExtensionFunctions' => [ ], 'ExtensionMessagesFiles' => [ ], 'MessagesDirs' => [ ], 'TranslationAliasesDirs' => [ ], 'ExtensionEntryPointListFiles' => [ ], 'EnableParserLimitReporting' => true, 'ValidSkinNames' => [ ], 'SpecialPages' => [ ], 'ExtensionCredits' => [ ], 'Hooks' => [ ], 'ServiceWiringFiles' => [ ], 'JobClasses' => [ 'deletePage' => 'MediaWiki\\Page\\DeletePageJob', 'refreshLinks' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'deleteLinks' => 'MediaWiki\\Page\\DeleteLinksJob', 'htmlCacheUpdate' => 'MediaWiki\\JobQueue\\Jobs\\HTMLCacheUpdateJob', 'sendMail' => [ 'class' => 'MediaWiki\\Mail\\EmaillingJob', 'services' => [ 'Emailer', ], ], 'enotifNotify' => [ 'class' => 'MediaWiki\\RecentChanges\\RecentChangeNotifyJob', 'services' => [ 'RecentChangeLookup', ], ], 'fixDoubleRedirect' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\DoubleRedirectJob', 'services' => [ 'RevisionLookup', 'MagicWordFactory', 'WikiPageFactory', ], 'needsPage' => true, ], 'AssembleUploadChunks' => 'MediaWiki\\JobQueue\\Jobs\\AssembleUploadChunksJob', 'PublishStashedFile' => 'MediaWiki\\JobQueue\\Jobs\\PublishStashedFileJob', 'ThumbnailRender' => 'MediaWiki\\JobQueue\\Jobs\\ThumbnailRenderJob', 'UploadFromUrl' => 'MediaWiki\\JobQueue\\Jobs\\UploadFromUrlJob', 'recentChangesUpdate' => 'MediaWiki\\RecentChanges\\RecentChangesUpdateJob', 'refreshLinksPrioritized' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'refreshLinksDynamic' => 'MediaWiki\\JobQueue\\Jobs\\RefreshLinksJob', 'activityUpdateJob' => 'MediaWiki\\Watchlist\\ActivityUpdateJob', 'categoryMembershipChange' => [ 'class' => 'MediaWiki\\RecentChanges\\CategoryMembershipChangeJob', 'services' => [ 'RecentChangeFactory', ], ], 'CategoryCountUpdateJob' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\CategoryCountUpdateJob', 'services' => [ 'ConnectionProvider', 'NamespaceInfo', ], ], 'clearUserWatchlist' => 'MediaWiki\\Watchlist\\ClearUserWatchlistJob', 'watchlistExpiry' => 'MediaWiki\\Watchlist\\WatchlistExpiryJob', 'cdnPurge' => 'MediaWiki\\JobQueue\\Jobs\\CdnPurgeJob', 'userGroupExpiry' => 'MediaWiki\\User\\UserGroupExpiryJob', 'clearWatchlistNotifications' => 'MediaWiki\\Watchlist\\ClearWatchlistNotificationsJob', 'userOptionsUpdate' => 'MediaWiki\\User\\Options\\UserOptionsUpdateJob', 'revertedTagUpdate' => 'MediaWiki\\JobQueue\\Jobs\\RevertedTagUpdateJob', 'null' => 'MediaWiki\\JobQueue\\Jobs\\NullJob', 'userEditCountInit' => 'MediaWiki\\User\\UserEditCountInitJob', 'parsoidCachePrewarm' => [ 'class' => 'MediaWiki\\JobQueue\\Jobs\\ParsoidCachePrewarmJob', 'services' => [ 'ParserOutputAccess', 'PageStore', 'RevisionLookup', 'ParsoidSiteConfig', ], 'needsPage' => false, ], 'renameUserTable' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserTableJob', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', ], ], 'renameUserDerived' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserDerivedJob', 'services' => [ 'RenameUserFactory', 'UserFactory', ], ], 'renameUser' => [ 'class' => 'MediaWiki\\RenameUser\\Job\\RenameUserTableJob', 'services' => [ 'MainConfig', 'DBLoadBalancerFactory', ], ], ], 'JobTypesExcludedFromDefaultQueue' => [ 'AssembleUploadChunks', 'PublishStashedFile', 'UploadFromUrl', ], 'JobBackoffThrottling' => [ ], 'JobTypeConf' => [ 'default' => [ 'class' => 'MediaWiki\\JobQueue\\JobQueueDB', 'order' => 'random', 'claimTTL' => 3600, ], ], 'JobQueueIncludeInMaxLagFactor' => false, 'SpecialPageCacheUpdates' => [ 'Statistics' => [ 'MediaWiki\\Deferred\\SiteStatsUpdate', 'cacheUpdate', ], ], 'PagePropLinkInvalidations' => [ 'hiddencat' => 'categorylinks', ], 'CategoryMagicGallery' => true, 'CategoryPagingLimit' => 200, 'CategoryCollation' => 'uppercase', 'TempCategoryCollations' => [ ], 'SortedCategories' => false, 'TrackingCategories' => [ ], 'LogTypes' => [ '', 'block', 'protect', 'rights', 'delete', 'upload', 'move', 'import', 'interwiki', 'patrol', 'merge', 'suppress', 'tag', 'managetags', 'contentmodel', 'renameuser', ], 'LogRestrictions' => [ 'suppress' => 'suppressionlog', ], 'FilterLogTypes' => [ 'patrol' => true, 'tag' => true, 'newusers' => false, ], 'LogNames' => [ '' => 'all-logs-page', 'block' => 'blocklogpage', 'protect' => 'protectlogpage', 'rights' => 'rightslog', 'delete' => 'dellogpage', 'upload' => 'uploadlogpage', 'move' => 'movelogpage', 'import' => 'importlogpage', 'patrol' => 'patrol-log-page', 'merge' => 'mergelog', 'suppress' => 'suppressionlog', ], 'LogHeaders' => [ '' => 'alllogstext', 'block' => 'blocklogtext', 'delete' => 'dellogpagetext', 'import' => 'importlogpagetext', 'merge' => 'mergelogpagetext', 'move' => 'movelogpagetext', 'patrol' => 'patrol-log-header', 'protect' => 'protectlogtext', 'rights' => 'rightslogtext', 'suppress' => 'suppressionlogtext', 'upload' => 'uploadlogpagetext', ], 'LogActions' => [ ], 'LogActionsHandlers' => [ 'block/block' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'block/reblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'block/unblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'contentmodel/change' => 'MediaWiki\\Logging\\ContentModelLogFormatter', 'contentmodel/new' => 'MediaWiki\\Logging\\ContentModelLogFormatter', 'delete/delete' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/delete_redir' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/delete_redir2' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/event' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/restore' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'delete/revision' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'import/interwiki' => 'MediaWiki\\Logging\\ImportLogFormatter', 'import/upload' => 'MediaWiki\\Logging\\ImportLogFormatter', 'interwiki/iw_add' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'interwiki/iw_delete' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'interwiki/iw_edit' => 'MediaWiki\\Logging\\InterwikiLogFormatter', 'managetags/activate' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/create' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/deactivate' => 'MediaWiki\\Logging\\LogFormatter', 'managetags/delete' => 'MediaWiki\\Logging\\LogFormatter', 'merge/merge' => [ 'class' => 'MediaWiki\\Logging\\MergeLogFormatter', 'services' => [ 'TitleParser', ], ], 'merge/merge-into' => [ 'class' => 'MediaWiki\\Logging\\MergeLogFormatter', 'services' => [ 'TitleParser', ], ], 'move/move' => [ 'class' => 'MediaWiki\\Logging\\MoveLogFormatter', 'services' => [ 'TitleParser', ], ], 'move/move_redir' => [ 'class' => 'MediaWiki\\Logging\\MoveLogFormatter', 'services' => [ 'TitleParser', ], ], 'patrol/patrol' => 'MediaWiki\\Logging\\PatrolLogFormatter', 'patrol/autopatrol' => 'MediaWiki\\Logging\\PatrolLogFormatter', 'protect/modify' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/move_prot' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/protect' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'protect/unprotect' => [ 'class' => 'MediaWiki\\Logging\\ProtectLogFormatter', 'services' => [ 'TitleParser', ], ], 'renameuser/renameuser' => [ 'class' => 'MediaWiki\\Logging\\RenameuserLogFormatter', 'services' => [ 'TitleParser', ], ], 'rights/autopromote' => 'MediaWiki\\Logging\\RightsLogFormatter', 'rights/rights' => 'MediaWiki\\Logging\\RightsLogFormatter', 'suppress/block' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'suppress/delete' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'suppress/event' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'suppress/reblock' => [ 'class' => 'MediaWiki\\Logging\\BlockLogFormatter', 'services' => [ 'TitleParser', 'NamespaceInfo', ], ], 'suppress/revision' => 'MediaWiki\\Logging\\DeleteLogFormatter', 'tag/update' => 'MediaWiki\\Logging\\TagLogFormatter', 'upload/overwrite' => 'MediaWiki\\Logging\\UploadLogFormatter', 'upload/revert' => 'MediaWiki\\Logging\\UploadLogFormatter', 'upload/upload' => 'MediaWiki\\Logging\\UploadLogFormatter', ], 'ActionFilteredLogs' => [ 'block' => [ 'block' => [ 'block', ], 'reblock' => [ 'reblock', ], 'unblock' => [ 'unblock', ], ], 'contentmodel' => [ 'change' => [ 'change', ], 'new' => [ 'new', ], ], 'delete' => [ 'delete' => [ 'delete', ], 'delete_redir' => [ 'delete_redir', 'delete_redir2', ], 'restore' => [ 'restore', ], 'event' => [ 'event', ], 'revision' => [ 'revision', ], ], 'import' => [ 'interwiki' => [ 'interwiki', ], 'upload' => [ 'upload', ], ], 'managetags' => [ 'create' => [ 'create', ], 'delete' => [ 'delete', ], 'activate' => [ 'activate', ], 'deactivate' => [ 'deactivate', ], ], 'move' => [ 'move' => [ 'move', ], 'move_redir' => [ 'move_redir', ], ], 'newusers' => [ 'create' => [ 'create', 'newusers', ], 'create2' => [ 'create2', ], 'autocreate' => [ 'autocreate', ], 'byemail' => [ 'byemail', ], ], 'protect' => [ 'protect' => [ 'protect', ], 'modify' => [ 'modify', ], 'unprotect' => [ 'unprotect', ], 'move_prot' => [ 'move_prot', ], ], 'rights' => [ 'rights' => [ 'rights', ], 'autopromote' => [ 'autopromote', ], ], 'suppress' => [ 'event' => [ 'event', ], 'revision' => [ 'revision', ], 'delete' => [ 'delete', ], 'block' => [ 'block', ], 'reblock' => [ 'reblock', ], ], 'upload' => [ 'upload' => [ 'upload', ], 'overwrite' => [ 'overwrite', ], 'revert' => [ 'revert', ], ], ], 'NewUserLog' => true, 'PageCreationLog' => true, 'AllowSpecialInclusion' => true, 'DisableQueryPageUpdate' => false, 'CountCategorizedImagesAsUsed' => false, 'MaxRedirectLinksRetrieved' => 500, 'RangeContributionsCIDRLimit' => [ 'IPv4' => 16, 'IPv6' => 32, ], 'Actions' => [ ], 'DefaultRobotPolicy' => 'index,follow', 'NamespaceRobotPolicies' => [ ], 'ArticleRobotPolicies' => [ ], 'ExemptFromUserRobotsControl' => null, 'DebugAPI' => false, 'APIModules' => [ ], 'APIFormatModules' => [ ], 'APIMetaModules' => [ ], 'APIPropModules' => [ ], 'APIListModules' => [ ], 'APIMaxDBRows' => 5000, 'APIMaxResultSize' => 8388608, 'APIMaxUncachedDiffs' => 1, 'APIMaxLagThreshold' => 7, 'APICacheHelpTimeout' => 3600, 'APIUselessQueryPages' => [ 'MIMEsearch', 'LinkSearch', ], 'AjaxLicensePreview' => true, 'CrossSiteAJAXdomains' => [ ], 'CrossSiteAJAXdomainExceptions' => [ ], 'AllowedCorsHeaders' => [ 'Accept', 'Accept-Language', 'Content-Language', 'Content-Type', 'Accept-Encoding', 'DNT', 'Origin', 'User-Agent', 'Api-User-Agent', 'Promise-Non-Write-API-Action', 'Access-Control-Max-Age', 'Authorization', ], 'RestAPIAdditionalRouteFiles' => [ ], 'RestSandboxSpecs' => [ ], 'RestLocalModuleTestBaseUrl' => null, 'RestModuleOverrides' => [ ], 'RestExternalModules' => [ ], 'MaxShellMemory' => 307200, 'MaxShellFileSize' => 102400, 'MaxShellTime' => 180, 'MaxShellWallClockTime' => 180, 'ShellCgroup' => false, 'PhpCli' => '/usr/bin/php', 'ShellRestrictionMethod' => 'autodetect', 'ShellboxUrls' => [ 'default' => null, ], 'ShellboxSecretKey' => null, 'ShellboxShell' => '/bin/sh', 'HTTPTimeout' => 25, 'HTTPConnectTimeout' => 5.0, 'HTTPMaxTimeout' => 0, 'HTTPMaxConnectTimeout' => 0, 'HTTPImportTimeout' => 25, 'AsyncHTTPTimeout' => 25, 'HTTPProxy' => '', 'LocalVirtualHosts' => [ ], 'LocalHTTPProxy' => false, 'AllowExternalReqID' => false, 'GenerateReqIDFormat' => 'rand24', 'JobRunRate' => 1, 'RunJobsAsync' => false, 'UpdateRowsPerJob' => 300, 'UpdateRowsPerQuery' => 100, 'RedirectOnLogin' => null, 'VirtualRestConfig' => [ 'paths' => [ ], 'modules' => [ ], 'global' => [ 'timeout' => 360, 'forwardCookies' => false, 'HTTPProxy' => null, ], ], 'EventRelayerConfig' => [ 'default' => [ 'class' => 'Wikimedia\\EventRelayer\\EventRelayerNull', ], ], 'Pingback' => false, 'OriginTrials' => [ ], 'ReportToExpiry' => 86400, 'ReportToEndpoints' => [ ], 'FeaturePolicyReportOnly' => [ ], 'SkinsPreferred' => [ 'vector-2022', 'vector', ], 'SpecialContributeSkinsEnabled' => [ ], 'SpecialContributeNewPageTarget' => null, 'EnableEditRecovery' => false, 'EditRecoveryExpiry' => 2592000, 'UseCodexSpecialBlock' => false, 'ShowLogoutConfirmation' => false, 'EnableProtectionIndicators' => true, 'OutputPipelineStages' => [ ], 'FeatureShutdown' => [ ], 'CloneArticleParserOutput' => true, 'UseLeximorph' => false, 'UsePostprocCacheLegacy' => false, 'UsePostprocCacheParsoid' => true, 'ParserOptionsLogUnsafeSampleRate' => 0, 'ReturnExperimentalPFragmentTypes' => [ ], ], 'type' => [ 'ConfigRegistry' => 'object', 'AssumeProxiesUseDefaultProtocolPorts' => 'boolean', 'ForceHTTPS' => 'boolean', 'ExtensionDirectory' => [ 'string', 'null', ], 'StyleDirectory' => [ 'string', 'null', ], 'UploadDirectory' => [ 'string', 'boolean', 'null', ], 'Logos' => [ 'object', 'boolean', ], 'ReferrerPolicy' => [ 'array', 'string', 'boolean', ], 'ActionPaths' => 'object', 'MainPageIsDomainRoot' => 'boolean', 'ImgAuthUrlPathMap' => 'object', 'LocalFileRepo' => 'object', 'ForeignFileRepos' => 'array', 'UseSharedUploads' => 'boolean', 'SharedUploadDirectory' => [ 'string', 'null', ], 'SharedUploadPath' => [ 'string', 'null', ], 'HashedSharedUploadDirectory' => 'boolean', 'FetchCommonsDescriptions' => 'boolean', 'SharedUploadDBname' => [ 'boolean', 'string', ], 'SharedUploadDBprefix' => 'string', 'CacheSharedUploads' => 'boolean', 'ForeignUploadTargets' => 'array', 'UploadDialog' => 'object', 'FileBackends' => 'object', 'LockManagers' => 'array', 'DefaultLockManager' => [ 'string', 'null', ], 'CopyUploadsDomains' => 'array', 'CopyUploadTimeout' => [ 'boolean', 'integer', ], 'SharedThumbnailScriptPath' => [ 'string', 'boolean', ], 'HashedUploadDirectory' => 'boolean', 'CSPUploadEntryPoint' => 'boolean', 'FileExtensions' => 'array', 'ProhibitedFileExtensions' => 'array', 'MimeTypeExclusions' => 'array', 'TrustedMediaFormats' => 'array', 'MediaHandlers' => 'object', 'NativeImageLazyLoading' => 'boolean', 'ParserTestMediaHandlers' => 'object', 'MaxInterlacingAreas' => 'object', 'SVGConverters' => 'object', 'SVGNativeRendering' => [ 'string', 'boolean', ], 'MaxImageArea' => [ 'string', 'integer', 'boolean', ], 'TiffThumbnailType' => 'array', 'GenerateThumbnailOnParse' => 'boolean', 'EnableAutoRotation' => [ 'boolean', 'null', ], 'Antivirus' => [ 'string', 'null', ], 'AntivirusSetup' => 'object', 'MimeDetectorCommand' => [ 'string', 'null', ], 'XMLMimeTypes' => 'object', 'ImageLimits' => 'array', 'ThumbLimits' => 'array', 'ThumbnailNamespaces' => 'array', 'ThumbnailSteps' => [ 'array', 'null', ], 'ThumbnailBuckets' => [ 'array', 'null', ], 'UploadThumbnailRenderMap' => 'object', 'GalleryOptions' => 'object', 'DjvuDump' => [ 'string', 'null', ], 'DjvuRenderer' => [ 'string', 'null', ], 'DjvuTxt' => [ 'string', 'null', ], 'DjvuPostProcessor' => [ 'string', 'null', ], 'RestTermsOfServiceUrl' => [ 'string', 'null', ], 'SMTP' => [ 'boolean', 'object', ], 'EnotifFromEditor' => 'boolean', 'EmailConfirmationBanner' => 'boolean', 'EnotifRevealEditorAddress' => 'boolean', 'UsersNotifiedOnAllChanges' => 'object', 'DBmwschema' => [ 'string', 'null', ], 'SharedTables' => 'array', 'DBservers' => [ 'boolean', 'array', ], 'LBFactoryConf' => 'object', 'LocalDatabases' => 'array', 'VirtualDomainsMapping' => 'object', 'FileSchemaMigrationStage' => 'integer', 'ExternalLinksDomainGaps' => 'object', 'ContentHandlers' => 'object', 'NamespaceContentModels' => 'object', 'TextModelsToParse' => 'array', 'ExternalStores' => 'array', 'ExternalServers' => 'object', 'DefaultExternalStore' => [ 'array', 'boolean', ], 'RevisionCacheExpiry' => 'integer', 'PageLanguageUseDB' => 'boolean', 'DiffEngine' => [ 'string', 'null', ], 'ExternalDiffEngine' => [ 'string', 'boolean', ], 'Wikidiff2Options' => 'object', 'RequestTimeLimit' => [ 'integer', 'null', ], 'CriticalSectionTimeLimit' => 'number', 'PoolCounterConf' => [ 'object', 'null', ], 'PoolCountClientConf' => 'object', 'MaxUserDBWriteDuration' => [ 'integer', 'boolean', ], 'MaxJobDBWriteDuration' => [ 'integer', 'boolean', ], 'MultiShardSiteStats' => 'boolean', 'ObjectCaches' => 'object', 'WANObjectCache' => 'object', 'MicroStashType' => [ 'string', 'integer', ], 'ParsoidCacheConfig' => 'object', 'ParsoidSelectiveUpdateSampleRate' => 'integer', 'ParserCacheFilterConfig' => 'object', 'ChronologyProtectorSecret' => 'string', 'SuspiciousIpExpiry' => [ 'integer', 'boolean', ], 'MemCachedServers' => 'array', 'LocalisationCacheConf' => 'object', 'ExtensionInfoMTime' => [ 'integer', 'boolean', ], 'CdnServers' => 'object', 'CdnServersNoPurge' => 'object', 'HTCPRouting' => 'object', 'GrammarForms' => 'object', 'ExtraInterlanguageLinkPrefixes' => 'array', 'InterlanguageLinkCodeMap' => 'object', 'ExtraLanguageNames' => 'object', 'ExtraLanguageCodes' => 'object', 'DummyLanguageCodes' => 'object', 'DisabledVariants' => 'object', 'ForceUIMsgAsContentMsg' => 'object', 'RawHtmlMessages' => 'array', 'OverrideUcfirstCharacters' => 'object', 'XhtmlNamespaces' => 'object', 'BrowserFormatDetection' => 'string', 'SkinMetaTags' => 'object', 'SkipSkins' => 'object', 'FragmentMode' => 'array', 'FooterIcons' => 'object', 'InterwikiLogoOverride' => 'array', 'ResourceModules' => 'object', 'ResourceModuleSkinStyles' => 'object', 'ResourceLoaderSources' => 'object', 'ResourceLoaderMaxage' => 'object', 'ResourceLoaderMaxQueryLength' => [ 'integer', 'boolean', ], 'CanonicalNamespaceNames' => 'object', 'ExtraNamespaces' => 'object', 'ExtraGenderNamespaces' => 'object', 'NamespaceAliases' => 'object', 'CapitalLinkOverrides' => 'object', 'NamespacesWithSubpages' => 'object', 'NamespacesWithoutAutoSummaries' => 'array', 'ContentNamespaces' => 'array', 'ShortPagesNamespaceExclusions' => 'array', 'ExtraSignatureNamespaces' => 'array', 'InvalidRedirectTargets' => 'array', 'LocalInterwikis' => 'array', 'InterwikiCache' => [ 'boolean', 'object', ], 'SiteTypes' => 'object', 'UrlProtocols' => 'array', 'TidyConfig' => 'object', 'ParsoidSettings' => 'object', 'ParsoidExperimentalParserFunctionOutput' => 'boolean', 'NoFollowNsExceptions' => 'array', 'NoFollowDomainExceptions' => 'array', 'ExternalLinksIgnoreDomains' => 'array', 'EnableMagicLinks' => 'object', 'ManualRevertSearchRadius' => 'integer', 'RevertedTagMaxDepth' => 'integer', 'CentralIdLookupProviders' => 'object', 'CentralIdLookupProvider' => 'string', 'UserRegistrationProviders' => 'object', 'PasswordPolicy' => 'object', 'AuthManagerConfig' => [ 'object', 'null', ], 'AuthManagerAutoConfig' => 'object', 'RememberMe' => 'string', 'ReauthenticateTime' => 'object', 'ChangeCredentialsBlacklist' => 'array', 'RemoveCredentialsBlacklist' => 'array', 'PasswordConfig' => 'object', 'PasswordResetRoutes' => 'object', 'SignatureAllowedLintErrors' => 'array', 'ReservedUsernames' => 'array', 'DefaultUserOptions' => 'object', 'ConditionalUserOptions' => 'object', 'HiddenPrefs' => 'array', 'UserJsPrefLimit' => 'integer', 'AuthenticationTokenVersion' => [ 'string', 'null', ], 'SessionProviders' => 'object', 'AutoCreateTempUser' => 'object', 'AutoblockExemptions' => 'array', 'BlockCIDRLimit' => 'object', 'EnableMultiBlocks' => 'boolean', 'GroupPermissions' => 'object', 'PrivilegedGroups' => 'array', 'RevokePermissions' => 'object', 'GroupInheritsPermissions' => 'object', 'ImplicitGroups' => 'array', 'GroupsAddToSelf' => 'object', 'GroupsRemoveFromSelf' => 'object', 'RestrictedGroups' => 'object', 'UserRequirementsPrivateConditions' => 'array', 'RestrictionTypes' => 'array', 'RestrictionLevels' => 'array', 'CascadingRestrictionLevels' => 'array', 'SemiprotectedRestrictionLevels' => 'array', 'NamespaceProtection' => 'object', 'RestrictUserPageEditing' => 'boolean', 'NonincludableNamespaces' => 'object', 'Autopromote' => 'object', 'AutopromoteOnce' => 'object', 'AutopromoteOnceRCExcludedGroups' => 'array', 'AddGroups' => 'object', 'RemoveGroups' => 'object', 'AvailableRights' => 'array', 'ImplicitRights' => 'array', 'AccountCreationThrottle' => [ 'integer', 'array', ], 'TempAccountCreationThrottle' => 'array', 'TempAccountNameAcquisitionThrottle' => 'array', 'SpamRegex' => 'array', 'SummarySpamRegex' => 'array', 'DnsBlacklistUrls' => 'array', 'ProxyList' => [ 'string', 'array', ], 'ProxyWhitelist' => 'array', 'SoftBlockRanges' => 'array', 'RateLimits' => 'object', 'RateLimitsExcludedIPs' => 'array', 'ExternalQuerySources' => 'object', 'PasswordAttemptThrottle' => 'array', 'GrantPermissions' => 'object', 'GrantPermissionGroups' => 'object', 'GrantRiskGroups' => 'object', 'EnableBotPasswords' => 'boolean', 'BotPasswordsCluster' => [ 'string', 'boolean', ], 'BotPasswordsDatabase' => [ 'string', 'boolean', ], 'BotPasswordsLimit' => 'integer', 'ReauthenticateForActions' => 'object', 'CSPHeader' => [ 'boolean', 'object', ], 'CSPReportOnlyHeader' => [ 'boolean', 'object', ], 'CSPUseReportURIDirective' => [ 'boolean', 'object', ], 'CSPFalsePositiveUrls' => 'object', 'AllowCrossOrigin' => 'boolean', 'RestAllowCrossOriginCookieAuth' => 'boolean', 'CookieSameSite' => [ 'string', 'null', ], 'CacheVaryCookies' => 'array', 'TrxProfilerLimits' => 'object', 'DebugLogGroups' => 'object', 'MWLoggerDefaultSpi' => 'object', 'Profiler' => 'object', 'StatsTarget' => [ 'string', 'null', ], 'StatsFormat' => [ 'string', 'null', ], 'StatsPrefix' => 'string', 'OpenTelemetryConfig' => [ 'object', 'null', ], 'OpenSearchTemplates' => 'object', 'NamespacesToBeSearchedDefault' => 'object', 'SitemapNamespaces' => [ 'boolean', 'array', ], 'SitemapNamespacesPriorities' => [ 'boolean', 'object', ], 'SitemapApiConfig' => 'object', 'SpecialSearchFormOptions' => 'object', 'SearchMatchRedirectPreference' => 'boolean', 'SearchRunSuggestedQuery' => 'boolean', 'PreviewOnOpenNamespaces' => 'object', 'ReadOnlyWatchedItemStore' => 'boolean', 'GitRepositoryViewers' => 'object', 'InstallerInitialPages' => 'array', 'RCLinkLimits' => 'array', 'RCLinkDays' => 'array', 'RCFeeds' => 'object', 'OverrideSiteFeed' => 'object', 'FeedClasses' => 'object', 'AdvertisedFeedTypes' => 'array', 'SoftwareTags' => 'object', 'RestrictedTagViewRights' => 'object', 'RecentChangesFlags' => 'object', 'WatchlistExpiry' => 'boolean', 'EnableWatchstarPopover' => 'boolean', 'EnableWatchlistLabels' => 'boolean', 'WatchlistLabelsMaxPerUser' => 'integer', 'WatchlistPurgeRate' => 'number', 'WatchlistExpiryMaxDuration' => [ 'string', 'null', ], 'EnableChangesListQueryPartitioning' => 'boolean', 'ImportSources' => 'object', 'ExtensionFunctions' => 'array', 'ExtensionMessagesFiles' => 'object', 'MessagesDirs' => 'object', 'TranslationAliasesDirs' => 'object', 'ExtensionEntryPointListFiles' => 'object', 'ValidSkinNames' => 'object', 'SpecialPages' => 'object', 'ExtensionCredits' => 'object', 'Hooks' => 'object', 'ServiceWiringFiles' => 'array', 'JobClasses' => 'object', 'JobTypesExcludedFromDefaultQueue' => 'array', 'JobBackoffThrottling' => 'object', 'JobTypeConf' => 'object', 'SpecialPageCacheUpdates' => 'object', 'PagePropLinkInvalidations' => 'object', 'TempCategoryCollations' => 'array', 'SortedCategories' => 'boolean', 'TrackingCategories' => 'array', 'LogTypes' => 'array', 'LogRestrictions' => 'object', 'FilterLogTypes' => 'object', 'LogNames' => 'object', 'LogHeaders' => 'object', 'LogActions' => 'object', 'LogActionsHandlers' => 'object', 'ActionFilteredLogs' => 'object', 'RangeContributionsCIDRLimit' => 'object', 'Actions' => 'object', 'NamespaceRobotPolicies' => 'object', 'ArticleRobotPolicies' => 'object', 'ExemptFromUserRobotsControl' => [ 'array', 'null', ], 'APIModules' => 'object', 'APIFormatModules' => 'object', 'APIMetaModules' => 'object', 'APIPropModules' => 'object', 'APIListModules' => 'object', 'APIUselessQueryPages' => 'array', 'CrossSiteAJAXdomains' => 'object', 'CrossSiteAJAXdomainExceptions' => 'object', 'AllowedCorsHeaders' => 'array', 'RestAPIAdditionalRouteFiles' => 'array', 'RestSandboxSpecs' => 'object', 'RestLocalModuleTestBaseUrl' => [ 'string', 'null', ], 'RestModuleOverrides' => 'object', 'RestExternalModules' => 'object', 'ShellRestrictionMethod' => [ 'string', 'boolean', ], 'ShellboxUrls' => 'object', 'ShellboxSecretKey' => [ 'string', 'null', ], 'ShellboxShell' => [ 'string', 'null', ], 'HTTPTimeout' => 'number', 'HTTPConnectTimeout' => 'number', 'HTTPMaxTimeout' => 'number', 'HTTPMaxConnectTimeout' => 'number', 'LocalVirtualHosts' => 'object', 'LocalHTTPProxy' => [ 'string', 'boolean', ], 'GenerateReqIDFormat' => 'string', 'VirtualRestConfig' => 'object', 'EventRelayerConfig' => 'object', 'Pingback' => 'boolean', 'OriginTrials' => 'array', 'ReportToExpiry' => 'integer', 'ReportToEndpoints' => 'array', 'FeaturePolicyReportOnly' => 'array', 'SkinsPreferred' => 'array', 'SpecialContributeSkinsEnabled' => 'array', 'SpecialContributeNewPageTarget' => [ 'string', 'null', ], 'EnableEditRecovery' => 'boolean', 'EditRecoveryExpiry' => 'integer', 'UseCodexSpecialBlock' => 'boolean', 'ShowLogoutConfirmation' => 'boolean', 'EnableProtectionIndicators' => 'boolean', 'OutputPipelineStages' => 'object', 'FeatureShutdown' => 'array', 'CloneArticleParserOutput' => 'boolean', 'UseLeximorph' => 'boolean', 'UsePostprocCacheLegacy' => 'boolean', 'UsePostprocCacheParsoid' => 'boolean', 'ParserOptionsLogUnsafeSampleRate' => 'integer', 'ReturnExperimentalPFragmentTypes' => 'array', ], 'mergeStrategy' => [ 'TiffThumbnailType' => 'replace', 'LBFactoryConf' => 'replace', 'InterwikiCache' => 'replace', 'PasswordPolicy' => 'array_replace_recursive', 'AuthManagerAutoConfig' => 'array_plus_2d', 'GroupPermissions' => 'array_plus_2d', 'RevokePermissions' => 'array_plus_2d', 'AddGroups' => 'array_merge_recursive', 'RemoveGroups' => 'array_merge_recursive', 'RateLimits' => 'array_plus_2d', 'GrantPermissions' => 'array_plus_2d', 'MWLoggerDefaultSpi' => 'replace', 'Profiler' => 'replace', 'Hooks' => 'array_merge_recursive', 'RestModuleOverrides' => 'array_replace_recursive', 'RestExternalModules' => 'array_replace_recursive', 'VirtualRestConfig' => 'array_plus_2d', ], 'dynamicDefault' => [ 'UsePathInfo' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultUsePathInfo', ], ], 'Script' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultScript', ], ], 'LoadScript' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLoadScript', ], ], 'RestPath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultRestPath', ], ], 'StylePath' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultStylePath', ], ], 'LocalStylePath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocalStylePath', ], ], 'ExtensionAssetsPath' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultExtensionAssetsPath', ], ], 'ArticlePath' => [ 'use' => [ 'Script', 'UsePathInfo', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultArticlePath', ], ], 'UploadPath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultUploadPath', ], ], 'FileCacheDirectory' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultFileCacheDirectory', ], ], 'Logo' => [ 'use' => [ 'ResourceBasePath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLogo', ], ], 'DeletedDirectory' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultDeletedDirectory', ], ], 'ShowEXIF' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultShowEXIF', ], ], 'SharedPrefix' => [ 'use' => [ 'DBprefix', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultSharedPrefix', ], ], 'SharedSchema' => [ 'use' => [ 'DBmwschema', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultSharedSchema', ], ], 'DBerrorLogTZ' => [ 'use' => [ 'Localtimezone', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultDBerrorLogTZ', ], ], 'Localtimezone' => [ 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocaltimezone', ], ], 'LocalTZoffset' => [ 'use' => [ 'Localtimezone', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultLocalTZoffset', ], ], 'ResourceBasePath' => [ 'use' => [ 'ScriptPath', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultResourceBasePath', ], ], 'MetaNamespace' => [ 'use' => [ 'Sitename', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultMetaNamespace', ], ], 'CookieSecure' => [ 'use' => [ 'ForceHTTPS', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultCookieSecure', ], ], 'CookiePrefix' => [ 'use' => [ 'SharedDB', 'SharedPrefix', 'SharedTables', 'DBname', 'DBprefix', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultCookiePrefix', ], ], 'ReadOnlyFile' => [ 'use' => [ 'UploadDirectory', ], 'callback' => [ 'MediaWiki\\MainConfigSchema', 'getDefaultReadOnlyFile', ], ], ], ], 'config-schema' => [ 'UploadStashScalerBaseUrl' => [ 'deprecated' => 'since 1.36 Use thumbProxyUrl in $wgLocalFileRepo', ], 'IllegalFileChars' => [ 'deprecated' => 'since 1.41; no longer customizable', ], 'ThumbnailNamespaces' => [ 'items' => [ 'type' => 'integer', ], ], 'LocalDatabases' => [ 'items' => [ 'type' => 'string', ], ], 'ParserCacheFilterConfig' => [ 'additionalProperties' => [ 'type' => 'object', 'description' => 'A map of namespace IDs to filter definitions.', 'additionalProperties' => [ 'type' => 'object', 'description' => 'A map of filter names to values.', 'properties' => [ 'minCpuTime' => [ 'type' => 'number', ], ], ], ], ], 'RawHtmlMessages' => [ 'items' => [ 'type' => 'string', ], ], 'InterwikiLogoOverride' => [ 'items' => [ 'type' => 'string', ], ], 'LegalTitleChars' => [ 'deprecated' => 'since 1.41; use Extension:TitleBlacklist to customize', ], 'ReauthenticateTime' => [ 'additionalProperties' => [ 'type' => 'integer', ], ], 'ChangeCredentialsBlacklist' => [ 'items' => [ 'type' => 'string', ], ], 'RemoveCredentialsBlacklist' => [ 'items' => [ 'type' => 'string', ], ], 'GroupPermissions' => [ 'additionalProperties' => [ 'type' => 'object', 'additionalProperties' => [ 'type' => 'boolean', ], ], ], 'GroupInheritsPermissions' => [ 'additionalProperties' => [ 'type' => 'string', ], ], 'AvailableRights' => [ 'items' => [ 'type' => 'string', ], ], 'ImplicitRights' => [ 'items' => [ 'type' => 'string', ], ], 'SoftBlockRanges' => [ 'items' => [ 'type' => 'string', ], ], 'ExternalQuerySources' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'enabled' => [ 'type' => 'boolean', 'default' => false, ], 'url' => [ 'type' => 'string', 'format' => 'uri', ], 'timeout' => [ 'type' => 'integer', 'default' => 10, ], ], 'required' => [ 'enabled', 'url', ], 'additionalProperties' => false, ], ], 'GrantPermissions' => [ 'additionalProperties' => [ 'type' => 'object', 'additionalProperties' => [ 'type' => 'boolean', ], ], ], 'GrantPermissionGroups' => [ 'additionalProperties' => [ 'type' => 'string', ], ], 'SitemapNamespacesPriorities' => [ 'deprecated' => 'since 1.45 and ignored', ], 'SitemapApiConfig' => [ 'additionalProperties' => [ 'enabled' => [ 'type' => 'bool', ], 'sitemapsPerIndex' => [ 'type' => 'int', ], 'pagesPerSitemap' => [ 'type' => 'int', ], 'expiry' => [ 'type' => 'int', ], ], ], 'SoftwareTags' => [ 'additionalProperties' => [ 'type' => 'boolean', ], ], 'UseCopyrightUpload' => [ 'deprecated' => 'since 1.47 This feature is being removed.', ], 'JobBackoffThrottling' => [ 'additionalProperties' => [ 'type' => 'number', ], ], 'JobTypeConf' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'class' => [ 'type' => 'string', ], 'order' => [ 'type' => 'string', ], 'claimTTL' => [ 'type' => 'integer', ], ], ], ], 'TrackingCategories' => [ 'deprecated' => 'since 1.25 Extensions should now register tracking categories using the new extension registration system.', ], 'RangeContributionsCIDRLimit' => [ 'additionalProperties' => [ 'type' => 'integer', ], ], 'RestSandboxSpecs' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'url' => [ 'type' => 'string', 'format' => 'url', ], 'name' => [ 'type' => 'string', ], 'file' => [ 'type' => 'string', ], 'msg' => [ 'type' => 'string', 'description' => 'a message key', ], ], ], ], 'RestModuleOverrides' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'mode' => [ 'type' => 'string', ], ], 'required' => [ 'mode', ], ], ], 'RestExternalModules' => [ 'additionalProperties' => [ 'type' => 'object', 'properties' => [ 'info' => [ 'type' => 'object', 'properties' => [ 'version' => [ 'type' => 'string', ], 'title' => [ 'type' => 'string', ], 'x-i18n-title' => [ 'type' => 'string', ], 'description' => [ 'type' => 'string', ], 'x-i18n-description' => [ 'type' => 'string', ], ], 'required' => [ 'version', ], ], 'base' => [ 'type' => 'string', 'format' => 'uri', ], 'spec' => [ 'type' => 'string', 'format' => 'uri', ], ], 'required' => [ 'info', 'base', 'spec', ], ], ], 'ShellboxUrls' => [ 'additionalProperties' => [ 'type' => [ 'string', 'boolean', 'null', ], ], ], ], 'obsolete-config' => [ 'MangleFlashPolicy' => 'Since 1.39; no longer has any effect.', 'EnableOpenSearchSuggest' => 'Since 1.35, no longer used', 'AutoloadAttemptLowercase' => 'Since 1.40; no longer has any effect.', ],]
Represents the target of a wiki link.
Service for page undelete actions.
Provide primary and replica IDatabase connections.
Interface for database access objects.
Result wrapper for grabbing data queried from an IDatabase object.
fetchObject()
Fetch the next row from the given result object, in object form.
numRows()
Get the number of rows in a result object.
setStashKey(string $keyValue)
doReauthRedirect(PermissionStatus $status, array $queryParams)
msg( $key,... $params)