/** * Deal with external interwiki links: add exitstitial class to them if needed * * @param $skin * @param $target * @param $options * @param $text * @param $attribs * @param $ret * * @return bool */ public static function onLinkEnd($skin, Title $target, array $options, &$text, array &$attribs, &$ret) { if ($target->isExternal()) { static::handleExternalLink($attribs['href'], $attribs); } return true; }
/** * Does various sanity checks that the move is * valid. Only things based on the two titles * should be checked here. * * @return Status */ public function isValidMove() { global $wgContentHandlerUseDB; $status = new Status(); if ($this->oldTitle->equals($this->newTitle)) { $status->fatal('selfmove'); } if (!$this->oldTitle->isMovable()) { $status->fatal('immobile-source-namespace', $this->oldTitle->getNsText()); } if ($this->newTitle->isExternal()) { $status->fatal('immobile-target-namespace-iw'); } if (!$this->newTitle->isMovable()) { $status->fatal('immobile-target-namespace', $this->newTitle->getNsText()); } $oldid = $this->oldTitle->getArticleID(); if (strlen($this->newTitle->getDBkey()) < 1) { $status->fatal('articleexists'); } if ($this->oldTitle->getDBkey() == '' || !$oldid || $this->newTitle->getDBkey() == '') { $status->fatal('badarticleerror'); } # The move is allowed only if (1) the target doesn't exist, or # (2) the target is a redirect to the source, and has no history # (so we can undo bad moves right after they're done). if ($this->newTitle->getArticleID() && !$this->isValidMoveTarget()) { $status->fatal('articleexists'); } // Content model checks if (!$wgContentHandlerUseDB && $this->oldTitle->getContentModel() !== $this->newTitle->getContentModel()) { // can't move a page if that would change the page's content model $status->fatal('bad-target-model', ContentHandler::getLocalizedName($this->oldTitle->getContentModel()), ContentHandler::getLocalizedName($this->newTitle->getContentModel())); } elseif (!ContentHandler::getForTitle($this->oldTitle)->canBeUsedOn($this->newTitle)) { $status->fatal('content-not-allowed-here', ContentHandler::getLocalizedName($this->oldTitle->getContentModel()), $this->newTitle->getPrefixedText()); } // Image-specific checks if ($this->oldTitle->inNamespace(NS_FILE)) { $status->merge($this->isValidFileMove()); } if ($this->newTitle->inNamespace(NS_FILE) && !$this->oldTitle->inNamespace(NS_FILE)) { $status->fatal('nonfile-cannot-move-to-file'); } // Hook for extensions to say a title can't be moved for technical reasons Hooks::run('MovePageIsValidMove', [$this->oldTitle, $this->newTitle, $status]); return $status; }
/** * @return Title */ private function getRbTitle() { if ($this->mTitleObj !== null) { return $this->mTitleObj; } $params = $this->extractRequestParams(); $this->mTitleObj = Title::newFromText($params['title']); if (!$this->mTitleObj || $this->mTitleObj->isExternal()) { $this->dieUsageMsg(array('invalidtitle', $params['title'])); } if (!$this->mTitleObj->exists()) { $this->dieUsageMsg('notanarticle'); } return $this->mTitleObj; }
/** * @param array $params * * @return Title */ private function getRbTitle(array $params) { if ($this->mTitleObj !== null) { return $this->mTitleObj; } $this->requireOnlyOneParameter($params, 'title', 'pageid'); if (isset($params['title'])) { $this->mTitleObj = Title::newFromText($params['title']); if (!$this->mTitleObj || $this->mTitleObj->isExternal()) { $this->dieUsageMsg(array('invalidtitle', $params['title'])); } } elseif (isset($params['pageid'])) { $this->mTitleObj = Title::newFromID($params['pageid']); if (!$this->mTitleObj) { $this->dieUsageMsg(array('nosuchpageid', $params['pageid'])); } } if (!$this->mTitleObj->exists()) { $this->dieUsageMsg('notanarticle'); } return $this->mTitleObj; }
/** * Does various sanity checks that the move is * valid. Only things based on the two titles * should be checked here. * * @return Status */ public function isValidMove() { global $wgContentHandlerUseDB; $status = new Status(); if ($this->oldTitle->equals($this->newTitle)) { $status->fatal('selfmove'); } if (!$this->oldTitle->isMovable()) { $status->fatal('immobile-source-namespace', $this->oldTitle->getNsText()); } if ($this->newTitle->isExternal()) { $status->fatal('immobile-target-namespace-iw'); } if (!$this->newTitle->isMovable()) { $status->fatal('immobile-target-namespace', $this->newTitle->getNsText()); } $oldid = $this->oldTitle->getArticleID(); if (strlen($this->newTitle->getDBkey()) < 1) { $status->fatal('articleexists'); } if ($this->oldTitle->getDBkey() == '' || !$oldid || $this->newTitle->getDBkey() == '') { $status->fatal('badarticleerror'); } // Content model checks if (!$wgContentHandlerUseDB && $this->oldTitle->getContentModel() !== $this->newTitle->getContentModel()) { // can't move a page if that would change the page's content model $status->fatal('bad-target-model', ContentHandler::getLocalizedName($this->oldTitle->getContentModel()), ContentHandler::getLocalizedName($this->newTitle->getContentModel())); } // Image-specific checks if ($this->oldTitle->inNamespace(NS_FILE)) { $status->merge($this->isValidFileMove()); } if ($this->newTitle->inNamespace(NS_FILE) && !$this->oldTitle->inNamespace(NS_FILE)) { $status->fatal('nonfile-cannot-move-to-file'); } // Hook for extensions to say a title can't be moved for technical reasons Hooks::run('MovePageIsValidMove', array($this->oldTitle, $this->newTitle, $status)); return $status; }
/** * Add a title to the link cache, return the page_id or zero if non-existent * * @param Title $nt Title object to add * @return int Page ID or zero */ public function addLinkObj(Title $nt) { global $wgContentHandlerUseDB; $key = $nt->getPrefixedDBkey(); if ($this->isBadLink($key) || $nt->isExternal()) { return 0; } $id = $this->getGoodLinkID($key); if ($id != 0) { return $id; } if ($key === '') { return 0; } // Some fields heavily used for linking... $db = $this->mForUpdate ? wfGetDB(DB_MASTER) : wfGetDB(DB_SLAVE); $fields = array('page_id', 'page_len', 'page_is_redirect', 'page_latest'); if ($wgContentHandlerUseDB) { $fields[] = 'page_content_model'; } $row = $db->selectRow('page', $fields, array('page_namespace' => $nt->getNamespace(), 'page_title' => $nt->getDBkey()), __METHOD__); if ($row !== false) { $this->addGoodLinkObjFromRow($nt, $row); $id = intval($row->page_id); } else { $this->addBadLinkObj($nt); $id = 0; } return $id; }
/** * Return prefixed text form of title, but using the content language's * canonical namespace. This skips any special-casing such as gendered * user namespaces -- which while useful, are not yet listed in the * XML "<siteinfo>" data so are unsafe in export. * * @param Title $title * @return string * @since 1.18 */ public static function canonicalTitle(Title $title) { if ($title->isExternal()) { return $title->getPrefixedText(); } global $wgContLang; $prefix = str_replace('_', ' ', $wgContLang->getNsText($title->getNamespace())); if ($prefix !== '') { $prefix .= ':'; } return $prefix . $title->getText(); }
/** * Validates watchlist entry * * @param Title $title * @param int $namespace * @param String $dbKey * @return bool: Whether this item is valid */ private function checkTitle($title, $namespace, $dbKey) { if ($title && ($title->isExternal() || $title->getNamespace() < 0)) { $title = false; // unrecoverable } if (!$title || $title->getNamespace() != $namespace || $title->getDBkey() != $dbKey) { $this->badItems[] = array($title, $namespace, $dbKey); } return (bool) $title; }
/** * Get the Title object or URL to use for a redirect. We use Title * objects for same-wiki, non-special redirects and URLs for everything * else. * @param Title $rt Redirect target * @return bool|Title|string False, Title object of local target, or string with URL */ public function getRedirectURL($rt) { if (!$rt) { return false; } if ($rt->isExternal()) { if ($rt->isLocal()) { // Offsite wikis need an HTTP redirect. // This can be hard to reverse and may produce loops, // so they may be disabled in the site configuration. $source = $this->mTitle->getFullURL('redirect=no'); return $rt->getFullURL(['rdfrom' => $source]); } else { // External pages without "local" bit set are not valid // redirect targets return false; } } if ($rt->isSpecialPage()) { // Gotta handle redirects to special pages differently: // Fill the HTTP response "Location" header and ignore the rest of the page we're on. // Some pages are not valid targets. if ($rt->isValidRedirectTarget()) { return $rt->getFullURL(); } else { return false; } } return $rt; }
/** * Record a local or interwiki inline link for saving in future link tables. * * @param $title Title object * @param $id Mixed: optional known page_id so we can skip the lookup */ function addLink(Title $title, $id = null) { if ($title->isExternal()) { // Don't record interwikis in pagelinks $this->addInterwikiLink($title); return; } $ns = $title->getNamespace(); $dbk = $title->getDBkey(); if ($ns == NS_MEDIA) { // Normalize this pseudo-alias if it makes it down here... $ns = NS_FILE; } elseif ($ns == NS_SPECIAL) { // We don't record Special: links currently // It might actually be wise to, but we'd need to do some normalization. return; } elseif ($dbk === '') { // Don't record self links - [[#Foo]] return; } if (!isset($this->mLinks[$ns])) { $this->mLinks[$ns] = array(); } if (is_null($id)) { $id = $title->getArticleID(); } $this->mLinks[$ns][$dbk] = $id; }
/** * Generates a link to the given Title * * @note This is only public for technical reasons. It's not intended for use outside Linker. * * @param Title $title * @param string $text * @param string|null $wikiId Id of the wiki to link to (if not the local wiki), * as used by WikiMap. * @param string|string[] $options See the $options parameter in Linker::link. * * @return string HTML link */ public static function makeCommentLink(Title $title, $text, $wikiId = null, $options = array()) { if ($wikiId !== null && !$title->isExternal()) { $link = Linker::makeExternalLink(WikiMap::getForeignURL($wikiId, $title->getPrefixedText(), $title->getFragment()), $text, false); } else { $link = Linker::link($title, $text, array(), array(), $options); } return $link; }
/** * Get the final destination of a redirect * * @param Title $title * * @return bool If the specified title is not a redirect, or if it is a circular redirect */ public static function getFinalDestination($title) { $dbw = wfGetDB(DB_MASTER); // Circular redirect check $seenTitles = array(); $dest = false; while (true) { $titleText = $title->getPrefixedDBkey(); if (isset($seenTitles[$titleText])) { wfDebug(__METHOD__, "Circular redirect detected, aborting\n"); return false; } $seenTitles[$titleText] = true; if ($title->isExternal()) { // If the target is interwiki, we have to break early (bug 40352). // Otherwise it will look up a row in the local page table // with the namespace/page of the interwiki target which can cause // unexpected results (e.g. X -> foo:Bar -> Bar -> .. ) break; } $row = $dbw->selectRow(array('redirect', 'page'), array('rd_namespace', 'rd_title', 'rd_interwiki'), array('rd_from=page_id', 'page_namespace' => $title->getNamespace(), 'page_title' => $title->getDBkey()), __METHOD__); if (!$row) { # No redirect from here, chain terminates break; } else { $dest = $title = Title::makeTitle($row->rd_namespace, $row->rd_title, '', $row->rd_interwiki); } } return $dest; }
/** * @param Title $title Title object, must be an interwiki link * @throws MWException If given invalid input */ public function addInterwikiLink($title) { if (!$title->isExternal()) { throw new MWException('Non-interwiki link passed, internal parser error.'); } $prefix = $title->getInterwiki(); if (!isset($this->mInterwikiLinks[$prefix])) { $this->mInterwikiLinks[$prefix] = array(); } $this->mInterwikiLinks[$prefix][$title->getDBkey()] = 1; }
static function linkBegin($skin, Title $target, &$text, array &$attribs, &$query, &$options, &$ret) { if ($target->isExternal()) { $args = ''; $u = $target->getFullURL(); $link = $target->getPrefixedURL(); if ('' == $text) { $text = $target->getPrefixedText(); } $style = Linker::getInterwikiLinkAttributes($link, $text, 'extiw'); if ($text == 'RTENOTITLE') { // 2223 $text = $u = $link; $args .= '_fcknotitle="true" '; } $t = "<a {$args}href=\"{$u}\"{$style}>{$text}</a>"; wfProfileOut(__METHOD__); $ret = $t; return false; } return true; }
/** * Returns the array of attributes used when linking to the Title $target * * @param Title $target * @param array $attribs * @param array $options * * @return array */ private static function linkAttribs($target, $attribs, $options) { global $wgUser; $defaults = array(); if (!in_array('noclasses', $options)) { # Now build the classes. $classes = array(); if (in_array('broken', $options)) { $classes[] = 'new'; } if ($target->isExternal()) { $classes[] = 'extiw'; } if (!in_array('broken', $options)) { # Avoid useless calls to LinkCache (see r50387) $colour = self::getLinkColour($target, $wgUser->getStubThreshold()); if ($colour !== '') { $classes[] = $colour; # mw-redirect or stub } } if ($classes != array()) { $defaults['class'] = implode(' ', $classes); } } # Get a default title attribute. if ($target->getPrefixedText() == '') { # A link like [[#Foo]]. This used to mean an empty title # attribute, but that's silly. Just don't output a title. } elseif (in_array('known', $options)) { $defaults['title'] = $target->getPrefixedText(); } else { $defaults['title'] = wfMessage('red-link-title', $target->getPrefixedText())->text(); } # Finally, merge the custom attribs with the default ones, and iterate # over that, deleting all "false" attributes. $ret = array(); $merged = Sanitizer::mergeAttributes($defaults, $attribs); foreach ($merged as $key => $val) { # A false value suppresses the attribute, and we don't want the # href attribute to be overridden. if ($key != 'href' && $val !== false) { $ret[$key] = $val; } } return $ret; }
/** * Add a title to the link cache, return the page_id or zero if non-existent * * @param Title $nt Title object to add * @return int */ public function addLinkObj($nt) { global $wgAntiLockFlags, $wgContentHandlerUseDB; wfProfileIn(__METHOD__); $key = $nt->getPrefixedDBkey(); if ($this->isBadLink($key) || $nt->isExternal()) { wfProfileOut(__METHOD__); return 0; } $id = $this->getGoodLinkID($key); if ($id != 0) { wfProfileOut(__METHOD__); return $id; } if ($key === '') { wfProfileOut(__METHOD__); return 0; } # Some fields heavily used for linking... if ($this->mForUpdate) { $db = wfGetDB(DB_MASTER); if (!($wgAntiLockFlags & ALF_NO_LINK_LOCK)) { $options = array('FOR UPDATE'); } else { $options = array(); } } else { $db = wfGetDB(DB_SLAVE); $options = array(); } $f = array('page_id', 'page_len', 'page_is_redirect', 'page_latest'); if ($wgContentHandlerUseDB) { $f[] = 'page_content_model'; } $s = $db->selectRow('page', $f, array('page_namespace' => $nt->getNamespace(), 'page_title' => $nt->getDBkey()), __METHOD__, $options); # Set fields... if ($s !== false) { $this->addGoodLinkObjFromRow($nt, $s); $id = intval($s->page_id); } else { $this->addBadLinkObj($nt); $id = 0; } wfProfileOut(__METHOD__); return $id; }