MediaWiki  master
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  [ $namespace, $fromKey, $from ] = $fromList;
214  [ , $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() ) ) {
243  $t = Title::newFromRow( $s );
244  $out .= '<li' .
245  ( $t->isRedirect() ? ' class="allpagesredirect"' : '' ) .
246  '>' .
247  $linkRenderer->makeLink( $t ) .
248  "</li>\n";
249  $n++;
250  }
251  $out .= Html::closeElement( 'ul' );
252 
253  if ( $res->numRows() > 2 ) {
254  // Only apply CSS column styles if there's more than 2 entries.
255  // Otherwise, rendering is broken as "mw-allpages-body"'s CSS column count is 3.
256  $out = Html::rawElement( 'div', [ 'class' => 'mw-allpages-body' ], $out );
257  }
258  } else {
259  $out = '';
260  }
261 
262  if ( $fromKey !== '' && !$this->including() ) {
263  # Get the first title from previous chunk
264  $prevConds = $filterConds;
265  $prevConds[] = 'page_title < ' . $dbr->addQuotes( $fromKey );
266  $prevKey = $dbr->selectField(
267  'page',
268  'page_title',
269  $prevConds,
270  __METHOD__,
271  [ 'ORDER BY' => 'page_title DESC', 'OFFSET' => $this->maxPerPage - 1 ]
272  );
273 
274  if ( $prevKey === false ) {
275  # The previous chunk is not complete, need to link to the very first title
276  # available in the database
277  $prevKey = $dbr->selectField(
278  'page',
279  'page_title',
280  $prevConds,
281  __METHOD__,
282  [ 'ORDER BY' => 'page_title' ]
283  );
284  }
285 
286  if ( $prevKey !== false ) {
287  $prevTitle = Title::makeTitle( $namespace, $prevKey );
288  }
289  }
290  }
291 
292  if ( $this->including() ) {
293  $output->addHTML( $out );
294  return;
295  }
296 
297  $navLinks = [];
298  $self = $this->getPageTitle();
299 
300  $linkRenderer = $this->getLinkRenderer();
301  // Generate a "previous page" link if needed
302  if ( $prevTitle ) {
303  $query = [ 'from' => $prevTitle->getText() ];
304 
305  if ( $namespace ) {
306  $query['namespace'] = $namespace;
307  }
308 
309  if ( $hideredirects ) {
310  $query['hideredirects'] = $hideredirects;
311  }
312 
313  $navLinks[] = $linkRenderer->makeKnownLink(
314  $self,
315  $this->msg( 'prevpage', $prevTitle->getText() )->text(),
316  [],
317  $query
318  );
319 
320  }
321 
322  // Generate a "next page" link if needed
323  // @phan-suppress-next-line PhanPossiblyUndeclaredVariable $res is declared when have maxPerPage
324  if ( $n == $this->maxPerPage && $s = $res->fetchObject() ) {
325  # $s is the first link of the next chunk
326  $t = Title::makeTitle( $namespace, $s->page_title );
327  $query = [ 'from' => $t->getText() ];
328 
329  if ( $namespace ) {
330  $query['namespace'] = $namespace;
331  }
332 
333  if ( $hideredirects ) {
334  $query['hideredirects'] = $hideredirects;
335  }
336 
337  $navLinks[] = $linkRenderer->makeKnownLink(
338  $self,
339  $this->msg( 'nextpage', $t->getText() )->text(),
340  [],
341  $query
342  );
343  }
344 
345  $this->outputHTMLForm( $namespace, $from, $to, $hideredirects );
346 
347  if ( count( $navLinks ) ) {
348  // Add pagination links
349  $pagination = Html::rawElement( 'div',
350  [ 'class' => 'mw-allpages-nav' ],
351  $this->getLanguage()->pipeList( $navLinks )
352  );
353 
354  $output->addHTML( $pagination );
355  $out .= Html::element( 'hr' ) . $pagination; // Footer
356  }
357 
358  $output->addHTML( $out );
359  }
360 
366  protected function getNamespaceKeyAndText( $ns, $text ) {
367  if ( $text == '' ) {
368  # shortcut for common case
369  return [ $ns, '', '' ];
370  }
371 
372  $t = Title::makeTitleSafe( $ns, $text );
373  if ( $t && $t->isLocal() ) {
374  return [ $t->getNamespace(), $t->getDBkey(), $t->getText() ];
375  } elseif ( $t ) {
376  return null;
377  }
378 
379  # try again, in case the problem was an empty pagename
380  $text = preg_replace( '/(#|$)/', 'X$1', $text );
381  $t = Title::makeTitleSafe( $ns, $text );
382  if ( $t && $t->isLocal() ) {
383  return [ $t->getNamespace(), '', '' ];
384  } else {
385  return null;
386  }
387  }
388 
397  public function prefixSearchSubpages( $search, $limit, $offset ) {
398  return $this->prefixSearchString( $search, $limit, $offset, $this->searchEngineFactory );
399  }
400 
401  protected function getGroupName() {
402  return 'pages';
403  }
404 }
const NS_MAIN
Definition: Defines.php:64
static factory( $displayFormat, $descriptor, IContextSource $context, $messagePrefix='')
Construct a HTMLForm object for given display type.
Definition: HTMLForm.php:349
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 makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:667
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:641
static newFromRow( $row)
Make a Title object from a DB row.
Definition: Title.php:576
$self
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
const DB_REPLICA
Definition: defines.php:26