129 $this->tempIdOffset = $idOffset = $this->parent->nextLinkID();
132 # Renumber internal links
133 foreach ( $other->internals as $ns => $nsLinks ) {
134 foreach ( $nsLinks as $key => $entry ) {
135 $newKey = $idOffset + $key;
136 $this->internals[$ns][$newKey] = $entry;
137 $maxId = $newKey > $maxId ? $newKey : $maxId;
140 $texts = preg_replace_callback(
'/(<!--LINK\'" \d+:)(\d+)(-->)/',
141 [ $this,
'mergeForeignCallback' ], $texts );
143 # Renumber interwiki links
144 foreach ( $other->interwikis as $key => $entry ) {
145 $newKey = $idOffset + $key;
146 $this->interwikis[$newKey] = $entry;
147 $maxId = $newKey > $maxId ? $newKey : $maxId;
149 $texts = preg_replace_callback(
'/(<!--IWLINK\'" )(\d+)(-->)/',
150 [ $this,
'mergeForeignCallback' ], $texts );
152 # Set the parent link ID to be beyond the highest used ID
153 $this->parent->setLinkID( $maxId + 1 );
154 $this->tempIdOffset =
null;
177 while ( $pos < strlen( $text ) ) {
178 if ( !preg_match(
'/<!--LINK\'" (\d+):(\d+)-->/',
179 $text, $m, PREG_OFFSET_CAPTURE, $pos )
185 $sub->internals[$ns][$key] = $this->internals[$ns][$key];
186 $pos = $m[0][1] + strlen( $m[0][0] );
191 while ( $pos < strlen( $text ) ) {
192 if ( !preg_match(
'/<!--IWLINK\'" (\d+)-->/', $text, $m, PREG_OFFSET_CAPTURE, $pos ) ) {
196 $sub->interwikis[$key] = $this->interwikis[$key];
197 $pos = $m[0][1] + strlen( $m[0][0] );
283 if ( !$this->internals ) {
288 $linkCache = MediaWikiServices::getInstance()->getLinkCache();
289 $output = $this->parent->getOutput();
290 $linkRenderer = $this->parent->getLinkRenderer();
295 ksort( $this->internals );
297 $linkcolour_ids = [];
301 $lb->setCaller( __METHOD__ );
303 foreach ( $this->internals as $ns => $entries ) {
304 foreach ( $entries as $entry ) {
307 $pdbk = $entry[
'pdbk'];
309 # Skip invalid entries.
310 # Result will be ugly, but prevents crash.
311 if ( is_null(
$title ) ) {
315 # Check if it's a static known link, e.g. interwiki
316 if (
$title->isAlwaysKnown() ) {
317 $colours[$pdbk] =
'';
319 $colours[$pdbk] =
'new';
321 $id = $linkCache->getGoodLinkID( $pdbk );
323 $colours[$pdbk] = $linkRenderer->getLinkClasses(
$title );
324 $output->addLink(
$title, $id );
325 $linkcolour_ids[$id] = $pdbk;
326 } elseif ( $linkCache->isBadLink( $pdbk ) ) {
327 $colours[$pdbk] =
'new';
329 # Not in the link cache, add it to the query
335 if ( !$lb->isEmpty() ) {
336 $fields = array_merge(
337 LinkCache::getSelectFields(),
338 [
'page_namespace',
'page_title' ]
344 $lb->constructSet(
'page',
$dbr ),
348 # Fetch data and form into an associative array
349 # non-existent = broken
351 $title = Title::makeTitle(
$s->page_namespace,
$s->page_title );
352 $pdbk =
$title->getPrefixedDBkey();
353 $linkCache->addGoodLinkObjFromRow(
$title,
$s );
354 $output->addLink(
$title,
$s->page_id );
355 $colours[$pdbk] = $linkRenderer->getLinkClasses(
$title );
357 $linkcolour_ids[
$s->page_id] = $pdbk;
361 if ( count( $linkcolour_ids ) ) {
363 Hooks::run(
'GetLinkColours', [ $linkcolour_ids, &$colours, $this->parent->getTitle() ] );
366 # Do a second query for different language variants of links and categories
367 if ( $this->parent->getContentLanguage()->hasVariants() ) {
371 # Construct search and replace arrays
373 foreach ( $this->internals as $ns => $entries ) {
374 foreach ( $entries as $index => $entry ) {
375 $pdbk = $entry[
'pdbk'];
377 $query = $entry[
'query'] ?? [];
379 $searchkey =
"<!--LINK'\" $key-->";
380 $displayText = $entry[
'text'];
381 if ( isset( $entry[
'selflink'] ) ) {
385 if ( $displayText ===
'' ) {
388 $displayText =
new HtmlArmor( $displayText );
390 if ( !isset( $colours[$pdbk] ) ) {
391 $colours[$pdbk] =
'new';
394 if ( $colours[$pdbk] ==
'new' ) {
395 $linkCache->addBadLinkObj(
$title );
396 $output->addLink(
$title, 0 );
397 $link = $linkRenderer->makeBrokenLink(
398 $title, $displayText, $attribs, $query
401 $link = $linkRenderer->makePreloadedLink(
402 $title, $displayText, $colours[$pdbk], $attribs, $query
406 $replacePairs[$searchkey] = $link;
411 $text = preg_replace_callback(
412 '/(<!--LINK\'" .*?-->)/',
413 function ( array
$matches ) use ( $replacePairs ) {
458 $output = $this->parent->getOutput();
459 $linkCache = MediaWikiServices::getInstance()->getLinkCache();
460 $titlesToBeConverted =
'';
466 foreach ( $this->internals as $ns => $entries ) {
470 foreach ( $entries as $index => $entry ) {
471 $pdbk = $entry[
'pdbk'];
473 if ( !isset( $colours[$pdbk] ) || $colours[$pdbk] ===
'new' ) {
474 $titlesAttrs[] = [ $index, $entry[
'title'] ];
477 $titlesToBeConverted .= $entry[
'title']->getText() .
"\0";
483 $titlesAllVariants = $this->parent->getContentLanguage()->
484 autoConvertToAllVariants( rtrim( $titlesToBeConverted,
"\0" ) );
485 $allVariantsName = array_keys( $titlesAllVariants );
486 foreach ( $titlesAllVariants as &$titlesVariant ) {
487 $titlesVariant = explode(
"\0", $titlesVariant );
491 $parentTitle = $this->parent->getTitle();
492 foreach ( $titlesAttrs as $i => $attrs ) {
494 list( $index,
$title ) = $attrs;
495 $ns =
$title->getNamespace();
496 $text =
$title->getText();
498 foreach ( $allVariantsName as $variantName ) {
499 $textVariant = $titlesAllVariants[$variantName][$i];
500 if ( $textVariant === $text ) {
504 $variantTitle = Title::makeTitle( $ns, $textVariant );
509 if ( $variantTitle->equals( $parentTitle ) && !
$title->hasFragment() ) {
510 $this->internals[$ns][$index][
'selflink'] =
true;
514 $linkBatch->addObj( $variantTitle );
515 $variantMap[$variantTitle->getPrefixedDBkey()][] =
"$ns:$index";
522 foreach ( $output->getCategoryLinks() as $category ) {
523 $categoryTitle = Title::makeTitleSafe(
NS_CATEGORY, $category );
524 $linkBatch->addObj( $categoryTitle );
525 $variants = $this->parent->getContentLanguage()->autoConvertToAllVariants( $category );
526 foreach ( $variants as $variant ) {
527 if ( $variant !== $category ) {
528 $variantTitle = Title::makeTitleSafe(
NS_CATEGORY, $variant );
529 if ( is_null( $variantTitle ) ) {
532 $linkBatch->addObj( $variantTitle );
533 $categoryMap[$variant] = [ $category, $categoryTitle ];
538 if ( !$linkBatch->isEmpty() ) {
541 $fields = array_merge(
542 LinkCache::getSelectFields(),
543 [
'page_namespace',
'page_title' ]
546 $varRes =
$dbr->select(
'page',
548 $linkBatch->constructSet(
'page',
$dbr ),
552 $linkcolour_ids = [];
553 $linkRenderer = $this->parent->getLinkRenderer();
556 foreach ( $varRes as
$s ) {
557 $variantTitle = Title::makeTitle(
$s->page_namespace,
$s->page_title );
558 $varPdbk = $variantTitle->getPrefixedDBkey();
559 $vardbk = $variantTitle->getDBkey();
562 if ( isset( $variantMap[$varPdbk] ) ) {
563 $holderKeys = $variantMap[$varPdbk];
564 $linkCache->addGoodLinkObjFromRow( $variantTitle,
$s );
565 $output->addLink( $variantTitle,
$s->page_id );
569 foreach ( $holderKeys as $key ) {
570 list( $ns, $index ) = explode(
':', $key, 2 );
571 $entry =& $this->internals[$ns][$index];
572 $pdbk = $entry[
'pdbk'];
574 if ( !isset( $colours[$pdbk] ) || $colours[$pdbk] ===
'new' ) {
576 $entry[
'title'] = $variantTitle;
577 $entry[
'pdbk'] = $varPdbk;
580 $colours[$varPdbk] = $linkRenderer->getLinkClasses( $variantTitle );
581 $linkcolour_ids[
$s->page_id] = $pdbk;
586 if ( isset( $categoryMap[$vardbk] ) ) {
587 list( $oldkey, $oldtitle ) = $categoryMap[$vardbk];
588 if ( !isset( $varCategories[$oldkey] ) && !$oldtitle->exists() ) {
589 $varCategories[$oldkey] = $vardbk;
593 Hooks::run(
'GetLinkColours', [ $linkcolour_ids, &$colours, $this->parent->getTitle() ] );
596 if ( count( $varCategories ) > 0 ) {
598 $originalCats = $output->getCategories();
599 foreach ( $originalCats as $cat => $sortkey ) {
601 if ( array_key_exists( $cat, $varCategories ) ) {
602 $newCats[$varCategories[$cat]] = $sortkey;
604 $newCats[$cat] = $sortkey;
607 $output->setCategoryLinks( $newCats );