98 private $watch =
false;
144 parent::__construct(
'Movepage' );
145 $this->movePageFactory = $movePageFactory;
146 $this->permManager = $permManager;
147 $this->userOptionsLookup = $userOptionsLookup;
148 $this->dbProvider = $dbProvider;
149 $this->contentHandlerFactory = $contentHandlerFactory;
150 $this->nsInfo = $nsInfo;
151 $this->linkBatchFactory = $linkBatchFactory;
152 $this->repoGroup = $repoGroup;
153 $this->wikiPageFactory = $wikiPageFactory;
154 $this->searchEngineFactory = $searchEngineFactory;
155 $this->watchlistManager = $watchlistManager;
156 $this->restrictionStore = $restrictionStore;
157 $this->titleFactory = $titleFactory;
173 $target = $par ?? $request->getText(
'target' );
174 $oldTitleText = $request->getText(
'wpOldTitle', $target );
175 $this->oldTitle = Title::newFromText( $oldTitleText );
177 if ( !$this->oldTitle ) {
181 $this->
getOutput()->addBacklinkSubtitle( $this->oldTitle );
183 if ( !$this->oldTitle->exists() ) {
187 $newTitleTextMain = $request->getText(
'wpNewTitleMain' );
188 $newTitleTextNs = $request->getInt(
'wpNewTitleNs', $this->oldTitle->getNamespace() );
191 $newTitleText_bc = $request->getText(
'wpNewTitle' );
192 $this->newTitle = strlen( $newTitleText_bc ) > 0
193 ? Title::newFromText( $newTitleText_bc )
194 : Title::makeTitleSafe( $newTitleTextNs, $newTitleTextMain );
197 $isSubmit = $request->getRawVal(
'action' ) ===
'submit' && $request->wasPosted();
199 $reasonList = $request->getText(
'wpReasonList',
'other' );
200 $reason = $request->getText(
'wpReason' );
201 if ( $reasonList ===
'other' ) {
204 $this->reason = $reasonList . $this->
msg(
'colon-separator' )->inContentLanguage()->text() .
$reason;
206 $this->reason = $reasonList;
211 $this->moveTalk = $request->getBool(
'wpMovetalk', $def );
212 $this->fixRedirects = $request->getBool(
'wpFixRedirects', $def );
213 $this->leaveRedirect = $request->getBool(
'wpLeaveRedirect', $def );
215 $this->moveSubpages = $request->getBool(
'wpMovesubpages', $def );
216 $this->deleteAndMove = $request->getBool(
'wpDeleteAndMove' );
217 $this->moveOverShared = $request->getBool(
'wpMoveOverSharedFile' );
218 $this->watch = $request->getCheck(
'wpWatch' ) && $user->isRegistered();
222 if ( $isSubmit && $user->matchEditToken( $request->getVal(
'wpEditToken' ) ) ) {
224 $permErrors = $this->permManager->getPermissionErrors(
'move', $user, $this->oldTitle,
225 PermissionManager::RIGOR_SECURE );
227 DeferredUpdates::addCallableUpdate( [ $user,
'spreadAnyEditBlock' ] );
234 $permErrors = $this->permManager->getPermissionErrors(
'move', $user, $this->oldTitle,
235 PermissionManager::RIGOR_FULL );
237 DeferredUpdates::addCallableUpdate( [ $user,
'spreadAnyEditBlock' ] );
252 protected function showForm( $err, $isPermError =
false ) {
253 $this->
getSkin()->setRelevantTitle( $this->oldTitle );
256 $out->setPageTitleMsg( $this->
msg(
'move-page' )->plaintextParams( $this->oldTitle->getPrefixedText() ) );
257 $out->addModuleStyles( [
259 'mediawiki.interface.helpers.styles'
261 $out->addModules(
'mediawiki.misc-authed-ooui' );
264 $handlerSupportsRedirects = $this->contentHandlerFactory
265 ->getContentHandler( $this->oldTitle->getContentModel() )
266 ->supportsRedirects();
269 $out->addWikiMsg(
'movepagetext' );
271 $out->addWikiMsg( $handlerSupportsRedirects ?
272 'movepagetext-noredirectfixer' :
273 'movepagetext-noredirectsupport' );
276 if ( $this->oldTitle->getNamespace() ===
NS_USER && !$this->oldTitle->isSubpage() ) {
279 $out->msg(
'moveuserpage-warning' )->parse(),
280 'mw-moveuserpage-warning'
283 } elseif ( $this->oldTitle->getNamespace() ===
NS_CATEGORY ) {
286 $out->msg(
'movecategorypage-warning' )->parse(),
287 'mw-movecategorypage-warning'
299 # Show the current title as a default
300 # when the form is first opened.
302 } elseif ( !count( $err ) ) {
303 # If a title was supplied, probably from the move log revert
304 # link, check for validity. We can then show some diagnostic
305 # information and save a click.
306 $mp = $this->movePageFactory->newMovePage( $this->oldTitle,
$newTitle );
307 $status = $mp->isValidMove();
308 $status->merge( $mp->probablyCanMove( $this->getAuthority() ) );
309 if ( $status->getMessages() ) {
310 $err = $status->getErrorsArray();
314 if ( count( $err ) == 1 && isset( $err[0][0] ) ) {
315 if ( $err[0][0] ==
'articleexists'
316 && $this->permManager->quickUserCan(
'delete', $user,
$newTitle )
325 } elseif ( $err[0][0] ==
'redirectexists' && (
327 $this->permManager->quickUserCan(
'delete-redirect', $user,
$newTitle ) ||
328 $this->permManager->quickUserCan(
'delete', $user,
$newTitle ) )
337 } elseif ( $err[0][0] ==
'file-exists-sharedrepo'
338 && $this->permManager->userHasRight( $user,
'reupload-shared' )
351 $oldTitleSubpages = $this->oldTitle->hasSubpages();
352 $oldTitleTalkSubpages = $this->oldTitle->getTalkPage()->hasSubpages();
354 $canMoveSubpage = ( $oldTitleSubpages || $oldTitleTalkSubpages ) &&
355 $this->permManager->quickUserCan(
361 # We also want to be able to move assoc. subpage talk-pages even if base page
362 # has no associated talk page, so || with $oldTitleTalkSubpages.
363 $considerTalk = !$this->oldTitle->isTalkPage() &&
365 || ( $oldTitleTalkSubpages && $canMoveSubpage ) );
368 $queryBuilder = $this->dbProvider->getReplicaDatabase()->newSelectQueryBuilder()
371 ->where( [
'rd_namespace' => $this->oldTitle->getNamespace() ] )
372 ->andWhere( [
'rd_title' => $this->oldTitle->getDBkey() ] )
373 ->andWhere( [
'rd_interwiki' =>
'' ] );
375 $hasRedirects = (bool)$queryBuilder->caller( __METHOD__ )->fetchField();
377 $hasRedirects =
false;
380 if ( count( $err ) ) {
381 '@phan-var array[] $err';
382 if ( $isPermError ) {
383 $action_desc = $this->
msg(
'action-move' )->plain();
384 $errMsgHtml = $this->
msg(
'permissionserrorstext-withaction',
385 count( $err ), $action_desc )->parseAsBlock();
387 $errMsgHtml = $this->
msg(
'cannotmove', count( $err ) )->parseAsBlock();
390 if ( count( $err ) == 1 ) {
392 $errMsgName = array_shift( $errMsg );
394 if ( $errMsgName ==
'hookaborted' ) {
395 $errMsgHtml .=
"<p>{$errMsg[0]}</p>\n";
397 $errMsgHtml .= $this->
msg( $errMsgName, $errMsg )->parseAsBlock();
402 foreach ( $err as $errMsg ) {
403 if ( $errMsg[0] ==
'hookaborted' ) {
404 $errStr[] = $errMsg[1];
406 $errMsgName = array_shift( $errMsg );
407 $errStr[] = $this->
msg( $errMsgName, $errMsg )->parse();
411 $errMsgHtml .=
'<ul><li>' . implode(
"</li>\n<li>", $errStr ) .
"</li></ul>\n";
413 $out->addHTML( Html::errorBox( $errMsgHtml ) );
416 if ( $this->restrictionStore->isProtected( $this->oldTitle,
'move' ) ) {
417 # Is the title semi-protected?
418 if ( $this->restrictionStore->isSemiProtected( $this->oldTitle,
'move' ) ) {
419 $noticeMsg =
'semiprotectedpagemovewarning';
421 # Then it must be protected based on static groups (regular)
422 $noticeMsg =
'protectedpagemovewarning';
424 LogEventsList::showLogExtract(
429 [
'lim' => 1,
'msgKey' => $noticeMsg ]
436 $immovableNamespaces = [];
437 foreach ( $this->
getLanguage()->getNamespaces() as $nsId => $_ ) {
438 if ( !$this->nsInfo->isMovable( $nsId ) ) {
439 $immovableNamespaces[] = $nsId;
446 $fields[] =
new FieldLayout(
448 'id' =>
'wpNewTitle',
450 'id' =>
'wpNewTitleNs',
451 'name' =>
'wpNewTitleNs',
453 'exclude' => $immovableNamespaces,
456 'id' =>
'wpNewTitleMain',
457 'name' =>
'wpNewTitleMain',
460 'suggestions' =>
false,
465 'label' => $this->msg(
'newtitle' )->text(),
470 $options = Html::listDropdownOptions(
471 $this->
msg(
'movepage-reason-dropdown' )
472 ->page( $this->oldTitle )
473 ->inContentLanguage()
475 [
'other' => $this->
msg(
'movereasonotherlist' )->text() ]
477 $options = Html::listDropdownOptionsOoui( $options );
479 $fields[] =
new FieldLayout(
480 new DropdownInputWidget( [
481 'name' =>
'wpReasonList',
482 'inputId' =>
'wpReasonList',
484 'value' => $this->
getRequest()->getText(
'wpReasonList',
'other' ),
485 'options' => $options,
488 'label' => $this->
msg(
'movereason' )->text(),
496 $fields[] =
new FieldLayout(
497 new TextInputWidget( [
498 'name' =>
'wpReason',
500 'maxLength' => CommentStore::COMMENT_CHARACTER_LIMIT,
502 'value' => $this->
getRequest()->getText(
'wpReason' ),
505 'label' => $this->
msg(
'moveotherreason' )->text(),
510 if ( $considerTalk ) {
511 $fields[] =
new FieldLayout(
512 new CheckboxInputWidget( [
513 'name' =>
'wpMovetalk',
514 'id' =>
'wpMovetalk',
516 'selected' => $this->moveTalk,
519 'label' => $this->
msg(
'movetalk' )->text(),
520 'help' =>
new HtmlSnippet( $this->
msg(
'movepagetalktext' )->parseAsBlock() ),
521 'helpInline' =>
true,
523 'id' =>
'wpMovetalk-field',
528 if ( $this->permManager->userHasRight( $user,
'suppressredirect' ) ) {
529 if ( $handlerSupportsRedirects ) {
536 $fields[] =
new FieldLayout(
537 new CheckboxInputWidget( [
538 'name' =>
'wpLeaveRedirect',
539 'id' =>
'wpLeaveRedirect',
541 'selected' => $isChecked,
542 'disabled' => $isDisabled,
545 'label' => $this->
msg(
'move-leave-redirect' )->text(),
551 if ( $hasRedirects ) {
552 $fields[] =
new FieldLayout(
553 new CheckboxInputWidget( [
554 'name' =>
'wpFixRedirects',
555 'id' =>
'wpFixRedirects',
557 'selected' => $this->fixRedirects,
560 'label' => $this->
msg(
'fix-double-redirects' )->text(),
566 if ( $canMoveSubpage ) {
568 $fields[] =
new FieldLayout(
569 new CheckboxInputWidget( [
570 'name' =>
'wpMovesubpages',
571 'id' =>
'wpMovesubpages',
573 'selected' => $this->moveSubpages,
576 'label' =>
new HtmlSnippet(
578 ( $this->oldTitle->hasSubpages()
580 :
'move-talk-subpages' )
581 )->numParams( $maximumMovedPages )->params( $maximumMovedPages )->parse()
588 # Don't allow watching if user is not logged in
589 if ( $user->isRegistered() ) {
590 $watchChecked = ( $this->watch || $this->userOptionsLookup->getBoolOption( $user,
'watchmoves' )
591 || $this->watchlistManager->isWatched( $user, $this->oldTitle ) );
592 $fields[] =
new FieldLayout(
593 new CheckboxInputWidget( [
595 'id' =>
'watch', # ew
597 'selected' => $watchChecked,
600 'label' => $this->
msg(
'move-watch' )->text(),
608 $hiddenFields .= Html::hidden(
'wpMoveOverSharedFile',
'1' );
612 $fields[] =
new FieldLayout(
613 new CheckboxInputWidget( [
614 'name' =>
'wpDeleteAndMove',
615 'id' =>
'wpDeleteAndMove',
619 'label' => $this->
msg(
'delete_and_move_confirm' )->text(),
625 $fields[] =
new FieldLayout(
626 new ButtonInputWidget( [
628 'value' => $this->
msg(
'movepagebtn' )->text(),
629 'label' => $this->
msg(
'movepagebtn' )->text(),
630 'flags' => [
'primary',
'progressive' ],
638 $fieldset =
new FieldsetLayout( [
639 'label' => $this->
msg(
'move-page-legend' )->text(),
640 'id' =>
'mw-movepage-table',
644 $form =
new FormLayout( [
646 'action' => $this->
getPageTitle()->getLocalURL(
'action=submit' ),
649 $form->appendContent(
653 Html::hidden(
'wpOldTitle', $this->oldTitle->getPrefixedText() ) .
654 Html::hidden(
'wpEditToken', $user->getEditToken() )
660 'classes' => [
'movepage-wrapper' ],
667 if ( $this->
getAuthority()->isAllowed(
'editinterface' ) ) {
669 $this->
msg(
'movepage-reason-dropdown' )->inContentLanguage()->
getTitle(),
670 $this->
msg(
'movepage-edit-reasonlist' )->text(),
672 [
'action' =>
'edit' ]
674 $out->addHTML( Html::rawElement(
'p', [
'class' =>
'mw-movepage-editreasons' ], $link ) );
677 $this->showLogFragment( $this->oldTitle );
678 $this->showSubpages( $this->oldTitle );
681 private function doSubmit() {
684 if ( $user->pingLimiter(
'move' ) ) {
691 # don't allow moving to pages with # in
692 if ( !$nt || $nt->hasFragment() ) {
693 $this->
showForm( [ [
'badtitletext' ] ] );
698 # Show a warning if the target file exists on a shared repo
699 if ( $nt->getNamespace() ===
NS_FILE
700 && !( $this->moveOverShared && $this->permManager->userHasRight( $user,
'reupload-shared' ) )
701 && !$this->repoGroup->getLocalRepo()->findFile( $nt )
702 && $this->repoGroup->findFile( $nt )
704 $this->
showForm( [ [
'file-exists-sharedrepo' ] ] );
709 # Delete to make way if requested
710 if ( $this->deleteAndMove ) {
711 $redir2 = $nt->isSingleRevRedirect();
713 $permErrors = $this->permManager->getPermissionErrors(
714 $redir2 ?
'delete-redirect' :
'delete',
717 if ( count( $permErrors ) ) {
719 if ( count( $this->permManager->getPermissionErrors(
'delete', $user, $nt ) ) ) {
722 $this->
showForm( $permErrors,
true );
732 $this->
showForm( $permErrors,
true );
737 $page = $this->wikiPageFactory->newFromTitle( $nt );
740 if ( $page->isBatchedDelete( 5 ) ) {
741 $this->
showForm( [ [
'movepage-delete-first' ] ] );
746 $reason = $this->
msg(
'delete_and_move_reason', $ot )->inContentLanguage()->text();
749 if ( $nt->getNamespace() ===
NS_FILE ) {
750 $file = $this->repoGroup->getLocalRepo()->newFile( $nt );
751 $file->load( IDBAccessObject::READ_LATEST );
752 if ( $file->exists() ) {
753 $file->deleteFile(
$reason, $user,
false );
758 $deletionLog = $redir2 ?
'delete_redir2' :
'delete';
759 $deleteStatus = $page->doDeleteArticleReal(
760 $reason, $user,
false,
null, $error,
761 null, [], $deletionLog
763 if ( !$deleteStatus->isGood() ) {
764 $this->
showForm( $deleteStatus->getErrorsArray() );
770 $handler = $this->contentHandlerFactory->getContentHandler( $ot->getContentModel() );
772 if ( !$handler->supportsRedirects() ) {
773 $createRedirect =
false;
774 } elseif ( $this->permManager->userHasRight( $user,
'suppressredirect' ) ) {
777 $createRedirect =
true;
780 # Do the actual move.
781 $mp = $this->movePageFactory->newMovePage( $ot, $nt );
783 # check whether the requested actions are permitted / possible
784 $userPermitted = $mp->authorizeMove( $this->
getAuthority(), $this->reason )->isOK();
785 if ( $ot->isTalkPage() || $nt->isTalkPage() ) {
786 $this->moveTalk =
false;
788 if ( $this->moveSubpages ) {
789 $this->moveSubpages = $this->permManager->userCan(
'move-subpages', $user, $ot );
792 $status = $mp->moveIfAllowed( $this->
getAuthority(), $this->reason, $createRedirect );
793 if ( !$status->isOK() ) {
794 $this->
showForm( $status->getErrorsArray(), !$userPermitted );
799 $this->fixRedirects ) {
804 $out->setPageTitleMsg( $this->
msg(
'pagemovedsub' ) );
807 $oldLink = $linkRenderer->makeLink(
810 [
'id' =>
'movepage-oldlink' ],
811 [
'redirect' =>
'no' ]
813 $newLink = $linkRenderer->makeKnownLink(
816 [
'id' =>
'movepage-newlink' ]
818 $oldText = $ot->getPrefixedText();
819 $newText = $nt->getPrefixedText();
821 if ( $status->getValue()[
'redirectRevision'] !==
null ) {
822 $msgName =
'movepage-moved-redirect';
824 $msgName =
'movepage-moved-noredirect';
827 $out->addHTML( $this->
msg(
'movepage-moved' )->rawParams( $oldLink,
828 $newLink )->params( $oldText, $newText )->parseAsBlock() );
829 $out->addWikiMsg( $msgName );
831 $this->
getHookRunner()->onSpecialMovepageAfterMove( $this, $ot, $nt );
848 $dbr = $this->dbProvider->getReplicaDatabase();
849 if ( $this->moveSubpages && (
850 $this->nsInfo->hasSubpages( $nt->getNamespace() ) || (
852 && $this->nsInfo->hasSubpages( $nt->getTalkPage()->getNamespace() )
859 new LikeValue( $ot->getDBkey() .
'/', $dbr->anyString() )
860 )->or(
'page_title',
'=', $ot->getDBkey() )
862 $conds[
'page_namespace'] = [];
863 if ( $this->nsInfo->hasSubpages( $nt->getNamespace() ) ) {
864 $conds[
'page_namespace'][] = $ot->getNamespace();
866 if ( $this->moveTalk &&
867 $this->nsInfo->hasSubpages( $nt->getTalkPage()->getNamespace() )
869 $conds[
'page_namespace'][] = $ot->getTalkPage()->getNamespace();
871 } elseif ( $this->moveTalk ) {
873 'page_namespace' => $ot->getTalkPage()->getNamespace(),
874 'page_title' => $ot->getDBkey()
882 if ( $conds !==
null ) {
883 $extraPages = $this->titleFactory->newTitleArrayFromResult(
884 $dbr->newSelectQueryBuilder()
885 ->select( [
'page_id',
'page_namespace',
'page_title' ] )
888 ->caller( __METHOD__ )->fetchResultSet()
894 foreach ( $extraPages as $oldSubpage ) {
895 if ( $ot->equals( $oldSubpage ) || $nt->equals( $oldSubpage ) ) {
896 # Already did this one.
900 $newPageName = preg_replace(
901 '#^' . preg_quote( $ot->getDBkey(),
'#' ) .
'#',
902 StringUtils::escapeRegexReplacement( $nt->getDBkey() ), # T23234
903 $oldSubpage->getDBkey()
906 if ( $oldSubpage->isSubpage() && ( $ot->isTalkPage() xor $nt->isTalkPage() ) ) {
908 $newNs = $nt->getNamespace();
909 } elseif ( $oldSubpage->isTalkPage() ) {
910 $newNs = $nt->getTalkPage()->getNamespace();
912 $newNs = $nt->getSubjectPage()->getNamespace();
915 # T16385: we need makeTitleSafe because the new page names may
916 # be longer than 255 characters.
917 $newSubpage = Title::makeTitleSafe( $newNs, $newPageName );
918 if ( !$newSubpage ) {
919 $oldLink = $linkRenderer->makeKnownLink( $oldSubpage );
920 $extraOutput[] = $this->
msg(
'movepage-page-unmoved' )->rawParams( $oldLink )
921 ->params( Title::makeName( $newNs, $newPageName ) )->escaped();
925 $mp = $this->movePageFactory->newMovePage( $oldSubpage, $newSubpage );
926 # This was copy-pasted from Renameuser, bleh.
927 if ( $newSubpage->exists() && !$mp->isValidMove()->isOK() ) {
928 $link = $linkRenderer->makeKnownLink( $newSubpage );
929 $extraOutput[] = $this->
msg(
'movepage-page-exists' )->rawParams( $link )->escaped();
931 $status = $mp->moveIfAllowed( $this->
getAuthority(), $this->reason, $createRedirect );
933 if ( $status->isOK() ) {
934 if ( $this->fixRedirects ) {
937 $oldLink = $linkRenderer->makeLink(
941 [
'redirect' =>
'no' ]
944 $newLink = $linkRenderer->makeKnownLink( $newSubpage );
945 $extraOutput[] = $this->
msg(
'movepage-page-moved' )
946 ->rawParams( $oldLink, $newLink )->escaped();
951 if ( $count >= $maximumMovedPages ) {
952 $extraOutput[] = $this->
msg(
'movepage-max-pages' )
953 ->numParams( $maximumMovedPages )->escaped();
957 $oldLink = $linkRenderer->makeKnownLink( $oldSubpage );
958 $newLink = $linkRenderer->makeLink( $newSubpage );
959 $extraOutput[] = $this->
msg(
'movepage-page-unmoved' )
960 ->rawParams( $oldLink, $newLink )->escaped();
965 if ( $extraOutput !== [] ) {
966 $out->addHTML(
"<ul>\n<li>" . implode(
"</li>\n<li>", $extraOutput ) .
"</li>\n</ul>" );
969 # Deal with watches (we don't watch subpages)
970 $this->watchlistManager->setWatch( $this->watch, $this->
getAuthority(), $ot );
971 $this->watchlistManager->setWatch( $this->watch, $this->
getAuthority(), $nt );
974 private function showLogFragment( $title ) {
975 $moveLogPage =
new LogPage(
'move' );
977 $out->addHTML( Xml::element(
'h2',
null, $moveLogPage->getName()->text() ) );
987 private function showSubpages( $title ) {
989 $nsHasSubpages = $this->nsInfo->hasSubpages( $title->getNamespace() );
990 $subpages = $title->getSubpages( $maximumMovedPages + 1 );
991 $count = $subpages instanceof TitleArrayFromResult ? $subpages->count() : 0;
993 $titleIsTalk = $title->isTalkPage();
994 $subpagesTalk = $title->getTalkPage()->getSubpages( $maximumMovedPages + 1 );
995 $countTalk = $subpagesTalk instanceof TitleArrayFromResult ? $subpagesTalk->count() : 0;
996 $totalCount = $count + $countTalk;
998 if ( !$nsHasSubpages && $countTalk == 0 ) {
1004 [
'movesubpage', ( $titleIsTalk ? $count : $totalCount ) ]
1007 if ( $nsHasSubpages ) {
1008 $this->showSubpagesList(
1009 $subpages, $count,
'movesubpagetext',
'movesubpagetext-truncated',
true
1013 if ( !$titleIsTalk && $countTalk > 0 ) {
1014 $this->showSubpagesList(
1015 $subpagesTalk, $countTalk,
'movesubpagetalktext',
'movesubpagetalktext-truncated'
1020 private function showSubpagesList( $subpages, $pagecount, $msg, $truncatedMsg, $noSubpageMsg =
false ) {
1024 if ( $pagecount == 0 && $noSubpageMsg ) {
1025 $out->addWikiMsg(
'movenosubpage' );
1031 if ( $pagecount > $maximumMovedPages ) {
1032 $subpages = $this->truncateSubpagesList( $subpages );
1033 $out->addWikiMsg( $truncatedMsg, $this->
getLanguage()->formatNum( $maximumMovedPages ) );
1035 $out->addWikiMsg( $msg, $this->
getLanguage()->formatNum( $pagecount ) );
1037 $out->addHTML(
"<ul>\n" );
1039 $linkBatch = $this->linkBatchFactory->newLinkBatch( $subpages );
1040 $linkBatch->setCaller( __METHOD__ );
1041 $linkBatch->execute();
1044 foreach ( $subpages as $subpage ) {
1045 $link = $linkRenderer->makeLink( $subpage );
1046 $out->addHTML(
"<li>$link</li>\n" );
1048 $out->addHTML(
"</ul>\n" );
1051 private function truncateSubpagesList( iterable $subpages ): array {
1053 foreach ( $subpages as $subpage ) {
1054 $returnArray[] = $subpage;
1059 return $returnArray;
1071 return $this->prefixSearchString( $search, $limit, $offset, $this->searchEngineFactory );