MediaWiki REL1_40
SpecialAllPages.php
Go to the documentation of this file.
1<?php
31
39
45 protected $maxPerPage = 345;
46
52 protected $nsfromMsg = 'allpagesfrom';
53
55 private $loadBalancer;
56
58 private $searchEngineFactory;
59
61 private $pageStore;
62
63 public function __construct(
64 ILoadBalancer $loadBalancer = null,
65 SearchEngineFactory $searchEngineFactory = null,
66 PageStore $pageStore = null
67 ) {
68 parent::__construct( 'Allpages' );
69 // This class is extended and therefore falls back to global state - T265309
70 $services = MediaWikiServices::getInstance();
71 $this->loadBalancer = $loadBalancer ?? $services->getDBLoadBalancer();
72 $this->searchEngineFactory = $searchEngineFactory ?? $services->getSearchEngineFactory();
73 $this->pageStore = $pageStore ?? $services->getPageStore();
74 }
75
81 public function execute( $par ) {
82 $request = $this->getRequest();
83 $out = $this->getOutput();
84
85 $this->setHeaders();
86 $this->outputHeader();
87 $out->setPreventClickjacking( false );
88
89 # GET values
90 $from = $request->getVal( 'from', null );
91 $to = $request->getVal( 'to', null );
92 $namespace = $request->getInt( 'namespace' );
93
94 $miserMode = (bool)$this->getConfig()->get( MainConfigNames::MiserMode );
95
96 // Redirects filter is disabled in MiserMode
97 $hideredirects = $request->getBool( 'hideredirects', false ) && !$miserMode;
98
99 $namespaces = $this->getLanguage()->getNamespaces();
100
101 $out->setPageTitle(
102 ( $namespace > 0 && array_key_exists( $namespace, $namespaces ) ) ?
103 $this->msg( 'allinnamespace', str_replace( '_', ' ', $namespaces[$namespace] ) ) :
104 $this->msg( 'allarticles' )
105 );
106 $out->addModuleStyles( 'mediawiki.special' );
107
108 if ( $par !== null ) {
109 $this->showChunk( $namespace, $par, $to, $hideredirects );
110 } elseif ( $from !== null && $to === null ) {
111 $this->showChunk( $namespace, $from, $to, $hideredirects );
112 } else {
113 $this->showToplevel( $namespace, $from, $to, $hideredirects );
114 }
115 }
116
125 protected function outputHTMLForm( $namespace = NS_MAIN,
126 $from = '', $to = '', $hideRedirects = false
127 ) {
128 $miserMode = (bool)$this->getConfig()->get( MainConfigNames::MiserMode );
129 $formDescriptor = [
130 'from' => [
131 'type' => 'text',
132 'name' => 'from',
133 'id' => 'nsfrom',
134 'size' => 30,
135 'label-message' => 'allpagesfrom',
136 'default' => str_replace( '_', ' ', $from ),
137 ],
138 'to' => [
139 'type' => 'text',
140 'name' => 'to',
141 'id' => 'nsto',
142 'size' => 30,
143 'label-message' => 'allpagesto',
144 'default' => str_replace( '_', ' ', $to ),
145 ],
146 'namespace' => [
147 'type' => 'namespaceselect',
148 'name' => 'namespace',
149 'id' => 'namespace',
150 'label-message' => 'namespace',
151 'all' => null,
152 'default' => $namespace,
153 ],
154 'hideredirects' => [
155 'type' => 'check',
156 'name' => 'hideredirects',
157 'id' => 'hidredirects',
158 'label-message' => 'allpages-hide-redirects',
159 'value' => $hideRedirects,
160 ],
161 ];
162
163 if ( $miserMode ) {
164 unset( $formDescriptor['hideredirects'] );
165 }
166
167 $htmlForm = HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() );
168 $htmlForm
169 ->setMethod( 'get' )
170 ->setTitle( $this->getPageTitle() ) // Remove subpage
171 ->setWrapperLegendMsg( 'allpages' )
172 ->setSubmitTextMsg( 'allpagessubmit' )
173 ->prepareForm()
174 ->displayForm( false );
175 }
176
183 private function showToplevel(
184 $namespace = NS_MAIN, $from = null, $to = null, $hideredirects = false
185 ) {
186 $from = $from ? Title::makeTitleSafe( $namespace, $from ) : null;
187 $to = $to ? Title::makeTitleSafe( $namespace, $to ) : null;
188 $from = ( $from && $from->isLocal() ) ? $from->getDBkey() : null;
189 $to = ( $to && $to->isLocal() ) ? $to->getDBkey() : null;
190
191 $this->showChunk( $namespace, $from, $to, $hideredirects );
192 }
193
200 private function showChunk(
201 $namespace = NS_MAIN, $from = null, $to = null, $hideredirects = false
202 ) {
203 $output = $this->getOutput();
204
205 $fromList = $this->getNamespaceKeyAndText( $namespace, $from );
206 $toList = $this->getNamespaceKeyAndText( $namespace, $to );
207 $namespaces = $this->getContext()->getLanguage()->getNamespaces();
208 $n = 0;
209 $prevTitle = null;
210
211 if ( !$fromList || !$toList ) {
212 $out = $this->msg( 'allpagesbadtitle' )->parseAsBlock();
213 } elseif ( !array_key_exists( $namespace, $namespaces ) ) {
214 // Show errormessage and reset to NS_MAIN
215 $out = $this->msg( 'allpages-bad-ns', $namespace )->parse();
216 $namespace = NS_MAIN;
217 } else {
218 [ $namespace, $fromKey, $from ] = $fromList;
219 [ , $toKey, $to ] = $toList;
220
221 $dbr = $this->loadBalancer->getConnection( ILoadBalancer::DB_REPLICA );
222 $filterConds = [ 'page_namespace' => $namespace ];
223 if ( $hideredirects ) {
224 $filterConds['page_is_redirect'] = 0;
225 }
226
227 $conds = $filterConds;
228 $conds[] = 'page_title >= ' . $dbr->addQuotes( $fromKey );
229 if ( $toKey !== "" ) {
230 $conds[] = 'page_title <= ' . $dbr->addQuotes( $toKey );
231 }
232
233 $res = $this->pageStore->newSelectQueryBuilder()
234 ->where( $conds )
235 ->caller( __METHOD__ )
236 ->orderBy( 'page_title' )
237 ->limit( $this->maxPerPage + 1 )
238 ->useIndex( 'page_name_title' )
239 ->fetchPageRecords();
240
241 // Eagerly fetch the set of pages to be displayed and warm up LinkCache (T328174).
242 // Note that we can't use fetchPageRecordArray() here as that returns an array keyed
243 // by page IDs; we need a simple sequence.
245 $pages = iterator_to_array( $res );
246
247 $linkRenderer = $this->getLinkRenderer();
248 if ( count( $pages ) > 0 ) {
249 $out = Html::openElement( 'ul', [ 'class' => 'mw-allpages-chunk' ] );
250
251 while ( $n < $this->maxPerPage && $n < count( $pages ) ) {
252 $page = $pages[$n];
253 $attributes = $page->isRedirect() ? [ 'class' => 'allpagesredirect' ] : [];
254
255 $out .= Html::rawElement( 'li', $attributes, $linkRenderer->makeKnownLink( $page ) ) . "\n";
256 $n++;
257 }
258 $out .= Html::closeElement( 'ul' );
259
260 if ( count( $pages ) > 2 ) {
261 // Only apply CSS column styles if there's more than 2 entries.
262 // Otherwise, rendering is broken as "mw-allpages-body"'s CSS column count is 3.
263 $out = Html::rawElement( 'div', [ 'class' => 'mw-allpages-body' ], $out );
264 }
265 } else {
266 $out = '';
267 }
268
269 if ( $fromKey !== '' && !$this->including() ) {
270 # Get the first title from previous chunk
271 $prevConds = $filterConds;
272 $prevConds[] = 'page_title < ' . $dbr->addQuotes( $fromKey );
273 $prevKey = $dbr->selectField(
274 'page',
275 'page_title',
276 $prevConds,
277 __METHOD__,
278 [ 'ORDER BY' => 'page_title DESC', 'OFFSET' => $this->maxPerPage - 1 ]
279 );
280
281 if ( $prevKey === false ) {
282 # The previous chunk is not complete, need to link to the very first title
283 # available in the database
284 $prevKey = $dbr->selectField(
285 'page',
286 'page_title',
287 $prevConds,
288 __METHOD__,
289 [ 'ORDER BY' => 'page_title' ]
290 );
291 }
292
293 if ( $prevKey !== false ) {
294 $prevTitle = Title::makeTitle( $namespace, $prevKey );
295 }
296 }
297 }
298
299 if ( $this->including() ) {
300 $output->addHTML( $out );
301 return;
302 }
303
304 $navLinks = [];
305 $self = $this->getPageTitle();
306
307 $linkRenderer = $this->getLinkRenderer();
308 // Generate a "previous page" link if needed
309 if ( $prevTitle ) {
310 $query = [ 'from' => $prevTitle->getText() ];
311
312 if ( $namespace ) {
313 $query['namespace'] = $namespace;
314 }
315
316 if ( $hideredirects ) {
317 $query['hideredirects'] = $hideredirects;
318 }
319
320 $navLinks[] = $linkRenderer->makeKnownLink(
321 $self,
322 $this->msg( 'prevpage', $prevTitle->getText() )->text(),
323 [],
324 $query
325 );
326
327 }
328
329 // Generate a "next page" link if needed
330 if ( $n === $this->maxPerPage && isset( $pages[$n] ) ) {
331 # $t is the first link of the next chunk
332 $t = TitleValue::newFromPage( $pages[$n] );
333 $query = [ 'from' => $t->getText() ];
334
335 if ( $namespace ) {
336 $query['namespace'] = $namespace;
337 }
338
339 if ( $hideredirects ) {
340 $query['hideredirects'] = $hideredirects;
341 }
342
343 $navLinks[] = $linkRenderer->makeKnownLink(
344 $self,
345 $this->msg( 'nextpage', $t->getText() )->text(),
346 [],
347 $query
348 );
349 }
350
351 $this->outputHTMLForm( $namespace, $from, $to, $hideredirects );
352
353 if ( count( $navLinks ) ) {
354 // Add pagination links
355 $pagination = Html::rawElement( 'div',
356 [ 'class' => 'mw-allpages-nav' ],
357 $this->getLanguage()->pipeList( $navLinks )
358 );
359
360 $output->addHTML( $pagination );
361 $out .= Html::element( 'hr' ) . $pagination; // Footer
362 }
363
364 $output->addHTML( $out );
365 }
366
372 protected function getNamespaceKeyAndText( $ns, $text ) {
373 if ( $text == '' ) {
374 # shortcut for common case
375 return [ $ns, '', '' ];
376 }
377
378 $t = Title::makeTitleSafe( $ns, $text );
379 if ( $t && $t->isLocal() ) {
380 return [ $t->getNamespace(), $t->getDBkey(), $t->getText() ];
381 } elseif ( $t ) {
382 return null;
383 }
384
385 # try again, in case the problem was an empty pagename
386 $text = preg_replace( '/(#|$)/', 'X$1', $text );
387 $t = Title::makeTitleSafe( $ns, $text );
388 if ( $t && $t->isLocal() ) {
389 return [ $t->getNamespace(), '', '' ];
390 } else {
391 return null;
392 }
393 }
394
403 public function prefixSearchSubpages( $search, $limit, $offset ) {
404 return $this->prefixSearchString( $search, $limit, $offset, $this->searchEngineFactory );
405 }
406
407 protected function getGroupName() {
408 return 'pages';
409 }
410}
const NS_MAIN
Definition Defines.php:64
Shortcut to construct an includable special page.
This class is a collection of static functions that serve two purposes:
Definition Html.php:55
A class containing constants representing the names of configuration variables.
Service locator for MediaWiki core services.
Represents a title within MediaWiki.
Definition Title.php:82
Factory class for SearchEngine.
Implements Special:Allpages.
int $maxPerPage
Maximum number of pages to show on single subpage.
__construct(ILoadBalancer $loadBalancer=null, SearchEngineFactory $searchEngineFactory=null, PageStore $pageStore=null)
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
execute( $par)
Entry point : initialise variables and call subfunctions.
prefixSearchSubpages( $search, $limit, $offset)
Return an array of subpages beginning with $search that this special page will accept.
string $nsfromMsg
Determines, which message describes the input field 'nsfrom'.
outputHTMLForm( $namespace=NS_MAIN, $from='', $to='', $hideRedirects=false)
Outputs the HTMLForm used on this page.
getNamespaceKeyAndText( $ns, $text)
outputHeader( $summaryMessageKey='')
Outputs a summary message on top of special pages Per default the message key is the canonical name o...
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!
getOutput()
Get the OutputPage being used for this instance.
getContext()
Gets the context this SpecialPage is executed in.
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getConfig()
Shortcut to get main config object.
getRequest()
Get the WebRequest being used for this instance.
prefixSearchString( $search, $limit, $offset, SearchEngineFactory $searchEngineFactory=null)
Perform a regular substring search for prefixSearchSubpages.
getPageTitle( $subpage=false)
Get a self-referential title object.
getLanguage()
Shortcut to get user's language.
including( $x=null)
Whether the special page is being evaluated via transclusion.
static newFromPage(PageReference $page)
Create a TitleValue from a local PageReference.
Data record representing a page that currently exists as an editable page on a wiki.
This class is a delegate to ILBFactory for a given database cluster.