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(
54  ILoadBalancer $loadBalancer,
55  LinkCache $linkCache
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  [ $namespace, $prefixKey, $prefix ] = $prefixList;
178  [ /* $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',
196  $conds,
197  __METHOD__,
198  [
199  'ORDER BY' => 'page_title',
200  'LIMIT' => $this->maxPerPage + 1,
201  'USE INDEX' => 'page_name_title',
202  ]
203  );
204 
205  // @todo FIXME: Side link to previous
206 
207  if ( $res->numRows() > 0 ) {
208  $out = Html::openElement( 'ul', [ 'class' => 'mw-prefixindex-list' ] );
209 
210  $prefixLength = strlen( $prefix );
211  foreach ( $res as $row ) {
212  if ( $n >= $this->maxPerPage ) {
213  $nextRow = $row;
214  break;
215  }
216  $title = Title::newFromRow( $row );
217  // Make sure it gets into LinkCache
218  $this->linkCache->addGoodLinkObjFromRow( $title, $row );
219  $displayed = $title->getText();
220  // Try not to generate unclickable links
221  if ( $this->stripPrefix && $prefixLength !== strlen( $displayed ) ) {
222  $displayed = substr( $displayed, $prefixLength );
223  }
224  $link = ( $title->isRedirect() ? '<div class="allpagesredirect">' : '' ) .
225  $this->getLinkRenderer()->makeKnownLink(
226  $title,
227  $displayed
228  ) .
229  ( $title->isRedirect() ? '</div>' : '' );
230 
231  $out .= "<li>$link</li>\n";
232  $n++;
233 
234  }
235  $out .= Html::closeElement( 'ul' );
236 
237  if ( $res->numRows() > 2 ) {
238  // Only apply CSS column styles if there's more than 2 entries.
239  // Otherwise rendering is broken as "mw-prefixindex-body"'s CSS column count is 3.
240  $out = Html::rawElement( 'div', [ 'class' => 'mw-prefixindex-body' ], $out );
241  }
242  } else {
243  $out = '';
244  }
245  }
246 
247  $output = $this->getOutput();
248 
249  if ( $this->including() ) {
250  // We don't show the nav-links and the form when included into other
251  // pages so let's just finish here.
252  $output->addHTML( $out );
253  return;
254  }
255 
256  $topOut = $this->namespacePrefixForm( $namespace, $prefix );
257 
258  if ( $res && ( $n == $this->maxPerPage ) && $nextRow ) {
259  $query = [
260  'from' => $nextRow->page_title,
261  'prefix' => $prefix,
262  'hideredirects' => $this->hideRedirects,
263  'stripprefix' => $this->stripPrefix,
264  ];
265 
266  if ( $namespace || $prefix == '' ) {
267  // Keep the namespace even if it's 0 for empty prefixes.
268  // This tells us we're not just a holdover from old links.
269  $query['namespace'] = $namespace;
270  }
271 
272  $nextLink = $this->getLinkRenderer()->makeKnownLink(
273  $this->getPageTitle(),
274  $this->msg( 'nextpage', str_replace( '_', ' ', $nextRow->page_title ) )->text(),
275  [],
276  $query
277  );
278 
279  // Link shown at the top of the page below the form
280  $topOut .= Html::rawElement( 'div',
281  [ 'class' => 'mw-prefixindex-nav' ],
282  $nextLink
283  );
284 
285  // Link shown at the footer
286  $out .= "\n" . Html::element( 'hr' ) .
288  'div',
289  [ 'class' => 'mw-prefixindex-nav' ],
290  $nextLink
291  );
292 
293  }
294 
295  $output->addHTML( $topOut . $out );
296  }
297 
298  protected function getGroupName() {
299  return 'pages';
300  }
301 }
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
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:357
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...
__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:576
Create and track the database connections and transactions for a given database cluster.
const DB_REPLICA
Definition: defines.php:26