MediaWiki REL1_37
SpecialWhatLinksHere.php
Go to the documentation of this file.
1<?php
30
38 protected $opts;
39
40 protected $selfTitle;
41
43 protected $target;
44
47
50
53
56
59
60 protected $limits = [ 20, 50, 100, 250, 500 ];
61
69 public function __construct(
75 ) {
76 parent::__construct( 'Whatlinkshere' );
77 $this->loadBalancer = $loadBalancer;
78 $this->linkBatchFactory = $linkBatchFactory;
79 $this->contentHandlerFactory = $contentHandlerFactory;
80 $this->searchEngineFactory = $searchEngineFactory;
81 $this->namespaceInfo = $namespaceInfo;
82 }
83
84 public function execute( $par ) {
85 $out = $this->getOutput();
86
87 $this->setHeaders();
88 $this->outputHeader();
89 $this->addHelpLink( 'Help:What links here' );
90 $out->addModuleStyles( 'mediawiki.special.changeslist' );
91 $out->addModules( 'mediawiki.special.recentchanges' );
92
93 $opts = new FormOptions();
94
95 $opts->add( 'target', '' );
96 $opts->add( 'namespace', '', FormOptions::INTNULL );
97 $opts->add( 'limit', $this->getConfig()->get( 'QueryPageDefaultLimit' ) );
98 $opts->add( 'offset', '' );
99 $opts->add( 'from', 0 );
100 $opts->add( 'dir', 'next' );
101 $opts->add( 'hideredirs', false );
102 $opts->add( 'hidetrans', false );
103 $opts->add( 'hidelinks', false );
104 $opts->add( 'hideimages', false );
105 $opts->add( 'invert', false );
106
108 $opts->validateIntBounds( 'limit', 0, 5000 );
109
110 // Give precedence to subpage syntax
111 if ( $par !== null ) {
112 $opts->setValue( 'target', $par );
113 }
114
115 // Bind to member variable
116 $this->opts = $opts;
117
118 $this->target = Title::newFromText( $opts->getValue( 'target' ) );
119 if ( !$this->target ) {
120 if ( !$this->including() ) {
121 $out->addHTML( $this->whatlinkshereForm() );
122 }
123
124 return;
125 }
126
127 $this->getSkin()->setRelevantTitle( $this->target );
128
129 $this->selfTitle = $this->getPageTitle( $this->target->getPrefixedDBkey() );
130
131 $out->setPageTitle( $this->msg( 'whatlinkshere-title', $this->target->getPrefixedText() ) );
132 $out->addBacklinkSubtitle( $this->target );
133
134 [ $offsetNamespace, $offsetPageID, $dir ] = $this->parseOffsetAndDir( $opts );
135
136 $this->showIndirectLinks(
137 0,
138 $this->target,
139 $opts->getValue( 'limit' ),
140 $offsetNamespace,
141 $offsetPageID,
142 $dir
143 );
144 }
145
157 private function parseOffsetAndDir( FormOptions $opts ): array {
158 $from = $opts->getValue( 'from' );
159 $opts->reset( 'from' );
160
161 if ( $from ) {
162 $dir = 'next';
163 $offsetNamespace = null;
164 $offsetPageID = $from - 1;
165 } else {
166 $dir = $opts->getValue( 'dir' );
167 [ $offsetNamespaceString, $offsetPageIDString ] = explode(
168 '|',
169 $opts->getValue( 'offset' ) . '|'
170 );
171 if ( !$offsetPageIDString ) {
172 $offsetPageIDString = $offsetNamespaceString;
173 $offsetNamespaceString = '';
174 }
175 if ( is_numeric( $offsetNamespaceString ) ) {
176 $offsetNamespace = (int)$offsetNamespaceString;
177 } else {
178 $offsetNamespace = null;
179 }
180 $offsetPageID = (int)$offsetPageIDString;
181 }
182
183 if ( $offsetNamespace === null ) {
184 $offsetTitle = MediaWikiServices::getInstance()
185 ->getTitleFactory()
186 ->newFromID( $offsetPageID );
187 $offsetNamespace = $offsetTitle ? $offsetTitle->getNamespace() : 0;
188 }
189
190 return [ $offsetNamespace, $offsetPageID, $dir ];
191 }
192
201 private function showIndirectLinks(
202 $level, $target, $limit, $offsetNamespace = 0, $offsetPageID = 0, $dir = 'next'
203 ) {
204 $out = $this->getOutput();
205 $dbr = $this->loadBalancer->getConnectionRef( ILoadBalancer::DB_REPLICA );
206
207 $hidelinks = $this->opts->getValue( 'hidelinks' );
208 $hideredirs = $this->opts->getValue( 'hideredirs' );
209 $hidetrans = $this->opts->getValue( 'hidetrans' );
210 $hideimages = $target->getNamespace() !== NS_FILE || $this->opts->getValue( 'hideimages' );
211
212 // For historical reasons `pagelinks` always contains an entry for the redirect target.
213 // So we only need to query `redirect` if `pagelinks` isn't being queried.
214 $fetchredirs = $hidelinks && !$hideredirs;
215
216 // Build query conds in concert for all four tables...
217 $conds = [];
218 $conds['redirect'] = [
219 'rd_namespace' => $target->getNamespace(),
220 'rd_title' => $target->getDBkey(),
221 ];
222 $conds['pagelinks'] = [
223 'pl_namespace' => $target->getNamespace(),
224 'pl_title' => $target->getDBkey(),
225 ];
226 $conds['templatelinks'] = [
227 'tl_namespace' => $target->getNamespace(),
228 'tl_title' => $target->getDBkey(),
229 ];
230 $conds['imagelinks'] = [
231 'il_to' => $target->getDBkey(),
232 ];
233
234 $namespace = $this->opts->getValue( 'namespace' );
235 if ( is_int( $namespace ) ) {
236 $invert = $this->opts->getValue( 'invert' );
237 if ( $invert ) {
238 // Select all namespaces except for the specified one.
239 // This allows the database to use the *_from_namespace index. (T241837)
240 $namespaces = array_diff(
241 $this->namespaceInfo->getValidNamespaces(), [ $namespace ] );
242 } else {
243 $namespaces = $namespace;
244 }
245 } else {
246 // Select all namespaces.
247 // This allows the database to use the *_from_namespace index. (T297754)
248 $namespaces = $this->namespaceInfo->getValidNamespaces();
249 }
250 $conds['redirect']['page_namespace'] = $namespaces;
251 $conds['pagelinks']['pl_from_namespace'] = $namespaces;
252 $conds['templatelinks']['tl_from_namespace'] = $namespaces;
253 $conds['imagelinks']['il_from_namespace'] = $namespaces;
254
255 if ( $offsetPageID ) {
256 $rel = $dir === 'prev' ? '<' : '>';
257 $conds['redirect'][] = "rd_from $rel $offsetPageID";
258 $conds['templatelinks'][] = "(tl_from_namespace = $offsetNamespace AND tl_from $rel $offsetPageID " .
259 "OR tl_from_namespace $rel $offsetNamespace)";
260 $conds['pagelinks'][] = "(pl_from_namespace = $offsetNamespace AND pl_from $rel $offsetPageID " .
261 "OR pl_from_namespace $rel $offsetNamespace)";
262 $conds['imagelinks'][] = "(il_from_namespace = $offsetNamespace AND il_from $rel $offsetPageID " .
263 "OR il_from_namespace $rel $offsetNamespace)";
264 }
265
266 if ( $hideredirs ) {
267 // For historical reasons `pagelinks` always contains an entry for the redirect target.
268 // So we hide that link when $hideredirs is set. There's unfortunately no way to tell when a
269 // redirect's content also links to the target.
270 $conds['pagelinks']['rd_from'] = null;
271 }
272
273 $sortDirection = $dir === 'prev' ? SelectQueryBuilder::SORT_DESC : SelectQueryBuilder::SORT_ASC;
274
275 $fname = __METHOD__;
276 $queryFunc = static function ( IDatabase $dbr, $table, $fromCol ) use (
277 $conds, $target, $limit, $sortDirection, $fname
278 ) {
279 // Read an extra row as an at-end check
280 $queryLimit = $limit + 1;
281 $on = [
282 "rd_from = $fromCol",
283 'rd_title' => $target->getDBkey(),
284 'rd_namespace' => $target->getNamespace(),
285 'rd_interwiki = ' . $dbr->addQuotes( '' ) . ' OR rd_interwiki IS NULL'
286 ];
287 // Inner LIMIT is 2X in case of stale backlinks with wrong namespaces
288 $subQuery = $dbr->newSelectQueryBuilder()
289 ->table( $table )
290 ->fields( [ $fromCol, 'rd_from', 'rd_fragment' ] )
291 ->conds( $conds[$table] )
292 ->orderBy( [ $fromCol . '_namespace', $fromCol ], $sortDirection )
293 ->limit( 2 * $queryLimit )
294 ->leftJoin( 'redirect', 'redirect', $on );
295
296 return $dbr->newSelectQueryBuilder()
297 ->table( $subQuery, 'temp_backlink_range' )
298 ->join( 'page', 'page', "$fromCol = page_id" )
299 ->fields( [ 'page_id', 'page_namespace', 'page_title',
300 'rd_from', 'rd_fragment', 'page_is_redirect' ] )
301 ->orderBy( [ 'page_namespace', 'page_id' ], $sortDirection )
302 ->limit( $queryLimit )
303 ->caller( $fname )
304 ->fetchResultSet();
305 };
306
307 if ( $fetchredirs ) {
308 $rdRes = $dbr->newSelectQueryBuilder()
309 ->table( 'redirect' )
310 ->fields( [ 'page_id', 'page_namespace', 'page_title', 'rd_from', 'rd_fragment', 'page_is_redirect' ] )
311 ->conds( $conds['redirect'] )
312 ->orderBy( 'rd_from', $sortDirection )
313 ->limit( $limit + 1 )
314 ->join( 'page', 'page', 'rd_from = page_id' )
315 ->caller( __METHOD__ )
316 ->fetchResultSet();
317 }
318
319 if ( !$hidelinks ) {
320 $plRes = $queryFunc( $dbr, 'pagelinks', 'pl_from' );
321 }
322
323 if ( !$hidetrans ) {
324 $tlRes = $queryFunc( $dbr, 'templatelinks', 'tl_from' );
325 }
326
327 if ( !$hideimages ) {
328 $ilRes = $queryFunc( $dbr, 'imagelinks', 'il_from' );
329 }
330
331 if ( ( !$fetchredirs || !$rdRes->numRows() )
332 && ( $hidelinks || !$plRes->numRows() )
333 && ( $hidetrans || !$tlRes->numRows() )
334 && ( $hideimages || !$ilRes->numRows() )
335 ) {
336 if ( $level == 0 && !$this->including() ) {
337 $out->addHTML( $this->whatlinkshereForm() );
338
339 // Show filters only if there are links
340 if ( $hidelinks || $hidetrans || $hideredirs || $hideimages ) {
341 $out->addHTML( $this->getFilterPanel() );
342 }
343 $msgKey = is_int( $namespace ) ? 'nolinkshere-ns' : 'nolinkshere';
344 $link = $this->getLinkRenderer()->makeLink(
345 $this->target,
346 null,
347 [],
348 $this->target->isRedirect() ? [ 'redirect' => 'no' ] : []
349 );
350
351 $errMsg = $this->msg( $msgKey )
352 ->params( $this->target->getPrefixedText() )
353 ->rawParams( $link )
354 ->parseAsBlock();
355 $out->addHTML( $errMsg );
356 $out->setStatusCode( 404 );
357 }
358
359 return;
360 }
361
362 // Read the rows into an array and remove duplicates
363 // templatelinks comes third so that the templatelinks row overwrites the
364 // pagelinks/redirect row, so we get (inclusion) rather than nothing
365 $rows = [];
366 if ( $fetchredirs ) {
367 foreach ( $rdRes as $row ) {
368 $row->is_template = 0;
369 $row->is_image = 0;
370 $rows[$row->page_id] = $row;
371 }
372 }
373 if ( !$hidelinks ) {
374 foreach ( $plRes as $row ) {
375 $row->is_template = 0;
376 $row->is_image = 0;
377 $rows[$row->page_id] = $row;
378 }
379 }
380 if ( !$hidetrans ) {
381 foreach ( $tlRes as $row ) {
382 $row->is_template = 1;
383 $row->is_image = 0;
384 $rows[$row->page_id] = $row;
385 }
386 }
387 if ( !$hideimages ) {
388 foreach ( $ilRes as $row ) {
389 $row->is_template = 0;
390 $row->is_image = 1;
391 $rows[$row->page_id] = $row;
392 }
393 }
394
395 // Sort by namespace + page ID, changing the keys to 0-based indices
396 usort( $rows, static function ( $rowA, $rowB ) {
397 if ( $rowA->page_namespace !== $rowB->page_namespace ) {
398 return $rowA->page_namespace < $rowB->page_namespace ? -1 : 1;
399 }
400 if ( $rowA->page_id !== $rowB->page_id ) {
401 return $rowA->page_id < $rowB->page_id ? -1 : 1;
402 }
403 return 0;
404 } );
405
406 $numRows = count( $rows );
407
408 // Work out the start and end IDs, for prev/next links
409 if ( !$limit ) { // T289351
410 $nextNamespace = $nextPageId = $prevNamespace = $prevPageId = false;
411 $rows = [];
412 } elseif ( $dir === 'prev' ) {
413 if ( $numRows > $limit ) {
414 // More rows available after these ones
415 // Get the next row from the last row in the result set
416 $nextNamespace = $rows[$limit]->page_namespace;
417 $nextPageId = $rows[$limit]->page_id;
418 // Remove undisplayed rows, for dir='prev' we need to discard first record after sorting
419 $rows = array_slice( $rows, 1, $limit );
420 // Get the prev row from the first displayed row
421 $prevNamespace = $rows[0]->page_namespace;
422 $prevPageId = $rows[0]->page_id;
423 } else {
424 // Get the next row from the last displayed row
425 $nextNamespace = $rows[$numRows - 1]->page_namespace;
426 $nextPageId = $rows[$numRows - 1]->page_id;
427 $prevNamespace = false;
428 $prevPageId = false;
429 }
430 } else {
431 // If offset is not set disable prev link
432 $prevNamespace = $offsetPageID ? $rows[0]->page_namespace : false;
433 $prevPageId = $offsetPageID ? $rows[0]->page_id : false;
434 if ( $numRows > $limit ) {
435 // Get the next row from the last displayed row
436 $nextNamespace = $rows[$limit - 1]->page_namespace;
437 $nextPageId = $rows[$limit - 1]->page_id;
438 // Remove undisplayed rows
439 $rows = array_slice( $rows, 0, $limit );
440 } else {
441 $nextNamespace = false;
442 $nextPageId = false;
443 }
444 }
445
446 // use LinkBatch to make sure, that all required data (associated with Titles)
447 // is loaded in one query
448 $lb = $this->linkBatchFactory->newLinkBatch();
449 foreach ( $rows as $row ) {
450 $lb->add( $row->page_namespace, $row->page_title );
451 }
452 $lb->execute();
453
454 if ( $level == 0 && !$this->including() ) {
455 $out->addHTML( $this->whatlinkshereForm() );
456 $out->addHTML( $this->getFilterPanel() );
457
458 $link = $this->getLinkRenderer()->makeLink(
459 $this->target,
460 null,
461 [],
462 $this->target->isRedirect() ? [ 'redirect' => 'no' ] : []
463 );
464
465 $msg = $this->msg( 'linkshere' )
466 ->params( $this->target->getPrefixedText() )
467 ->rawParams( $link )
468 ->parseAsBlock();
469 $out->addHTML( $msg );
470
471 $out->addWikiMsg( 'whatlinkshere-count', Message::numParam( count( $rows ) ) );
472
473 $prevnext = $this->getPrevNext( $prevNamespace, $prevPageId, $nextNamespace, $nextPageId );
474 $out->addHTML( $prevnext );
475 }
476 $out->addHTML( $this->listStart( $level ) );
477 foreach ( $rows as $row ) {
478 $nt = Title::makeTitle( $row->page_namespace, $row->page_title );
479
480 if ( $row->rd_from && $level < 2 ) {
481 $out->addHTML( $this->listItem( $row, $nt, $target, true ) );
482 $this->showIndirectLinks(
483 $level + 1,
484 $nt,
485 $this->getConfig()->get( 'MaxRedirectLinksRetrieved' )
486 );
487 $out->addHTML( Xml::closeElement( 'li' ) );
488 } else {
489 $out->addHTML( $this->listItem( $row, $nt, $target ) );
490 }
491 }
492
493 $out->addHTML( $this->listEnd() );
494
495 if ( $level == 0 && !$this->including() ) {
496 $out->addHTML( $prevnext );
497 }
498 }
499
500 protected function listStart( $level ) {
501 return Xml::openElement( 'ul', ( $level ? [] : [ 'id' => 'mw-whatlinkshere-list' ] ) );
502 }
503
504 protected function listItem( $row, $nt, $target, $notClose = false ) {
505 $dirmark = $this->getLanguage()->getDirMark();
506
507 if ( $row->rd_from ) {
508 $query = [ 'redirect' => 'no' ];
509 } else {
510 $query = [];
511 }
512
513 $link = $this->getLinkRenderer()->makeKnownLink(
514 $nt,
515 null,
516 $row->page_is_redirect ? [ 'class' => 'mw-redirect' ] : [],
517 $query
518 );
519
520 // Display properties (redirect or template)
521 $propsText = '';
522 $props = [];
523 if ( (string)$row->rd_fragment !== '' ) {
524 $props[] = $this->msg( 'whatlinkshere-sectionredir' )
525 ->rawParams( $this->getLinkRenderer()->makeLink(
526 $target->createFragmentTarget( $row->rd_fragment ),
527 $row->rd_fragment
528 ) )->escaped();
529 } elseif ( $row->rd_from ) {
530 $props[] = $this->msg( 'isredirect' )->escaped();
531 }
532 if ( $row->is_template ) {
533 $props[] = $this->msg( 'istemplate' )->escaped();
534 }
535 if ( $row->is_image ) {
536 $props[] = $this->msg( 'isimage' )->escaped();
537 }
538
539 $this->getHookRunner()->onWhatLinksHereProps( $row, $nt, $target, $props );
540
541 if ( count( $props ) ) {
542 $propsText = $this->msg( 'parentheses' )
543 ->rawParams( $this->getLanguage()->semicolonList( $props ) )->escaped();
544 }
545
546 # Space for utilities links, with a what-links-here link provided
547 $wlhLink = $this->wlhLink(
548 $nt,
549 $this->msg( 'whatlinkshere-links' )->text(),
550 $this->msg( 'editlink' )->text()
551 );
552 $wlh = Xml::wrapClass(
553 $this->msg( 'parentheses' )->rawParams( $wlhLink )->escaped(),
554 'mw-whatlinkshere-tools'
555 );
556
557 return $notClose ?
558 Xml::openElement( 'li' ) . "$link $propsText $dirmark $wlh\n" :
559 Xml::tags( 'li', null, "$link $propsText $dirmark $wlh" ) . "\n";
560 }
561
562 protected function listEnd() {
563 return Xml::closeElement( 'ul' );
564 }
565
566 protected function wlhLink( Title $target, $text, $editText ) {
567 static $title = null;
568 if ( $title === null ) {
569 $title = $this->getPageTitle();
570 }
571
572 $linkRenderer = $this->getLinkRenderer();
573
574 // always show a "<- Links" link
575 $links = [
576 'links' => $linkRenderer->makeKnownLink(
577 $title,
578 $text,
579 [],
580 [ 'target' => $target->getPrefixedText() ]
581 ),
582 ];
583
584 // if the page is editable, add an edit link
585 if (
586 // check user permissions
587 $this->getAuthority()->isAllowed( 'edit' ) &&
588 // check, if the content model is editable through action=edit
589 $this->contentHandlerFactory->getContentHandler( $target->getContentModel() )
590 ->supportsDirectEditing()
591 ) {
592 $links['edit'] = $linkRenderer->makeKnownLink(
593 $target,
594 $editText,
595 [],
596 [ 'action' => 'edit' ]
597 );
598 }
599
600 // build the links html
601 return $this->getLanguage()->pipeList( $links );
602 }
603
604 private function makeSelfLink( $text, $query ) {
605 return $this->getLinkRenderer()->makeKnownLink(
606 $this->selfTitle,
607 $text,
608 [],
609 $query
610 );
611 }
612
613 private function getPrevNext( $prevNamespace, $prevPageId, $nextNamespace, $nextPageId ) {
614 $currentLimit = $this->opts->getValue( 'limit' );
615 $prev = $this->msg( 'whatlinkshere-prev' )->numParams( $currentLimit )->text();
616 $next = $this->msg( 'whatlinkshere-next' )->numParams( $currentLimit )->text();
617
618 $changed = $this->opts->getChangedValues();
619 unset( $changed['target'] ); // Already in the request title
620
621 if ( $prevPageId != 0 ) {
622 $overrides = [ 'dir' => 'prev', 'offset' => "$prevNamespace|$prevPageId", ];
623 $prev = Message::rawParam( $this->makeSelfLink( $prev, array_merge( $changed, $overrides ) ) );
624 }
625 if ( $nextPageId != 0 ) {
626 $overrides = [ 'dir' => 'next', 'offset' => "$nextNamespace|$nextPageId", ];
627 $next = Message::rawParam( $this->makeSelfLink( $next, array_merge( $changed, $overrides ) ) );
628 }
629
630 $limitLinks = [];
631 $lang = $this->getLanguage();
632 foreach ( $this->limits as $limit ) {
633 $prettyLimit = $lang->formatNum( $limit );
634 $overrides = [ 'limit' => $limit ];
635 $limitLinks[] = $this->makeSelfLink( $prettyLimit, array_merge( $changed, $overrides ) );
636 }
637
638 $nums = $lang->pipeList( $limitLinks );
639
640 return $this->msg( 'viewprevnext' )->params( $prev, $next )->rawParams( $nums )->escaped();
641 }
642
643 private function whatlinkshereForm() {
644 // We get nicer value from the title object
645 $this->opts->consumeValue( 'target' );
646
647 $target = $this->target ? $this->target->getPrefixedText() : '';
648 $namespace = $this->opts->consumeValue( 'namespace' );
649 $nsinvert = $this->opts->consumeValue( 'invert' );
650
651 # Build up the form
652 $f = Xml::openElement( 'form', [ 'action' => wfScript() ] );
653
654 # Values that should not be forgotten
655 $f .= Html::hidden( 'title', $this->getPageTitle()->getPrefixedText() );
656 foreach ( $this->opts->getUnconsumedValues() as $name => $value ) {
657 $f .= Html::hidden( $name, $value );
658 }
659
660 $f .= Xml::fieldset( $this->msg( 'whatlinkshere' )->text() );
661
662 # Target input (.mw-searchInput enables suggestions)
663 $f .= Xml::inputLabel( $this->msg( 'whatlinkshere-page' )->text(), 'target',
664 'mw-whatlinkshere-target', 40, $target, [ 'class' => 'mw-searchInput' ] );
665
666 $f .= ' ';
667
668 # Namespace selector
669 $f .= Html::namespaceSelector(
670 [
671 'selected' => $namespace,
672 'all' => '',
673 'label' => $this->msg( 'namespace' )->text(),
674 'in-user-lang' => true,
675 ], [
676 'name' => 'namespace',
677 'id' => 'namespace',
678 'class' => 'namespaceselector',
679 ]
680 );
681
682 $hidden = $namespace === '' ? ' mw-input-hidden' : '';
683 $f .= ' ' .
684 Html::rawElement( 'span',
685 [ 'class' => 'mw-input-with-label' . $hidden ],
686 Xml::checkLabel(
687 $this->msg( 'invert' )->text(),
688 'invert',
689 'nsinvert',
690 $nsinvert,
691 [ 'title' => $this->msg( 'tooltip-whatlinkshere-invert' )->text() ]
692 )
693 );
694
695 $f .= ' ';
696
697 # Submit
698 $f .= Xml::submitButton( $this->msg( 'whatlinkshere-submit' )->text() );
699
700 # Close
701 $f .= Xml::closeElement( 'fieldset' ) . Xml::closeElement( 'form' ) . "\n";
702
703 return $f;
704 }
705
711 private function getFilterPanel() {
712 $show = $this->msg( 'show' )->text();
713 $hide = $this->msg( 'hide' )->text();
714
715 $changed = $this->opts->getChangedValues();
716 unset( $changed['target'] ); // Already in the request title
717
718 $links = [];
719 $types = [ 'hidetrans', 'hidelinks', 'hideredirs' ];
720 if ( $this->target->getNamespace() === NS_FILE ) {
721 $types[] = 'hideimages';
722 }
723
724 // Combined message keys: 'whatlinkshere-hideredirs', 'whatlinkshere-hidetrans',
725 // 'whatlinkshere-hidelinks', 'whatlinkshere-hideimages'
726 // To be sure they will be found by grep
727 foreach ( $types as $type ) {
728 $chosen = $this->opts->getValue( $type );
729 $msg = $chosen ? $show : $hide;
730 $overrides = [ $type => !$chosen ];
731 $links[] = $this->msg( "whatlinkshere-{$type}" )->rawParams(
732 $this->makeSelfLink( $msg, array_merge( $changed, $overrides ) ) )->escaped();
733 }
734
735 return Xml::fieldset(
736 $this->msg( 'whatlinkshere-filters' )->text(),
737 $this->getLanguage()->pipeList( $links )
738 );
739 }
740
749 public function prefixSearchSubpages( $search, $limit, $offset ) {
750 return $this->prefixSearchString( $search, $limit, $offset, $this->searchEngineFactory );
751 }
752
753 protected function getGroupName() {
754 return 'pagetools';
755 }
756}
getAuthority()
const NS_FILE
Definition Defines.php:70
wfScript( $script='index')
Get the path to a specified script file, respecting file extensions; this is a wrapper around $wgScri...
if(ini_get('mbstring.func_overload')) if(!defined('MW_ENTRY_POINT'))
Pre-config setup: Before loading LocalSettings.php.
Definition Setup.php:88
Helper class to keep track of options when mixing links and form elements.
add( $name, $default, $type=self::AUTO)
Add an option to be handled by this FormOptions instance.
setValue( $name, $value, $force=false)
Use to set the value of an option.
reset( $name)
Delete the option value.
fetchValuesFromRequest(WebRequest $r, $optionKeys=null)
Fetch values for all options (or selected options) from the given WebRequest, making them available f...
validateIntBounds( $name, $min, $max)
getValue( $name)
Get the value for the given option name.
Shortcut to construct an includable special page.
MediaWikiServices is the service locator for the application scope of MediaWiki.
static rawParam( $raw)
Definition Message.php:1090
static numParam( $num)
Definition Message.php:1101
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
Factory class for SearchEngine.
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.
getSkin()
Shortcut to get the skin being used for this instance.
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.
getPageTitle( $subpage=false)
Get a self-referential title object.
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
including( $x=null)
Whether the special page is being evaluated via transclusion.
Implements Special:Whatlinkshere.
parseOffsetAndDir(FormOptions $opts)
Parse the offset and direction parameters.
LinkBatchFactory $linkBatchFactory
execute( $par)
Default execute method Checks user permissions.
wlhLink(Title $target, $text, $editText)
__construct(ILoadBalancer $loadBalancer, LinkBatchFactory $linkBatchFactory, IContentHandlerFactory $contentHandlerFactory, SearchEngineFactory $searchEngineFactory, NamespaceInfo $namespaceInfo)
listItem( $row, $nt, $target, $notClose=false)
IContentHandlerFactory $contentHandlerFactory
prefixSearchSubpages( $search, $limit, $offset)
Return an array of subpages beginning with $search that this special page will accept.
showIndirectLinks( $level, $target, $limit, $offsetNamespace=0, $offsetPageID=0, $dir='next')
getFilterPanel()
Create filter panel.
SearchEngineFactory $searchEngineFactory
getPrevNext( $prevNamespace, $prevPageId, $nextNamespace, $nextPageId)
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
Represents a title within MediaWiki.
Definition Title.php:48
getNamespace()
Get the namespace index, i.e.
Definition Title.php:1078
createFragmentTarget( $fragment)
Creates a new Title for a different fragment of the same page.
Definition Title.php:1854
getDBkey()
Get the main part with underscores.
Definition Title.php:1069
getContentModel( $flags=0)
Get the page's content model id, see the CONTENT_MODEL_XXX constants.
Definition Title.php:1090
getPrefixedText()
Get the prefixed title with spaces.
Definition Title.php:1921
Basic database interface for live and lazy-loaded relation database handles.
Definition IDatabase.php:38
Database cluster connection, tracking, load balancing, and transaction manager interface.
if(!isset( $args[0])) $lang