53 parent::__construct(
'Allpages' );
56 $this->dbProvider = $dbProvider ?? $services->getConnectionProvider();
57 $this->searchEngineFactory = $searchEngineFactory ?? $services->getSearchEngineFactory();
58 $this->pageStore = $pageStore ?? $services->getPageStore();
72 $out->getMetadata()->setPreventClickjacking(
false );
75 $from = $request->getVal(
'from',
null );
76 $to = $request->getVal(
'to',
null );
77 $namespace = $request->getInt(
'namespace' );
82 $hideredirects = $request->getBool(
'hideredirects',
false ) && !$miserMode;
84 $namespaces = $this->
getLanguage()->getNamespaces();
86 $out->setPageTitleMsg(
87 ( $namespace > 0 && array_key_exists( $namespace, $namespaces ) ) ?
88 $this->
msg(
'allinnamespace' )->plaintextParams( str_replace(
'_',
' ', $namespaces[$namespace] ) ) :
89 $this->
msg(
'allarticles' )
91 $out->addModuleStyles(
'mediawiki.special' );
93 if ( $par !==
null ) {
94 $this->showChunk( $namespace, $par, $to, $hideredirects );
95 } elseif ( $from !==
null && $to ===
null ) {
96 $this->showChunk( $namespace, $from, $to, $hideredirects );
98 $this->showToplevel( $namespace, $from, $to, $hideredirects );
111 $from =
'', $to =
'', $hideRedirects =
false
120 'label-message' =>
'allpagesfrom',
121 'default' => str_replace(
'_',
' ', $from ),
128 'label-message' =>
'allpagesto',
129 'default' => str_replace(
'_',
' ', $to ),
132 'type' =>
'namespaceselect',
133 'name' =>
'namespace',
135 'label-message' =>
'namespace',
137 'default' => $namespace,
141 'name' =>
'hideredirects',
142 'id' =>
'hidredirects',
143 'label-message' =>
'allpages-hide-redirects',
144 'value' => $hideRedirects,
149 unset( $formDescriptor[
'hideredirects'] );
152 $htmlForm = HTMLForm::factory(
'ooui', $formDescriptor, $this->
getContext() );
156 ->setWrapperLegendMsg(
'allpages' )
157 ->setSubmitTextMsg(
'allpagessubmit' )
159 ->displayForm(
false );
168 private function showToplevel(
169 $namespace =
NS_MAIN, $from =
null, $to =
null, $hideredirects =
false
171 $from = $from ? Title::makeTitleSafe( $namespace, $from ) : null;
172 $to = $to ? Title::makeTitleSafe( $namespace, $to ) : null;
173 $from = ( $from && $from->isLocal() ) ? $from->getDBkey() :
null;
174 $to = ( $to && $to->isLocal() ) ? $to->getDBkey() :
null;
176 $this->showChunk( $namespace, $from, $to, $hideredirects );
185 private function showChunk(
186 $namespace =
NS_MAIN, $from =
null, $to =
null, $hideredirects =
false
192 $namespaces = $this->
getContext()->getLanguage()->getNamespaces();
196 if ( !$fromList || !$toList ) {
197 $out = $this->
msg(
'allpagesbadtitle' )->parseAsBlock();
198 } elseif ( !array_key_exists( $namespace, $namespaces ) ) {
200 $out = $this->
msg(
'allpages-bad-ns', $namespace )->parse();
203 [ $namespace, $fromKey, $from ] = $fromList;
204 [ , $toKey, $to ] = $toList;
206 $dbr = $this->dbProvider->getReplicaDatabase();
207 $filterConds = [
'page_namespace' => $namespace ];
208 if ( $hideredirects ) {
209 $filterConds[
'page_is_redirect'] = 0;
212 $conds = $filterConds;
213 $conds[] = $dbr->expr(
'page_title',
'>=', $fromKey );
214 if ( $toKey !==
"" ) {
215 $conds[] = $dbr->expr(
'page_title',
'<=', $toKey );
218 $res = $this->pageStore->newSelectQueryBuilder()
220 ->caller( __METHOD__ )
221 ->orderBy(
'page_title' )
222 ->limit( $this->maxPerPage + 1 )
223 ->useIndex(
'page_name_title' )
224 ->fetchPageRecords();
230 $pages = iterator_to_array( $res );
233 if ( count( $pages ) > 0 ) {
234 $out = Html::openElement(
'ul', [
'class' =>
'mw-allpages-chunk' ] );
236 while ( $n < $this->maxPerPage && $n < count( $pages ) ) {
238 $attributes = $page->isRedirect() ? [
'class' =>
'allpagesredirect' ] : [];
240 $out .= Html::rawElement(
'li', $attributes, $linkRenderer->makeKnownLink( $page ) ) .
"\n";
243 $out .= Html::closeElement(
'ul' );
245 if ( count( $pages ) > 2 ) {
248 $out = Html::rawElement(
'div', [
'class' =>
'mw-allpages-body' ], $out );
254 if ( $fromKey !==
'' && !$this->
including() ) {
255 # Get the first title from previous chunk
256 $prevConds = $filterConds;
257 $prevConds[] = $dbr->expr(
'page_title',
'<', $fromKey );
258 $prevKey = $dbr->newSelectQueryBuilder()
259 ->select(
'page_title' )
261 ->where( $prevConds )
262 ->orderBy(
'page_title', SelectQueryBuilder::SORT_DESC )
263 ->offset( $this->maxPerPage - 1 )
264 ->caller( __METHOD__ )->fetchField();
266 if ( $prevKey ===
false ) {
267 # The previous chunk is not complete, need to link to the very first title
268 # available in the database
269 $prevKey = $dbr->newSelectQueryBuilder()
270 ->select(
'page_title' )
272 ->where( $prevConds )
273 ->orderBy(
'page_title' )
274 ->caller( __METHOD__ )->fetchField();
277 if ( $prevKey !==
false ) {
284 $output->addHTML( $out );
294 $query = [
'from' => $prevTitle->getText() ];
297 $query[
'namespace'] = $namespace;
300 if ( $hideredirects ) {
301 $query[
'hideredirects'] = $hideredirects;
304 $navLinks[] = $linkRenderer->makeKnownLink(
306 $this->
msg(
'prevpage', $prevTitle->getText() )->text(),
314 if ( $n === $this->maxPerPage && isset( $pages[$n] ) ) {
315 # $t is the first link of the next chunk
316 $t = TitleValue::newFromPage( $pages[$n] );
317 $query = [
'from' =>
$t->getText() ];
320 $query[
'namespace'] = $namespace;
323 if ( $hideredirects ) {
324 $query[
'hideredirects'] = $hideredirects;
327 $navLinks[] = $linkRenderer->makeKnownLink(
329 $this->
msg(
'nextpage',
$t->getText() )->text(),
335 $this->
outputHTMLForm( $namespace, $from ??
'', $to ??
'', $hideredirects );
337 if ( count( $navLinks ) ) {
339 $pagination = Html::rawElement(
'div',
340 [
'class' =>
'mw-allpages-nav' ],
344 $output->addHTML( $pagination );
348 $output->addHTML( $out );
358 # shortcut for common case
359 return [ $ns,
'',
'' ];
362 $t = Title::makeTitleSafe( $ns, $text );
363 if ( $t && $t->isLocal() ) {
364 return [ $t->getNamespace(), $t->getDBkey(), $t->getText() ];
369 # try again, in case the problem was an empty pagename
370 $text = preg_replace(
'/(#|$)/',
'X$1', $text );
371 $t = Title::makeTitleSafe( $ns, $text );
372 if ( $t && $t->isLocal() ) {
373 return [ $t->getNamespace(),
'',
'' ];
388 return $this->
prefixSearchString( $search, $limit, $offset, $this->searchEngineFactory );