MediaWiki REL1_39
SpecialAllPages.php
Go to the documentation of this file.
1<?php
27
35
41 protected $maxPerPage = 345;
42
48 protected $nsfromMsg = 'allpagesfrom';
49
51 private $loadBalancer;
52
54 private $searchEngineFactory;
55
60 public function __construct(
61 ILoadBalancer $loadBalancer = null,
62 SearchEngineFactory $searchEngineFactory = null
63 ) {
64 parent::__construct( 'Allpages' );
65 // This class is extended and therefore falls back to global state - T265309
66 $services = MediaWikiServices::getInstance();
67 $this->loadBalancer = $loadBalancer ?? $services->getDBLoadBalancer();
68 $this->searchEngineFactory = $searchEngineFactory ?? $services->getSearchEngineFactory();
69 }
70
76 public function execute( $par ) {
77 $request = $this->getRequest();
78 $out = $this->getOutput();
79
80 $this->setHeaders();
81 $this->outputHeader();
82 $out->setPreventClickjacking( false );
83
84 # GET values
85 $from = $request->getVal( 'from', null );
86 $to = $request->getVal( 'to', null );
87 $namespace = $request->getInt( 'namespace' );
88
89 $miserMode = (bool)$this->getConfig()->get( MainConfigNames::MiserMode );
90
91 // Redirects filter is disabled in MiserMode
92 $hideredirects = $request->getBool( 'hideredirects', false ) && !$miserMode;
93
94 $namespaces = $this->getLanguage()->getNamespaces();
95
96 $out->setPageTitle(
97 ( $namespace > 0 && array_key_exists( $namespace, $namespaces ) ) ?
98 $this->msg( 'allinnamespace', str_replace( '_', ' ', $namespaces[$namespace] ) ) :
99 $this->msg( 'allarticles' )
100 );
101 $out->addModuleStyles( 'mediawiki.special' );
102
103 if ( $par !== null ) {
104 $this->showChunk( $namespace, $par, $to, $hideredirects );
105 } elseif ( $from !== null && $to === null ) {
106 $this->showChunk( $namespace, $from, $to, $hideredirects );
107 } else {
108 $this->showToplevel( $namespace, $from, $to, $hideredirects );
109 }
110 }
111
120 protected function outputHTMLForm( $namespace = NS_MAIN,
121 $from = '', $to = '', $hideRedirects = false
122 ) {
123 $miserMode = (bool)$this->getConfig()->get( MainConfigNames::MiserMode );
124 $formDescriptor = [
125 'from' => [
126 'type' => 'text',
127 'name' => 'from',
128 'id' => 'nsfrom',
129 'size' => 30,
130 'label-message' => 'allpagesfrom',
131 'default' => str_replace( '_', ' ', $from ),
132 ],
133 'to' => [
134 'type' => 'text',
135 'name' => 'to',
136 'id' => 'nsto',
137 'size' => 30,
138 'label-message' => 'allpagesto',
139 'default' => str_replace( '_', ' ', $to ),
140 ],
141 'namespace' => [
142 'type' => 'namespaceselect',
143 'name' => 'namespace',
144 'id' => 'namespace',
145 'label-message' => 'namespace',
146 'all' => null,
147 'default' => $namespace,
148 ],
149 'hideredirects' => [
150 'type' => 'check',
151 'name' => 'hideredirects',
152 'id' => 'hidredirects',
153 'label-message' => 'allpages-hide-redirects',
154 'value' => $hideRedirects,
155 ],
156 ];
157
158 if ( $miserMode ) {
159 unset( $formDescriptor['hideredirects'] );
160 }
161
162 $htmlForm = HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() );
163 $htmlForm
164 ->setMethod( 'get' )
165 ->setTitle( $this->getPageTitle() ) // Remove subpage
166 ->setWrapperLegendMsg( 'allpages' )
167 ->setSubmitTextMsg( 'allpagessubmit' )
168 ->prepareForm()
169 ->displayForm( false );
170 }
171
178 private function showToplevel(
179 $namespace = NS_MAIN, $from = null, $to = null, $hideredirects = false
180 ) {
181 $from = $from ? Title::makeTitleSafe( $namespace, $from ) : null;
182 $to = $to ? Title::makeTitleSafe( $namespace, $to ) : null;
183 $from = ( $from && $from->isLocal() ) ? $from->getDBkey() : null;
184 $to = ( $to && $to->isLocal() ) ? $to->getDBkey() : null;
185
186 $this->showChunk( $namespace, $from, $to, $hideredirects );
187 }
188
195 private function showChunk(
196 $namespace = NS_MAIN, $from = null, $to = null, $hideredirects = false
197 ) {
198 $output = $this->getOutput();
199
200 $fromList = $this->getNamespaceKeyAndText( $namespace, $from );
201 $toList = $this->getNamespaceKeyAndText( $namespace, $to );
202 $namespaces = $this->getContext()->getLanguage()->getNamespaces();
203 $n = 0;
204 $prevTitle = null;
205
206 if ( !$fromList || !$toList ) {
207 $out = $this->msg( 'allpagesbadtitle' )->parseAsBlock();
208 } elseif ( !array_key_exists( $namespace, $namespaces ) ) {
209 // Show errormessage and reset to NS_MAIN
210 $out = $this->msg( 'allpages-bad-ns', $namespace )->parse();
211 $namespace = NS_MAIN;
212 } else {
213 list( $namespace, $fromKey, $from ) = $fromList;
214 list( , $toKey, $to ) = $toList;
215
216 $dbr = $this->loadBalancer->getConnectionRef( ILoadBalancer::DB_REPLICA );
217 $filterConds = [ 'page_namespace' => $namespace ];
218 if ( $hideredirects ) {
219 $filterConds['page_is_redirect'] = 0;
220 }
221
222 $conds = $filterConds;
223 $conds[] = 'page_title >= ' . $dbr->addQuotes( $fromKey );
224 if ( $toKey !== "" ) {
225 $conds[] = 'page_title <= ' . $dbr->addQuotes( $toKey );
226 }
227 $res = $dbr->select( 'page',
228 [ 'page_namespace', 'page_title', 'page_is_redirect', 'page_id' ],
229 $conds,
230 __METHOD__,
231 [
232 'ORDER BY' => 'page_title',
233 'LIMIT' => $this->maxPerPage + 1,
234 'USE INDEX' => 'page_name_title',
235 ]
236 );
237
238 $linkRenderer = $this->getLinkRenderer();
239 if ( $res->numRows() > 0 ) {
240 $out = Html::openElement( 'ul', [ 'class' => 'mw-allpages-chunk' ] );
241
242 while ( ( $n < $this->maxPerPage ) && ( $s = $res->fetchObject() ) ) {
244 if ( $t ) {
245 $out .= '<li' .
246 ( $s->page_is_redirect ? ' class="allpagesredirect"' : '' ) .
247 '>' .
248 $linkRenderer->makeLink( $t ) .
249 "</li>\n";
250 } else {
251 $out .= '<li>[[' . htmlspecialchars( $s->page_title ) . "]]</li>\n";
252 }
253 $n++;
254 }
255 $out .= Html::closeElement( 'ul' );
256
257 if ( $res->numRows() > 2 ) {
258 // Only apply CSS column styles if there's more than 2 entries.
259 // Otherwise, rendering is broken as "mw-allpages-body"'s CSS column count is 3.
260 $out = Html::rawElement( 'div', [ 'class' => 'mw-allpages-body' ], $out );
261 }
262 } else {
263 $out = '';
264 }
265
266 if ( $fromKey !== '' && !$this->including() ) {
267 # Get the first title from previous chunk
268 $prevConds = $filterConds;
269 $prevConds[] = 'page_title < ' . $dbr->addQuotes( $fromKey );
270 $prevKey = $dbr->selectField(
271 'page',
272 'page_title',
273 $prevConds,
274 __METHOD__,
275 [ 'ORDER BY' => 'page_title DESC', 'OFFSET' => $this->maxPerPage - 1 ]
276 );
277
278 if ( $prevKey === false ) {
279 # The previous chunk is not complete, need to link to the very first title
280 # available in the database
281 $prevKey = $dbr->selectField(
282 'page',
283 'page_title',
284 $prevConds,
285 __METHOD__,
286 [ 'ORDER BY' => 'page_title' ]
287 );
288 }
289
290 if ( $prevKey !== false ) {
291 $prevTitle = Title::makeTitle( $namespace, $prevKey );
292 }
293 }
294 }
295
296 if ( $this->including() ) {
297 $output->addHTML( $out );
298 return;
299 }
300
301 $navLinks = [];
302 $self = $this->getPageTitle();
303
304 $linkRenderer = $this->getLinkRenderer();
305 // Generate a "previous page" link if needed
306 if ( $prevTitle ) {
307 $query = [ 'from' => $prevTitle->getText() ];
308
309 if ( $namespace ) {
310 $query['namespace'] = $namespace;
311 }
312
313 if ( $hideredirects ) {
314 $query['hideredirects'] = $hideredirects;
315 }
316
317 $navLinks[] = $linkRenderer->makeKnownLink(
318 $self,
319 $this->msg( 'prevpage', $prevTitle->getText() )->text(),
320 [],
321 $query
322 );
323
324 }
325
326 // Generate a "next page" link if needed
327 // @phan-suppress-next-line PhanPossiblyUndeclaredVariable $res is declared when have maxPerPage
328 if ( $n == $this->maxPerPage && $s = $res->fetchObject() ) {
329 # $s is the first link of the next chunk
330 $t = Title::makeTitle( $namespace, $s->page_title );
331 $query = [ 'from' => $t->getText() ];
332
333 if ( $namespace ) {
334 $query['namespace'] = $namespace;
335 }
336
337 if ( $hideredirects ) {
338 $query['hideredirects'] = $hideredirects;
339 }
340
341 $navLinks[] = $linkRenderer->makeKnownLink(
342 $self,
343 $this->msg( 'nextpage', $t->getText() )->text(),
344 [],
345 $query
346 );
347 }
348
349 $this->outputHTMLForm( $namespace, $from ?? '', $to ?? '', $hideredirects );
350
351 if ( count( $navLinks ) ) {
352 // Add pagination links
353 $pagination = Html::rawElement( 'div',
354 [ 'class' => 'mw-allpages-nav' ],
355 $this->getLanguage()->pipeList( $navLinks )
356 );
357
358 $output->addHTML( $pagination );
359 $out .= Html::element( 'hr' ) . $pagination; // Footer
360 }
361
362 $output->addHTML( $out );
363 }
364
370 protected function getNamespaceKeyAndText( $ns, $text ) {
371 if ( $text == '' ) {
372 # shortcut for common case
373 return [ $ns, '', '' ];
374 }
375
376 $t = Title::makeTitleSafe( $ns, $text );
377 if ( $t && $t->isLocal() ) {
378 return [ $t->getNamespace(), $t->getDBkey(), $t->getText() ];
379 } elseif ( $t ) {
380 return null;
381 }
382
383 # try again, in case the problem was an empty pagename
384 $text = preg_replace( '/(#|$)/', 'X$1', $text );
385 $t = Title::makeTitleSafe( $ns, $text );
386 if ( $t && $t->isLocal() ) {
387 return [ $t->getNamespace(), '', '' ];
388 } else {
389 return null;
390 }
391 }
392
401 public function prefixSearchSubpages( $search, $limit, $offset ) {
402 return $this->prefixSearchString( $search, $limit, $offset, $this->searchEngineFactory );
403 }
404
405 protected function getGroupName() {
406 return 'pages';
407 }
408}
const NS_MAIN
Definition Defines.php:64
static element( $element, $attribs=[], $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition Html.php:236
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition Html.php:214
static openElement( $element, $attribs=[])
Identical to rawElement(), but has no third parameter and omits the end tag (and the self-closing '/'...
Definition Html.php:256
static closeElement( $element)
Returns "</$element>".
Definition Html.php:320
Shortcut to construct an includable special page.
A class containing constants representing the names of configuration variables.
Service locator for MediaWiki core services.
Factory class for SearchEngine.
Implements Special:Allpages.
int $maxPerPage
Maximum number of pages to show on single subpage.
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'.
__construct(ILoadBalancer $loadBalancer=null, SearchEngineFactory $searchEngineFactory=null)
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 makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition Title.php:638
static newFromRow( $row)
Make a Title object from a DB row.
Definition Title.php:573
Create and track the database connections and transactions for a given database cluster.
foreach( $mmfl['setupFiles'] as $fileName) if($queue) if(empty( $mmfl['quiet'])) $s