128 $this->tempIdOffset = $idOffset = $this->parent->nextLinkID();
131 # Renumber internal links
132 foreach ( $other->internals
as $ns => $nsLinks ) {
133 foreach ( $nsLinks
as $key => $entry ) {
134 $newKey = $idOffset + $key;
135 $this->internals[$ns][$newKey] = $entry;
136 $maxId = $newKey > $maxId ? $newKey : $maxId;
139 $texts = preg_replace_callback(
'/(<!--LINK\'" \d+:)(\d+)(-->)/',
140 [ $this,
'mergeForeignCallback' ], $texts );
142 # Renumber interwiki links
143 foreach ( $other->interwikis
as $key => $entry ) {
144 $newKey = $idOffset + $key;
145 $this->interwikis[$newKey] = $entry;
146 $maxId = $newKey > $maxId ? $newKey : $maxId;
148 $texts = preg_replace_callback(
'/(<!--IWLINK\'" )(\d+)(-->)/',
149 [ $this,
'mergeForeignCallback' ], $texts );
151 # Set the parent link ID to be beyond the highest used ID
152 $this->parent->setLinkID( $maxId + 1 );
153 $this->tempIdOffset =
null;
176 while ( $pos < strlen( $text ) ) {
177 if ( !preg_match(
'/<!--LINK\'" (\d+):(\d+)-->/',
178 $text, $m, PREG_OFFSET_CAPTURE, $pos )
184 $sub->internals[$ns][$key] = $this->internals[$ns][$key];
185 $pos = $m[0][1] + strlen( $m[0][0] );
190 while ( $pos < strlen( $text ) ) {
191 if ( !preg_match(
'/<!--IWLINK\'" (\d+)-->/', $text, $m, PREG_OFFSET_CAPTURE, $pos ) ) {
195 $sub->interwikis[$key] = $this->interwikis[$key];
196 $pos = $m[0][1] + strlen( $m[0][0] );
234 if ( !is_object( $nt ) ) {
236 $retVal =
"<!-- ERROR -->{$prefix}{$text}{$trail}";
238 # Separate the link trail from the rest of the link
243 'text' => $prefix . $text . $inside,
244 'pdbk' => $nt->getPrefixedDBkey(),
250 if ( $nt->isExternal() ) {
252 $key = $this->parent->nextLinkID();
253 $this->interwikis[$key] = $entry;
254 $retVal =
"<!--IWLINK'\" $key-->{$trail}";
256 $key = $this->parent->nextLinkID();
257 $ns = $nt->getNamespace();
258 $this->internals[$ns][$key] = $entry;
259 $retVal =
"<!--LINK'\" $ns:$key-->{$trail}";
281 if ( !$this->internals ) {
286 $linkCache = MediaWikiServices::getInstance()->getLinkCache();
287 $output = $this->parent->getOutput();
293 ksort( $this->internals );
295 $linkcolour_ids = [];
299 $lb->setCaller( __METHOD__ );
301 foreach ( $this->internals
as $ns => $entries ) {
302 foreach ( $entries
as $entry ) {
305 $pdbk = $entry[
'pdbk'];
307 # Skip invalid entries.
308 # Result will be ugly, but prevents crash.
309 if ( is_null(
$title ) ) {
313 # Check if it's a static known link, e.g. interwiki
314 if (
$title->isAlwaysKnown() ) {
319 $id = $linkCache->getGoodLinkID( $pdbk );
323 $linkcolour_ids[$id] = $pdbk;
324 } elseif ( $linkCache->isBadLink( $pdbk ) ) {
327 # Not in the link cache, add it to the query
333 if ( !$lb->isEmpty() ) {
334 $fields = array_merge(
335 LinkCache::getSelectFields(),
336 [
'page_namespace',
'page_title' ]
342 $lb->constructSet(
'page',
$dbr ),
346 # Fetch data and form into an associative array
347 # non-existent = broken
349 $title = Title::makeTitle(
$s->page_namespace,
$s->page_title );
350 $pdbk =
$title->getPrefixedDBkey();
351 $linkCache->addGoodLinkObjFromRow(
$title,
$s );
355 $linkcolour_ids[
$s->page_id] = $pdbk;
359 if ( count( $linkcolour_ids ) ) {
361 Hooks::run(
'GetLinkColours', [ $linkcolour_ids, &
$colours, $this->parent->getTitle() ] );
364 # Do a second query for different language variants of links and categories
365 if ( $this->parent->getContentLanguage()->hasVariants() ) {
369 # Construct search and replace arrays
371 foreach ( $this->internals
as $ns => $entries ) {
372 foreach ( $entries
as $index => $entry ) {
373 $pdbk = $entry[
'pdbk'];
375 $query = $entry[
'query'] ?? [];
377 $searchkey =
"<!--LINK'\" $key-->";
378 $displayText = $entry[
'text'];
379 if ( isset( $entry[
'selflink'] ) ) {
383 if ( $displayText ===
'' ) {
386 $displayText =
new HtmlArmor( $displayText );
393 $linkCache->addBadLinkObj(
$title );
404 $replacePairs[$searchkey] =
$link;
409 $text = preg_replace_callback(
410 '/(<!--LINK\'" .*?-->)/',
455 $output = $this->parent->getOutput();
456 $linkCache = MediaWikiServices::getInstance()->getLinkCache();
457 $titlesToBeConverted =
'';
463 foreach ( $this->internals
as $ns => $entries ) {
467 foreach ( $entries
as $index => $entry ) {
468 $pdbk = $entry[
'pdbk'];
471 $titlesAttrs[] = [ $index, $entry[
'title'] ];
474 $titlesToBeConverted .= $entry[
'title']->getText() .
"\0";
480 $titlesAllVariants = $this->parent->getContentLanguage()->
481 autoConvertToAllVariants( rtrim( $titlesToBeConverted,
"\0" ) );
482 $allVariantsName = array_keys( $titlesAllVariants );
483 foreach ( $titlesAllVariants
as &$titlesVariant ) {
484 $titlesVariant = explode(
"\0", $titlesVariant );
488 $parentTitle = $this->parent->getTitle();
489 foreach ( $titlesAttrs
as $i => $attrs ) {
492 $ns =
$title->getNamespace();
493 $text =
$title->getText();
495 foreach ( $allVariantsName
as $variantName ) {
496 $textVariant = $titlesAllVariants[$variantName][$i];
497 if ( $textVariant === $text ) {
501 $variantTitle = Title::makeTitle( $ns, $textVariant );
506 if ( $variantTitle->equals( $parentTitle ) && !
$title->hasFragment() ) {
507 $this->internals[$ns][$index][
'selflink'] =
true;
511 $linkBatch->addObj( $variantTitle );
512 $variantMap[$variantTitle->getPrefixedDBkey()][] =
"$ns:$index";
519 foreach (
$output->getCategoryLinks()
as $category ) {
520 $categoryTitle = Title::makeTitleSafe(
NS_CATEGORY, $category );
521 $linkBatch->addObj( $categoryTitle );
522 $variants = $this->parent->getContentLanguage()->autoConvertToAllVariants( $category );
523 foreach ( $variants
as $variant ) {
524 if ( $variant !== $category ) {
525 $variantTitle = Title::makeTitleSafe(
NS_CATEGORY, $variant );
526 if ( is_null( $variantTitle ) ) {
529 $linkBatch->addObj( $variantTitle );
530 $categoryMap[$variant] = [ $category, $categoryTitle ];
535 if ( !$linkBatch->isEmpty() ) {
538 $fields = array_merge(
539 LinkCache::getSelectFields(),
540 [
'page_namespace',
'page_title' ]
543 $varRes =
$dbr->select(
'page',
545 $linkBatch->constructSet(
'page',
$dbr ),
549 $linkcolour_ids = [];
553 foreach ( $varRes
as $s ) {
554 $variantTitle = Title::makeTitle(
$s->page_namespace,
$s->page_title );
555 $varPdbk = $variantTitle->getPrefixedDBkey();
556 $vardbk = $variantTitle->getDBkey();
559 if ( isset( $variantMap[$varPdbk] ) ) {
560 $holderKeys = $variantMap[$varPdbk];
561 $linkCache->addGoodLinkObjFromRow( $variantTitle,
$s );
562 $output->addLink( $variantTitle,
$s->page_id );
566 foreach ( $holderKeys
as $key ) {
567 list( $ns, $index ) = explode(
':', $key, 2 );
568 $entry =& $this->internals[$ns][$index];
569 $pdbk = $entry[
'pdbk'];
573 $entry[
'title'] = $variantTitle;
574 $entry[
'pdbk'] = $varPdbk;
578 $linkcolour_ids[
$s->page_id] = $pdbk;
583 if ( isset( $categoryMap[$vardbk] ) ) {
584 list( $oldkey, $oldtitle ) = $categoryMap[$vardbk];
585 if ( !isset( $varCategories[$oldkey] ) && !$oldtitle->exists() ) {
586 $varCategories[$oldkey] = $vardbk;
590 Hooks::run(
'GetLinkColours', [ $linkcolour_ids, &
$colours, $this->parent->getTitle() ] );
593 if ( count( $varCategories ) > 0 ) {
595 $originalCats =
$output->getCategories();
596 foreach ( $originalCats
as $cat => $sortkey ) {
598 if ( array_key_exists( $cat, $varCategories ) ) {
599 $newCats[$varCategories[$cat]] = $sortkey;
601 $newCats[$cat] = $sortkey;
604 $output->setCategoryLinks( $newCats );