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 
55 
60  public function __construct(
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() ) ) {
243  $t = Title::newFromRow( $s );
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 factory( $displayFormat, $descriptor, IContextSource $context, $messagePrefix='')
Construct a HTMLForm object for given display type.
Definition: HTMLForm.php:338
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.
MediaWikiServices is the service locator for the application scope of MediaWiki.
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...
showChunk( $namespace=NS_MAIN, $from=null, $to=null, $hideredirects=false)
execute( $par)
Entry point : initialise variables and call subfunctions.
ILoadBalancer $loadBalancer
prefixSearchSubpages( $search, $limit, $offset)
Return an array of subpages beginning with $search that this special page will accept.
showToplevel( $namespace=NS_MAIN, $from=null, $to=null, $hideredirects=false)
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.
SearchEngineFactory $searchEngineFactory
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.
LinkRenderer null $linkRenderer
Definition: SpecialPage.php:81
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:663
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:637
static newFromRow( $row)
Make a Title object from a DB row.
Definition: Title.php:572
$self
Database cluster connection, tracking, load balancing, and transaction manager interface.
foreach( $mmfl['setupFiles'] as $fileName) if( $queue) if(empty( $mmfl['quiet'])) $s
const DB_REPLICA
Definition: defines.php:25