MediaWiki REL1_39
SpecialWhatLinksHere.php
Go to the documentation of this file.
1<?php
31
39 protected $opts;
40
42 protected $target;
43
45 private $loadBalancer;
46
48 private $linkBatchFactory;
49
51 private $contentHandlerFactory;
52
54 private $searchEngineFactory;
55
57 private $namespaceInfo;
58
60 private $titleFactory;
61
63 private $linksMigration;
64
65 protected $limits = [ 20, 50, 100, 250, 500 ];
66
76 public function __construct(
77 ILoadBalancer $loadBalancer,
78 LinkBatchFactory $linkBatchFactory,
79 IContentHandlerFactory $contentHandlerFactory,
80 SearchEngineFactory $searchEngineFactory,
81 NamespaceInfo $namespaceInfo,
82 TitleFactory $titleFactory,
83 LinksMigration $linksMigration
84 ) {
85 parent::__construct( 'Whatlinkshere' );
86 $this->loadBalancer = $loadBalancer;
87 $this->linkBatchFactory = $linkBatchFactory;
88 $this->contentHandlerFactory = $contentHandlerFactory;
89 $this->searchEngineFactory = $searchEngineFactory;
90 $this->namespaceInfo = $namespaceInfo;
91 $this->titleFactory = $titleFactory;
92 $this->linksMigration = $linksMigration;
93 }
94
95 public function execute( $par ) {
96 $out = $this->getOutput();
97
98 $this->setHeaders();
99 $this->outputHeader();
100 $this->addHelpLink( 'Help:What links here' );
101 $out->addModuleStyles( 'mediawiki.special' );
102
103 $opts = new FormOptions();
104
105 $opts->add( 'target', '' );
106 $opts->add( 'namespace', '', FormOptions::INTNULL );
107 $opts->add( 'limit', $this->getConfig()->get( MainConfigNames::QueryPageDefaultLimit ) );
108 $opts->add( 'offset', '' );
109 $opts->add( 'from', 0 );
110 $opts->add( 'dir', 'next' );
111 $opts->add( 'hideredirs', false );
112 $opts->add( 'hidetrans', false );
113 $opts->add( 'hidelinks', false );
114 $opts->add( 'hideimages', false );
115 $opts->add( 'invert', false );
116
118 $opts->validateIntBounds( 'limit', 0, 5000 );
119
120 // Give precedence to subpage syntax
121 if ( $par !== null ) {
122 $opts->setValue( 'target', $par );
123 }
124
125 // Bind to member variable
126 $this->opts = $opts;
127
128 $this->target = Title::newFromText( $opts->getValue( 'target' ) );
129 if ( !$this->target ) {
130 if ( !$this->including() ) {
131 $out->addHTML( $this->whatlinkshereForm() );
132 }
133
134 return;
135 }
136
137 $this->getSkin()->setRelevantTitle( $this->target );
138
139 $out->setPageTitle( $this->msg( 'whatlinkshere-title', $this->target->getPrefixedText() ) );
140 $out->addBacklinkSubtitle( $this->target );
141
142 [ $offsetNamespace, $offsetPageID, $dir ] = $this->parseOffsetAndDir( $opts );
143
144 $this->showIndirectLinks(
145 0,
146 $this->target,
147 $opts->getValue( 'limit' ),
148 $offsetNamespace,
149 $offsetPageID,
150 $dir
151 );
152 }
153
165 private function parseOffsetAndDir( FormOptions $opts ): array {
166 $from = $opts->getValue( 'from' );
167 $opts->reset( 'from' );
168
169 if ( $from ) {
170 $dir = 'next';
171 $offsetNamespace = null;
172 $offsetPageID = $from - 1;
173 } else {
174 $dir = $opts->getValue( 'dir' );
175 [ $offsetNamespaceString, $offsetPageIDString ] = explode(
176 '|',
177 $opts->getValue( 'offset' ) . '|'
178 );
179 if ( !$offsetPageIDString ) {
180 $offsetPageIDString = $offsetNamespaceString;
181 $offsetNamespaceString = '';
182 }
183 if ( is_numeric( $offsetNamespaceString ) ) {
184 $offsetNamespace = (int)$offsetNamespaceString;
185 } else {
186 $offsetNamespace = null;
187 }
188 $offsetPageID = (int)$offsetPageIDString;
189 }
190
191 if ( $offsetNamespace === null ) {
192 $offsetTitle = $this->titleFactory->newFromID( $offsetPageID );
193 $offsetNamespace = $offsetTitle ? $offsetTitle->getNamespace() : NS_MAIN;
194 }
195
196 return [ $offsetNamespace, $offsetPageID, $dir ];
197 }
198
207 private function showIndirectLinks(
208 $level, $target, $limit, $offsetNamespace = 0, $offsetPageID = 0, $dir = 'next'
209 ) {
210 $out = $this->getOutput();
211 $dbr = $this->loadBalancer->getConnectionRef( ILoadBalancer::DB_REPLICA );
212
213 $hidelinks = $this->opts->getValue( 'hidelinks' );
214 $hideredirs = $this->opts->getValue( 'hideredirs' );
215 $hidetrans = $this->opts->getValue( 'hidetrans' );
216 $hideimages = $target->getNamespace() !== NS_FILE || $this->opts->getValue( 'hideimages' );
217
218 // For historical reasons `pagelinks` always contains an entry for the redirect target.
219 // So we only need to query `redirect` if `pagelinks` isn't being queried.
220 $fetchredirs = $hidelinks && !$hideredirs;
221
222 // Build query conds in concert for all four tables...
223 $conds = [];
224 $conds['redirect'] = [
225 'rd_namespace' => $target->getNamespace(),
226 'rd_title' => $target->getDBkey(),
227 ];
228 $conds['pagelinks'] = [
229 'pl_namespace' => $target->getNamespace(),
230 'pl_title' => $target->getDBkey(),
231 ];
232 $conds['templatelinks'] = $this->linksMigration->getLinksConditions( 'templatelinks', $target );
233 $conds['imagelinks'] = [
234 'il_to' => $target->getDBkey(),
235 ];
236
237 $namespace = $this->opts->getValue( 'namespace' );
238 if ( is_int( $namespace ) ) {
239 $invert = $this->opts->getValue( 'invert' );
240 if ( $invert ) {
241 // Select all namespaces except for the specified one.
242 // This allows the database to use the *_from_namespace index. (T241837)
243 $namespaces = array_diff(
244 $this->namespaceInfo->getValidNamespaces(), [ $namespace ] );
245 } else {
246 $namespaces = $namespace;
247 }
248 } else {
249 // Select all namespaces.
250 // This allows the database to use the *_from_namespace index. (T297754)
251 $namespaces = $this->namespaceInfo->getValidNamespaces();
252 }
253 $conds['redirect']['page_namespace'] = $namespaces;
254 $conds['pagelinks']['pl_from_namespace'] = $namespaces;
255 $conds['templatelinks']['tl_from_namespace'] = $namespaces;
256 $conds['imagelinks']['il_from_namespace'] = $namespaces;
257
258 if ( $offsetPageID ) {
259 $rel = $dir === 'prev' ? '<' : '>';
260 $conds['redirect'][] = "rd_from $rel $offsetPageID";
261 $conds['templatelinks'][] = "(tl_from_namespace = $offsetNamespace AND tl_from $rel $offsetPageID " .
262 "OR tl_from_namespace $rel $offsetNamespace)";
263 $conds['pagelinks'][] = "(pl_from_namespace = $offsetNamespace AND pl_from $rel $offsetPageID " .
264 "OR pl_from_namespace $rel $offsetNamespace)";
265 $conds['imagelinks'][] = "(il_from_namespace = $offsetNamespace AND il_from $rel $offsetPageID " .
266 "OR il_from_namespace $rel $offsetNamespace)";
267 }
268
269 if ( $hideredirs ) {
270 // For historical reasons `pagelinks` always contains an entry for the redirect target.
271 // So we hide that link when $hideredirs is set. There's unfortunately no way to tell when a
272 // redirect's content also links to the target.
273 $conds['pagelinks']['rd_from'] = null;
274 }
275
276 $sortDirection = $dir === 'prev' ? SelectQueryBuilder::SORT_DESC : SelectQueryBuilder::SORT_ASC;
277
278 $fname = __METHOD__;
279 $queryFunc = static function ( IDatabase $dbr, $table, $fromCol ) use (
280 $conds, $target, $limit, $sortDirection, $fname
281 ) {
282 // Read an extra row as an at-end check
283 $queryLimit = $limit + 1;
284 $on = [
285 "rd_from = $fromCol",
286 'rd_title' => $target->getDBkey(),
287 'rd_namespace' => $target->getNamespace(),
288 'rd_interwiki = ' . $dbr->addQuotes( '' ) . ' OR rd_interwiki IS NULL'
289 ];
290 // Inner LIMIT is 2X in case of stale backlinks with wrong namespaces
291 $subQuery = $dbr->newSelectQueryBuilder()
292 ->table( $table )
293 ->fields( [ $fromCol, 'rd_from', 'rd_fragment' ] )
294 ->conds( $conds[$table] )
295 ->orderBy( [ $fromCol . '_namespace', $fromCol ], $sortDirection )
296 ->limit( 2 * $queryLimit )
297 ->leftJoin( 'redirect', 'redirect', $on );
298
299 return $dbr->newSelectQueryBuilder()
300 ->table( $subQuery, 'temp_backlink_range' )
301 ->join( 'page', 'page', "$fromCol = page_id" )
302 ->fields( [ 'page_id', 'page_namespace', 'page_title',
303 'rd_from', 'rd_fragment', 'page_is_redirect' ] )
304 ->orderBy( [ 'page_namespace', 'page_id' ], $sortDirection )
305 ->limit( $queryLimit )
306 ->caller( $fname )
307 ->fetchResultSet();
308 };
309
310 if ( $fetchredirs ) {
311 $rdRes = $dbr->newSelectQueryBuilder()
312 ->table( 'redirect' )
313 ->fields( [ 'page_id', 'page_namespace', 'page_title', 'rd_from', 'rd_fragment', 'page_is_redirect' ] )
314 ->conds( $conds['redirect'] )
315 ->orderBy( 'rd_from', $sortDirection )
316 ->limit( $limit + 1 )
317 ->join( 'page', 'page', 'rd_from = page_id' )
318 ->caller( __METHOD__ )
319 ->fetchResultSet();
320 }
321
322 if ( !$hidelinks ) {
323 $plRes = $queryFunc( $dbr, 'pagelinks', 'pl_from' );
324 }
325
326 if ( !$hidetrans ) {
327 $tlRes = $queryFunc( $dbr, 'templatelinks', 'tl_from' );
328 }
329
330 if ( !$hideimages ) {
331 $ilRes = $queryFunc( $dbr, 'imagelinks', 'il_from' );
332 }
333
334 // @phan-suppress-next-line PhanPossiblyUndeclaredVariable $rdRes is declared when fetching redirs
335 if ( ( !$fetchredirs || !$rdRes->numRows() )
336 // @phan-suppress-next-line PhanPossiblyUndeclaredVariable $plRes is declared when fetching links
337 && ( $hidelinks || !$plRes->numRows() )
338 // @phan-suppress-next-line PhanPossiblyUndeclaredVariable $tlRes is declared when fetching trans
339 && ( $hidetrans || !$tlRes->numRows() )
340 // @phan-suppress-next-line PhanPossiblyUndeclaredVariable $ilRes is declared when fetching images
341 && ( $hideimages || !$ilRes->numRows() )
342 ) {
343 if ( $level == 0 && !$this->including() ) {
344 $out->addHTML( $this->whatlinkshereForm() );
345
346 $msgKey = is_int( $namespace ) ? 'nolinkshere-ns' : 'nolinkshere';
347 $link = $this->getLinkRenderer()->makeLink(
348 $this->target,
349 null,
350 [],
351 $this->target->isRedirect() ? [ 'redirect' => 'no' ] : []
352 );
353
354 $errMsg = $this->msg( $msgKey )
355 ->params( $this->target->getPrefixedText() )
356 ->rawParams( $link )
357 ->parseAsBlock();
358 $out->addHTML( $errMsg );
359 $out->setStatusCode( 404 );
360 }
361
362 return;
363 }
364
365 // Read the rows into an array and remove duplicates
366 // templatelinks comes third so that the templatelinks row overwrites the
367 // pagelinks/redirect row, so we get (inclusion) rather than nothing
368 $rows = [];
369 if ( $fetchredirs ) {
370 // @phan-suppress-next-line PhanPossiblyUndeclaredVariable $rdRes is declared when fetching redirs
371 foreach ( $rdRes as $row ) {
372 $row->is_template = 0;
373 $row->is_image = 0;
374 $rows[$row->page_id] = $row;
375 }
376 }
377 if ( !$hidelinks ) {
378 // @phan-suppress-next-line PhanPossiblyUndeclaredVariable $plRes is declared when fetching links
379 foreach ( $plRes as $row ) {
380 $row->is_template = 0;
381 $row->is_image = 0;
382 $rows[$row->page_id] = $row;
383 }
384 }
385 if ( !$hidetrans ) {
386 // @phan-suppress-next-line PhanPossiblyUndeclaredVariable $tlRes is declared when fetching trans
387 foreach ( $tlRes as $row ) {
388 $row->is_template = 1;
389 $row->is_image = 0;
390 $rows[$row->page_id] = $row;
391 }
392 }
393 if ( !$hideimages ) {
394 // @phan-suppress-next-line PhanPossiblyUndeclaredVariable $ilRes is declared when fetching images
395 foreach ( $ilRes as $row ) {
396 $row->is_template = 0;
397 $row->is_image = 1;
398 $rows[$row->page_id] = $row;
399 }
400 }
401
402 // Sort by namespace + page ID, changing the keys to 0-based indices
403 usort( $rows, static function ( $rowA, $rowB ) {
404 if ( $rowA->page_namespace !== $rowB->page_namespace ) {
405 return $rowA->page_namespace < $rowB->page_namespace ? -1 : 1;
406 }
407 if ( $rowA->page_id !== $rowB->page_id ) {
408 return $rowA->page_id < $rowB->page_id ? -1 : 1;
409 }
410 return 0;
411 } );
412
413 $numRows = count( $rows );
414
415 // Work out the start and end IDs, for prev/next links
416 if ( !$limit ) { // T289351
417 $nextNamespace = $nextPageId = $prevNamespace = $prevPageId = false;
418 $rows = [];
419 } elseif ( $dir === 'prev' ) {
420 if ( $numRows > $limit ) {
421 // More rows available after these ones
422 // Get the next row from the last row in the result set
423 $nextNamespace = $rows[$limit]->page_namespace;
424 $nextPageId = $rows[$limit]->page_id;
425 // Remove undisplayed rows, for dir='prev' we need to discard first record after sorting
426 $rows = array_slice( $rows, 1, $limit );
427 // Get the prev row from the first displayed row
428 $prevNamespace = $rows[0]->page_namespace;
429 $prevPageId = $rows[0]->page_id;
430 } else {
431 // Get the next row from the last displayed row
432 $nextNamespace = $rows[$numRows - 1]->page_namespace;
433 $nextPageId = $rows[$numRows - 1]->page_id;
434 $prevNamespace = false;
435 $prevPageId = false;
436 }
437 } else {
438 // If offset is not set disable prev link
439 $prevNamespace = $offsetPageID ? $rows[0]->page_namespace : false;
440 $prevPageId = $offsetPageID ? $rows[0]->page_id : false;
441 if ( $numRows > $limit ) {
442 // Get the next row from the last displayed row
443 $nextNamespace = $rows[$limit - 1]->page_namespace;
444 $nextPageId = $rows[$limit - 1]->page_id;
445 // Remove undisplayed rows
446 $rows = array_slice( $rows, 0, $limit );
447 } else {
448 $nextNamespace = false;
449 $nextPageId = false;
450 }
451 }
452
453 // use LinkBatch to make sure, that all required data (associated with Titles)
454 // is loaded in one query
455 $lb = $this->linkBatchFactory->newLinkBatch();
456 foreach ( $rows as $row ) {
457 $lb->add( $row->page_namespace, $row->page_title );
458 }
459 $lb->execute();
460
461 if ( $level == 0 && !$this->including() ) {
462 $out->addHTML( $this->whatlinkshereForm() );
463
464 $link = $this->getLinkRenderer()->makeLink(
465 $this->target,
466 null,
467 [],
468 $this->target->isRedirect() ? [ 'redirect' => 'no' ] : []
469 );
470
471 $msg = $this->msg( 'linkshere' )
472 ->params( $this->target->getPrefixedText() )
473 ->rawParams( $link )
474 ->parseAsBlock();
475 $out->addHTML( $msg );
476
477 $out->addWikiMsg( 'whatlinkshere-count', Message::numParam( count( $rows ) ) );
478
479 $prevnext = $this->getPrevNext( $prevNamespace, $prevPageId, $nextNamespace, $nextPageId );
480 $out->addHTML( $prevnext );
481 }
482 $out->addHTML( $this->listStart( $level ) );
483 foreach ( $rows as $row ) {
484 $nt = Title::makeTitle( $row->page_namespace, $row->page_title );
485
486 if ( $row->rd_from && $level < 2 ) {
487 $out->addHTML( $this->listItem( $row, $nt, $target, true ) );
488 $this->showIndirectLinks(
489 $level + 1,
490 $nt,
491 $this->getConfig()->get( MainConfigNames::MaxRedirectLinksRetrieved )
492 );
493 $out->addHTML( Xml::closeElement( 'li' ) );
494 } else {
495 $out->addHTML( $this->listItem( $row, $nt, $target ) );
496 }
497 }
498
499 $out->addHTML( $this->listEnd() );
500
501 if ( $level == 0 && !$this->including() ) {
502 // @phan-suppress-next-next-line PhanPossiblyUndeclaredVariable $prevnext is defined with $level is 0
503 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable prevnext is set when used
504 $out->addHTML( $prevnext );
505 }
506 }
507
508 protected function listStart( $level ) {
509 return Xml::openElement( 'ul', ( $level ? [] : [ 'id' => 'mw-whatlinkshere-list' ] ) );
510 }
511
512 protected function listItem( $row, $nt, $target, $notClose = false ) {
513 $dirmark = $this->getLanguage()->getDirMark();
514
515 if ( $row->rd_from ) {
516 $query = [ 'redirect' => 'no' ];
517 } else {
518 $query = [];
519 }
520
521 $link = $this->getLinkRenderer()->makeKnownLink(
522 $nt,
523 null,
524 $row->page_is_redirect ? [ 'class' => 'mw-redirect' ] : [],
525 $query
526 );
527
528 // Display properties (redirect or template)
529 $propsText = '';
530 $props = [];
531 if ( (string)$row->rd_fragment !== '' ) {
532 $props[] = $this->msg( 'whatlinkshere-sectionredir' )
533 ->rawParams( $this->getLinkRenderer()->makeLink(
534 $target->createFragmentTarget( $row->rd_fragment ),
535 $row->rd_fragment
536 ) )->escaped();
537 } elseif ( $row->rd_from ) {
538 $props[] = $this->msg( 'isredirect' )->escaped();
539 }
540 if ( $row->is_template ) {
541 $props[] = $this->msg( 'istemplate' )->escaped();
542 }
543 if ( $row->is_image ) {
544 $props[] = $this->msg( 'isimage' )->escaped();
545 }
546
547 $this->getHookRunner()->onWhatLinksHereProps( $row, $nt, $target, $props );
548
549 if ( count( $props ) ) {
550 $propsText = $this->msg( 'parentheses' )
551 ->rawParams( $this->getLanguage()->semicolonList( $props ) )->escaped();
552 }
553
554 # Space for utilities links, with a what-links-here link provided
555 $wlhLink = $this->wlhLink(
556 $nt,
557 $this->msg( 'whatlinkshere-links' )->text(),
558 $this->msg( 'editlink' )->text()
559 );
560 $wlh = Xml::wrapClass(
561 $this->msg( 'parentheses' )->rawParams( $wlhLink )->escaped(),
562 'mw-whatlinkshere-tools'
563 );
564
565 return $notClose ?
566 Xml::openElement( 'li' ) . "$link $propsText $dirmark $wlh\n" :
567 Xml::tags( 'li', null, "$link $propsText $dirmark $wlh" ) . "\n";
568 }
569
570 protected function listEnd() {
571 return Xml::closeElement( 'ul' );
572 }
573
574 protected function wlhLink( Title $target, $text, $editText ) {
575 static $title = null;
576 if ( $title === null ) {
577 $title = $this->getPageTitle();
578 }
579
580 $linkRenderer = $this->getLinkRenderer();
581
582 // always show a "<- Links" link
583 $links = [
584 'links' => $linkRenderer->makeKnownLink(
585 $title,
586 $text,
587 [],
588 [ 'target' => $target->getPrefixedText() ]
589 ),
590 ];
591
592 // if the page is editable, add an edit link
593 if (
594 // check user permissions
595 $this->getAuthority()->isAllowed( 'edit' ) &&
596 // check, if the content model is editable through action=edit
597 $this->contentHandlerFactory->getContentHandler( $target->getContentModel() )
598 ->supportsDirectEditing()
599 ) {
600 $links['edit'] = $linkRenderer->makeKnownLink(
601 $target,
602 $editText,
603 [],
604 [ 'action' => 'edit' ]
605 );
606 }
607
608 // build the links html
609 return $this->getLanguage()->pipeList( $links );
610 }
611
612 private function getPrevNext( $prevNamespace, $prevPageId, $nextNamespace, $nextPageId ) {
613 $navBuilder = new PagerNavigationBuilder( $this->getContext() );
614
615 $navBuilder
616 ->setPage( $this->getPageTitle( $this->target->getPrefixedDBkey() ) )
617 // Remove 'target', already included in the request title
618 ->setLinkQuery( array_diff_key( $this->opts->getChangedValues(), [ 'target' => null ] ) )
619 ->setLimits( $this->limits )
620 ->setLimitLinkQueryParam( 'limit' )
621 ->setCurrentLimit( $this->opts->getValue( 'limit' ) )
622 ->setPrevMsg( 'whatlinkshere-prev' )
623 ->setNextMsg( 'whatlinkshere-next' );
624
625 if ( $prevPageId != 0 ) {
626 $navBuilder->setPrevLinkQuery( [ 'dir' => 'prev', 'offset' => "$prevNamespace|$prevPageId" ] );
627 }
628 if ( $nextPageId != 0 ) {
629 $navBuilder->setNextLinkQuery( [ 'dir' => 'next', 'offset' => "$nextNamespace|$nextPageId" ] );
630 }
631
632 return $navBuilder->getHtml();
633 }
634
635 private function whatlinkshereForm() {
636 // We get nicer value from the title object
637 $this->opts->consumeValue( 'target' );
638 $target = $this->target ? $this->target->getPrefixedText() : '';
639 $this->opts->consumeValue( 'namespace' );
640 $this->opts->consumeValue( 'invert' );
641
642 $fields = [
643 'target' => [
644 'type' => 'title',
645 'name' => 'target',
646 'default' => $target,
647 'id' => 'mw-whatlinkshere-target',
648 'label-message' => 'whatlinkshere-page',
649 'section' => 'whatlinkshere-target',
650 ],
651 'namespace' => [
652 'type' => 'namespaceselect',
653 'name' => 'namespace',
654 'id' => 'namespace',
655 'label-message' => 'namespace',
656 'all' => '',
657 'in-user-lang' => true,
658 'section' => 'whatlinkshere-ns',
659 ],
660 'invert' => [
661 'type' => 'check',
662 'name' => 'invert',
663 'id' => 'nsinvert',
664 'hide-if' => [ '===', 'namespace', '' ],
665 'label-message' => 'invert',
666 'help-message' => 'tooltip-whatlinkshere-invert',
667 'help-inline' => false,
668 'section' => 'whatlinkshere-ns',
669 ],
670 ];
671
672 $filters = [ 'hidetrans', 'hidelinks', 'hideredirs' ];
673 if ( $this->target instanceof Title &&
674 $this->target->getNamespace() == NS_FILE ) {
675 $filters[] = 'hideimages';
676 }
677
678 // Combined message keys: 'whatlinkshere-hideredirs', 'whatlinkshere-hidetrans',
679 // 'whatlinkshere-hidelinks', 'whatlinkshere-hideimages'
680 // To be sure they will be found by grep
681 foreach ( $filters as $filter ) {
682 // Parameter only provided for backwards-compatibility with old translations
683 $hide = $this->msg( 'hide' )->text();
684 $msg = $this->msg( "whatlinkshere-{$filter}", $hide )->text();
685 $fields[$filter] = [
686 'type' => 'check',
687 'name' => $filter,
688 'label' => $msg,
689 'section' => 'whatlinkshere-filter',
690 ];
691 }
692
693 $form = HTMLForm::factory( 'ooui', $fields, $this->getContext() )
694 ->setMethod( 'GET' )
695 ->setTitle( $this->getPageTitle() )
696 ->setWrapperLegendMsg( 'whatlinkshere' )
697 ->setSubmitTextMsg( 'whatlinkshere-submit' );
698
699 return $form->prepareForm()->getHTML( false );
700 }
701
710 public function prefixSearchSubpages( $search, $limit, $offset ) {
711 return $this->prefixSearchString( $search, $limit, $offset, $this->searchEngineFactory );
712 }
713
714 protected function getGroupName() {
715 return 'pagetools';
716 }
717}
getAuthority()
const NS_FILE
Definition Defines.php:70
const NS_MAIN
Definition Defines.php:64
getContext()
if(!defined('MW_SETUP_CALLBACK'))
The persistent session ID (if any) loaded at startup.
Definition WebStart.php:82
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.
static factory( $displayFormat, $descriptor, IContextSource $context, $messagePrefix='')
Construct a HTMLForm object for given display type.
Definition HTMLForm.php:348
Shortcut to construct an includable special page.
Service for compat reading of links tables.
A class containing constants representing the names of configuration variables.
Build the navigation for a pager, with links to prev/next page, links to change limits,...
static numParam( $num)
Definition Message.php:1145
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.
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.
execute( $par)
Default execute method Checks user permissions.
wlhLink(Title $target, $text, $editText)
listItem( $row, $nt, $target, $notClose=false)
__construct(ILoadBalancer $loadBalancer, LinkBatchFactory $linkBatchFactory, IContentHandlerFactory $contentHandlerFactory, SearchEngineFactory $searchEngineFactory, NamespaceInfo $namespaceInfo, TitleFactory $titleFactory, LinksMigration $linksMigration)
prefixSearchSubpages( $search, $limit, $offset)
Return an array of subpages beginning with $search that this special page will accept.
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
Creates Title objects.
Represents a title within MediaWiki.
Definition Title.php:49
getNamespace()
Get the namespace index, i.e.
Definition Title.php:1066
getDBkey()
Get the main part with underscores.
Definition Title.php:1057
createFragmentTarget(string $fragment)
Creates a new Title for a different fragment of the same page.
Definition Title.php:1823
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition Title.php:638
getContentModel( $flags=0)
Get the page's content model id, see the CONTENT_MODEL_XXX constants.
Definition Title.php:1088
getPrefixedText()
Get the prefixed title with spaces.
Definition Title.php:1890
Note that none of the methods in this class are stable to override.
Basic database interface for live and lazy-loaded relation database handles.
Definition IDatabase.php:39
Create and track the database connections and transactions for a given database cluster.