public function setUp() { global $wgOut, $wgUser, $wgMessageCache; $wgOut = $this; $wgUser = new User(2); $wgMessageCache = new MessageCache(); $wgMessageCache->addMessages(array('rdf' => 'Rdf', 'rdf-inpage' => "Embedded In-page Turtle", 'rdf-dcmes' => "Dublin Core Metadata Element Set", 'rdf-cc' => "Creative Commons", 'rdf-image' => "Embedded images", 'rdf-linksfrom' => "Links from the page", 'rdf-links' => "All links", 'rdf-history' => "Historical versions", 'rdf-interwiki' => "Interwiki links", 'rdf-categories' => "Categories", 'rdf-target' => "Target page", 'rdf-modelnames' => "Model(s)", 'rdf-format' => "Output format", 'rdf-output-xml' => "XML", 'rdf-output-turtle' => "Turtle", 'rdf-output-ntriples' => "NTriples", 'rdf-instructions' => "Select the target page and RDF models you're interested in.")); MwRdf::$ModelMakers = array('MwRdf_CreativeCommons_Modeler', 'MwRdf_LinksFrom_Modeler', 'MwRdf_LinksTo_Modeler', 'MwRdf_InPage_Modeler', 'MwRdf_DCmes_Modeler', 'MwRdf_History_Modeler', 'MwRdf_Image_Modeler', 'MwRdf_Categories_Modeler', 'MwRdf_Interwiki_Modeler'); MwRdf::ShowForm(); }
/** Build $this->messages array */ private function initMessagesHref() { # List of default messages for the sidebar: $URL_messages = array('mainpage', 'portal-url', 'currentevents-url', 'recentchanges-url', 'randompage-url', 'helppage'); foreach ($URL_messages as $m) { $titleName = MessageCache::singleton()->get($m); $title = Title::newFromText($titleName); $this->messages[$m]['href'] = $title->getLocalURL(); } }
protected function setUp() { global $wgLanguageCode, $wgContLang; if ($wgLanguageCode != $wgContLang->getCode()) { throw new MWException("Error in MediaWikiLangTestCase::setUp(): " . "\$wgLanguageCode ('{$wgLanguageCode}') is different from " . "\$wgContLang->getCode() (" . $wgContLang->getCode() . ")"); } parent::setUp(); $this->setUserLang('en'); // For mainpage to be 'Main Page' $this->setContentLang('en'); MessageCache::singleton()->disable(); }
protected function setUp() { global $wgLanguageCode, $wgContLang; parent::setUp(); if ($wgLanguageCode != $wgContLang->getCode()) { throw new MWException("Error in MediaWikiLangTestCase::setUp(): " . "\$wgLanguageCode ('{$wgLanguageCode}') is different from " . "\$wgContLang->getCode() (" . $wgContLang->getCode() . ")"); } $langCode = 'en'; # For mainpage to be 'Main Page' $langObj = Language::factory($langCode); $this->setMwGlobals(array('wgLanguageCode' => $langCode, 'wgLang' => $langObj, 'wgContLang' => $langObj)); MessageCache::singleton()->disable(); }
public function setUp() { global $wgLanguageCode, $wgLang, $wgContLang; self::$oldLang = $wgLang; self::$oldContLang = $wgContLang; if ($wgLanguageCode != $wgContLang->getCode()) { throw new MWException("Error in MediaWikiLangTestCase::setUp(): " . "\$wgLanguageCode ('{$wgLanguageCode}') is different from " . "\$wgContLang->getCode() (" . $wgContLang->getCode() . ")"); } $wgLanguageCode = 'en'; # For mainpage to be 'Main Page' $wgContLang = $wgLang = Language::factory($wgLanguageCode); MessageCache::singleton()->disable(); }
public function setUp() { global $wgLanguageCode, $wgLang, $wgContLang; self::$oldLang = $wgLang; self::$oldContLang = $wgContLang; if ($wgLanguageCode != $wgContLang->getCode()) { die("nooo!"); } $wgLanguageCode = 'en'; # For mainpage to be 'Main Page' $wgContLang = $wgLang = Language::factory($wgLanguageCode); MessageCache::singleton()->disable(); }
/** * From WikimediaMessages * * @return bool */ public static function onMessageCacheGet(&$lcKey) { global $wgLanguageCode; static $keys = array('centralauth-groupname'); if (in_array($lcKey, $keys, true)) { $prefixedKey = "miraheze-{$lcKey}"; // MessageCache uses ucfirst if ord( key ) is < 128, which is true of all // of the above. Revisit if non-ASCII keys are used. $ucKey = ucfirst($lcKey); $cache = MessageCache::singleton(); if ($cache->getMsgFromNamespace($ucKey, $wgLanguageCode) === false) { $lcKey = $prefixedKey; } } return true; }
protected function setUp() { global $wgLanguageCode, $wgContLang; parent::setUp(); if ($wgLanguageCode != $wgContLang->getCode()) { throw new MWException("Error in MediaWikiLangTestCase::setUp(): " . "\$wgLanguageCode ('{$wgLanguageCode}') is different from " . "\$wgContLang->getCode() (" . $wgContLang->getCode() . ")"); } // HACK: Call getLanguage() so the real $wgContLang is cached as the user language // rather than our fake one. This is to avoid breaking other, unrelated tests. RequestContext::getMain()->getLanguage(); $langCode = 'en'; # For mainpage to be 'Main Page' $langObj = Language::factory($langCode); $this->setMwGlobals(array('wgLanguageCode' => $langCode, 'wgLang' => $langObj, 'wgContLang' => $langObj)); MessageCache::singleton()->disable(); }
/** * When core requests certain messages, change the key to a Kol-Zchut version. * * @note Don't make this a closure, it causes the Database Dumps to fail. * See https://bugs.php.net/bug.php?id=52144 * * @param String &$lcKey message key to check and possibly convert * * @return bool */ public static function onMessageCacheGet(&$lcKey) { global $wgLanguageCode; static $keys = array('aboutpage', 'aboutsite', 'copyright', 'copyrightwarning', 'deletereason-dropdown', 'edithelppage', 'hidetoc', 'showtoc', 'lastmodifiedat', 'lastmodifiedatby', 'login', 'logouttext', 'nav-login-createaccount', 'userlogin', 'userloginnocreate', 'logout', 'userlogout', 'notloggedin', 'nologin', 'gotaccountlink', 'createaccounterror', 'signupstart', 'noarticletext', 'noarticletext-nopermission', 'protect-dropdown', 'siteuser', 'siteusers', 'tagline', 'tooltip-p-logo', 'tooltip-n-mainpage', 'tooltip-n-mainpage-description', "accesskey-p-logo", "accesskey-n-mainpage", 'enotif_body_intro_deleted', 'enotif_body_intro_created', 'enotif_body_intro_moved', 'enotif_body_intro_restored', 'enotif_body_intro_changed', 'enotif_lastvisited', 'enotif_lastdiff', 'enotif_body', 'search-nonefound', 'upload', 'userpage', 'helena-disclaimers', 'wr-langlinks-label'); if (in_array($lcKey, $keys, true)) { $prefixedKey = "kz-{$lcKey}"; // MessageCache uses ucfirst if ord( key ) is < 128, which is true of all // of the above. Revisit if non-ASCII keys are used. $ucKey = ucfirst($lcKey); $cache = MessageCache::singleton(); if ($cache->getMsgFromNamespace($ucKey, $wgLanguageCode) === false) { $lcKey = $prefixedKey; } } return true; }
/** * @param $title Title * @return null|string */ protected function getContent($title) { if ($title->getNamespace() === NS_MEDIAWIKI) { // The first "true" is to use the database, the second is to use the content langue // and the last one is to specify the message key already contains the language in it ("/de", etc.) $text = MessageCache::singleton()->get($title->getDBkey(), true, true, true); return $text === false ? '' : $text; } if (!$title->isCssJsSubpage() && !$title->isCssOrJsPage()) { return null; } $revision = Revision::newFromTitle($title, false, Revision::READ_NORMAL); if (!$revision) { return null; } return $revision->getRawText(); }
/** Build $this->messages array */ private function initMessagesHref() { # List of default messages for the sidebar. The sidebar doesn't care at # all whether they are full URLs, interwiki links or local titles. $URL_messages = array('mainpage', 'portal-url', 'currentevents-url', 'recentchanges-url', 'randompage-url', 'helppage'); # We're assuming that isValidURI works as advertised: it's also # tested separately, in tests/phpunit/includes/HttpTest.php. foreach ($URL_messages as $m) { $titleName = MessageCache::singleton()->get($m); if (Http::isValidURI($titleName)) { $this->messages[$m]['href'] = $titleName; } else { $title = Title::newFromText($titleName); $this->messages[$m]['href'] = $title->getLocalURL(); } } }
/** * */ function wfSpecialAllmessages() { global $wgOut, $wgRequest, $wgMessageCache, $wgTitle; global $wgUseDatabaseMessages; # The page isn't much use if the MediaWiki namespace is not being used if (!$wgUseDatabaseMessages) { $wgOut->addWikiText(wfMsg('allmessagesnotsupportedDB')); return; } $fname = "wfSpecialAllMessages"; wfProfileIn($fname); wfProfileIn("{$fname}-setup"); $ot = $wgRequest->getText('ot'); $navText = wfMsg('allmessagestext'); # Make sure all extension messages are available MessageCache::loadAllMessages(); $first = true; $sortedArray = array_merge(Language::getMessagesFor('en'), $wgMessageCache->getExtensionMessagesFor('en')); ksort($sortedArray); $messages = array(); $wgMessageCache->disableTransform(); foreach ($sortedArray as $key => $value) { $messages[$key]['enmsg'] = $value; $messages[$key]['statmsg'] = wfMsgNoDb($key); $messages[$key]['msg'] = wfMsg($key); } $wgMessageCache->enableTransform(); wfProfileOut("{$fname}-setup"); wfProfileIn("{$fname}-output"); if ($ot == 'php') { $navText .= makePhp($messages); $wgOut->addHTML('PHP | <a href="' . $wgTitle->escapeLocalUrl('ot=html') . '">HTML</a><pre>' . htmlspecialchars($navText) . '</pre>'); } else { $wgOut->addHTML('<a href="' . $wgTitle->escapeLocalUrl('ot=php') . '">PHP</a> | HTML'); $wgOut->addWikiText($navText); $wgOut->addHTML(makeHTMLText($messages)); } wfProfileOut("{$fname}-output"); wfProfileOut($fname); }
/** * Set up the global variables for a consistent environment for each test. * Ideally this should replace the global configuration entirely. * @param array $opts * @param string $config * @return RequestContext */ protected function setupGlobals($opts = array(), $config = '') { global $wgFileBackends; # Find out values for some special options. $lang = self::getOptionValue('language', $opts, 'en'); $variant = self::getOptionValue('variant', $opts, false); $maxtoclevel = self::getOptionValue('wgMaxTocLevel', $opts, 999); $linkHolderBatchSize = self::getOptionValue('wgLinkHolderBatchSize', $opts, 1000); $uploadDir = $this->getUploadDir(); if ($this->getCliArg('use-filebackend')) { if (self::$backendToUse) { $backend = self::$backendToUse; } else { $name = $this->getCliArg('use-filebackend'); $useConfig = array(); foreach ($wgFileBackends as $conf) { if ($conf['name'] == $name) { $useConfig = $conf; } } $useConfig['name'] = 'local-backend'; // swap name unset($useConfig['lockManager']); unset($useConfig['fileJournal']); $class = $useConfig['class']; self::$backendToUse = new $class($useConfig); $backend = self::$backendToUse; } } else { # Replace with a mock. We do not care about generating real # files on the filesystem, just need to expose the file # informations. $backend = new MockFileBackend(array('name' => 'local-backend', 'wikiId' => wfWikiId())); } $settings = array('wgLocalFileRepo' => array('class' => 'LocalRepo', 'name' => 'local', 'url' => 'http://example.com/images', 'hashLevels' => 2, 'transformVia404' => false, 'backend' => $backend), 'wgEnableUploads' => self::getOptionValue('wgEnableUploads', $opts, true), 'wgLanguageCode' => $lang, 'wgDBprefix' => $this->db->getType() != 'oracle' ? 'unittest_' : 'ut_', 'wgRawHtml' => self::getOptionValue('wgRawHtml', $opts, false), 'wgNamespacesWithSubpages' => array(NS_MAIN => isset($opts['subpage'])), 'wgAllowExternalImages' => self::getOptionValue('wgAllowExternalImages', $opts, true), 'wgThumbLimits' => array(self::getOptionValue('thumbsize', $opts, 180)), 'wgMaxTocLevel' => $maxtoclevel, 'wgUseTeX' => isset($opts['math']) || isset($opts['texvc']), 'wgMathDirectory' => $uploadDir . '/math', 'wgDefaultLanguageVariant' => $variant, 'wgLinkHolderBatchSize' => $linkHolderBatchSize, 'wgUseTidy' => isset($opts['tidy'])); if ($config) { $configLines = explode("\n", $config); foreach ($configLines as $line) { list($var, $value) = explode('=', $line, 2); $settings[$var] = eval("return {$value};"); // ??? } } $this->savedGlobals = array(); /** @since 1.20 */ Hooks::run('ParserTestGlobals', array(&$settings)); $langObj = Language::factory($lang); $settings['wgContLang'] = $langObj; $settings['wgLang'] = $langObj; $context = new RequestContext(); $settings['wgOut'] = $context->getOutput(); $settings['wgUser'] = $context->getUser(); $settings['wgRequest'] = $context->getRequest(); // We (re)set $wgThumbLimits to a single-element array above. $context->getUser()->setOption('thumbsize', 0); foreach ($settings as $var => $val) { if (array_key_exists($var, $GLOBALS)) { $this->savedGlobals[$var] = $GLOBALS[$var]; } $GLOBALS[$var] = $val; } MWTidy::destroySingleton(); MagicWord::clearCache(); # The entries saved into RepoGroup cache with previous globals will be wrong. RepoGroup::destroySingleton(); FileBackendGroup::destroySingleton(); # Create dummy files in storage $this->setupUploads(); # Publish the articles after we have the final language set $this->publishTestArticles(); MessageCache::destroyInstance(); return $context; }
/** * Clears caches when article is deleted * * @param Title $title */ public static function onArticleDelete(Title $title) { global $wgContLang; // Update existence markers on article/talk tabs... $other = $title->getOtherPage(); $other->purgeSquid(); $title->touchLinks(); $title->purgeSquid(); MediaWikiServices::getInstance()->getLinkCache()->invalidateTitle($title); // File cache HTMLFileCache::clearFileCache($title); InfoAction::invalidateCache($title); // Messages if ($title->getNamespace() == NS_MEDIAWIKI) { MessageCache::singleton()->replace($title->getDBkey(), false); if ($wgContLang->hasVariants()) { $wgContLang->updateConversionTable($title); } } // Images if ($title->getNamespace() == NS_FILE) { DeferredUpdates::addUpdate(new HTMLCacheUpdate($title, 'imagelinks')); } // User talk pages if ($title->getNamespace() == NS_USER_TALK) { $user = User::newFromName($title->getText(), false); if ($user) { $user->setNewtalk(false); } } // Image redirects RepoGroup::singleton()->getLocalRepo()->invalidateImageRedirect($title); }
/** * Since wfMsg() and co suck, they don't return false if the message key they * looked up didn't exist but instead the key wrapped in <>'s, this function checks for the * nonexistence of messages by checking the MessageCache::get() result directly. * * @deprecated since 1.18. Use Message::isDisabled(). * * @param string $key The message key looked up * @return bool True if the message *doesn't* exist. */ function wfEmptyMsg($key) { wfDeprecated(__METHOD__, '1.21'); return MessageCache::singleton()->get($key, true, false) === false; }
<?php /** * Bootstrapping for MediaWiki PHPUnit tests * This file is included by phpunit and is NOT in the global scope. * * @file */ if (!defined('MW_PHPUNIT_TEST')) { echo <<<EOF You are running these tests directly from phpunit. You may not have all globals correctly set. Running phpunit.php instead is recommended. EOF; require_once __DIR__ . "/phpunit.php"; } // Output a notice when running with older versions of PHPUnit if (version_compare(PHPUnit_Runner_Version::id(), "3.6.7", "<")) { echo <<<EOF ******************************************************************************** These tests run best with version PHPUnit 3.6.7 or better. Earlier versions may show failures because earlier versions of PHPUnit do not properly implement dependencies. ******************************************************************************** EOF; } /** @todo Check if this is really needed */ MessageCache::destroyInstance();
/** * @dataProvider provideNormalizeKey */ public function testNormalizeKey($key, $expected) { $actual = MessageCache::normalizeKey($key); $this->assertEquals($expected, $actual); }
/** * Generate the generic "this page has been changed" e-mail text. */ private function composeCommonMailtext() { global $wgPasswordSender, $wgNoReplyAddress; global $wgEnotifFromEditor, $wgEnotifRevealEditorAddress; global $wgEnotifImpersonal, $wgEnotifUseRealName; $this->composed_common = true; # You as the WikiAdmin and Sysops can make use of plenty of # named variables when composing your notification emails while # simply editing the Meta pages $keys = array(); $postTransformKeys = array(); $pageTitleUrl = $this->title->getCanonicalURL(); $pageTitle = $this->title->getPrefixedText(); if ($this->oldid) { // Always show a link to the diff which triggered the mail. See bug 32210. $keys['$NEWPAGE'] = "\n\n" . wfMessage('enotif_lastdiff', $this->title->getCanonicalURL(array('diff' => 'next', 'oldid' => $this->oldid)))->inContentLanguage()->text(); if (!$wgEnotifImpersonal) { // For personal mail, also show a link to the diff of all changes // since last visited. $keys['$NEWPAGE'] .= "\n\n" . wfMessage('enotif_lastvisited', $this->title->getCanonicalURL(array('diff' => '0', 'oldid' => $this->oldid)))->inContentLanguage()->text(); } $keys['$OLDID'] = $this->oldid; // Deprecated since MediaWiki 1.21, not used by default. Kept for backwards-compatibility. $keys['$CHANGEDORCREATED'] = wfMessage('changed')->inContentLanguage()->text(); } else { # clear $OLDID placeholder in the message template $keys['$OLDID'] = ''; $keys['$NEWPAGE'] = ''; // Deprecated since MediaWiki 1.21, not used by default. Kept for backwards-compatibility. $keys['$CHANGEDORCREATED'] = wfMessage('created')->inContentLanguage()->text(); } $keys['$PAGETITLE'] = $this->title->getPrefixedText(); $keys['$PAGETITLE_URL'] = $this->title->getCanonicalURL(); $keys['$PAGEMINOREDIT'] = $this->minorEdit ? wfMessage('minoredit')->inContentLanguage()->text() : ''; $keys['$UNWATCHURL'] = $this->title->getCanonicalURL('action=unwatch'); if ($this->editor->isAnon()) { # real anon (user:xxx.xxx.xxx.xxx) $keys['$PAGEEDITOR'] = wfMessage('enotif_anon_editor', $this->editor->getName())->inContentLanguage()->text(); $keys['$PAGEEDITOR_EMAIL'] = wfMessage('noemailtitle')->inContentLanguage()->text(); } else { $keys['$PAGEEDITOR'] = $wgEnotifUseRealName && $this->editor->getRealName() !== '' ? $this->editor->getRealName() : $this->editor->getName(); $emailPage = SpecialPage::getSafeTitleFor('Emailuser', $this->editor->getName()); $keys['$PAGEEDITOR_EMAIL'] = $emailPage->getCanonicalURL(); } $keys['$PAGEEDITOR_WIKI'] = $this->editor->getUserPage()->getCanonicalURL(); $keys['$HELPPAGE'] = wfExpandUrl(Skin::makeInternalOrExternalUrl(wfMessage('helppage')->inContentLanguage()->text())); # Replace this after transforming the message, bug 35019 $postTransformKeys['$PAGESUMMARY'] = $this->summary == '' ? ' - ' : $this->summary; // Now build message's subject and body // Messages: // enotif_subject_deleted, enotif_subject_created, enotif_subject_moved, // enotif_subject_restored, enotif_subject_changed $this->subject = wfMessage('enotif_subject_' . $this->pageStatus)->inContentLanguage()->params($pageTitle, $keys['$PAGEEDITOR'])->text(); // Messages: // enotif_body_intro_deleted, enotif_body_intro_created, enotif_body_intro_moved, // enotif_body_intro_restored, enotif_body_intro_changed $keys['$PAGEINTRO'] = wfMessage('enotif_body_intro_' . $this->pageStatus)->inContentLanguage()->params($pageTitle, $keys['$PAGEEDITOR'], $pageTitleUrl)->text(); $body = wfMessage('enotif_body')->inContentLanguage()->plain(); $body = strtr($body, $keys); $body = MessageCache::singleton()->transform($body, false, null, $this->title); $this->body = wordwrap(strtr($body, $postTransformKeys), 72); # Reveal the page editor's address as REPLY-TO address only if # the user has not opted-out and the option is enabled at the # global configuration level. $adminAddress = new MailAddress($wgPasswordSender, wfMessage('emailsender')->inContentLanguage()->text()); if ($wgEnotifRevealEditorAddress && $this->editor->getEmail() != '' && $this->editor->getOption('enotifrevealaddr')) { $editorAddress = MailAddress::newFromUser($this->editor); if ($wgEnotifFromEditor) { $this->from = $editorAddress; } else { $this->from = $adminAddress; $this->replyto = $editorAddress; } } else { $this->from = $adminAddress; $this->replyto = new MailAddress($wgNoReplyAddress); } }
/** * Add content from plain text * @since 1.17 * @param $bar array * @param $text string * @return Array */ function addToSidebarPlain(&$bar, $text) { $lines = explode("\n", $text); $heading = ''; foreach ($lines as $line) { if (strpos($line, '*') !== 0) { continue; } $line = rtrim($line, "\r"); // for Windows compat if (strpos($line, '**') !== 0) { $heading = trim($line, '* '); if (!array_key_exists($heading, $bar)) { $bar[$heading] = array(); } } else { $line = trim($line, '* '); if (strpos($line, '|') !== false) { // sanity check $line = MessageCache::singleton()->transform($line, false, null, $this->getTitle()); $line = array_map('trim', explode('|', $line, 2)); if (count($line) !== 2) { // Second sanity check, could be hit by people doing // funky stuff with parserfuncs... (bug 33321) continue; } $extraAttribs = array(); $msgLink = $this->msg($line[0])->inContentLanguage(); if ($msgLink->exists()) { $link = $msgLink->text(); if ($link == '-') { continue; } } else { $link = $line[0]; } $msgText = $this->msg($line[1]); if ($msgText->exists()) { $text = $msgText->text(); } else { $text = $line[1]; } if (preg_match('/^(?i:' . wfUrlProtocols() . ')/', $link)) { $href = $link; // Parser::getExternalLinkAttribs won't work here because of the Namespace things global $wgNoFollowLinks, $wgNoFollowDomainExceptions; if ($wgNoFollowLinks && !wfMatchesDomainList($href, $wgNoFollowDomainExceptions)) { $extraAttribs['rel'] = 'nofollow'; } global $wgExternalLinkTarget; if ($wgExternalLinkTarget) { $extraAttribs['target'] = $wgExternalLinkTarget; } } else { $title = Title::newFromText($link); if ($title) { $title = $title->fixSpecialName(); $href = $title->getLinkURL(); } else { $href = 'INVALID-TITLE'; } } $bar[$heading][] = array_merge(array('text' => $text, 'href' => $href, 'id' => 'n-' . Sanitizer::escapeId(strtr($line[1], ' ', '-'), 'noninitial'), 'active' => false), $extraAttribs); } else { continue; } } } return $bar; }
/** * Get the language in which the content of this page is written. * Defaults to $wgContLang, but in certain cases it can be e.g. * $wgLang (such as special pages, which are in the user language). * * @since 1.18 * @return object Language */ public function getPageLanguage() { global $wgLang; if ($this->getNamespace() == NS_SPECIAL) { // special pages are in the user language return $wgLang; } elseif ($this->isRedirect()) { // the arrow on a redirect page is aligned according to the user language return $wgLang; } elseif ($this->isCssOrJsPage()) { // css/js should always be LTR and is, in fact, English return wfGetLangObj('en'); } elseif ($this->getNamespace() == NS_MEDIAWIKI) { // Parse mediawiki messages with correct target language list(, $lang) = MessageCache::singleton()->figureMessage($this->getText()); return wfGetLangObj($lang); } global $wgContLang; // If nothing special, it should be in the wiki content language $pageLang = $wgContLang; // Hook at the end because we don't want to override the above stuff wfRunHooks('PageContentLanguage', array($this, &$pageLang, $wgLang)); return wfGetLangObj($pageLang); }
/** * Get the language in which the content of the given page is written. * * This default implementation just returns $wgContLang (except for pages * in the MediaWiki namespace) * * Note that the pages language is not cacheable, since it may in some * cases depend on user settings. * * Also note that the page language may or may not depend on the actual content of the page, * that is, this method may load the content in order to determine the language. * * @since 1.21 * * @param Title $title The page to determine the language for. * @param Content $content The page's content, if you have it handy, to avoid reloading it. * * @return Language The page's language */ public function getPageLanguage(Title $title, Content $content = null) { global $wgContLang, $wgLang; $pageLang = $wgContLang; if ($title->getNamespace() == NS_MEDIAWIKI) { // Parse mediawiki messages with correct target language list(, $lang) = MessageCache::singleton()->figureMessage($title->getText()); $pageLang = wfGetLangObj($lang); } wfRunHooks('PageContentLanguage', array($title, &$pageLang, $wgLang)); return wfGetLangObj($pageLang); }
/** * Wrapper for what ever method we use to get message contents. * * @since 1.17 * * @return string * @throws MWException If message key array is empty. */ protected function fetchMessage() { if ($this->message === null) { $cache = MessageCache::singleton(); foreach ($this->keysToTry as $key) { $message = $cache->get($key, $this->useDatabase, $this->language); if ($message !== false && $message !== '') { break; } } // NOTE: The constructor makes sure keysToTry isn't empty, // so we know that $key and $message are initialized. $this->key = $key; $this->message = $message; } return $this->message; }
/** * Clears caches when article is deleted * * @param $title Title */ public static function onArticleDelete( $title ) { // Update existence markers on article/talk tabs... if ( $title->isTalkPage() ) { $other = $title->getSubjectPage(); } else { $other = $title->getTalkPage(); } $other->invalidateCache(); $other->purgeSquid(); $title->touchLinks(); $title->purgeSquid(); // File cache HTMLFileCache::clearFileCache( $title ); InfoAction::invalidateCache( $title ); // Messages if ( $title->getNamespace() == NS_MEDIAWIKI ) { MessageCache::singleton()->replace( $title->getDBkey(), false ); } // Images if ( $title->getNamespace() == NS_FILE ) { $update = new HTMLCacheUpdate( $title, 'imagelinks' ); $update->doUpdate(); } // User talk pages if ( $title->getNamespace() == NS_USER_TALK ) { $user = User::newFromName( $title->getText(), false ); if ( $user ) { $user->setNewtalk( false ); } } // Image redirects RepoGroup::singleton()->getLocalRepo()->invalidateImageRedirect( $title ); }
/** * Get a message from either the content language or the user language. * * First, assemble a list of languages to attempt getting the message from. This * chain begins with the requested language and its fallbacks and then continues with * the content language and its fallbacks. For each language in the chain, the following * process will occur (in this order): * 1. If a language-specific override, i.e., [[MW:msg/lang]], is available, use that. * Note: for the content language, there is no /lang subpage. * 2. Fetch from the static CDB cache. * 3. If available, check the database for fallback language overrides. * * This process provides a number of guarantees. When changing this code, make sure all * of these guarantees are preserved. * * If the requested language is *not* the content language, then the CDB cache for that * specific language will take precedence over the root database page ([[MW:msg]]). * * Fallbacks will be just that: fallbacks. A fallback language will never be reached if * the message is available *anywhere* in the language for which it is a fallback. * * @param string $key The message key * @param bool $useDB If true, look for the message in the DB, false * to use only the compiled l10n cache. * @param bool|string|object $langcode Code of the language to get the message for. * - If string and a valid code, will create a standard language object * - If string but not a valid code, will create a basic language object * - If boolean and false, create object from the current users language * - If boolean and true, create object from the wikis content language * - If language object, use it as given * @param bool $isFullKey Specifies whether $key is a two part key "msg/lang". * * @throws MWException When given an invalid key * @return string|bool False if the message doesn't exist, otherwise the * message (which can be empty) */ function get($key, $useDB = true, $langcode = true, $isFullKey = false) { global $wgContLang; if (is_int($key)) { // Fix numerical strings that somehow become ints // on their way here $key = (string) $key; } elseif (!is_string($key)) { throw new MWException('Non-string key given'); } elseif ($key === '') { // Shortcut: the empty key is always missing return false; } // For full keys, get the language code from the key $pos = strrpos($key, '/'); if ($isFullKey && $pos !== false) { $langcode = substr($key, $pos + 1); $key = substr($key, 0, $pos); } // Normalise title-case input (with some inlining) $lckey = MessageCache::normalizeKey($key); Hooks::run('MessageCache::get', array(&$lckey)); if (ord($lckey) < 128) { $uckey = ucfirst($lckey); } else { $uckey = $wgContLang->ucfirst($lckey); } // Loop through each language in the fallback list until we find something useful $lang = wfGetLangObj($langcode); $message = $this->getMessageFromFallbackChain($lang, $lckey, $uckey, !$this->mDisable && $useDB); // If we still have no message, maybe the key was in fact a full key so try that if ($message === false) { $parts = explode('/', $lckey); // We may get calls for things that are http-urls from sidebar // Let's not load nonexistent languages for those // They usually have more than one slash. if (count($parts) == 2 && $parts[1] !== '') { $message = Language::getMessageFor($parts[0], $parts[1]); if ($message === null) { $message = false; } } } // Post-processing if the message exists if ($message !== false) { // Fix whitespace $message = str_replace(array(' ', ' ', ' '), array(' ', " ", " "), $message); } return $message; }
/** * This function commits all DB changes as needed before * the user can receive a response (in case commit fails) * * @param IContextSource $context * @param callable $postCommitWork [default: null] * @since 1.27 */ public static function preOutputCommit(IContextSource $context, callable $postCommitWork = null) { // Either all DBs should commit or none ignore_user_abort(true); $config = $context->getConfig(); $request = $context->getRequest(); $output = $context->getOutput(); $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory(); // Commit all changes $lbFactory->commitMasterChanges(__METHOD__, ['maxWriteDuration' => $config->get('MaxUserDBWriteDuration')]); wfDebug(__METHOD__ . ': primary transaction round committed'); // Run updates that need to block the user or affect output (this is the last chance) DeferredUpdates::doUpdates('enqueue', DeferredUpdates::PRESEND); wfDebug(__METHOD__ . ': pre-send deferred updates completed'); // Decide when clients block on ChronologyProtector DB position writes $urlDomainDistance = $request->wasPosted() && $output->getRedirect() && $lbFactory->hasOrMadeRecentMasterChanges(INF) ? self::getUrlDomainDistance($output->getRedirect(), $context) : false; if ($urlDomainDistance === 'local' || $urlDomainDistance === 'remote') { // OutputPage::output() will be fast; $postCommitWork will not be useful for // masking the latency of syncing DB positions accross all datacenters synchronously. // Instead, make use of the RTT time of the client follow redirects. $flags = $lbFactory::SHUTDOWN_CHRONPROT_ASYNC; $cpPosTime = microtime(true); // Client's next request should see 1+ positions with this DBMasterPos::asOf() time if ($urlDomainDistance === 'local') { // Client will stay on this domain, so set an unobtrusive cookie $expires = time() + ChronologyProtector::POSITION_TTL; $options = ['prefix' => '']; $request->response()->setCookie('cpPosTime', $cpPosTime, $expires, $options); } else { // Cookies may not work across wiki domains, so use a URL parameter $safeUrl = $lbFactory->appendPreShutdownTimeAsQuery($output->getRedirect(), $cpPosTime); $output->redirect($safeUrl); } } else { // OutputPage::output() is fairly slow; run it in $postCommitWork to mask // the latency of syncing DB positions accross all datacenters synchronously $flags = $lbFactory::SHUTDOWN_CHRONPROT_SYNC; if ($lbFactory->hasOrMadeRecentMasterChanges(INF)) { $cpPosTime = microtime(true); // Set a cookie in case the DB position store cannot sync accross datacenters. // This will at least cover the common case of the user staying on the domain. $expires = time() + ChronologyProtector::POSITION_TTL; $options = ['prefix' => '']; $request->response()->setCookie('cpPosTime', $cpPosTime, $expires, $options); } } // Record ChronologyProtector positions for DBs affected in this request at this point $lbFactory->shutdown($flags, $postCommitWork); wfDebug(__METHOD__ . ': LBFactory shutdown completed'); // Set a cookie to tell all CDN edge nodes to "stick" the user to the DC that handles this // POST request (e.g. the "master" data center). Also have the user briefly bypass CDN so // ChronologyProtector works for cacheable URLs. if ($request->wasPosted() && $lbFactory->hasOrMadeRecentMasterChanges()) { $expires = time() + $config->get('DataCenterUpdateStickTTL'); $options = ['prefix' => '']; $request->response()->setCookie('UseDC', 'master', $expires, $options); $request->response()->setCookie('UseCDNCache', 'false', $expires, $options); } // Avoid letting a few seconds of replica DB lag cause a month of stale data. This logic is // also intimately related to the value of $wgCdnReboundPurgeDelay. if ($lbFactory->laggedReplicaUsed()) { $maxAge = $config->get('CdnMaxageLagged'); $output->lowerCdnMaxage($maxAge); $request->response()->header("X-Database-Lagged: true"); wfDebugLog('replication', "Lagged DB used; CDN cache TTL limited to {$maxAge} seconds"); } // Avoid long-term cache pollution due to message cache rebuild timeouts (T133069) if (MessageCache::singleton()->isDisabled()) { $maxAge = $config->get('CdnMaxageSubstitute'); $output->lowerCdnMaxage($maxAge); $request->response()->header("X-Response-Substitute: true"); } }
/** * Get the error message as HTML. This is done by parsing the wikitext error * message. * @param string $shortContext A short enclosing context message name, to * be used when there is a single error * @param string $longContext A long enclosing context message name, for a list * @return string */ public function getHTML($shortContext = false, $longContext = false) { $text = $this->getWikiText($shortContext, $longContext); $out = MessageCache::singleton()->parse($text, null, true, true); return $out instanceof ParserOutput ? $out->getText() : $out; }
/** * Get the default message text or false if the message doesn't exist * * @return string|bool */ public function getDefaultMessageText() { global $wgContLang; if ($this->getNamespace() != NS_MEDIAWIKI) { // Just in case return false; } list($name, $lang) = MessageCache::singleton()->figureMessage($wgContLang->lcfirst($this->getText())); $message = wfMessage($name)->inLanguage($lang)->useDatabase(false); if ($message->exists()) { return $message->plain(); } else { return false; } }
/** * Wrapper for what ever method we use to get message contents * * @return string */ protected function fetchMessage() { if (!isset($this->message)) { $cache = MessageCache::singleton(); if (is_array($this->key)) { if (!count($this->key)) { throw new MWException("Given empty message key array."); } foreach ($this->key as $key) { $message = $cache->get($key, $this->useDatabase, $this->language, false, $this->fixWhitespace); if ($message !== false && $message !== '') { break; } } $this->message = $message; } else { $this->message = $cache->get($this->key, $this->useDatabase, $this->language, false, $this->fixWhitespace); } } return $this->message; }
/** * Parse the conversion table stored in the cache. * * The tables should be in blocks of the following form: * -{ * word => word ; * word => word ; * ... * }- * * To make the tables more manageable, subpages are allowed * and will be parsed recursively if $recursive == true. * * @param string $code Language code * @param string $subpage Subpage name * @param bool $recursive Parse subpages recursively? Defaults to true. * * @return array */ function parseCachedTable($code, $subpage = '', $recursive = true) { static $parsed = []; $key = 'Conversiontable/' . $code; if ($subpage) { $key .= '/' . $subpage; } if (array_key_exists($key, $parsed)) { return []; } $parsed[$key] = true; if ($subpage === '') { $txt = MessageCache::singleton()->getMsgFromNamespace($key, $code); } else { $txt = false; $title = Title::makeTitleSafe(NS_MEDIAWIKI, $key); if ($title && $title->exists()) { $revision = Revision::newFromTitle($title); if ($revision) { if ($revision->getContentModel() == CONTENT_MODEL_WIKITEXT) { $txt = $revision->getContent(Revision::RAW)->getNativeData(); } // @todo in the future, use a specialized content model, perhaps based on json! } } } # Nothing to parse if there's no text if ($txt === false || $txt === null || $txt === '') { return []; } // get all subpage links of the form // [[MediaWiki:Conversiontable/zh-xx/...|...]] $linkhead = $this->mLangObj->getNsText(NS_MEDIAWIKI) . ':Conversiontable'; $subs = StringUtils::explode('[[', $txt); $sublinks = []; foreach ($subs as $sub) { $link = explode(']]', $sub, 2); if (count($link) != 2) { continue; } $b = explode('|', $link[0], 2); $b = explode('/', trim($b[0]), 3); if (count($b) == 3) { $sublink = $b[2]; } else { $sublink = ''; } if ($b[0] == $linkhead && $b[1] == $code) { $sublinks[] = $sublink; } } // parse the mappings in this page $blocks = StringUtils::explode('-{', $txt); $ret = []; $first = true; foreach ($blocks as $block) { if ($first) { // Skip the part before the first -{ $first = false; continue; } $mappings = explode('}-', $block, 2)[0]; $stripped = str_replace(["'", '"', '*', '#'], '', $mappings); $table = StringUtils::explode(';', $stripped); foreach ($table as $t) { $m = explode('=>', $t, 3); if (count($m) != 2) { continue; } // trim any trailling comments starting with '//' $tt = explode('//', $m[1], 2); $ret[trim($m[0])] = trim($tt[0]); } } // recursively parse the subpages if ($recursive) { foreach ($sublinks as $link) { $s = $this->parseCachedTable($code, $link, $recursive); $ret = $s + $ret; } } if ($this->mUcfirst) { foreach ($ret as $k => $v) { $ret[$this->mLangObj->ucfirst($k)] = $this->mLangObj->ucfirst($v); } } return $ret; }
/** * @return string Safe HTML */ function getHTML() { global $wgShowDBErrorBacktrace, $wgShowHostnames, $wgShowSQLErrors; $sorry = htmlspecialchars($this->msg('dberr-problems', 'Sorry! This site is experiencing technical difficulties.')); $again = htmlspecialchars($this->msg('dberr-again', 'Try waiting a few minutes and reloading.')); if ($wgShowHostnames || $wgShowSQLErrors) { $info = str_replace('$1', Html::element('span', array('dir' => 'ltr'), $this->error), htmlspecialchars($this->msg('dberr-info', '(Cannot access the database: $1)'))); } else { $info = htmlspecialchars($this->msg('dberr-info-hidden', '(Cannot access the database)')); } # No database access MessageCache::singleton()->disable(); $html = "<h1>{$sorry}</h1><p>{$again}</p><p><small>{$info}</small></p>"; if ($wgShowDBErrorBacktrace) { $html .= '<p>Backtrace:</p><pre>' . htmlspecialchars($this->getTraceAsString()) . '</pre>'; } $html .= '<hr />'; $html .= $this->searchForm(); return $html; }