/** * Get the modification times of all titles that would be loaded for * a given context. * @param $context ResourceLoaderContext: Context object * @return array( prefixed DB key => UNIX timestamp ), nonexistent titles are dropped */ protected function getTitleMtimes(ResourceLoaderContext $context) { $dbr = $this->getDB(); if (!$dbr) { // We're dealing with a subclass that doesn't have a DB return array(); } $hash = $context->getHash(); if (isset($this->titleMtimes[$hash])) { return $this->titleMtimes[$hash]; } $this->titleMtimes[$hash] = array(); $batch = new LinkBatch(); foreach ($this->getPages($context) as $titleText => $options) { $batch->addObj(Title::newFromText($titleText)); } if (!$batch->isEmpty()) { $res = $dbr->select('page', array('page_namespace', 'page_title', 'page_touched'), $batch->constructSet('page', $dbr), __METHOD__); foreach ($res as $row) { $title = Title::makeTitle($row->page_namespace, $row->page_title); $this->titleMtimes[$hash][$title->getPrefixedDBkey()] = wfTimestamp(TS_UNIX, $row->page_touched); } } return $this->titleMtimes[$hash]; }
/** * Modify $this->internals and $colours according to language variant linking rules */ protected function doVariants(&$colours) { global $wgContLang; $linkBatch = new LinkBatch(); $variantMap = array(); // maps $pdbkey_Variant => $keys (of link holders) $output = $this->parent->getOutput(); $linkCache = LinkCache::singleton(); $threshold = $this->parent->getOptions()->getStubThreshold(); $titlesToBeConverted = ''; $titlesAttrs = array(); // Concatenate titles to a single string, thus we only need auto convert the // single string to all variants. This would improve parser's performance // significantly. foreach ($this->internals as $ns => $entries) { foreach ($entries as $index => $entry) { $pdbk = $entry['pdbk']; // we only deal with new links (in its first query) if (!isset($colours[$pdbk])) { $title = $entry['title']; $titleText = $title->getText(); $titlesAttrs[] = array('ns' => $ns, 'key' => "{$ns}:{$index}", 'titleText' => $titleText); // separate titles with \0 because it would never appears // in a valid title $titlesToBeConverted .= $titleText . ""; } } } // Now do the conversion and explode string to text of titles $titlesAllVariants = $wgContLang->autoConvertToAllVariants($titlesToBeConverted); $allVariantsName = array_keys($titlesAllVariants); foreach ($titlesAllVariants as &$titlesVariant) { $titlesVariant = explode("", $titlesVariant); } $l = count($titlesAttrs); // Then add variants of links to link batch for ($i = 0; $i < $l; $i++) { foreach ($allVariantsName as $variantName) { $textVariant = $titlesAllVariants[$variantName][$i]; if ($textVariant != $titlesAttrs[$i]['titleText']) { $variantTitle = Title::makeTitle($titlesAttrs[$i]['ns'], $textVariant); if (is_null($variantTitle)) { continue; } $linkBatch->addObj($variantTitle); $variantMap[$variantTitle->getPrefixedDBkey()][] = $titlesAttrs[$i]['key']; } } } // process categories, check if a category exists in some variant $categoryMap = array(); // maps $category_variant => $category (dbkeys) $varCategories = array(); // category replacements oldDBkey => newDBkey foreach ($output->getCategoryLinks() as $category) { $variants = $wgContLang->autoConvertToAllVariants($category); foreach ($variants as $variant) { if ($variant != $category) { $variantTitle = Title::newFromDBkey(Title::makeName(NS_CATEGORY, $variant)); if (is_null($variantTitle)) { continue; } $linkBatch->addObj($variantTitle); $categoryMap[$variant] = $category; } } } if (!$linkBatch->isEmpty()) { // construct query $dbr = wfGetDB(DB_SLAVE); $varRes = $dbr->select('page', array('page_id', 'page_namespace', 'page_title', 'page_is_redirect', 'page_len', 'page_latest'), $linkBatch->constructSet('page', $dbr), __METHOD__); $linkcolour_ids = array(); // for each found variants, figure out link holders and replace foreach ($varRes as $s) { $variantTitle = Title::makeTitle($s->page_namespace, $s->page_title); $varPdbk = $variantTitle->getPrefixedDBkey(); $vardbk = $variantTitle->getDBkey(); $holderKeys = array(); if (isset($variantMap[$varPdbk])) { $holderKeys = $variantMap[$varPdbk]; $linkCache->addGoodLinkObjFromRow($variantTitle, $s); $output->addLink($variantTitle, $s->page_id); } // loop over link holders foreach ($holderKeys as $key) { list($ns, $index) = explode(':', $key, 2); $entry =& $this->internals[$ns][$index]; $pdbk = $entry['pdbk']; if (!isset($colours[$pdbk])) { // found link in some of the variants, replace the link holder data $entry['title'] = $variantTitle; $entry['pdbk'] = $varPdbk; // set pdbk and colour # @todo FIXME: Convoluted data flow # The redirect status and length is passed to getLinkColour via the LinkCache # Use formal parameters instead $colours[$varPdbk] = Linker::getLinkColour($variantTitle, $threshold); $linkcolour_ids[$s->page_id] = $pdbk; } } // check if the object is a variant of a category if (isset($categoryMap[$vardbk])) { $oldkey = $categoryMap[$vardbk]; if ($oldkey != $vardbk) { $varCategories[$oldkey] = $vardbk; } } } wfRunHooks('GetLinkColours', array($linkcolour_ids, &$colours)); // rebuild the categories in original order (if there are replacements) if (count($varCategories) > 0) { $newCats = array(); $originalCats = $output->getCategories(); foreach ($originalCats as $cat => $sortkey) { // make the replacement if (array_key_exists($cat, $varCategories)) { $newCats[$varCategories[$cat]] = $sortkey; } else { $newCats[$cat] = $sortkey; } } $output->setCategoryLinks($newCats); } } }
/** * Modify $this->internals and $colours according to language variant linking rules * @param array $colours */ protected function doVariants(&$colours) { global $wgContLang, $wgContentHandlerUseDB; $linkBatch = new LinkBatch(); $variantMap = array(); // maps $pdbkey_Variant => $keys (of link holders) $output = $this->parent->getOutput(); $linkCache = LinkCache::singleton(); $threshold = $this->parent->getOptions()->getStubThreshold(); $titlesToBeConverted = ''; $titlesAttrs = array(); // Concatenate titles to a single string, thus we only need auto convert the // single string to all variants. This would improve parser's performance // significantly. foreach ($this->internals as $ns => $entries) { if ($ns == NS_SPECIAL) { continue; } foreach ($entries as $index => $entry) { $pdbk = $entry['pdbk']; // we only deal with new links (in its first query) if (!isset($colours[$pdbk]) || $colours[$pdbk] === 'new') { $titlesAttrs[] = array($index, $entry['title']); // separate titles with \0 because it would never appears // in a valid title $titlesToBeConverted .= $entry['title']->getText() . ""; } } } // Now do the conversion and explode string to text of titles $titlesAllVariants = $wgContLang->autoConvertToAllVariants(rtrim($titlesToBeConverted, "")); $allVariantsName = array_keys($titlesAllVariants); foreach ($titlesAllVariants as &$titlesVariant) { $titlesVariant = explode("", $titlesVariant); } // Then add variants of links to link batch $parentTitle = $this->parent->getTitle(); foreach ($titlesAttrs as $i => $attrs) { /** @var Title $title */ list($index, $title) = $attrs; $ns = $title->getNamespace(); $text = $title->getText(); foreach ($allVariantsName as $variantName) { $textVariant = $titlesAllVariants[$variantName][$i]; if ($textVariant === $text) { continue; } $variantTitle = Title::makeTitle($ns, $textVariant); if (is_null($variantTitle)) { continue; } // Self-link checking for mixed/different variant titles. At this point, we // already know the exact title does not exist, so the link cannot be to a // variant of the current title that exists as a separate page. if ($variantTitle->equals($parentTitle) && !$title->hasFragment()) { $this->internals[$ns][$index]['selflink'] = true; continue 2; } $linkBatch->addObj($variantTitle); $variantMap[$variantTitle->getPrefixedDBkey()][] = "{$ns}:{$index}"; } } // process categories, check if a category exists in some variant $categoryMap = array(); // maps $category_variant => $category (dbkeys) $varCategories = array(); // category replacements oldDBkey => newDBkey foreach ($output->getCategoryLinks() as $category) { $categoryTitle = Title::makeTitleSafe(NS_CATEGORY, $category); $linkBatch->addObj($categoryTitle); $variants = $wgContLang->autoConvertToAllVariants($category); foreach ($variants as $variant) { if ($variant !== $category) { $variantTitle = Title::makeTitleSafe(NS_CATEGORY, $variant); if (is_null($variantTitle)) { continue; } $linkBatch->addObj($variantTitle); $categoryMap[$variant] = array($category, $categoryTitle); } } } if (!$linkBatch->isEmpty()) { // construct query $dbr = wfGetDB(DB_SLAVE); $fields = array('page_id', 'page_namespace', 'page_title', 'page_is_redirect', 'page_len', 'page_latest'); if ($wgContentHandlerUseDB) { $fields[] = 'page_content_model'; } $varRes = $dbr->select('page', $fields, $linkBatch->constructSet('page', $dbr), __METHOD__); $linkcolour_ids = array(); // for each found variants, figure out link holders and replace foreach ($varRes as $s) { $variantTitle = Title::makeTitle($s->page_namespace, $s->page_title); $varPdbk = $variantTitle->getPrefixedDBkey(); $vardbk = $variantTitle->getDBkey(); $holderKeys = array(); if (isset($variantMap[$varPdbk])) { $holderKeys = $variantMap[$varPdbk]; $linkCache->addGoodLinkObjFromRow($variantTitle, $s); $output->addLink($variantTitle, $s->page_id); } // loop over link holders foreach ($holderKeys as $key) { list($ns, $index) = explode(':', $key, 2); $entry =& $this->internals[$ns][$index]; $pdbk = $entry['pdbk']; if (!isset($colours[$pdbk]) || $colours[$pdbk] === 'new') { // found link in some of the variants, replace the link holder data $entry['title'] = $variantTitle; $entry['pdbk'] = $varPdbk; // set pdbk and colour # @todo FIXME: Convoluted data flow # The redirect status and length is passed to getLinkColour via the LinkCache # Use formal parameters instead $colours[$varPdbk] = Linker::getLinkColour($variantTitle, $threshold); $linkcolour_ids[$s->page_id] = $pdbk; } } // check if the object is a variant of a category if (isset($categoryMap[$vardbk])) { list($oldkey, $oldtitle) = $categoryMap[$vardbk]; if (!isset($varCategories[$oldkey]) && !$oldtitle->exists()) { $varCategories[$oldkey] = $vardbk; } } } Hooks::run('GetLinkColours', array($linkcolour_ids, &$colours)); // rebuild the categories in original order (if there are replacements) if (count($varCategories) > 0) { $newCats = array(); $originalCats = $output->getCategories(); foreach ($originalCats as $cat => $sortkey) { // make the replacement if (array_key_exists($cat, $varCategories)) { $newCats[$varCategories[$cat]] = $sortkey; } else { $newCats[$cat] = $sortkey; } } $output->setCategoryLinks($newCats); } } }
/** * Get the information about the wiki pages for a given context. * @param ResourceLoaderContext $context * @return array Keyed by page name. Contains arrays with 'rev_len' and 'rev_sha1' keys */ protected function getTitleInfo(ResourceLoaderContext $context) { $dbr = $this->getDB(); if (!$dbr) { // We're dealing with a subclass that doesn't have a DB return array(); } $pages = $this->getPages($context); $key = implode('|', array_keys($pages)); if (!isset($this->titleInfo[$key])) { $this->titleInfo[$key] = array(); $batch = new LinkBatch(); foreach ($pages as $titleText => $options) { $batch->addObj(Title::newFromText($titleText)); } if (!$batch->isEmpty()) { $res = $dbr->select(array('page', 'revision'), array('page_namespace', 'page_title', 'rev_len', 'rev_sha1'), $batch->constructSet('page', $dbr), __METHOD__, array(), array('revision' => array('INNER JOIN', array('page_latest=rev_id')))); foreach ($res as $row) { // Avoid including ids or timestamps of revision/page tables so // that versions are not wasted $title = Title::makeTitle($row->page_namespace, $row->page_title); $this->titleInfo[$key][$title->getPrefixedText()] = array('rev_len' => $row->rev_len, 'rev_sha1' => $row->rev_sha1); } } } return $this->titleInfo[$key]; }
/** * Replace <!--LINK--> link placeholders with actual links, in the buffer * Placeholders created in Skin::makeLinkObj() * Returns an array of link CSS classes, indexed by PDBK. * $options is a bit field, RLH_FOR_UPDATE to select for update */ function replaceLinkHolders(&$text, $options = 0) { global $wgUser; global $wgContLang; $fname = 'Parser::replaceLinkHolders'; wfProfileIn($fname); $pdbks = array(); $colours = array(); $linkcolour_ids = array(); $sk = $this->mOptions->getSkin(); $linkCache =& LinkCache::singleton(); if (!empty($this->mLinkHolders['namespaces'])) { wfProfileIn($fname . '-check'); $dbr = wfGetDB(DB_SLAVE); $page = $dbr->tableName('page'); $threshold = $wgUser->getOption('stubthreshold'); # Sort by namespace asort($this->mLinkHolders['namespaces']); # Generate query $query = false; $current = null; foreach ($this->mLinkHolders['namespaces'] as $key => $ns) { # Make title object $title = $this->mLinkHolders['titles'][$key]; # Skip invalid entries. # Result will be ugly, but prevents crash. if (is_null($title)) { continue; } $pdbk = $pdbks[$key] = $title->getPrefixedDBkey(); # Check if it's a static known link, e.g. interwiki if ($title->isAlwaysKnown()) { $colours[$pdbk] = ''; } elseif (($id = $linkCache->getGoodLinkID($pdbk)) != 0) { $colours[$pdbk] = ''; $this->mOutput->addLink($title, $id); } elseif ($linkCache->isBadLink($pdbk)) { $colours[$pdbk] = 'new'; } elseif ($title->getNamespace() == NS_SPECIAL && !SpecialPage::exists($pdbk)) { $colours[$pdbk] = 'new'; } else { # Not in the link cache, add it to the query if (!isset($current)) { $current = $ns; $query = "SELECT page_id, page_namespace, page_title, page_is_redirect"; if ($threshold > 0) { $query .= ', page_len'; } $query .= " FROM {$page} WHERE (page_namespace={$ns} AND page_title IN("; } elseif ($current != $ns) { $current = $ns; $query .= ")) OR (page_namespace={$ns} AND page_title IN("; } else { $query .= ', '; } $query .= $dbr->addQuotes($this->mLinkHolders['dbkeys'][$key]); } } if ($query) { $query .= '))'; if ($options & RLH_FOR_UPDATE) { $query .= ' FOR UPDATE'; } $res = $dbr->query($query, $fname); # Fetch data and form into an associative array # non-existent = broken while ($s = $dbr->fetchObject($res)) { $title = Title::makeTitle($s->page_namespace, $s->page_title); $pdbk = $title->getPrefixedDBkey(); $linkCache->addGoodLinkObj($s->page_id, $title); $this->mOutput->addLink($title, $s->page_id); $colours[$pdbk] = $sk->getLinkColour($s, $threshold); //add id to the extension todolist $linkcolour_ids[$s->page_id] = $pdbk; } //pass an array of page_ids to an extension wfRunHooks('GetLinkColours', array($linkcolour_ids, &$colours)); } wfProfileOut($fname . '-check'); # Do a second query for different language variants of links and categories if ($wgContLang->hasVariants()) { $linkBatch = new LinkBatch(); $variantMap = array(); // maps $pdbkey_Variant => $keys (of link holders) $categoryMap = array(); // maps $category_variant => $category (dbkeys) $varCategories = array(); // category replacements oldDBkey => newDBkey $categories = $this->mOutput->getCategoryLinks(); // Add variants of links to link batch foreach ($this->mLinkHolders['namespaces'] as $key => $ns) { $title = $this->mLinkHolders['titles'][$key]; if (is_null($title)) { continue; } $pdbk = $title->getPrefixedDBkey(); $titleText = $title->getText(); // generate all variants of the link title text $allTextVariants = $wgContLang->convertLinkToAllVariants($titleText); // if link was not found (in first query), add all variants to query if (!isset($colours[$pdbk])) { foreach ($allTextVariants as $textVariant) { if ($textVariant != $titleText) { $variantTitle = Title::makeTitle($ns, $textVariant); if (is_null($variantTitle)) { continue; } $linkBatch->addObj($variantTitle); $variantMap[$variantTitle->getPrefixedDBkey()][] = $key; } } } } // process categories, check if a category exists in some variant foreach ($categories as $category) { $variants = $wgContLang->convertLinkToAllVariants($category); foreach ($variants as $variant) { if ($variant != $category) { $variantTitle = Title::newFromDBkey(Title::makeName(NS_CATEGORY, $variant)); if (is_null($variantTitle)) { continue; } $linkBatch->addObj($variantTitle); $categoryMap[$variant] = $category; } } } if (!$linkBatch->isEmpty()) { // construct query $titleClause = $linkBatch->constructSet('page', $dbr); $variantQuery = "SELECT page_id, page_namespace, page_title, page_is_redirect"; if ($threshold > 0) { $variantQuery .= ', page_len'; } $variantQuery .= " FROM {$page} WHERE {$titleClause}"; if ($options & RLH_FOR_UPDATE) { $variantQuery .= ' FOR UPDATE'; } $varRes = $dbr->query($variantQuery, $fname); // for each found variants, figure out link holders and replace while ($s = $dbr->fetchObject($varRes)) { $variantTitle = Title::makeTitle($s->page_namespace, $s->page_title); $varPdbk = $variantTitle->getPrefixedDBkey(); $vardbk = $variantTitle->getDBkey(); $holderKeys = array(); if (isset($variantMap[$varPdbk])) { $holderKeys = $variantMap[$varPdbk]; $linkCache->addGoodLinkObj($s->page_id, $variantTitle); $this->mOutput->addLink($variantTitle, $s->page_id); } // loop over link holders foreach ($holderKeys as $key) { $title = $this->mLinkHolders['titles'][$key]; if (is_null($title)) { continue; } $pdbk = $title->getPrefixedDBkey(); if (!isset($colours[$pdbk])) { // found link in some of the variants, replace the link holder data $this->mLinkHolders['titles'][$key] = $variantTitle; $this->mLinkHolders['dbkeys'][$key] = $variantTitle->getDBkey(); // set pdbk and colour $pdbks[$key] = $varPdbk; $colours[$varPdbk] = $sk->getLinkColour($s, $threshold); $linkcolour_ids[$s->page_id] = $pdbk; } wfRunHooks('GetLinkColours', array($linkcolour_ids, &$colours)); } // check if the object is a variant of a category if (isset($categoryMap[$vardbk])) { $oldkey = $categoryMap[$vardbk]; if ($oldkey != $vardbk) { $varCategories[$oldkey] = $vardbk; } } } // rebuild the categories in original order (if there are replacements) if (count($varCategories) > 0) { $newCats = array(); $originalCats = $this->mOutput->getCategories(); foreach ($originalCats as $cat => $sortkey) { // make the replacement if (array_key_exists($cat, $varCategories)) { $newCats[$varCategories[$cat]] = $sortkey; } else { $newCats[$cat] = $sortkey; } } $this->mOutput->setCategoryLinks($newCats); } } } # Construct search and replace arrays wfProfileIn($fname . '-construct'); $replacePairs = array(); foreach ($this->mLinkHolders['namespaces'] as $key => $ns) { $pdbk = $pdbks[$key]; $searchkey = "<!--LINK {$key}-->"; $title = $this->mLinkHolders['titles'][$key]; if (!isset($colours[$pdbk]) || $colours[$pdbk] == 'new') { $linkCache->addBadLinkObj($title); $colours[$pdbk] = 'new'; $this->mOutput->addLink($title, 0); $replacePairs[$searchkey] = $sk->makeBrokenLinkObj($title, $this->mLinkHolders['texts'][$key], $this->mLinkHolders['queries'][$key]); } else { $replacePairs[$searchkey] = $sk->makeColouredLinkObj($title, $colours[$pdbk], $this->mLinkHolders['texts'][$key], $this->mLinkHolders['queries'][$key]); } } $replacer = new HashtableReplacer($replacePairs, 1); wfProfileOut($fname . '-construct'); # Do the thing wfProfileIn($fname . '-replace'); $text = preg_replace_callback('/(<!--LINK .*?-->)/', $replacer->cb(), $text); wfProfileOut($fname . '-replace'); } # Now process interwiki link holders # This is quite a bit simpler than internal links if (!empty($this->mInterwikiLinkHolders['texts'])) { wfProfileIn($fname . '-interwiki'); # Make interwiki link HTML $replacePairs = array(); foreach ($this->mInterwikiLinkHolders['texts'] as $key => $link) { $title = $this->mInterwikiLinkHolders['titles'][$key]; $replacePairs[$key] = $sk->makeLinkObj($title, $link); } $replacer = new HashtableReplacer($replacePairs, 1); $text = preg_replace_callback('/<!--IWLINK (.*?)-->/', $replacer->cb(), $text); wfProfileOut($fname . '-interwiki'); } wfProfileOut($fname); return $colours; }
/** * Modify $this->internals and $colours according to language variant linking rules */ protected function doVariants(&$colours) { global $wgContLang; $linkBatch = new LinkBatch(); $variantMap = array(); // maps $pdbkey_Variant => $keys (of link holders) $output = $this->parent->getOutput(); $linkCache = LinkCache::singleton(); $sk = $this->parent->getOptions()->getSkin(); $threshold = $this->getStubThreshold(); // Add variants of links to link batch foreach ($this->internals as $ns => $entries) { foreach ($entries as $index => $entry) { $key = "{$ns}:{$index}"; $pdbk = $entry['pdbk']; $title = $entry['title']; $titleText = $title->getText(); // generate all variants of the link title text $allTextVariants = $wgContLang->convertLinkToAllVariants($titleText); // if link was not found (in first query), add all variants to query if (!isset($colours[$pdbk])) { foreach ($allTextVariants as $textVariant) { if ($textVariant != $titleText) { $variantTitle = Title::makeTitle($ns, $textVariant); if (is_null($variantTitle)) { continue; } $linkBatch->addObj($variantTitle); $variantMap[$variantTitle->getPrefixedDBkey()][] = $key; } } } } } // process categories, check if a category exists in some variant $categoryMap = array(); // maps $category_variant => $category (dbkeys) $varCategories = array(); // category replacements oldDBkey => newDBkey foreach ($output->getCategoryLinks() as $category) { $variants = $wgContLang->convertLinkToAllVariants($category); foreach ($variants as $variant) { if ($variant != $category) { $variantTitle = Title::newFromDBkey(Title::makeName(NS_CATEGORY, $variant)); if (is_null($variantTitle)) { continue; } $linkBatch->addObj($variantTitle); $categoryMap[$variant] = $category; } } } if (!$linkBatch->isEmpty()) { // construct query $dbr = wfGetDB(DB_SLAVE); $page = $dbr->tableName('page'); $titleClause = $linkBatch->constructSet('page', $dbr); $variantQuery = "SELECT page_id, page_namespace, page_title, page_is_redirect, page_len"; $variantQuery .= " FROM {$page} WHERE {$titleClause}"; $varRes = $dbr->query($variantQuery, __METHOD__); $linkcolour_ids = array(); // for each found variants, figure out link holders and replace while ($s = $dbr->fetchObject($varRes)) { $variantTitle = Title::makeTitle($s->page_namespace, $s->page_title); $varPdbk = $variantTitle->getPrefixedDBkey(); $vardbk = $variantTitle->getDBkey(); $holderKeys = array(); if (isset($variantMap[$varPdbk])) { $holderKeys = $variantMap[$varPdbk]; $linkCache->addGoodLinkObj($s->page_id, $variantTitle, $s->page_len, $s->page_is_redirect); $output->addLink($variantTitle, $s->page_id); } // loop over link holders foreach ($holderKeys as $key) { list($ns, $index) = explode(':', $key, 2); $entry =& $this->internals[$ns][$index]; $pdbk = $entry['pdbk']; if (!isset($colours[$pdbk])) { // found link in some of the variants, replace the link holder data $entry['title'] = $variantTitle; $entry['pdbk'] = $varPdbk; // set pdbk and colour # FIXME: convoluted data flow # The redirect status and length is passed to getLinkColour via the LinkCache # Use formal parameters instead $colours[$varPdbk] = $sk->getLinkColour($variantTitle, $threshold); $linkcolour_ids[$s->page_id] = $pdbk; } } // check if the object is a variant of a category if (isset($categoryMap[$vardbk])) { $oldkey = $categoryMap[$vardbk]; if ($oldkey != $vardbk) { $varCategories[$oldkey] = $vardbk; } } } wfRunHooks('GetLinkColours', array($linkcolour_ids, &$colours)); // rebuild the categories in original order (if there are replacements) if (count($varCategories) > 0) { $newCats = array(); $originalCats = $output->getCategories(); foreach ($originalCats as $cat => $sortkey) { // make the replacement if (array_key_exists($cat, $varCategories)) { $newCats[$varCategories[$cat]] = $sortkey; } else { $newCats[$cat] = $sortkey; } } $output->setCategoryLinks($newCats); } } }
protected function reallyGetTitleMtimes(ResourceLoaderContext $context) { wfProfileIn(__METHOD__); $dbr = $this->getDB(); if (!$dbr) { // We're dealing with a subclass that doesn't have a DB wfProfileOut(__METHOD__); return array(); } $mtimes = array(); $local = array(); $byWiki = array(); $pages = $this->getPages($context); foreach ($pages as $titleText => $options) { $title = $this->createTitle($titleText, $options); if ($title instanceof GlobalTitle) { $byWiki[$title->getCityId()][] = array($title, $titleText, $options); } else { $local[] = array($title, $titleText, $options); } } if (!empty($local)) { $batch = new LinkBatch(); foreach ($local as $page) { list($title, $titleText, $options) = $page; $batch->addObj($title); } if (!$batch->isEmpty()) { $res = $dbr->select('page', array('page_namespace', 'page_title', 'page_touched'), $batch->constructSet('page', $dbr), __METHOD__); foreach ($res as $row) { $title = Title::makeTitle($row->page_namespace, $row->page_title); $mtimes[$title->getPrefixedDBkey()] = wfTimestamp(TS_UNIX, $row->page_touched); } } } foreach ($byWiki as $cityId => $pages) { // $pages[0][0] has to be GlobalTitle $dbName = $pages[0][0]->getDatabaseName(); $dbr = wfGetDB(DB_SLAVE, array(), $dbName); $pagesData = array(); foreach ($pages as $page) { list($title, $titleText, $options) = $page; /** @var $title GlobalTitle */ $pagesData[$title->getNamespace()][$title->getDBkey()] = true; } $res = $dbr->select('page', array('page_namespace', 'page_title', 'page_touched'), $dbr->makeWhereFrom2d($pagesData, 'page_namespace', 'page_title'), __METHOD__); foreach ($res as $row) { $title = GlobalTitle::newFromTextCached($row->page_title, $row->page_namespace, $cityId); $mtimes[$dbName . '::' . $title->getPrefixedDBkey()] = wfTimestamp(TS_UNIX, $row->page_touched); } } wfProfileOut(__METHOD__); return $mtimes; }
/** * Perform the search * * @param string $search Text to search * @param int $limit Maximum items to return * @param array $namespaces Namespaces to search * @param bool $resolveRedir Whether to resolve redirects * @param array &$results Put results here. Keys have to be integers. */ protected function search($search, $limit, $namespaces, $resolveRedir, &$results) { $searchEngine = MediaWikiServices::getInstance()->newSearchEngine(); $searchEngine->setLimitOffset($limit); $searchEngine->setNamespaces($namespaces); $titles = $searchEngine->extractTitles($searchEngine->completionSearchWithVariants($search)); if (!$titles) { return; } // Special pages need unique integer ids in the return list, so we just // assign them negative numbers because those won't clash with the // always positive articleIds that non-special pages get. $nextSpecialPageId = -1; if ($resolveRedir) { // Query for redirects $redirects = []; $lb = new LinkBatch($titles); if (!$lb->isEmpty()) { $db = $this->getDB(); $res = $db->select(['page', 'redirect'], ['page_namespace', 'page_title', 'rd_namespace', 'rd_title'], ['rd_from = page_id', 'rd_interwiki IS NULL OR rd_interwiki = ' . $db->addQuotes(''), $lb->constructSet('page', $db)], __METHOD__); foreach ($res as $row) { $redirects[$row->page_namespace][$row->page_title] = [$row->rd_namespace, $row->rd_title]; } } // Bypass any redirects $seen = []; foreach ($titles as $title) { $ns = $title->getNamespace(); $dbkey = $title->getDBkey(); $from = null; if (isset($redirects[$ns][$dbkey])) { list($ns, $dbkey) = $redirects[$ns][$dbkey]; $from = $title; $title = Title::makeTitle($ns, $dbkey); } if (!isset($seen[$ns][$dbkey])) { $seen[$ns][$dbkey] = true; $resultId = $title->getArticleID(); if ($resultId === 0) { $resultId = $nextSpecialPageId; $nextSpecialPageId -= 1; } $results[$resultId] = ['title' => $title, 'redirect from' => $from, 'extract' => false, 'extract trimmed' => false, 'image' => false, 'url' => wfExpandUrl($title->getFullURL(), PROTO_CURRENT)]; } } } else { foreach ($titles as $title) { $resultId = $title->getArticleID(); if ($resultId === 0) { $resultId = $nextSpecialPageId; $nextSpecialPageId -= 1; } $results[$resultId] = ['title' => $title, 'redirect from' => null, 'extract' => false, 'extract trimmed' => false, 'image' => false, 'url' => wfExpandUrl($title->getFullURL(), PROTO_CURRENT)]; } } }
/** * Replace <!--LINK--> link placeholders with actual links, in the buffer * Placeholders created in Skin::makeLinkObj() * Returns an array of links found, indexed by PDBK: * 0 - broken * 1 - normal link * 2 - stub * $options is a bit field, RLH_FOR_UPDATE to select for update */ function replaceLinkHolders(&$text, $options = 0) { global $wgUser; global $wgOutputReplace; global $wgContLang, $wgLanguageCode; $fname = 'Parser::replaceLinkHolders'; wfProfileIn($fname); $pdbks = array(); $colours = array(); $sk =& $this->mOptions->getSkin(); $linkCache =& LinkCache::singleton(); if (!empty($this->mLinkHolders['namespaces'])) { wfProfileIn($fname . '-check'); $dbr =& wfGetDB(DB_SLAVE); $page = $dbr->tableName('page'); $threshold = $wgUser->getOption('stubthreshold'); # Sort by namespace asort($this->mLinkHolders['namespaces']); # Generate query $query = false; foreach ($this->mLinkHolders['namespaces'] as $key => $ns) { # Make title object $title = $this->mLinkHolders['titles'][$key]; # Skip invalid entries. # Result will be ugly, but prevents crash. if (is_null($title)) { continue; } $pdbk = $pdbks[$key] = $title->getPrefixedDBkey(); # Check if it's a static known link, e.g. interwiki if ($title->isAlwaysKnown()) { $colours[$pdbk] = 1; } elseif (($id = $linkCache->getGoodLinkID($pdbk)) != 0) { $colours[$pdbk] = 1; $this->mOutput->addLink($title, $id); } elseif ($linkCache->isBadLink($pdbk)) { $colours[$pdbk] = 0; } else { # Not in the link cache, add it to the query if (!isset($current)) { $current = $ns; $query = "SELECT page_id, page_namespace, page_title"; if ($threshold > 0) { $query .= ', page_len, page_is_redirect'; } $query .= " FROM {$page} WHERE (page_namespace={$ns} AND page_title IN("; } elseif ($current != $ns) { $current = $ns; $query .= ")) OR (page_namespace={$ns} AND page_title IN("; } else { $query .= ', '; } $query .= $dbr->addQuotes($this->mLinkHolders['dbkeys'][$key]); } } if ($query) { $query .= '))'; if ($options & RLH_FOR_UPDATE) { $query .= ' FOR UPDATE'; } $res = $dbr->query($query, $fname); # Fetch data and form into an associative array # non-existent = broken # 1 = known # 2 = stub while ($s = $dbr->fetchObject($res)) { $title = Title::makeTitle($s->page_namespace, $s->page_title); $pdbk = $title->getPrefixedDBkey(); $linkCache->addGoodLinkObj($s->page_id, $title); $this->mOutput->addLink($title, $s->page_id); if ($threshold > 0) { $size = $s->page_len; if ($s->page_is_redirect || $s->page_namespace != 0 || $size >= $threshold) { $colours[$pdbk] = 1; } else { $colours[$pdbk] = 2; } } else { $colours[$pdbk] = 1; } } } wfProfileOut($fname . '-check'); # Do a second query for different language variants of links (if needed) if ($wgContLang->hasVariants()) { $linkBatch = new LinkBatch(); $variantMap = array(); // maps $pdbkey_Variant => $pdbkey_original // Add variants of links to link batch foreach ($this->mLinkHolders['namespaces'] as $key => $ns) { $title = $this->mLinkHolders['titles'][$key]; if (is_null($title)) { continue; } $pdbk = $title->getPrefixedDBkey(); // generate all variants of the link title text $allTextVariants = $wgContLang->convertLinkToAllVariants($title->getText()); // if link was not found (in first query), add all variants to query if (!isset($colours[$pdbk])) { foreach ($allTextVariants as $textVariant) { $variantTitle = Title::makeTitle($ns, $textVariant); if (is_null($variantTitle)) { continue; } $linkBatch->addObj($variantTitle); $variantMap[$variantTitle->getPrefixedDBkey()][] = $key; } } } if (!$linkBatch->isEmpty()) { // construct query $titleClause = $linkBatch->constructSet('page', $dbr); $variantQuery = "SELECT page_id, page_namespace, page_title"; if ($threshold > 0) { $variantQuery .= ', page_len, page_is_redirect'; } $variantQuery .= " FROM {$page} WHERE {$titleClause}"; if ($options & RLH_FOR_UPDATE) { $variantQuery .= ' FOR UPDATE'; } $varRes = $dbr->query($variantQuery, $fname); // for each found variants, figure out link holders and replace while ($s = $dbr->fetchObject($varRes)) { $variantTitle = Title::makeTitle($s->page_namespace, $s->page_title); $varPdbk = $variantTitle->getPrefixedDBkey(); $linkCache->addGoodLinkObj($s->page_id, $variantTitle); $this->mOutput->addLink($variantTitle, $s->page_id); $holderKeys = $variantMap[$varPdbk]; // loop over link holders foreach ($holderKeys as $key) { $title = $this->mLinkHolders['titles'][$key]; if (is_null($title)) { continue; } $pdbk = $title->getPrefixedDBkey(); if (!isset($colours[$pdbk])) { // found link in some of the variants, replace the link holder data $this->mLinkHolders['titles'][$key] = $variantTitle; $this->mLinkHolders['dbkeys'][$key] = $variantTitle->getDBkey(); // set pdbk and colour $pdbks[$key] = $varPdbk; if ($threshold > 0) { $size = $s->page_len; if ($s->page_is_redirect || $s->page_namespace != 0 || $size >= $threshold) { $colours[$varPdbk] = 1; } else { $colours[$varPdbk] = 2; } } else { $colours[$varPdbk] = 1; } } } } } } # Construct search and replace arrays wfProfileIn($fname . '-construct'); $wgOutputReplace = array(); foreach ($this->mLinkHolders['namespaces'] as $key => $ns) { $pdbk = $pdbks[$key]; $searchkey = "<!--LINK {$key}-->"; $title = $this->mLinkHolders['titles'][$key]; if (empty($colours[$pdbk])) { $linkCache->addBadLinkObj($title); $colours[$pdbk] = 0; $this->mOutput->addLink($title, 0); $wgOutputReplace[$searchkey] = $sk->makeBrokenLinkObj($title, $this->mLinkHolders['texts'][$key], $this->mLinkHolders['queries'][$key]); } elseif ($colours[$pdbk] == 1) { $wgOutputReplace[$searchkey] = $sk->makeKnownLinkObj($title, $this->mLinkHolders['texts'][$key], $this->mLinkHolders['queries'][$key]); } elseif ($colours[$pdbk] == 2) { $wgOutputReplace[$searchkey] = $sk->makeStubLinkObj($title, $this->mLinkHolders['texts'][$key], $this->mLinkHolders['queries'][$key]); } } wfProfileOut($fname . '-construct'); # Do the thing wfProfileIn($fname . '-replace'); $text = preg_replace_callback('/(<!--LINK .*?-->)/', "wfOutputReplaceMatches", $text); wfProfileOut($fname . '-replace'); } # Now process interwiki link holders # This is quite a bit simpler than internal links if (!empty($this->mInterwikiLinkHolders['texts'])) { wfProfileIn($fname . '-interwiki'); # Make interwiki link HTML $wgOutputReplace = array(); foreach ($this->mInterwikiLinkHolders['texts'] as $key => $link) { $title = $this->mInterwikiLinkHolders['titles'][$key]; $wgOutputReplace[$key] = $sk->makeLinkObj($title, $link); } $text = preg_replace_callback('/<!--IWLINK (.*?)-->/', "wfOutputReplaceMatches", $text); wfProfileOut($fname . '-interwiki'); } wfProfileOut($fname); return $colours; }
protected static function fetchTitleInfo(IDatabase $db, array $pages, $fname = __METHOD__) { $titleInfo = []; $batch = new LinkBatch(); foreach ($pages as $titleText) { $title = Title::newFromText($titleText); if ($title) { // Page name may be invalid if user-provided (e.g. gadgets) $batch->addObj($title); } } if (!$batch->isEmpty()) { $res = $db->select('page', ['page_namespace', 'page_title', 'page_touched', 'page_len', 'page_latest'], $batch->constructSet('page', $db), $fname); foreach ($res as $row) { // Avoid including ids or timestamps of revision/page tables so // that versions are not wasted $title = Title::makeTitle($row->page_namespace, $row->page_title); $titleInfo[$title->getPrefixedText()] = ['page_len' => $row->page_len, 'page_latest' => $row->page_latest, 'page_touched' => $row->page_touched]; } } return $titleInfo; }