MediaWiki master
SpecialAllPages.php
Go to the documentation of this file.
1<?php
7namespace MediaWiki\Specials;
8
21
29
35 protected $maxPerPage = 345;
36
42 protected $nsfromMsg = 'allpagesfrom';
43
44 private IConnectionProvider $dbProvider;
45 private SearchEngineFactory $searchEngineFactory;
46 private PageStore $pageStore;
47
48 public function __construct(
49 ?IConnectionProvider $dbProvider = null,
50 ?SearchEngineFactory $searchEngineFactory = null,
51 ?PageStore $pageStore = null
52 ) {
53 parent::__construct( 'Allpages' );
54 // This class is extended and therefore falls back to global state - T265309
56 $this->dbProvider = $dbProvider ?? $services->getConnectionProvider();
57 $this->searchEngineFactory = $searchEngineFactory ?? $services->getSearchEngineFactory();
58 $this->pageStore = $pageStore ?? $services->getPageStore();
59 }
60
66 public function execute( $par ) {
67 $request = $this->getRequest();
68 $out = $this->getOutput();
69
70 $this->setHeaders();
71 $this->outputHeader();
72 $out->getMetadata()->setPreventClickjacking( false );
73
74 # GET values
75 $from = $request->getVal( 'from', null );
76 $to = $request->getVal( 'to', null );
77 $namespace = $request->getInt( 'namespace' );
78
79 $miserMode = (bool)$this->getConfig()->get( MainConfigNames::MiserMode );
80
81 // Redirects filter is disabled in MiserMode
82 $hideredirects = $request->getBool( 'hideredirects', false ) && !$miserMode;
83
84 $namespaces = $this->getLanguage()->getNamespaces();
85
86 $out->setPageTitleMsg(
87 ( $namespace > 0 && array_key_exists( $namespace, $namespaces ) ) ?
88 $this->msg( 'allinnamespace' )->plaintextParams( str_replace( '_', ' ', $namespaces[$namespace] ) ) :
89 $this->msg( 'allarticles' )
90 );
91 $out->addModuleStyles( 'mediawiki.special' );
92
93 if ( $par !== null ) {
94 $this->showChunk( $namespace, $par, $to, $hideredirects );
95 } elseif ( $from !== null && $to === null ) {
96 $this->showChunk( $namespace, $from, $to, $hideredirects );
97 } else {
98 $this->showToplevel( $namespace, $from, $to, $hideredirects );
99 }
100 }
101
110 protected function outputHTMLForm( $namespace = NS_MAIN,
111 $from = '', $to = '', $hideRedirects = false
112 ) {
113 $miserMode = (bool)$this->getConfig()->get( MainConfigNames::MiserMode );
114 $formDescriptor = [
115 'from' => [
116 'type' => 'text',
117 'name' => 'from',
118 'id' => 'nsfrom',
119 'size' => 30,
120 'label-message' => 'allpagesfrom',
121 'default' => str_replace( '_', ' ', $from ),
122 ],
123 'to' => [
124 'type' => 'text',
125 'name' => 'to',
126 'id' => 'nsto',
127 'size' => 30,
128 'label-message' => 'allpagesto',
129 'default' => str_replace( '_', ' ', $to ),
130 ],
131 'namespace' => [
132 'type' => 'namespaceselect',
133 'name' => 'namespace',
134 'id' => 'namespace',
135 'label-message' => 'namespace',
136 'all' => null,
137 'default' => $namespace,
138 ],
139 'hideredirects' => [
140 'type' => 'check',
141 'name' => 'hideredirects',
142 'id' => 'hidredirects',
143 'label-message' => 'allpages-hide-redirects',
144 'value' => $hideRedirects,
145 ],
146 ];
147
148 if ( $miserMode ) {
149 unset( $formDescriptor['hideredirects'] );
150 }
151
152 $htmlForm = HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() );
153 $htmlForm
154 ->setMethod( 'get' )
155 ->setTitle( $this->getPageTitle() ) // Remove subpage
156 ->setWrapperLegendMsg( 'allpages' )
157 ->setSubmitTextMsg( 'allpagessubmit' )
158 ->prepareForm()
159 ->displayForm( false );
160 }
161
168 private function showToplevel(
169 $namespace = NS_MAIN, $from = null, $to = null, $hideredirects = false
170 ) {
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;
175
176 $this->showChunk( $namespace, $from, $to, $hideredirects );
177 }
178
185 private function showChunk(
186 $namespace = NS_MAIN, $from = null, $to = null, $hideredirects = false
187 ) {
188 $output = $this->getOutput();
189
190 $fromList = $this->getNamespaceKeyAndText( $namespace, $from );
191 $toList = $this->getNamespaceKeyAndText( $namespace, $to );
192 $namespaces = $this->getContext()->getLanguage()->getNamespaces();
193 $n = 0;
194 $prevTitle = null;
195
196 if ( !$fromList || !$toList ) {
197 $out = $this->msg( 'allpagesbadtitle' )->parseAsBlock();
198 } elseif ( !array_key_exists( $namespace, $namespaces ) ) {
199 // Show errormessage and reset to NS_MAIN
200 $out = $this->msg( 'allpages-bad-ns', $namespace )->parse();
201 $namespace = NS_MAIN;
202 } else {
203 [ $namespace, $fromKey, $from ] = $fromList;
204 [ , $toKey, $to ] = $toList;
205
206 $dbr = $this->dbProvider->getReplicaDatabase();
207 $filterConds = [ 'page_namespace' => $namespace ];
208 if ( $hideredirects ) {
209 $filterConds['page_is_redirect'] = 0;
210 }
211
212 $conds = $filterConds;
213 $conds[] = $dbr->expr( 'page_title', '>=', $fromKey );
214 if ( $toKey !== "" ) {
215 $conds[] = $dbr->expr( 'page_title', '<=', $toKey );
216 }
217
218 $res = $this->pageStore->newSelectQueryBuilder()
219 ->where( $conds )
220 ->caller( __METHOD__ )
221 ->orderBy( 'page_title' )
222 ->limit( $this->maxPerPage + 1 )
223 ->useIndex( 'page_name_title' )
224 ->fetchPageRecords();
225
226 // Eagerly fetch the set of pages to be displayed and warm up LinkCache (T328174).
227 // Note that we can't use fetchPageRecordArray() here as that returns an array keyed
228 // by page IDs; we need a simple sequence.
230 $pages = iterator_to_array( $res );
231
232 $linkRenderer = $this->getLinkRenderer();
233 if ( count( $pages ) > 0 ) {
234 $out = Html::openElement( 'ul', [ 'class' => 'mw-allpages-chunk' ] );
235
236 while ( $n < $this->maxPerPage && $n < count( $pages ) ) {
237 $page = $pages[$n];
238 $attributes = $page->isRedirect() ? [ 'class' => 'allpagesredirect' ] : [];
239
240 $out .= Html::rawElement( 'li', $attributes, $linkRenderer->makeKnownLink( $page ) ) . "\n";
241 $n++;
242 }
243 $out .= Html::closeElement( 'ul' );
244
245 if ( count( $pages ) > 2 ) {
246 // Only apply CSS column styles if there's more than 2 entries.
247 // Otherwise, rendering is broken as "mw-allpages-body"'s CSS column count is 3.
248 $out = Html::rawElement( 'div', [ 'class' => 'mw-allpages-body' ], $out );
249 }
250 } else {
251 $out = '';
252 }
253
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' )
260 ->from( 'page' )
261 ->where( $prevConds )
262 ->orderBy( 'page_title', SelectQueryBuilder::SORT_DESC )
263 ->offset( $this->maxPerPage - 1 )
264 ->caller( __METHOD__ )->fetchField();
265
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' )
271 ->from( 'page' )
272 ->where( $prevConds )
273 ->orderBy( 'page_title' )
274 ->caller( __METHOD__ )->fetchField();
275 }
276
277 if ( $prevKey !== false ) {
278 $prevTitle = Title::makeTitle( $namespace, $prevKey );
279 }
280 }
281 }
282
283 if ( $this->including() ) {
284 $output->addHTML( $out );
285 return;
286 }
287
288 $navLinks = [];
289 $self = $this->getPageTitle();
290
291 $linkRenderer = $this->getLinkRenderer();
292 // Generate a "previous page" link if needed
293 if ( $prevTitle ) {
294 $query = [ 'from' => $prevTitle->getText() ];
295
296 if ( $namespace ) {
297 $query['namespace'] = $namespace;
298 }
299
300 if ( $hideredirects ) {
301 $query['hideredirects'] = $hideredirects;
302 }
303
304 $navLinks[] = $linkRenderer->makeKnownLink(
305 $self,
306 $this->msg( 'prevpage', $prevTitle->getText() )->text(),
307 [],
308 $query
309 );
310
311 }
312
313 // Generate a "next page" link if needed
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() ];
318
319 if ( $namespace ) {
320 $query['namespace'] = $namespace;
321 }
322
323 if ( $hideredirects ) {
324 $query['hideredirects'] = $hideredirects;
325 }
326
327 $navLinks[] = $linkRenderer->makeKnownLink(
328 $self,
329 $this->msg( 'nextpage', $t->getText() )->text(),
330 [],
331 $query
332 );
333 }
334
335 $this->outputHTMLForm( $namespace, $from ?? '', $to ?? '', $hideredirects );
336
337 if ( count( $navLinks ) ) {
338 // Add pagination links
339 $pagination = Html::rawElement( 'div',
340 [ 'class' => 'mw-allpages-nav' ],
341 $this->getLanguage()->pipeList( $navLinks )
342 );
343
344 $output->addHTML( $pagination );
345 $out .= Html::element( 'hr' ) . $pagination; // Footer
346 }
347
348 $output->addHTML( $out );
349 }
350
356 protected function getNamespaceKeyAndText( $ns, $text ) {
357 if ( $text == '' ) {
358 # shortcut for common case
359 return [ $ns, '', '' ];
360 }
361
362 $t = Title::makeTitleSafe( $ns, $text );
363 if ( $t && $t->isLocal() ) {
364 return [ $t->getNamespace(), $t->getDBkey(), $t->getText() ];
365 } elseif ( $t ) {
366 return null;
367 }
368
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(), '', '' ];
374 } else {
375 return null;
376 }
377 }
378
387 public function prefixSearchSubpages( $search, $limit, $offset ) {
388 return $this->prefixSearchString( $search, $limit, $offset, $this->searchEngineFactory );
389 }
390
392 protected function getGroupName() {
393 return 'pages';
394 }
395}
396
398class_alias( SpecialAllPages::class, 'SpecialAllPages' );
const NS_MAIN
Definition Defines.php:51
makeTitle( $linkId)
Convert a link ID to a Title.to override Title
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition HTMLForm.php:195
This class is a collection of static functions that serve two purposes:
Definition Html.php:43
A class containing constants representing the names of configuration variables.
const MiserMode
Name constant for the MiserMode setting, for use with Config::get()
Service locator for MediaWiki core services.
static getInstance()
Returns the global default instance of the top level service locator.
Factory class for SearchEngine.
Shortcut to construct an includable special page.
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!
getPageTitle( $subpage=false)
Get a self-referential title object.
getConfig()
Shortcut to get main config object.
getContext()
Gets the context this SpecialPage is executed in.
getRequest()
Get the WebRequest being used for this instance.
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getOutput()
Get the OutputPage being used for this instance.
prefixSearchString( $search, $limit, $offset, ?SearchEngineFactory $searchEngineFactory=null)
Perform a regular substring search for prefixSearchSubpages.
including( $x=null)
Whether the special page is being evaluated via transclusion.
getLanguage()
Shortcut to get user's language.
outputHeader( $summaryMessageKey='')
Outputs a summary message on top of special pages By default the message key is the canonical name of...
Implements Special:Allpages.
string $nsfromMsg
Determines, which message describes the input field 'nsfrom'.
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
int $maxPerPage
Maximum number of pages to show on single subpage.
prefixSearchSubpages( $search, $limit, $offset)
Return an array of subpages beginning with $search that this special page will accept.
execute( $par)
Entry point : initialise variables and call subfunctions.
outputHTMLForm( $namespace=NS_MAIN, $from='', $to='', $hideRedirects=false)
Outputs the HTMLForm used on this page.
__construct(?IConnectionProvider $dbProvider=null, ?SearchEngineFactory $searchEngineFactory=null, ?PageStore $pageStore=null)
Represents the target of a wiki link.
Represents a title within MediaWiki.
Definition Title.php:69
Build SELECT queries with a fluent interface.
Data record representing a page that currently exists as an editable page on a wiki.
Provide primary and replica IDatabase connections.
element(SerializerNode $parent, SerializerNode $node, $contents)