MediaWiki master
SpecialUndelete.php
Go to the documentation of this file.
1<?php
7namespace MediaWiki\Specials;
8
32use MediaWiki\Page\UndeletePage;
54use OOUI\ActionFieldLayout;
55use OOUI\ButtonInputWidget;
56use OOUI\CheckboxInputWidget;
57use OOUI\DropdownInputWidget;
58use OOUI\FieldLayout;
59use OOUI\FieldsetLayout;
60use OOUI\FormLayout;
61use OOUI\HorizontalLayout;
62use OOUI\HtmlSnippet;
63use OOUI\Layout;
64use OOUI\PanelLayout;
65use OOUI\TextInputWidget;
66use OOUI\Widget;
70use Wikimedia\Timestamp\TimestampFormat as TS;
71
79
84 private const REVISION_HISTORY_LIMIT = 500;
85
87 private $mAction;
89 private $mTarget;
91 private $mTimestamp;
93 private $mRestore;
95 private $mRevdel;
97 private $mInvert;
99 private $mFilename;
101 private $mTargetTimestamp = [];
103 private $mAllowed;
105 private $mCanView;
107 private $mComment = '';
109 private $mToken;
111 private $mPreview;
113 private $mDiff;
115 private $mDiffOnly;
117 private $mUnsuppress;
119 private $mFileVersions = [];
121 private $mUndeleteTalk;
123 private $mHistoryOffset;
124
126 private $mTargetObj;
130 private $mSearchPrefix;
131
132 private LocalRepo $localRepo;
133
134 public function __construct(
135 private readonly PermissionManager $permissionManager,
136 private readonly RevisionStore $revisionStore,
137 private readonly RevisionRenderer $revisionRenderer,
138 private readonly IContentHandlerFactory $contentHandlerFactory,
139 private readonly NameTableStore $changeTagDefStore,
140 private readonly LinkBatchFactory $linkBatchFactory,
141 RepoGroup $repoGroup,
142 private readonly IConnectionProvider $dbProvider,
143 private readonly UserOptionsLookup $userOptionsLookup,
144 private readonly WikiPageFactory $wikiPageFactory,
145 private readonly SearchEngineFactory $searchEngineFactory,
146 private readonly UndeletePageFactory $undeletePageFactory,
147 private readonly ArchivedRevisionLookup $archivedRevisionLookup,
148 private readonly CommentFormatter $commentFormatter,
149 private readonly WatchlistManager $watchlistManager,
150 ) {
151 parent::__construct( 'Undelete' );
152 $this->localRepo = $repoGroup->getLocalRepo();
153 }
154
156 public function getRestriction(): string {
157 return 'deletedhistory';
158 }
159
161 public function doesWrites() {
162 return true;
163 }
164
165 private function loadRequest( ?string $par ) {
166 $request = $this->getRequest();
167 $user = $this->getUser();
168
169 $this->mAction = $request->getRawVal( 'action' );
170 if ( $par !== null && $par !== '' ) {
171 $this->mTarget = $par;
172 } else {
173 $this->mTarget = $request->getVal( 'target' );
174 }
175
176 $this->mTargetObj = null;
177
178 if ( $this->mTarget !== null && $this->mTarget !== '' ) {
179 $this->mTargetObj = Title::newFromText( $this->mTarget );
180 }
181
182 $this->mSearchPrefix = $request->getText( 'prefix' );
183 $time = $request->getVal( 'timestamp' );
184 $this->mTimestamp = $time ? wfTimestamp( TS::MW, $time ) : '';
185 $this->mFilename = $request->getVal( 'file' );
186
187 $posted = $request->wasPosted() &&
188 $user->matchEditToken( $request->getVal( 'wpEditToken' ) );
189 $this->mRestore = $request->getCheck( 'restore' ) && $posted;
190 $this->mRevdel = $request->getCheck( 'revdel' ) && $posted;
191 $this->mInvert = $request->getCheck( 'invert' ) && $posted;
192 $this->mPreview = $request->getCheck( 'preview' ) && $posted;
193 $this->mDiff = $request->getCheck( 'diff' );
194 $this->mDiffOnly = $request->getBool( 'diffonly',
195 $this->userOptionsLookup->getOption( $this->getUser(), 'diffonly' ) );
196 $commentList = $request->getText( 'wpCommentList', 'other' );
197 $comment = $request->getText( 'wpComment' );
198 if ( $commentList === 'other' ) {
199 $this->mComment = $comment;
200 } elseif ( $comment !== '' ) {
201 $this->mComment = $commentList . $this->msg( 'colon-separator' )->inContentLanguage()->text() . $comment;
202 } else {
203 $this->mComment = $commentList;
204 }
205 $this->mUnsuppress = $request->getVal( 'wpUnsuppress' ) &&
206 $this->permissionManager->userHasRight( $user, 'suppressrevision' );
207 $this->mToken = $request->getVal( 'token' );
208 $this->mUndeleteTalk = $request->getCheck( 'undeletetalk' );
209 $this->mHistoryOffset = $request->getVal( 'historyoffset' );
210
211 if ( $this->isAllowed( 'undelete' ) ) {
212 $this->mAllowed = true; // user can restore
213 $this->mCanView = true; // user can view content
214 } elseif ( $this->isAllowed( 'deletedtext' ) ) {
215 $this->mAllowed = false; // user cannot restore
216 $this->mCanView = true; // user can view content
217 $this->mRestore = false;
218 } else { // user can only view the list of revisions
219 $this->mAllowed = false;
220 $this->mCanView = false;
221 $this->mTimestamp = '';
222 $this->mRestore = false;
223 }
224
225 if ( $this->mRestore || $this->mInvert ) {
226 $timestamps = [];
227 $this->mFileVersions = [];
228 foreach ( $request->getValues() as $key => $val ) {
229 $matches = [];
230 if ( preg_match( '/^ts(\d{14})$/', $key, $matches ) ) {
231 $timestamps[] = $matches[1];
232 }
233
234 if ( preg_match( '/^fileid(\d+)$/', $key, $matches ) ) {
235 $this->mFileVersions[] = intval( $matches[1] );
236 }
237 }
238 rsort( $timestamps );
239 $this->mTargetTimestamp = $timestamps;
240 }
241 }
242
251 protected function isAllowed( $permission, ?User $user = null ) {
252 $user ??= $this->getUser();
253 $block = $user->getBlock();
254
255 if ( $this->mTargetObj !== null ) {
256 return $this->permissionManager->userCan( $permission, $user, $this->mTargetObj );
257 } else {
258 $hasRight = $this->permissionManager->userHasRight( $user, $permission );
259 $sitewideBlock = $block && $block->isSitewide();
260 return $permission === 'undelete' ? ( $hasRight && !$sitewideBlock ) : $hasRight;
261 }
262 }
263
265 public function userCanExecute( User $user ) {
266 return $this->isAllowed( $this->getRestriction(), $user );
267 }
268
272 public function checkPermissions() {
273 $user = $this->getUser();
274
275 // First check if user has the right to use this page. If not,
276 // show a permissions error whether they are blocked or not.
277 if ( !parent::userCanExecute( $user ) ) {
278 $this->displayRestrictionError();
279 }
280
281 // If a user has the right to use this page, but is blocked from
282 // the target, show a block error.
283 if (
284 $this->mTargetObj && $this->permissionManager->isBlockedFrom( $user, $this->mTargetObj ) ) {
285 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable Block is checked and not null
286 throw new UserBlockedError( $user->getBlock() );
287 }
288
289 // Finally, do the comprehensive permission check via isAllowed.
290 if ( !$this->userCanExecute( $user ) ) {
291 $this->displayRestrictionError();
292 }
293 }
294
296 public function execute( $par ) {
297 $this->useTransactionalTimeLimit();
298
299 $user = $this->getUser();
300
301 $this->setHeaders();
302 $this->outputHeader();
303 $this->addHelpLink( 'Help:Deletion_and_undeletion' );
304
305 $this->loadRequest( $par );
306 $this->checkPermissions(); // Needs to be after mTargetObj is set
307
308 $out = $this->getOutput();
309 // This page uses Html::warningBox and Html::errorBox
310 $out->addModuleStyles( 'mediawiki.codex.messagebox.styles' );
311
312 if ( $this->mTargetObj === null ) {
313 $out->addWikiMsg( 'undelete-header' );
314
315 # Not all users can just browse every deleted page from the list
316 if ( $this->permissionManager->userHasRight( $user, 'browsearchive' ) ) {
317 $this->showSearchForm();
318 }
319
320 return;
321 }
322
323 $this->addHelpLink( 'Help:Undelete' );
324 if ( $this->mAllowed ) {
325 $out->setPageTitleMsg( $this->msg( 'undeletepage' ) );
326 } else {
327 $out->setPageTitleMsg( $this->msg( 'viewdeletedpage' ) );
328 }
329
330 $this->getSkin()->setRelevantTitle( $this->mTargetObj );
331
332 if ( $this->mTimestamp !== '' ) {
333 $this->showRevision( $this->mTimestamp );
334 } elseif ( $this->mFilename !== null && $this->mTargetObj->inNamespace( NS_FILE ) ) {
335 $file = new ArchivedFile( $this->mTargetObj, 0, $this->mFilename );
336 // Check if user is allowed to see this file
337 if ( !$file->exists() ) {
338 $out->addWikiMsg( 'filedelete-nofile', $this->mFilename );
339 } elseif ( !$file->userCan( File::DELETED_FILE, $user ) ) {
340 if ( $file->isDeleted( File::DELETED_RESTRICTED ) ) {
341 throw new PermissionsError( 'suppressrevision' );
342 } else {
343 throw new PermissionsError( 'deletedtext' );
344 }
345 } elseif ( !$user->matchEditToken( $this->mToken, $this->mFilename ) ) {
346 $this->showFileConfirmationForm( $this->mFilename );
347 } else {
348 $this->showFile( $this->mFilename );
349 }
350 } elseif ( $this->mAction === 'submit' ) {
351 if ( $this->mRestore ) {
352 $this->undelete();
353 } elseif ( $this->mRevdel ) {
354 $this->redirectToRevDel();
355 }
356 } elseif ( $this->mAction === 'render' ) {
357 $this->showMoreHistory();
358 } else {
359 $this->showHistory();
360 }
361 }
362
367 private function redirectToRevDel() {
368 $revisionIds = [];
369 $fileArchiveIds = [];
370
371 foreach ( $this->getRequest()->getValues() as $key => $val ) {
372 $matches = [];
373 if ( preg_match( "/^ts(\d{14})$/", $key, $matches ) ) {
374 $revisionRecord = $this->archivedRevisionLookup
375 ->getRevisionRecordByTimestamp( $this->mTargetObj, $matches[1] );
376 if ( $revisionRecord ) {
377 $revisionIds[] = (int)$revisionRecord->getId();
378 }
379 } elseif ( preg_match( '/^fileid(\d+)$/', $key, $matches ) ) {
380 $fileArchiveIds[] = (int)$matches[1];
381 }
382 }
383
384 $hasRevisions = count( $revisionIds ) > 0;
385 $hasFiles = count( $fileArchiveIds ) > 0;
386
387 // Check selection validity, see if mixed or nothing selected
388 if ( $hasRevisions && $hasFiles ) {
389 $this->renderUndeleteSelectionError( 'mixed' );
390 return;
391 } elseif ( !$hasRevisions && !$hasFiles ) {
392 $this->renderUndeleteSelectionError( 'none' );
393 return;
394 }
395
396 // Exp. assoc array of id => 1 (ids[123]=1)
397 $idsForQuery = $hasFiles
398 ? array_fill_keys( $fileArchiveIds, 1 )
399 : array_fill_keys( $revisionIds, 1 );
400
401 $query = [
402 'type' => $hasFiles ? 'filearchive' : 'revision',
403 'ids' => $idsForQuery,
404 'target' => $this->mTargetObj->getPrefixedText()
405 ];
406
407 $url = SpecialPage::getTitleFor( 'Revisiondelete' )->getFullURL( $query );
408 $this->getOutput()->redirect( $url );
409 }
410
416 private function renderUndeleteSelectionError( string $case ): void {
417 $msg = $case === 'mixed'
418 ? $this->msg( 'undelete-error-mixed' )
419 : $this->msg( 'undelete-error-none' );
420
421 $this->getOutput()->addHTML( Html::errorBox( $msg->parse() ) );
422 }
423
424 private function showSearchForm() {
425 $out = $this->getOutput();
426 $out->setPageTitleMsg( $this->msg( 'undelete-search-title' ) );
427 $fuzzySearch = $this->getRequest()->getVal( 'fuzzy', '1' );
428
429 $out->enableOOUI();
430
431 $fields = [];
432 $fields[] = new ActionFieldLayout(
433 new TextInputWidget( [
434 'name' => 'prefix',
435 'inputId' => 'prefix',
436 'infusable' => true,
437 'value' => $this->mSearchPrefix,
438 'autofocus' => true,
439 ] ),
440 new ButtonInputWidget( [
441 'label' => $this->msg( 'undelete-search-submit' )->text(),
442 'flags' => [ 'primary', 'progressive' ],
443 'inputId' => 'searchUndelete',
444 'type' => 'submit',
445 ] ),
446 [
447 'label' => new HtmlSnippet(
448 $this->msg(
449 $fuzzySearch ? 'undelete-search-full' : 'undelete-search-prefix'
450 )->parse()
451 ),
452 'align' => 'left',
453 ]
454 );
455
456 $fieldset = new FieldsetLayout( [
457 'label' => $this->msg( 'undelete-search-box' )->text(),
458 'items' => $fields,
459 ] );
460
461 $form = new FormLayout( [
462 'method' => 'get',
463 'action' => wfScript(),
464 ] );
465
466 $form->appendContent(
467 $fieldset,
468 new HtmlSnippet(
469 Html::hidden( 'title', $this->getPageTitle()->getPrefixedDBkey() ) .
470 Html::hidden( 'fuzzy', $fuzzySearch )
471 )
472 );
473
474 $out->addHTML(
475 ( new PanelLayout( [
476 'expanded' => false,
477 'padded' => true,
478 'framed' => true,
479 'content' => $form,
480 ] ) )->toString()
481 );
482
483 # List undeletable articles
484 if ( $this->mSearchPrefix ) {
485 // For now, we enable search engine match only when specifically asked to
486 // by using fuzzy=1 parameter.
487 if ( $fuzzySearch ) {
488 $result = PageArchive::listPagesBySearch( $this->mSearchPrefix );
489 } else {
490 $result = PageArchive::listPagesByPrefix( $this->mSearchPrefix );
491 }
492 $this->showList( $result );
493 }
494 }
495
502 private function showList( $result ) {
503 $out = $this->getOutput();
504
505 if ( $result->numRows() == 0 ) {
506 $out->addWikiMsg( 'undelete-no-results' );
507
508 return false;
509 }
510
511 $out->addWikiMsg( 'undeletepagetext', $this->getLanguage()->formatNum( $result->numRows() ) );
512
513 $linkRenderer = $this->getLinkRenderer();
514 $undelete = $this->getPageTitle();
515 $out->addHTML( "<ul id='undeleteResultsList'>\n" );
516 foreach ( $result as $row ) {
517 $title = Title::makeTitleSafe( $row->ar_namespace, $row->ar_title );
518 if ( $title !== null ) {
519 $item = $linkRenderer->makeKnownLink(
520 $undelete,
521 $title->getPrefixedText(),
522 [],
523 [ 'target' => $title->getPrefixedText() ]
524 );
525 } else {
526 // The title is no longer valid, show as text
527 $item = Html::element(
528 'span',
529 [ 'class' => 'mw-invalidtitle' ],
530 Linker::getInvalidTitleDescription(
531 $this->getContext(),
532 $row->ar_namespace,
533 $row->ar_title
534 )
535 );
536 }
537 $revs = $this->msg( 'undeleterevisions' )->numParams( $row->count )->parse();
538 $out->addHTML(
539 Html::rawElement(
540 'li',
541 [ 'class' => 'undeleteResult' ],
542 $item . $this->msg( 'word-separator' )->escaped() .
543 $this->msg( 'parentheses' )->rawParams( $revs )->escaped()
544 )
545 );
546 }
547 $result->free();
548 $out->addHTML( "</ul>\n" );
549
550 return true;
551 }
552
553 private function showRevision( string $timestamp ) {
554 if ( !preg_match( '/[0-9]{14}/', $timestamp ) ) {
555 return;
556 }
557 $out = $this->getOutput();
558 $out->addModuleStyles( 'mediawiki.interface.helpers.styles' );
559
560 // When viewing a specific revision, add a subtitle link back to the overall
561 // history, see T284114
562 $listLink = $this->getLinkRenderer()->makeKnownLink(
563 $this->getPageTitle(),
564 $this->msg( 'undelete-back-to-list' )->text(),
565 [],
566 [ 'target' => $this->mTargetObj->getPrefixedText() ]
567 );
568 // same < arrow as with subpages
569 $subtitle = "&lt; $listLink";
570 $out->setSubtitle( $subtitle );
571
572 $archive = new PageArchive( $this->mTargetObj );
573 // FIXME: This hook must be deprecated, passing PageArchive by ref is awful.
574 if ( !$this->getHookRunner()->onUndeleteForm__showRevision(
575 $archive, $this->mTargetObj )
576 ) {
577 return;
578 }
579 $revRecord = $this->archivedRevisionLookup->getRevisionRecordByTimestamp( $this->mTargetObj, $timestamp );
580
581 $user = $this->getUser();
582
583 if ( !$revRecord ) {
584 $out->addWikiMsg( 'undeleterevision-missing' );
585 return;
586 }
587
588 if ( $revRecord->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
589 // Used in wikilinks, should not contain whitespaces
590 $titleText = $this->mTargetObj->getPrefixedURL();
591 if ( !$revRecord->userCan( RevisionRecord::DELETED_TEXT, $this->getAuthority() ) ) {
592 $msg = $revRecord->isDeleted( RevisionRecord::DELETED_RESTRICTED )
593 ? [ 'rev-suppressed-text-permission', $titleText ]
594 : [ 'rev-deleted-text-permission', $titleText ];
595 $out->addHTML(
596 Html::warningBox(
597 $this->msg( $msg[0], $msg[1] )->parse(),
598 'plainlinks'
599 )
600 );
601 return;
602 }
603
604 $msg = $revRecord->isDeleted( RevisionRecord::DELETED_RESTRICTED )
605 ? [ 'rev-suppressed-text-view', $titleText ]
606 : [ 'rev-deleted-text-view', $titleText ];
607 $out->addHTML(
608 Html::warningBox(
609 $this->msg( $msg[0], $msg[1] )->parse(),
610 'plainlinks'
611 )
612 );
613 // and we are allowed to see...
614 }
615
616 if ( $this->mDiff ) {
617 $previousRevRecord = $this->archivedRevisionLookup
618 ->getPreviousRevisionRecord( $this->mTargetObj, $timestamp );
619 if ( $previousRevRecord ) {
620 $this->showDiff( $previousRevRecord, $revRecord );
621 if ( $this->mDiffOnly ) {
622 return;
623 }
624
625 $out->addHTML( '<hr />' );
626 } else {
627 $out->addWikiMsg( 'undelete-nodiff' );
628 }
629 }
630
631 $link = $this->getLinkRenderer()->makeKnownLink(
632 $this->getPageTitle( $this->mTargetObj->getPrefixedDBkey() ),
633 $this->mTargetObj->getPrefixedText()
634 );
635
636 $lang = $this->getLanguage();
637
638 // date and time are separate parameters to facilitate localisation.
639 // $time is kept for backward compat reasons.
640 $time = $lang->userTimeAndDate( $timestamp, $user );
641 $d = $lang->userDate( $timestamp, $user );
642 $t = $lang->userTime( $timestamp, $user );
643 $userLink = Linker::revUserTools( $revRecord );
644
645 try {
646 $content = $revRecord->getContent(
647 SlotRecord::MAIN,
648 RevisionRecord::FOR_THIS_USER,
649 $user
650 );
651 } catch ( RevisionAccessException ) {
652 $content = null;
653 }
654
655 // TODO: MCR: this will have to become something like $hasTextSlots and $hasNonTextSlots
656 $isText = ( $content instanceof TextContent );
657
658 $undeleteRevisionContent = '';
659 // Revision delete links
660 if ( !$this->mDiff ) {
661 $revdel = Linker::getRevDeleteLink(
662 $user,
663 $revRecord,
664 $this->mTargetObj
665 );
666 if ( $revdel ) {
667 $undeleteRevisionContent = $revdel . ' ';
668 }
669 }
670
671 $undeleteRevisionContent .= $out->msg(
672 'undelete-revision',
673 Message::rawParam( $link ),
674 $time,
675 Message::rawParam( $userLink ),
676 $d,
677 $t
678 )->parseAsBlock();
679
680 if ( $this->mPreview || $isText ) {
681 $out->addHTML(
682 Html::warningBox(
683 $undeleteRevisionContent,
684 'mw-undelete-revision'
685 )
686 );
687 } else {
688 $out->addHTML(
689 Html::rawElement(
690 'div',
691 [ 'class' => 'mw-undelete-revision', ],
692 $undeleteRevisionContent
693 )
694 );
695 }
696
697 if ( $this->mPreview || !$isText ) {
698 // NOTE: non-text content has no source view, so always use rendered preview
699
700 $popts = ParserOptions::newFromContext( $this->getContext() );
701
702 try {
703 $rendered = $this->revisionRenderer->getRenderedRevision(
704 $revRecord,
705 $popts,
706 $user,
707 [ 'audience' => RevisionRecord::FOR_THIS_USER, 'causeAction' => 'undelete-preview' ]
708 );
709
710 // Fail hard if the audience check fails, since we already checked
711 // at the beginning of this method.
712 $pout = $rendered->getRevisionParserOutput();
713
714 $popts->setSuppressSectionEditLinks();
715 $out->addParserOutput( $pout, $popts );
716 } catch ( RevisionAccessException ) {
717 }
718 }
719
720 $out->enableOOUI();
721 $buttonFields = [];
722
723 if ( $isText ) {
724 '@phan-var TextContent $content';
725 // TODO: MCR: make this work for multiple slots
726 // source view for textual content
727 $sourceView = Html::element( 'textarea', [
728 'readonly' => 'readonly',
729 'cols' => 80,
730 'rows' => 25
731 ], $content->getText() . "\n" );
732
733 $buttonFields[] = new ButtonInputWidget( [
734 'type' => 'submit',
735 'name' => 'preview',
736 'label' => $this->msg( 'showpreview' )->text()
737 ] );
738 } else {
739 $sourceView = '';
740 }
741
742 $buttonFields[] = new ButtonInputWidget( [
743 'name' => 'diff',
744 'type' => 'submit',
745 'label' => $this->msg( 'showdiff' )->text()
746 ] );
747
748 $out->addHTML(
749 $sourceView .
750 Html::openElement( 'div', [
751 'style' => 'clear: both' ] ) .
752 Html::openElement( 'form', [
753 'method' => 'post',
754 'action' => $this->getPageTitle()->getLocalURL( [ 'action' => 'submit' ] ) ] ) .
755 Html::element( 'input', [
756 'type' => 'hidden',
757 'name' => 'target',
758 'value' => $this->mTargetObj->getPrefixedDBkey() ] ) .
759 Html::element( 'input', [
760 'type' => 'hidden',
761 'name' => 'timestamp',
762 'value' => $timestamp ] ) .
763 Html::element( 'input', [
764 'type' => 'hidden',
765 'name' => 'wpEditToken',
766 'value' => $user->getEditToken() ] ) .
767 new FieldLayout(
768 new Widget( [
769 'content' => new HorizontalLayout( [
770 'items' => $buttonFields
771 ] )
772 ] )
773 ) .
774 Html::closeElement( 'form' ) .
775 Html::closeElement( 'div' )
776 );
777 }
778
786 private function showDiff(
787 RevisionRecord $previousRevRecord,
788 RevisionRecord $currentRevRecord
789 ) {
790 $currentTitle = Title::newFromPageIdentity( $currentRevRecord->getPage() );
791
792 $diffContext = new DerivativeContext( $this->getContext() );
793 $diffContext->setTitle( $currentTitle );
794 $diffContext->setWikiPage( $this->wikiPageFactory->newFromTitle( $currentTitle ) );
795
796 $contentModel = $currentRevRecord->getSlot(
797 SlotRecord::MAIN,
798 RevisionRecord::RAW
799 )->getModel();
800
801 $diffEngine = $this->contentHandlerFactory->getContentHandler( $contentModel )
802 ->createDifferenceEngine( $diffContext );
803
804 $diffEngine->setRevisions( $previousRevRecord, $currentRevRecord );
805 $diffEngine->showDiffStyle();
806 $formattedDiff = $diffEngine->getDiff(
807 $this->diffHeader( $previousRevRecord, 'o' ),
808 $this->diffHeader( $currentRevRecord, 'n' )
809 );
810
811 if ( $formattedDiff === false ) {
812 if ( $diffEngine->hasSuppressedRevision() ) {
813 $error = 'rev-suppressed-no-diff';
814 } elseif ( $diffEngine->hasDeletedRevision() ) {
815 $error = 'rev-deleted-no-diff';
816 } else {
817 // Something else went wrong when loading the diff - at least explain that something was wrong ...
818 $error = 'undelete-error-loading-diff';
819 }
820 $this->getOutput()->addHTML( $this->msg( $error )->parse() );
821 } else {
822 $this->getOutput()->addHTML( "<div>$formattedDiff</div>\n" );
823 }
824 }
825
831 private function diffHeader( RevisionRecord $revRecord, $prefix ) {
832 if ( $revRecord instanceof RevisionArchiveRecord ) {
833 // Revision in the archive table, only viewable via this special page
834 $targetPage = $this->getPageTitle();
835 $targetQuery = [
836 'target' => $this->mTargetObj->getPrefixedText(),
837 'timestamp' => wfTimestamp( TS::MW, $revRecord->getTimestamp() )
838 ];
839 } else {
840 // Revision in the revision table, viewable by oldid
841 $targetPage = $revRecord->getPageAsLinkTarget();
842 $targetQuery = [ 'oldid' => $revRecord->getId() ];
843 }
844
845 // Add show/hide deletion links if available
846 $user = $this->getUser();
847 $lang = $this->getLanguage();
848 $rdel = Linker::getRevDeleteLink( $user, $revRecord, $this->mTargetObj );
849
850 if ( $rdel ) {
851 $rdel = " $rdel";
852 }
853
854 $minor = $revRecord->isMinor() ? ChangesList::flag( 'minor', $this->getContext() ) : '';
855
856 $dbr = $this->dbProvider->getReplicaDatabase();
857 $tagIds = $dbr->newSelectQueryBuilder()
858 ->select( 'ct_tag_id' )
859 ->from( 'change_tag' )
860 ->where( [ 'ct_rev_id' => $revRecord->getId() ] )
861 ->caller( __METHOD__ )->fetchFieldValues();
862 $tags = [];
863 foreach ( $tagIds as $tagId ) {
864 try {
865 $tags[] = $this->changeTagDefStore->getName( (int)$tagId );
866 } catch ( NameTableAccessException ) {
867 continue;
868 }
869 }
870 $tags = implode( ',', $tags );
871 $tagSummary = ChangeTags::formatSummaryRow( $tags, 'deleteddiff', $this->getContext() );
872 $asof = $this->getLinkRenderer()->makeLink(
873 $targetPage,
874 $this->msg(
875 'revisionasof',
876 $lang->userTimeAndDate( $revRecord->getTimestamp(), $user ),
877 $lang->userDate( $revRecord->getTimestamp(), $user ),
878 $lang->userTime( $revRecord->getTimestamp(), $user )
879 )->text(),
880 [],
881 $targetQuery
882 );
883 if ( $revRecord->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
884 $asof = Html::rawElement(
885 'span',
886 [ 'class' => Linker::getRevisionDeletedClass( $revRecord ) ],
887 $asof
888 );
889 }
890
891 // FIXME This is reimplementing DifferenceEngine#getRevisionHeader
892 // and partially #showDiffPage, but worse
893 return '<div id="mw-diff-' . $prefix . 'title1"><strong>' .
894 $asof .
895 '</strong></div>' .
896 '<div id="mw-diff-' . $prefix . 'title2">' .
897 Linker::revUserTools( $revRecord ) . '<br />' .
898 '</div>' .
899 '<div id="mw-diff-' . $prefix . 'title3">' .
900 $minor . $this->commentFormatter->formatRevision( $revRecord, $user ) . $rdel . '<br />' .
901 '</div>' .
902 '<div id="mw-diff-' . $prefix . 'title5">' .
903 $tagSummary[0] . '<br />' .
904 '</div>';
905 }
906
911 private function showFileConfirmationForm( $key ) {
912 $out = $this->getOutput();
913 $lang = $this->getLanguage();
914 $user = $this->getUser();
915 $file = new ArchivedFile( $this->mTargetObj, 0, $this->mFilename );
916 $out->addWikiMsg( 'undelete-show-file-confirm',
917 $this->mTargetObj->getText(),
918 $lang->userDate( $file->getTimestamp(), $user ),
919 $lang->userTime( $file->getTimestamp(), $user ) );
920 $out->addHTML(
921 Html::rawElement( 'form', [
922 'method' => 'POST',
923 'action' => $this->getPageTitle()->getLocalURL( [
924 'target' => $this->mTarget,
925 'file' => $key,
926 'token' => $user->getEditToken( $key ),
927 ] ),
928 ],
929 Html::submitButton( $this->msg( 'undelete-show-file-submit' )->text() )
930 )
931 );
932 }
933
938 private function showFile( $key ) {
939 $this->getOutput()->disable();
940
941 # We mustn't allow the output to be CDN cached, otherwise
942 # if an admin previews a deleted image, and it's cached, then
943 # a user without appropriate permissions can toddle off and
944 # nab the image, and CDN will serve it
945 $response = $this->getRequest()->response();
946 $response->header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', 0 ) . ' GMT' );
947 $response->header( 'Cache-Control: no-cache, no-store, max-age=0, must-revalidate' );
948
949 $path = $this->localRepo->getZonePath( 'deleted' ) . '/' . $this->localRepo->getDeletedHashPath( $key ) . $key;
950 $this->localRepo->streamFileWithStatus( $path );
951 }
952
953 private function addRevisionsToBatch( LinkBatch $batch, IResultWrapper $revisions ) {
954 foreach ( $revisions as $row ) {
955 $batch->addUser( new UserIdentityValue( (int)$row->ar_user, $row->ar_user_text ) );
956 }
957 }
958
959 private function addFilesToBatch( LinkBatch $batch, IResultWrapper $files ) {
960 foreach ( $files as $row ) {
961 $batch->add( NS_USER, $row->fa_user_text );
962 $batch->add( NS_USER_TALK, $row->fa_user_text );
963 }
964 }
965
969 protected function showMoreHistory() {
970 $out = $this->getOutput();
971 $out->setArticleBodyOnly( true );
972 $dbr = $this->dbProvider->getReplicaDatabase();
973 if ( $this->mHistoryOffset ) {
974 $extraConds = [ $dbr->expr( 'ar_timestamp', '<', $dbr->timestamp( $this->mHistoryOffset ) ) ];
975 } else {
976 $extraConds = [];
977 }
978 $revisions = $this->archivedRevisionLookup->listRevisions(
979 $this->mTargetObj,
980 $extraConds,
981 self::REVISION_HISTORY_LIMIT + 1
982 );
983 $batch = $this->linkBatchFactory->newLinkBatch()->setCaller( __METHOD__ );
984 $this->addRevisionsToBatch( $batch, $revisions );
985 $batch->execute();
986 $out->addHTML( $this->formatRevisionHistory( $revisions ) );
987
988 if ( $revisions->numRows() > self::REVISION_HISTORY_LIMIT ) {
989 // Indicate to JS that the "show more" button should remain active
990 $out->setStatusCode( 206 );
991 }
992 }
993
1000 protected function formatRevisionHistory( IResultWrapper $revisions ) {
1001 $history = Html::openElement( 'ul', [ 'class' => 'mw-undelete-revlist' ] );
1002
1003 // Exclude the last data row if there is more data than history limit amount
1004 $numRevisions = $revisions->numRows();
1005 $displayCount = min( $numRevisions, self::REVISION_HISTORY_LIMIT );
1006 $firstRev = $this->revisionStore->getFirstRevision( $this->mTargetObj );
1007 $earliestLiveTime = $firstRev ? $firstRev->getTimestamp() : null;
1008
1009 $sizes = [];
1010 foreach ( $revisions as $rev ) {
1011 $sizes[$rev->ar_rev_id] = $rev->ar_len;
1012 }
1013
1014 $revisions->rewind();
1015 for ( $i = 0; $i < $displayCount; $i++ ) {
1016 $row = $revisions->fetchObject();
1017 // The $remaining parameter controls diff links and so must
1018 // include the undisplayed row beyond the display limit.
1019 $history .= $this->formatRevisionRow( $row, $earliestLiveTime, $numRevisions - $i, $sizes );
1020 }
1021 $history .= Html::closeElement( 'ul' );
1022 return $history;
1023 }
1024
1025 protected function showHistory() {
1026 $this->checkReadOnly();
1027
1028 $out = $this->getOutput();
1029 if ( $this->mAllowed ) {
1030 $out->addModules( 'mediawiki.misc-authed-ooui' );
1031 $out->addModuleStyles( 'mediawiki.special' );
1032 }
1033 $out->addModuleStyles( 'mediawiki.interface.helpers.styles' );
1034 $out->addModuleStyles( 'mediawiki.special.changeslist' );
1035
1036 $out->wrapWikiMsg(
1037 "<div class='mw-undelete-pagetitle'>\n$1\n</div>\n",
1038 [ 'undeletepagetitle', wfEscapeWikiText( $this->mTargetObj->getPrefixedText() ) ]
1039 );
1040
1041 $archive = new PageArchive( $this->mTargetObj );
1042 // FIXME: This hook must be deprecated, passing PageArchive by ref is awful.
1043 $this->getHookRunner()->onUndeleteForm__showHistory( $archive, $this->mTargetObj );
1044
1045 $out->addHTML( Html::openElement( 'div', [ 'class' => 'mw-undelete-history' ] ) );
1046 if ( $this->mAllowed ) {
1047 $out->addWikiMsg( 'undeletehistory' );
1048 $out->addWikiMsg( 'undeleterevdel' );
1049 } else {
1050 $out->addWikiMsg( 'undeletehistorynoadmin' );
1051 }
1052 $out->addHTML( Html::closeElement( 'div' ) );
1053
1054 # List all stored revisions
1055 $revisions = $this->archivedRevisionLookup->listRevisions(
1056 $this->mTargetObj,
1057 [],
1058 self::REVISION_HISTORY_LIMIT + 1
1059 );
1060 $files = $archive->listFiles();
1061 $numRevisions = $revisions->numRows();
1062 $showLoadMore = $numRevisions > self::REVISION_HISTORY_LIMIT;
1063 $haveRevisions = $numRevisions > 0;
1064 $haveFiles = $files && $files->numRows() > 0;
1065
1066 # Batch existence check on user and talk pages
1067 if ( $haveRevisions || $haveFiles ) {
1068 $batch = $this->linkBatchFactory->newLinkBatch()->setCaller( __METHOD__ );
1069 $this->addRevisionsToBatch( $batch, $revisions );
1070 if ( $haveFiles ) {
1071 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable -- $files is non-null
1072 $this->addFilesToBatch( $batch, $files );
1073 }
1074 $batch->execute();
1075 }
1076
1077 if ( $this->mAllowed ) {
1078 $out->enableOOUI();
1079
1080 $action = $this->getPageTitle()->getLocalURL( [ 'action' => 'submit' ] );
1081 # Start the form here
1082 $form = new FormLayout( [
1083 'method' => 'post',
1084 'action' => $action,
1085 'id' => 'undelete',
1086 ] );
1087 }
1088
1089 # Show relevant lines from the deletion log:
1090 $deleteLogPage = new LogPage( 'delete' );
1091 $out->addHTML( Html::element( 'h2', [], $deleteLogPage->getName()->text() ) . "\n" );
1092 LogEventsList::showLogExtract( $out, 'delete', $this->mTargetObj );
1093 # Show relevant lines from the suppression log:
1094 $suppressLogPage = new LogPage( 'suppress' );
1095 if ( $this->permissionManager->userHasRight( $this->getUser(), 'suppressionlog' ) ) {
1096 $out->addHTML( Html::element( 'h2', [], $suppressLogPage->getName()->text() ) . "\n" );
1097 LogEventsList::showLogExtract( $out, 'suppress', $this->mTargetObj );
1098 }
1099
1100 if ( $this->mAllowed && ( $haveRevisions || $haveFiles ) ) {
1101 $unsuppressAllowed = $this->permissionManager->userHasRight( $this->getUser(), 'suppressrevision' );
1102 $fields = [];
1103 $fields[] = new Layout( [
1104 'content' => new HtmlSnippet( $this->msg( 'undeleteextrahelp' )->parseAsBlock() )
1105 ] );
1106
1107 $dropdownComment = $this->msg( 'undelete-comment-dropdown' )
1108 ->page( $this->mTargetObj )->inContentLanguage()->text();
1109 // Add additional specific reasons for unsuppress
1110 if ( $unsuppressAllowed ) {
1111 $dropdownComment .= "\n" . $this->msg( 'undelete-comment-dropdown-unsuppress' )
1112 ->page( $this->mTargetObj )->inContentLanguage()->text();
1113 }
1114 $options = Html::listDropdownOptions(
1115 $dropdownComment,
1116 [ 'other' => $this->msg( 'undeletecommentotherlist' )->text() ]
1117 );
1118 $options = Html::listDropdownOptionsOoui( $options );
1119
1120 $fields[] = new FieldLayout(
1121 new DropdownInputWidget( [
1122 'name' => 'wpCommentList',
1123 'inputId' => 'wpCommentList',
1124 'infusable' => true,
1125 'value' => $this->getRequest()->getText( 'wpCommentList', 'other' ),
1126 'options' => $options,
1127 ] ),
1128 [
1129 'label' => $this->msg( 'undeletecomment' )->text(),
1130 'align' => 'top',
1131 ]
1132 );
1133
1134 $fields[] = new FieldLayout(
1135 new TextInputWidget( [
1136 'name' => 'wpComment',
1137 'inputId' => 'wpComment',
1138 'infusable' => true,
1139 'value' => $this->getRequest()->getText( 'wpComment' ),
1140 'autofocus' => true,
1141 // HTML maxlength uses "UTF-16 code units", which means that characters outside BMP
1142 // (e.g. emojis) count for two each. This limit is overridden in JS to instead count
1143 // Unicode codepoints.
1144 'maxLength' => CommentStore::COMMENT_CHARACTER_LIMIT,
1145 ] ),
1146 [
1147 'label' => $this->msg( 'undeleteothercomment' )->text(),
1148 'align' => 'top',
1149 ]
1150 );
1151
1152 if ( $this->getUser()->isRegistered() ) {
1153 $checkWatch = $this->watchlistManager->isWatched( $this->getUser(), $this->mTargetObj )
1154 || $this->getRequest()->getText( 'wpWatch' );
1155 $fields[] = new FieldLayout(
1156 new CheckboxInputWidget( [
1157 'name' => 'wpWatch',
1158 'inputId' => 'mw-undelete-watch',
1159 'value' => '1',
1160 'selected' => $checkWatch,
1161 ] ),
1162 [
1163 'label' => $this->msg( 'watchthis' )->text(),
1164 'align' => 'inline',
1165 ]
1166 );
1167 }
1168
1169 if ( $unsuppressAllowed ) {
1170 $fields[] = new FieldLayout(
1171 new CheckboxInputWidget( [
1172 'name' => 'wpUnsuppress',
1173 'inputId' => 'mw-undelete-unsuppress',
1174 'value' => '1',
1175 ] ),
1176 [
1177 'label' => $this->msg( 'revdelete-unsuppress' )->text(),
1178 'align' => 'inline',
1179 ]
1180 );
1181 }
1182
1183 $undelPage = $this->undeletePageFactory->newUndeletePage(
1184 $this->wikiPageFactory->newFromTitle( $this->mTargetObj ),
1185 $this->getContext()->getAuthority()
1186 );
1187 if ( $undelPage->canProbablyUndeleteAssociatedTalk()->isGood() ) {
1188 $fields[] = new FieldLayout(
1189 new CheckboxInputWidget( [
1190 'name' => 'undeletetalk',
1191 'inputId' => 'mw-undelete-undeletetalk',
1192 'selected' => false,
1193 ] ),
1194 [
1195 'label' => $this->msg( 'undelete-undeletetalk' )->text(),
1196 'align' => 'inline',
1197 ]
1198 );
1199 }
1200
1201 $fields[] = new FieldLayout(
1202 new Widget( [
1203 'content' => new HorizontalLayout( [
1204 'items' => [
1205 new ButtonInputWidget( [
1206 'name' => 'restore',
1207 'inputId' => 'mw-undelete-submit',
1208 'value' => '1',
1209 'label' => $this->msg( 'undeletebtn' )->text(),
1210 'flags' => [ 'primary', 'progressive' ],
1211 'type' => 'submit',
1212 ] ),
1213 new ButtonInputWidget( [
1214 'name' => 'invert',
1215 'inputId' => 'mw-undelete-invert',
1216 'value' => '1',
1217 'label' => $this->msg( 'undeleteinvert' )->text()
1218 ] ),
1219 ]
1220 ] )
1221 ] )
1222 );
1223
1224 $fieldset = new FieldsetLayout( [
1225 'label' => $this->msg( 'undelete-fieldset-title' )->text(),
1226 'id' => 'mw-undelete-table',
1227 'items' => $fields,
1228 ] );
1229
1230 $link = '';
1231 if ( $this->getAuthority()->isAllowed( 'editinterface' ) ) {
1232 if ( $unsuppressAllowed ) {
1233 $link .= $this->getLinkRenderer()->makeKnownLink(
1234 $this->msg( 'undelete-comment-dropdown-unsuppress' )->inContentLanguage()->getTitle(),
1235 $this->msg( 'undelete-edit-commentlist-unsuppress' )->text(),
1236 [],
1237 [ 'action' => 'edit' ]
1238 );
1239 $link .= $this->msg( 'pipe-separator' )->escaped();
1240 }
1241 $link .= $this->getLinkRenderer()->makeKnownLink(
1242 $this->msg( 'undelete-comment-dropdown' )->inContentLanguage()->getTitle(),
1243 $this->msg( 'undelete-edit-commentlist' )->text(),
1244 [],
1245 [ 'action' => 'edit' ]
1246 );
1247
1248 $link = Html::rawElement( 'p', [ 'class' => 'mw-undelete-editcomments' ], $link );
1249 }
1250
1251 // @phan-suppress-next-line PhanPossiblyUndeclaredVariable form is set, when used here
1252 $form->appendContent(
1253 new PanelLayout( [
1254 'expanded' => false,
1255 'padded' => true,
1256 'framed' => true,
1257 'content' => $fieldset,
1258 ] ),
1259 new HtmlSnippet(
1260 $link .
1261 Html::hidden( 'target', $this->mTarget ) .
1262 Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() )
1263 )
1264 );
1265 }
1266
1267 $history = '';
1268 $history .= Html::element( 'h2', [], $this->msg( 'history' )->text() ) . "\n";
1269
1270 if ( $haveRevisions ) {
1271 # Show the page's stored (deleted) history
1272
1273 if ( $this->mAllowed && $this->permissionManager->userHasRight( $this->getUser(), 'deleterevision' ) ) {
1274 $history .= Html::element(
1275 'button',
1276 [
1277 'name' => 'revdel',
1278 'type' => 'submit',
1279 'class' => [ 'deleterevision-log-submit', 'mw-log-deleterevision-button' ]
1280 ],
1281 $this->msg( 'showhideselectedversions' )->text()
1282 ) . "\n";
1283 }
1284
1285 $history .= $this->formatRevisionHistory( $revisions );
1286
1287 if ( $showLoadMore ) {
1288 $history .= Html::rawElement( 'div', [],
1289 Html::element( 'span', [ 'id' => 'mw-load-more-revisions' ],
1290 $this->msg( 'undelete-load-more-revisions' )->text()
1291 )
1292 ) . "\n";
1293 }
1294 } else {
1295 $out->addWikiMsg( 'nohistory' );
1296 }
1297
1298 if ( $haveFiles ) {
1299 $history .= Html::element( 'h2', [], $this->msg( 'filehist' )->text() ) . "\n";
1300 $history .= Html::openElement( 'ul', [ 'class' => 'mw-undelete-revlist' ] );
1301 foreach ( $files as $row ) {
1302 $history .= $this->formatFileRow( $row );
1303 }
1304 $files->free();
1305 $history .= Html::closeElement( 'ul' );
1306 }
1307
1308 if ( $this->mAllowed ) {
1309 # Slip in the hidden controls here
1310 $misc = Html::hidden( 'target', $this->mTarget );
1311 $misc .= Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() );
1312 $history .= $misc;
1313
1314 // @phan-suppress-next-line PhanPossiblyUndeclaredVariable form is set, when used here
1315 $form->appendContent( new HtmlSnippet( $history ) );
1316 // @phan-suppress-next-line PhanPossiblyUndeclaredVariable form is set, when used here
1317 $out->addHTML( (string)$form );
1318 } else {
1319 $out->addHTML( $history );
1320 }
1321 }
1322
1330 protected function formatRevisionRow( $row, $earliestLiveTime, $remaining, $sizes ) {
1331 $revRecord = $this->revisionStore->newRevisionFromArchiveRow(
1332 $row,
1333 IDBAccessObject::READ_NORMAL,
1334 $this->mTargetObj
1335 );
1336
1337 $revTextSize = '';
1338 $ts = wfTimestamp( TS::MW, $row->ar_timestamp );
1339 // Build checkboxen...
1340 if ( $this->mAllowed ) {
1341 if ( $this->mInvert ) {
1342 if ( in_array( $ts, $this->mTargetTimestamp ) ) {
1343 $checkBox = Html::check( "ts$ts" );
1344 } else {
1345 $checkBox = Html::check( "ts$ts", true );
1346 }
1347 } else {
1348 $checkBox = Html::check( "ts$ts" );
1349 }
1350 } else {
1351 $checkBox = '';
1352 }
1353
1354 // Build page & diff links...
1355 $user = $this->getUser();
1356 if ( $this->mCanView ) {
1357 $titleObj = $this->getPageTitle();
1358 # Last link
1359 if ( !$revRecord->userCan( RevisionRecord::DELETED_TEXT, $this->getAuthority() ) ) {
1360 $pageLink = htmlspecialchars( $this->getLanguage()->userTimeAndDate( $ts, $user ) );
1361 $last = $this->msg( 'diff' )->escaped();
1362 } elseif ( $remaining > 0 || ( $earliestLiveTime && $ts > $earliestLiveTime ) ) {
1363 $pageLink = $this->getPageLink( $revRecord, $titleObj, $ts );
1364 $last = $this->getLinkRenderer()->makeKnownLink(
1365 $titleObj,
1366 $this->msg( 'diff' )->text(),
1367 [],
1368 [
1369 'target' => $this->mTargetObj->getPrefixedText(),
1370 'timestamp' => $ts,
1371 'diff' => 'prev'
1372 ]
1373 );
1374 } else {
1375 $pageLink = $this->getPageLink( $revRecord, $titleObj, $ts );
1376 $last = $this->msg( 'diff' )->escaped();
1377 }
1378 } else {
1379 $pageLink = htmlspecialchars( $this->getLanguage()->userTimeAndDate( $ts, $user ) );
1380 $last = $this->msg( 'diff' )->escaped();
1381 }
1382
1383 // User links
1384 $userLink = Linker::revUserTools( $revRecord );
1385
1386 // Minor edit
1387 $minor = $revRecord->isMinor() ? ChangesList::flag( 'minor', $this->getContext() ) : '';
1388
1389 // Revision text size
1390 $size = $row->ar_len;
1391 if ( $size !== null ) {
1392 $revTextSize = Linker::formatRevisionSize( $size );
1393 $prevSize = $sizes[$row->ar_parent_id ?? 0] ?? 0;
1394 $sDiff = ChangesList::showCharacterDifference( $prevSize, $size, $this->getContext() );
1395 $revTextSize = "$revTextSize $sDiff";
1396 }
1397
1398 // Edit summary
1399 $comment = $this->commentFormatter->formatRevision( $revRecord, $user );
1400
1401 // Tags
1402 $attribs = [];
1403 [ $tagSummary, $classes ] = ChangeTags::formatSummaryRow(
1404 $row->ts_tags,
1405 'deletedhistory',
1406 $this->getContext()
1407 );
1408 $attribs['class'] = $classes;
1409
1410 $revisionRow = $this->msg( 'undelete-revision-row2' )
1411 ->rawParams(
1412 $checkBox,
1413 $last,
1414 $pageLink,
1415 $userLink,
1416 $minor,
1417 $revTextSize,
1418 $comment,
1419 $tagSummary
1420 )
1421 ->escaped();
1422
1423 return Html::rawElement( 'li', $attribs, $revisionRow ) . "\n";
1424 }
1425
1426 private function formatFileRow( \stdClass $row ): string {
1427 $file = ArchivedFile::newFromRow( $row );
1428 $ts = wfTimestamp( TS::MW, $row->fa_timestamp );
1429 $user = $this->getUser();
1430
1431 $checkBox = '';
1432 if ( $this->mCanView && $row->fa_storage_key ) {
1433 if ( $this->mAllowed ) {
1434 $checkBox = Html::check( 'fileid' . $row->fa_id );
1435 }
1436 $key = urlencode( $row->fa_storage_key );
1437 $pageLink = $this->getFileLink( $file, $this->getPageTitle(), $ts, $key );
1438 } else {
1439 $pageLink = htmlspecialchars( $this->getLanguage()->userTimeAndDate( $ts, $user ) );
1440 }
1441 $userLink = $this->getFileUser( $file );
1442 $data = $this->msg( 'widthheight' )->numParams( $row->fa_width, $row->fa_height )->text();
1443 $bytes = $this->msg( 'parentheses' )
1444 ->plaintextParams( $this->msg( 'nbytes' )->numParams( $row->fa_size )->text() )
1445 ->plain();
1446 $data = htmlspecialchars( $data . ' ' . $bytes );
1447 $comment = $this->getFileComment( $file );
1448
1449 // Add show/hide deletion links if available
1450 $canHide = $this->isAllowed( 'deleterevision' );
1451 if ( $canHide || ( $file->getVisibility() && $this->isAllowed( 'deletedhistory' ) ) ) {
1452 if ( !$file->userCan( File::DELETED_RESTRICTED, $user ) ) {
1453 // Revision was hidden from sysops
1454 $revdlink = Linker::revDeleteLinkDisabled( $canHide );
1455 } else {
1456 $query = [
1457 'type' => 'filearchive',
1458 'target' => $this->mTargetObj->getPrefixedDBkey(),
1459 'ids' => $row->fa_id
1460 ];
1461 $revdlink = Linker::revDeleteLink( $query,
1462 $file->isDeleted( File::DELETED_RESTRICTED ), $canHide );
1463 }
1464 } else {
1465 $revdlink = '';
1466 }
1467
1468 return "<li>$checkBox $revdlink $pageLink . . $userLink $data $comment</li>\n";
1469 }
1470
1479 private function getPageLink( RevisionRecord $revRecord, LinkTarget $target, $ts ) {
1480 $user = $this->getUser();
1481 $time = $this->getLanguage()->userTimeAndDate( $ts, $user );
1482
1483 if ( !$revRecord->userCan( RevisionRecord::DELETED_TEXT, $this->getAuthority() ) ) {
1484 // TODO The condition cannot be true when the function is called
1485 return Html::element(
1486 'span',
1487 [ 'class' => 'history-deleted' ],
1488 $time
1489 );
1490 }
1491
1492 $link = $this->getLinkRenderer()->makeKnownLink(
1493 $target,
1494 $time,
1495 [],
1496 [
1497 'target' => $this->mTargetObj->getPrefixedText(),
1498 'timestamp' => $ts
1499 ]
1500 );
1501
1502 if ( $revRecord->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
1503 $class = Linker::getRevisionDeletedClass( $revRecord );
1504 $link = '<span class="' . $class . '">' . $link . '</span>';
1505 }
1506
1507 return $link;
1508 }
1509
1520 private function getFileLink( $file, LinkTarget $target, $ts, $key ) {
1521 $user = $this->getUser();
1522 $time = $this->getLanguage()->userTimeAndDate( $ts, $user );
1523
1524 if ( !$file->userCan( File::DELETED_FILE, $user ) ) {
1525 return Html::element(
1526 'span',
1527 [ 'class' => 'history-deleted' ],
1528 $time
1529 );
1530 }
1531
1532 if ( $file->exists() ) {
1533 $link = $this->getLinkRenderer()->makeKnownLink(
1534 $target,
1535 $time,
1536 [],
1537 [
1538 'target' => $this->mTargetObj->getPrefixedText(),
1539 'file' => $key,
1540 'token' => $user->getEditToken( $key )
1541 ]
1542 );
1543 } else {
1544 $link = htmlspecialchars( $time );
1545 }
1546
1547 if ( $file->isDeleted( File::DELETED_FILE ) ) {
1548 $link = '<span class="history-deleted">' . $link . '</span>';
1549 }
1550
1551 return $link;
1552 }
1553
1560 private function getFileUser( $file ) {
1561 $uploader = $file->getUploader( File::FOR_THIS_USER, $this->getAuthority() );
1562 if ( !$uploader ) {
1563 return Html::element( 'span',
1564 [ 'class' => 'history-deleted' ],
1565 $this->msg( 'rev-deleted-user' )->text()
1566 );
1567 }
1568
1569 $link = Linker::userLink( $uploader->getId(), $uploader->getName() ) .
1570 Linker::userToolLinks( $uploader->getId(), $uploader->getName() );
1571
1572 if ( $file->isDeleted( File::DELETED_USER ) ) {
1573 $link = Html::rawElement(
1574 'span',
1575 [ 'class' => 'history-deleted' ],
1576 $link
1577 );
1578 }
1579
1580 return $link;
1581 }
1582
1589 private function getFileComment( $file ) {
1590 if ( !$file->userCan( File::DELETED_COMMENT, $this->getAuthority() ) ) {
1591 return Html::rawElement(
1592 'span',
1593 [ 'class' => 'history-deleted' ],
1594 Html::element( 'span',
1595 [ 'class' => 'comment' ],
1596 $this->msg( 'rev-deleted-comment' )->text()
1597 )
1598 );
1599 }
1600
1601 $comment = $file->getDescription( File::FOR_THIS_USER, $this->getAuthority() );
1602 $link = $this->commentFormatter->formatBlock( $comment );
1603
1604 if ( $file->isDeleted( File::DELETED_COMMENT ) ) {
1605 $link = Html::rawElement(
1606 'span',
1607 [ 'class' => 'history-deleted' ],
1608 $link
1609 );
1610 }
1611
1612 return $link;
1613 }
1614
1615 private function undelete() {
1616 if ( $this->getConfig()->get( MainConfigNames::UploadMaintenance )
1617 && $this->mTargetObj->getNamespace() === NS_FILE
1618 ) {
1619 throw new ErrorPageError( 'undelete-error', 'filedelete-maintenance' );
1620 }
1621
1622 $this->checkReadOnly();
1623
1624 $out = $this->getOutput();
1625 $undeletePage = $this->undeletePageFactory->newUndeletePage(
1626 $this->wikiPageFactory->newFromTitle( $this->mTargetObj ),
1627 $this->getAuthority()
1628 );
1629 if ( $this->mUndeleteTalk && $undeletePage->canProbablyUndeleteAssociatedTalk()->isGood() ) {
1630 $undeletePage->setUndeleteAssociatedTalk( true );
1631 }
1632 $status = $undeletePage
1633 ->setUndeleteOnlyTimestamps( $this->mTargetTimestamp )
1634 ->setUndeleteOnlyFileVersions( $this->mFileVersions )
1635 ->setUnsuppress( $this->mUnsuppress )
1636 // TODO This is currently duplicating some permission checks, but we do need it (T305680)
1637 ->undeleteIfAllowed( $this->mComment );
1638
1639 if ( !$status->isGood() ) {
1640 $out->setPageTitleMsg( $this->msg( 'undelete-error' ) );
1641 foreach ( $status->getMessages() as $msg ) {
1642 $out->addHTML( Html::errorBox(
1643 $this->msg( $msg )->parse()
1644 ) );
1645 }
1646 return;
1647 }
1648
1649 $restoredRevs = $status->getValue()[UndeletePage::REVISIONS_RESTORED];
1650 $restoredFiles = $status->getValue()[UndeletePage::FILES_RESTORED];
1651
1652 if ( $restoredRevs === 0 && $restoredFiles === 0 ) {
1653 // TODO Should use a different message here
1654 $out->setPageTitleMsg( $this->msg( 'undelete-error' ) );
1655 } else {
1656 if ( $status->getValue()[UndeletePage::FILES_RESTORED] !== 0 ) {
1657 $this->getHookRunner()->onFileUndeleteComplete(
1658 $this->mTargetObj, $this->mFileVersions, $this->getUser(), $this->mComment );
1659 }
1660
1661 $link = $this->getLinkRenderer()->makeKnownLink( $this->mTargetObj );
1662 $out->addWikiMsg( 'undeletedpage', Message::rawParam( $link ) );
1663
1664 $this->watchlistManager->setWatch(
1665 $this->getRequest()->getCheck( 'wpWatch' ),
1666 $this->getAuthority(),
1667 $this->mTargetObj
1668 );
1669 }
1670 }
1671
1680 public function prefixSearchSubpages( $search, $limit, $offset ) {
1681 return $this->prefixSearchString( $search, $limit, $offset, $this->searchEngineFactory );
1682 }
1683
1685 protected function getGroupName() {
1686 return 'pagetools';
1687 }
1688}
1689
1694class_alias( SpecialUndelete::class, 'SpecialUndelete' );
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.
Recent changes tagging.
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:45
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:47
Class to simplify the use of log pages.
Definition LogPage.php:35
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()-...
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.
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
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
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,)
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:130
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'=>[], '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, 'ThumbnailStepsRatio'=> 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, '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',],], 'json'=>['class'=> 'MediaWiki\\Content\\JsonContentHandler', 'services'=>['ParsoidParserFactory', 'TitleFactory',],], 'css'=>['class'=> 'MediaWiki\\Content\\CssContentHandler', 'services'=>['MainConfig', 'ParserFactory', 'UserOptionsLookup',],], 'vue'=>['class'=> 'MediaWiki\\Content\\VueContentHandler', 'services'=>['MainConfig', 'ParserFactory',],], '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, 'PHPSessionHandling'=> 'warn', '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',],],], '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, ], '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, 'UseLegacyMediaStyles' => 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, ], ], '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, ], 'AllowSecuritySensitiveOperationIfCannotReauthenticate' => [ 'default' => true, ], '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, '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, ], '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, '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' => [ ], '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, ], ], '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', ], '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, 'AllowUserCss' => false, 'AllowUserCssPrefs' => true, 'UseSiteJs' => true, 'UseSiteCss' => true, 'BreakFrames' => false, 'EditPageFrameOptions' => 'DENY', 'ApiFrameOptions' => 'DENY', 'CSPHeader' => false, 'CSPReportOnlyHeader' => 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: ], '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, ], '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, '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\\JobQueue\\Jobs\\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', 'Access-Control-Max-Age', 'Authorization', ], 'RestAPIAdditionalRouteFiles' => [ ], 'RestSandboxSpecs' => [ ], '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, ], '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', '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', ], 'ThumbnailStepsRatio' => [ 'number', 'null', ], 'ThumbnailBuckets' => [ 'array', 'null', ], 'UploadThumbnailRenderMap' => 'object', 'GalleryOptions' => 'object', 'DjvuDump' => [ 'string', 'null', ], 'DjvuRenderer' => [ 'string', 'null', ], 'DjvuTxt' => [ 'string', 'null', ], 'DjvuPostProcessor' => [ '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', 'PHPSessionHandling' => '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', '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', 'AllowSecuritySensitiveOperationIfCannotReauthenticate' => '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', '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', 'CSPHeader' => [ 'boolean', 'object', ], 'CSPReportOnlyHeader' => [ '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', 'RecentChangesFlags' => 'object', 'WatchlistExpiry' => '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', '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', ], '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', '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', ], ], ], ], ], 'PHPSessionHandling' => [ 'deprecated' => 'since 1.45 Integration with PHP session handling will be removed in the future', ], 'RawHtmlMessages' => [ 'items' => [ 'type' => 'string', ], ], 'InterwikiLogoOverride' => [ 'items' => [ 'type' => 'string', ], ], 'LegalTitleChars' => [ 'deprecated' => 'since 1.41; use Extension:TitleBlacklist to customize', ], 'ReauthenticateTime' => [ 'additionalProperties' => [ 'type' => 'integer', ], ], 'AllowSecuritySensitiveOperationIfCannotReauthenticate' => [ 'additionalProperties' => [ 'type' => 'boolean', ], ], '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', ], ], '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', ], ], ], ], '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.
msg( $key,... $params)