MediaWiki  master
SpecialPrefixindex.php
Go to the documentation of this file.
1 <?php
25 
32 
37  protected $stripPrefix = false;
38 
39  protected $hideRedirects = false;
40 
41  // Inherit $maxPerPage
42 
44  private $loadBalancer;
45 
47  private $linkCache;
48 
53  public function __construct(
56  ) {
57  parent::__construct( $loadBalancer );
58  $this->mName = 'Prefixindex';
59  $this->loadBalancer = $loadBalancer;
60  $this->linkCache = $linkCache;
61  }
62 
67  public function execute( $par ) {
68  $this->setHeaders();
69  $this->outputHeader();
70 
71  $out = $this->getOutput();
72  $out->addModuleStyles( 'mediawiki.special' );
73 
74  # GET values
75  $request = $this->getRequest();
76  $from = $request->getVal( 'from', '' );
77  $prefix = $request->getVal( 'prefix', '' );
78  $ns = $request->getIntOrNull( 'namespace' );
79  $namespace = (int)$ns; // if no namespace given, use 0 (NS_MAIN).
80  $this->hideRedirects = $request->getBool( 'hideredirects', $this->hideRedirects );
81  $this->stripPrefix = $request->getBool( 'stripprefix', $this->stripPrefix );
82 
83  $namespaces = $this->getContentLanguage()->getNamespaces();
84  $out->setPageTitle(
85  ( $namespace > 0 && array_key_exists( $namespace, $namespaces ) )
86  ? $this->msg( 'prefixindex-namespace', str_replace( '_', ' ', $namespaces[$namespace] ) )
87  : $this->msg( 'prefixindex' )
88  );
89 
90  $showme = '';
91  if ( $par !== null ) {
92  $showme = $par;
93  } elseif ( $prefix != '' ) {
94  $showme = $prefix;
95  } elseif ( $from != '' && $ns === null ) {
96  // For back-compat with Special:Allpages
97  // Don't do this if namespace is passed, so paging works when doing NS views.
98  $showme = $from;
99  }
100 
101  // T29864: if transcluded, show all pages instead of the form.
102  if ( $this->including() || $showme != '' || $ns !== null ) {
103  $this->showPrefixChunk( $namespace, $showme, $from );
104  } else {
105  $out->addHTML( $this->namespacePrefixForm( $namespace, '' ) );
106  }
107  }
108 
115  protected function namespacePrefixForm( $namespace = NS_MAIN, $from = '' ) {
116  $formDescriptor = [
117  'prefix' => [
118  'label-message' => 'allpagesprefix',
119  'name' => 'prefix',
120  'id' => 'nsfrom',
121  'type' => 'text',
122  'size' => '30',
123  'default' => str_replace( '_', ' ', $from ),
124  ],
125  'namespace' => [
126  'type' => 'namespaceselect',
127  'name' => 'namespace',
128  'id' => 'namespace',
129  'label-message' => 'namespace',
130  'all' => null,
131  'default' => $namespace,
132  ],
133  'hidedirects' => [
134  'class' => HTMLCheckField::class,
135  'name' => 'hideredirects',
136  'label-message' => 'allpages-hide-redirects',
137  ],
138  'stripprefix' => [
139  'class' => HTMLCheckField::class,
140  'name' => 'stripprefix',
141  'label-message' => 'prefixindex-strip',
142  ],
143  ];
144  $htmlForm = HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() )
145  ->setMethod( 'get' )
146  ->setTitle( $this->getPageTitle() ) // Remove subpage
147  ->setWrapperLegendMsg( 'prefixindex' )
148  ->setSubmitTextMsg( 'prefixindex-submit' );
149 
150  return $htmlForm->prepareForm()->getHTML( false );
151  }
152 
158  protected function showPrefixChunk( $namespace, $prefix, $from = null ) {
159  if ( $from === null ) {
160  $from = $prefix;
161  }
162 
163  $fromList = $this->getNamespaceKeyAndText( $namespace, $from );
164  $prefixList = $this->getNamespaceKeyAndText( $namespace, $prefix );
165  $namespaces = $this->getContentLanguage()->getNamespaces();
166  $res = null;
167  $n = 0;
168  $nextRow = null;
169 
170  if ( !$prefixList || !$fromList ) {
171  $out = $this->msg( 'allpagesbadtitle' )->parseAsBlock();
172  } elseif ( !array_key_exists( $namespace, $namespaces ) ) {
173  // Show errormessage and reset to NS_MAIN
174  $out = $this->msg( 'allpages-bad-ns', $namespace )->parse();
175  $namespace = NS_MAIN;
176  } else {
177  list( $namespace, $prefixKey, $prefix ) = $prefixList;
178  list( /* $fromNS */, $fromKey, ) = $fromList;
179 
180  # ## @todo FIXME: Should complain if $fromNs != $namespace
181 
182  $dbr = $this->loadBalancer->getConnectionRef( ILoadBalancer::DB_REPLICA );
183 
184  $conds = [
185  'page_namespace' => $namespace,
186  'page_title' . $dbr->buildLike( $prefixKey, $dbr->anyString() ),
187  'page_title >= ' . $dbr->addQuotes( $fromKey ),
188  ];
189 
190  if ( $this->hideRedirects ) {
191  $conds['page_is_redirect'] = 0;
192  }
193 
194  $res = $dbr->select( 'page',
195  array_merge(
196  [ 'page_namespace', 'page_title' ],
198  ),
199  $conds,
200  __METHOD__,
201  [
202  'ORDER BY' => 'page_title',
203  'LIMIT' => $this->maxPerPage + 1,
204  'USE INDEX' => 'page_name_title',
205  ]
206  );
207 
208  // @todo FIXME: Side link to previous
209 
210  if ( $res->numRows() > 0 ) {
211  $out = Html::openElement( 'ul', [ 'class' => 'mw-prefixindex-list' ] );
212 
213  $prefixLength = strlen( $prefix );
214  foreach ( $res as $row ) {
215  if ( $n >= $this->maxPerPage ) {
216  $nextRow = $row;
217  break;
218  }
219  $title = Title::newFromRow( $row );
220  // Make sure it gets into LinkCache
221  $this->linkCache->addGoodLinkObjFromRow( $title, $row );
222  $displayed = $title->getText();
223  // Try not to generate unclickable links
224  if ( $this->stripPrefix && $prefixLength !== strlen( $displayed ) ) {
225  $displayed = substr( $displayed, $prefixLength );
226  }
227  $link = ( $title->isRedirect() ? '<div class="allpagesredirect">' : '' ) .
228  $this->getLinkRenderer()->makeKnownLink(
229  $title,
230  $displayed
231  ) .
232  ( $title->isRedirect() ? '</div>' : '' );
233 
234  $out .= "<li>$link</li>\n";
235  $n++;
236 
237  }
238  $out .= Html::closeElement( 'ul' );
239 
240  if ( $res->numRows() > 2 ) {
241  // Only apply CSS column styles if there's more than 2 entries.
242  // Otherwise rendering is broken as "mw-prefixindex-body"'s CSS column count is 3.
243  $out = Html::rawElement( 'div', [ 'class' => 'mw-prefixindex-body' ], $out );
244  }
245  } else {
246  $out = '';
247  }
248  }
249 
250  $output = $this->getOutput();
251 
252  if ( $this->including() ) {
253  // We don't show the nav-links and the form when included into other
254  // pages so let's just finish here.
255  $output->addHTML( $out );
256  return;
257  }
258 
259  $topOut = $this->namespacePrefixForm( $namespace, $prefix );
260 
261  if ( $res && ( $n == $this->maxPerPage ) && $nextRow ) {
262  $query = [
263  'from' => $nextRow->page_title,
264  'prefix' => $prefix,
265  'hideredirects' => $this->hideRedirects,
266  'stripprefix' => $this->stripPrefix,
267  ];
268 
269  if ( $namespace || $prefix == '' ) {
270  // Keep the namespace even if it's 0 for empty prefixes.
271  // This tells us we're not just a holdover from old links.
272  $query['namespace'] = $namespace;
273  }
274 
275  $nextLink = $this->getLinkRenderer()->makeKnownLink(
276  $this->getPageTitle(),
277  $this->msg( 'nextpage', str_replace( '_', ' ', $nextRow->page_title ) )->text(),
278  [],
279  $query
280  );
281 
282  // Link shown at the top of the page below the form
283  $topOut .= Html::rawElement( 'div',
284  [ 'class' => 'mw-prefixindex-nav' ],
285  $nextLink
286  );
287 
288  // Link shown at the footer
289  $out .= "\n" . Html::element( 'hr' ) .
291  'div',
292  [ 'class' => 'mw-prefixindex-nav' ],
293  $nextLink
294  );
295 
296  }
297 
298  $output->addHTML( $topOut . $out );
299  }
300 
301  protected function getGroupName() {
302  return 'pages';
303  }
304 }
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
Cache for article titles (prefixed DB keys) and ids linked from one source.
Definition: LinkCache.php:42
static getSelectFields()
Fields that LinkCache needs to select.
Definition: LinkCache.php:380
Implements Special:Allpages.
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.
getRequest()
Get the WebRequest being used for this instance.
getPageTitle( $subpage=false)
Get a self-referential title object.
getContentLanguage()
Shortcut to get content language.
including( $x=null)
Whether the special page is being evaluated via transclusion.
Implements Special:Prefixindex.
showPrefixChunk( $namespace, $prefix, $from=null)
namespacePrefixForm( $namespace=NS_MAIN, $from='')
HTML for the top form.
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
ILoadBalancer $loadBalancer
__construct(ILoadBalancer $loadBalancer, LinkCache $linkCache)
$stripPrefix
Whether to remove the searched prefix from the displayed link.
execute( $par)
Entry point : initialise variables and call subfunctions.
static newFromRow( $row)
Make a Title object from a DB row.
Definition: Title.php:573
Database cluster connection, tracking, load balancing, and transaction manager interface.
const DB_REPLICA
Definition: defines.php:25