202 $level, $target, $limit, $offsetNamespace = 0, $offsetPageID = 0, $dir =
'next'
204 $out = $this->getOutput();
205 $dbr = $this->loadBalancer->getConnectionRef( ILoadBalancer::DB_REPLICA );
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' );
214 $fetchredirs = $hidelinks && !$hideredirs;
218 $conds[
'redirect'] = [
222 $conds[
'pagelinks'] = [
226 $conds[
'templatelinks'] = [
230 $conds[
'imagelinks'] = [
234 $namespace = $this->opts->getValue(
'namespace' );
235 if ( is_int( $namespace ) ) {
236 $invert = $this->opts->getValue(
'invert' );
240 $namespaces = array_diff(
241 $this->namespaceInfo->getValidNamespaces(), [ $namespace ] );
243 $namespaces = $namespace;
248 $namespaces = $this->namespaceInfo->getValidNamespaces();
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;
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)";
270 $conds[
'pagelinks'][
'rd_from'] =
null;
273 $sortDirection = $dir ===
'prev' ? SelectQueryBuilder::SORT_DESC : SelectQueryBuilder::SORT_ASC;
276 $queryFunc =
static function (
IDatabase $dbr, $table, $fromCol ) use (
277 $conds, $target, $limit, $sortDirection, $fname
280 $queryLimit = $limit + 1;
282 "rd_from = $fromCol",
285 'rd_interwiki = ' .
$dbr->addQuotes(
'' ) .
' OR rd_interwiki IS NULL'
288 $subQuery =
$dbr->newSelectQueryBuilder()
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 );
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 )
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__ )
320 $plRes = $queryFunc(
$dbr,
'pagelinks',
'pl_from' );
324 $tlRes = $queryFunc(
$dbr,
'templatelinks',
'tl_from' );
327 if ( !$hideimages ) {
328 $ilRes = $queryFunc(
$dbr,
'imagelinks',
'il_from' );
331 if ( ( !$fetchredirs || !$rdRes->numRows() )
332 && ( $hidelinks || !$plRes->numRows() )
333 && ( $hidetrans || !$tlRes->numRows() )
334 && ( $hideimages || !$ilRes->numRows() )
336 if ( $level == 0 && !$this->including() ) {
337 $out->addHTML( $this->whatlinkshereForm() );
340 if ( $hidelinks || $hidetrans || $hideredirs || $hideimages ) {
341 $out->addHTML( $this->getFilterPanel() );
343 $msgKey = is_int( $namespace ) ?
'nolinkshere-ns' :
'nolinkshere';
344 $link = $this->getLinkRenderer()->makeLink(
348 $this->target->isRedirect() ? [
'redirect' =>
'no' ] : []
351 $errMsg = $this->msg( $msgKey )
352 ->params( $this->target->getPrefixedText() )
355 $out->addHTML( $errMsg );
356 $out->setStatusCode( 404 );
366 if ( $fetchredirs ) {
367 foreach ( $rdRes as $row ) {
368 $row->is_template = 0;
370 $rows[$row->page_id] = $row;
374 foreach ( $plRes as $row ) {
375 $row->is_template = 0;
377 $rows[$row->page_id] = $row;
381 foreach ( $tlRes as $row ) {
382 $row->is_template = 1;
384 $rows[$row->page_id] = $row;
387 if ( !$hideimages ) {
388 foreach ( $ilRes as $row ) {
389 $row->is_template = 0;
391 $rows[$row->page_id] = $row;
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;
400 if ( $rowA->page_id !== $rowB->page_id ) {
401 return $rowA->page_id < $rowB->page_id ? -1 : 1;
406 $numRows = count( $rows );
410 $nextNamespace = $nextPageId = $prevNamespace = $prevPageId =
false;
412 } elseif ( $dir ===
'prev' ) {
413 if ( $numRows > $limit ) {
416 $nextNamespace = $rows[$limit]->page_namespace;
417 $nextPageId = $rows[$limit]->page_id;
419 $rows = array_slice( $rows, 1, $limit );
421 $prevNamespace = $rows[0]->page_namespace;
422 $prevPageId = $rows[0]->page_id;
425 $nextNamespace = $rows[$numRows - 1]->page_namespace;
426 $nextPageId = $rows[$numRows - 1]->page_id;
427 $prevNamespace =
false;
432 $prevNamespace = $offsetPageID ? $rows[0]->page_namespace :
false;
433 $prevPageId = $offsetPageID ? $rows[0]->page_id :
false;
434 if ( $numRows > $limit ) {
436 $nextNamespace = $rows[$limit - 1]->page_namespace;
437 $nextPageId = $rows[$limit - 1]->page_id;
439 $rows = array_slice( $rows, 0, $limit );
441 $nextNamespace =
false;
448 $lb = $this->linkBatchFactory->newLinkBatch();
449 foreach ( $rows as $row ) {
450 $lb->add( $row->page_namespace, $row->page_title );
454 if ( $level == 0 && !$this->including() ) {
455 $out->addHTML( $this->whatlinkshereForm() );
456 $out->addHTML( $this->getFilterPanel() );
458 $link = $this->getLinkRenderer()->makeLink(
462 $this->target->isRedirect() ? [
'redirect' =>
'no' ] : []
465 $msg = $this->msg(
'linkshere' )
466 ->params( $this->target->getPrefixedText() )
469 $out->addHTML( $msg );
473 $prevnext = $this->getPrevNext( $prevNamespace, $prevPageId, $nextNamespace, $nextPageId );
474 $out->addHTML( $prevnext );
476 $out->addHTML( $this->listStart( $level ) );
477 foreach ( $rows as $row ) {
478 $nt = Title::makeTitle( $row->page_namespace, $row->page_title );
480 if ( $row->rd_from && $level < 2 ) {
481 $out->addHTML( $this->listItem( $row, $nt, $target,
true ) );
482 $this->showIndirectLinks(
485 $this->getConfig()->
get(
'MaxRedirectLinksRetrieved' )
487 $out->addHTML( Xml::closeElement(
'li' ) );
489 $out->addHTML( $this->listItem( $row, $nt, $target ) );
493 $out->addHTML( $this->listEnd() );
495 if ( $level == 0 && !$this->including() ) {
496 $out->addHTML( $prevnext );