diff options
author | Pierre Schmitz <pierre@archlinux.de> | 2013-01-18 16:46:04 +0100 |
---|---|---|
committer | Pierre Schmitz <pierre@archlinux.de> | 2013-01-18 16:46:04 +0100 |
commit | 63601400e476c6cf43d985f3e7b9864681695ed4 (patch) | |
tree | f7846203a952e38aaf66989d0a4702779f549962 /includes/OutputPage.php | |
parent | 8ff01378c9e0207f9169b81966a51def645b6a51 (diff) |
Update to MediaWiki 1.20.2
this update includes:
* adjusted Arch Linux skin
* updated FluxBBAuthPlugin
* patch for https://bugzilla.wikimedia.org/show_bug.cgi?id=44024
Diffstat (limited to 'includes/OutputPage.php')
-rw-r--r-- | includes/OutputPage.php | 403 |
1 files changed, 245 insertions, 158 deletions
diff --git a/includes/OutputPage.php b/includes/OutputPage.php index a91d5465..b4a81bb1 100644 --- a/includes/OutputPage.php +++ b/includes/OutputPage.php @@ -1,7 +1,24 @@ <?php -if ( !defined( 'MEDIAWIKI' ) ) { - die( 1 ); -} +/** + * Preparation for the final page rendering. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + */ /** * This class should be covered by a general architecture document which does @@ -19,10 +36,10 @@ if ( !defined( 'MEDIAWIKI' ) ) { * @todo document */ class OutputPage extends ContextSource { - /// Should be private. Used with addMeta() which adds <meta> + /// Should be private. Used with addMeta() which adds "<meta>" var $mMetatags = array(); - /// <meta keyworkds="stuff"> most of the time the first 10 links to an article + /// "<meta keywords='stuff'>" most of the time the first 10 links to an article var $mKeywords = array(); var $mLinktags = array(); @@ -33,17 +50,17 @@ class OutputPage extends ContextSource { /// Should be private - has getter and setter. Contains the HTML title var $mPagetitle = ''; - /// Contains all of the <body> content. Should be private we got set/get accessors and the append() method. + /// Contains all of the "<body>" content. Should be private we got set/get accessors and the append() method. var $mBodytext = ''; /** * Holds the debug lines that will be output as comments in page source if * $wgDebugComments is enabled. See also $wgShowDebug. - * TODO: make a getter method for this + * @deprecated since 1.20; use MWDebug class instead. */ - public $mDebugtext = ''; // TODO: we might want to replace it by wfDebug() wfDebugLog() + public $mDebugtext = ''; - /// Should be private. Stores contents of <title> tag + /// Should be private. Stores contents of "<title>" tag var $mHTMLtitle = ''; /// Should be private. Is the displayed content related to the source of the corresponding wiki article. @@ -99,8 +116,8 @@ class OutputPage extends ContextSource { /** * Should be private. Used for JavaScript (pre resource loader) * We should split js / css. - * mScripts content is inserted as is in <head> by Skin. This might contains - * either a link to a stylesheet or inline css. + * mScripts content is inserted as is in "<head>" by Skin. This might + * contains either a link to a stylesheet or inline css. */ var $mScripts = ''; @@ -118,7 +135,7 @@ class OutputPage extends ContextSource { */ var $mPageLinkTitle = ''; - /// Array of elements in <head>. Parser might add its own headers! + /// Array of elements in "<head>". Parser might add its own headers! var $mHeadItems = array(); // @todo FIXME: Next variables probably comes from the resource loader @@ -180,7 +197,7 @@ class OutputPage extends ContextSource { /** * Comes from the parser. This was probably made to load CSS/JS only - * if we had <gallery>. Used directly in CategoryPage.php + * if we had "<gallery>". Used directly in CategoryPage.php * Looks like resource loader can replace this. */ var $mNoGallery = false; @@ -220,7 +237,6 @@ class OutputPage extends ContextSource { private $mFollowPolicy = 'follow'; private $mVaryHeader = array( 'Accept-Encoding' => array( 'list-contains=gzip' ), - 'Cookie' => null ); /** @@ -276,7 +292,7 @@ class OutputPage extends ContextSource { } /** - * Add a new <meta> tag + * Add a new "<meta>" tag * To add an http-equiv meta tag, precede the name with "http:" * * @param $name String tag name @@ -389,7 +405,7 @@ class OutputPage extends ContextSource { /** * Add a self-contained script tag with the given contents * - * @param $script String: JavaScript text, no <script> tags + * @param $script String: JavaScript text, no "<script>" tags */ public function addInlineScript( $script ) { $this->mScripts .= Html::inlineScript( "\n$script\n" ) . "\n"; @@ -631,24 +647,16 @@ class OutputPage extends ContextSource { $maxModified = max( $modifiedTimes ); $this->mLastModified = wfTimestamp( TS_RFC2822, $maxModified ); - if( empty( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) { + $clientHeader = $this->getRequest()->getHeader( 'If-Modified-Since' ); + if ( $clientHeader === false ) { wfDebug( __METHOD__ . ": client did not send If-Modified-Since header\n", false ); return false; } - # Make debug info - $info = ''; - foreach ( $modifiedTimes as $name => $value ) { - if ( $info !== '' ) { - $info .= ', '; - } - $info .= "$name=" . wfTimestamp( TS_ISO_8601, $value ); - } - # IE sends sizes after the date like this: # Wed, 20 Aug 2003 06:51:19 GMT; length=5202 # this breaks strtotime(). - $clientHeader = preg_replace( '/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"] ); + $clientHeader = preg_replace( '/;.*$/', '', $clientHeader ); wfSuppressWarnings(); // E_STRICT system time bitching $clientHeaderTime = strtotime( $clientHeader ); @@ -659,6 +667,15 @@ class OutputPage extends ContextSource { } $clientHeaderTime = wfTimestamp( TS_MW, $clientHeaderTime ); + # Make debug info + $info = ''; + foreach ( $modifiedTimes as $name => $value ) { + if ( $info !== '' ) { + $info .= ', '; + } + $info .= "$name=" . wfTimestamp( TS_ISO_8601, $value ); + } + wfDebug( __METHOD__ . ": client sent If-Modified-Since: " . wfTimestamp( TS_ISO_8601, $clientHeaderTime ) . "\n", false ); wfDebug( __METHOD__ . ": effective Last-Modified: " . @@ -763,7 +780,7 @@ class OutputPage extends ContextSource { } /** - * "HTML title" means the contents of <title>. + * "HTML title" means the contents of "<title>". * It is stored as plain, unescaped text and will be run through htmlspecialchars in the skin file. * * @param $name string @@ -777,7 +794,7 @@ class OutputPage extends ContextSource { } /** - * Return the "HTML title", i.e. the content of the <title> tag. + * Return the "HTML title", i.e. the content of the "<title>" tag. * * @return String */ @@ -788,7 +805,7 @@ class OutputPage extends ContextSource { /** * Set $mRedirectedFrom, the Title of the page which redirected us to the current page. * - * param @t Title + * @param $t Title */ public function setRedirectedFrom( $t ) { $this->mRedirectedFrom = $t; @@ -1075,7 +1092,7 @@ class OutputPage extends ContextSource { /** * Add new language links * - * @param $newLinkArray Associative array mapping language code to the page + * @param $newLinkArray array Associative array mapping language code to the page * name */ public function addLanguageLinks( $newLinkArray ) { @@ -1085,7 +1102,7 @@ class OutputPage extends ContextSource { /** * Reset the language links and add new language links * - * @param $newLinkArray Associative array mapping language code to the page + * @param $newLinkArray array Associative array mapping language code to the page * name */ public function setLanguageLinks( $newLinkArray ) { @@ -1298,18 +1315,9 @@ class OutputPage extends ContextSource { } /** - * Add $text to the debug output - * - * @param $text String: debug text - */ - public function debug( $text ) { - $this->mDebugtext .= $text; - } - - /** * Get/set the ParserOptions object to use for wikitext parsing * - * @param $options either the ParserOption to use or null to only get the + * @param $options ParserOptions|null either the ParserOption to use or null to only get the * current ParserOption object * @return ParserOptions object */ @@ -1346,11 +1354,11 @@ class OutputPage extends ContextSource { * Set the timestamp of the revision which will be displayed. This is used * to avoid a extra DB call in Skin::lastModified(). * - * @param $revid Mixed: string, or null + * @param $timestamp Mixed: string, or null * @return Mixed: previous value */ - public function setRevisionTimestamp( $timestmap ) { - return wfSetVar( $this->mRevisionTimestamp, $timestmap ); + public function setRevisionTimestamp( $timestamp) { + return wfSetVar( $this->mRevisionTimestamp, $timestamp ); } /** @@ -1366,7 +1374,7 @@ class OutputPage extends ContextSource { /** * Set the displayed file version * - * @param $file File|false + * @param $file File|bool * @return Mixed: previous value */ public function setFileVersion( $file ) { @@ -1662,18 +1670,6 @@ class OutputPage extends ContextSource { } /** - * Return whether this page is not cacheable because "useskin" or "uselang" - * URL parameters were passed. - * - * @return Boolean - */ - function uncacheableBecauseRequestVars() { - $request = $this->getRequest(); - return $request->getText( 'useskin', false ) === false - && $request->getText( 'uselang', false ) === false; - } - - /** * Check if the request has a cache-varying cookie header * If it does, it's very important that we don't allow public caching * @@ -1718,6 +1714,16 @@ class OutputPage extends ContextSource { } /** + * Return a Vary: header on which to vary caches. Based on the keys of $mVaryHeader, + * such as Accept-Encoding or Cookie + * + * @return String + */ + public function getVaryHeader() { + return 'Vary: ' . join( ', ', array_keys( $this->mVaryHeader ) ); + } + + /** * Get a complete X-Vary-Options header * * @return String @@ -1734,7 +1740,7 @@ class OutputPage extends ContextSource { $headers = array(); foreach( $this->mVaryHeader as $header => $option ) { $newheader = $header; - if( is_array( $option ) ) { + if ( is_array( $option ) && count( $option ) > 0 ) { $newheader .= ';' . implode( ';', $option ); } $headers[] = $newheader; @@ -1829,18 +1835,19 @@ class OutputPage extends ContextSource { $response->header( "ETag: $this->mETag" ); } + $this->addVaryHeader( 'Cookie' ); $this->addAcceptLanguage(); # don't serve compressed data to clients who can't handle it # maintain different caches for logged-in users and non-logged in ones - $response->header( 'Vary: ' . join( ', ', array_keys( $this->mVaryHeader ) ) ); + $response->header( $this->getVaryHeader() ); if ( $wgUseXVO ) { # Add an X-Vary-Options header for Squid with Wikimedia patches $response->header( $this->getXVO() ); } - if( !$this->uncacheableBecauseRequestVars() && $this->mEnableClientCache ) { + if( $this->mEnableClientCache ) { if( $wgUseSquid && session_id() == '' && !$this->isPrintable() && $this->mSquidMaxage != 0 && !$this->haveCacheVaryCookies() @@ -1983,6 +1990,9 @@ class OutputPage extends ContextSource { wfProfileOut( 'Output-skin' ); } + // This hook allows last minute changes to final overall output by modifying output buffer + wfRunHooks( 'AfterFinalPageOutput', array( $this ) ); + $this->sendCacheControl(); ob_end_flush(); wfProfileOut( __METHOD__ ); @@ -2008,18 +2018,14 @@ class OutputPage extends ContextSource { /** * Prepare this object to display an error page; disable caching and * indexing, clear the current text and redirect, set the page's title - * and optionally an custom HTML title (content of the <title> tag). + * and optionally an custom HTML title (content of the "<title>" tag). * * @param $pageTitle String|Message will be passed directly to setPageTitle() * @param $htmlTitle String|Message will be passed directly to setHTMLTitle(); - * optional, if not passed the <title> attribute will be + * optional, if not passed the "<title>" attribute will be * based on $pageTitle */ public function prepareErrorPage( $pageTitle, $htmlTitle = false ) { - if ( $this->getTitle() ) { - $this->mDebugtext .= 'Original title: ' . $this->getTitle()->getPrefixedText() . "\n"; - } - $this->setPageTitle( $pageTitle ); if ( $htmlTitle !== false ) { $this->setHTMLTitle( $htmlTitle ); @@ -2037,13 +2043,18 @@ class OutputPage extends ContextSource { * * showErrorPage( 'titlemsg', 'pagetextmsg', array( 'param1', 'param2' ) ); * showErrorPage( 'titlemsg', $messageObject ); + * showErrorPage( $titleMessageObj, $messageObject ); * - * @param $title String: message key for page title + * @param $title Mixed: message key (string) for page title, or a Message object * @param $msg Mixed: message key (string) for page text, or a Message object * @param $params Array: message parameters; ignored if $msg is a Message object */ public function showErrorPage( $title, $msg, $params = array() ) { - $this->prepareErrorPage( $this->msg( $title ), $this->msg( 'errorpagetitle' ) ); + if( !$title instanceof Message ) { + $title = $this->msg( $title ); + } + + $this->prepareErrorPage( $title ); if ( $msg instanceof Message ){ $this->addHTML( $msg->parse() ); @@ -2330,7 +2341,7 @@ $templates * Add a "return to" link pointing to a specified title * * @param $title Title to link - * @param $query String query string + * @param $query Array query string parameters * @param $text String text of the link (input is not escaped) */ public function addReturnTo( $title, $query = array(), $text = null ) { @@ -2344,7 +2355,7 @@ $templates * Add a "return to" link pointing to a specified title, * or the title indicated in the request, or else the main page * - * @param $unused No longer used + * @param $unused * @param $returnto Title or String to return to * @param $returntoquery String: query string for the return to link */ @@ -2370,13 +2381,13 @@ $templates $titleObj = Title::newMainPage(); } - $this->addReturnTo( $titleObj, $returntoquery ); + $this->addReturnTo( $titleObj, wfCgiToArray( $returntoquery ) ); } /** * @param $sk Skin The given Skin * @param $includeStyle Boolean: unused - * @return String: The doctype, opening <html>, and head element. + * @return String: The doctype, opening "<html>", and head element. */ public function headElement( Skin $sk, $includeStyle = true ) { global $wgContLang; @@ -2440,7 +2451,7 @@ $templates */ private function addDefaultModules() { global $wgIncludeLegacyJavaScript, $wgPreloadJavaScriptMwUtil, $wgUseAjax, - $wgAjaxWatch, $wgEnableMWSuggest; + $wgAjaxWatch; // Add base resources $this->addModules( array( @@ -2465,11 +2476,11 @@ $templates wfRunHooks( 'AjaxAddScript', array( &$this ) ); if( $wgAjaxWatch && $this->getUser()->isLoggedIn() ) { - $this->addModules( 'mediawiki.action.watch.ajax' ); + $this->addModules( 'mediawiki.page.watch.ajax' ); } - if ( $wgEnableMWSuggest && !$this->getUser()->getOption( 'disablesuggest', false ) ) { - $this->addModules( 'mediawiki.legacy.mwsuggest' ); + if ( !$this->getUser()->getOption( 'disablesuggest', false ) ) { + $this->addModules( 'mediawiki.searchSuggest' ); } } @@ -2501,19 +2512,21 @@ $templates * @param $only String ResourceLoaderModule TYPE_ class constant * @param $useESI boolean * @param $extraQuery Array with extra query parameters to add to each request. array( param => value ) - * @param $loadCall boolean If true, output an (asynchronous) mw.loader.load() call rather than a <script src="..."> tag - * @return string html <script> and <style> tags + * @param $loadCall boolean If true, output an (asynchronous) mw.loader.load() call rather than a "<script src='...'>" tag + * @return string html "<script>" and "<style>" tags */ protected function makeResourceLoaderLink( $modules, $only, $useESI = false, array $extraQuery = array(), $loadCall = false ) { global $wgResourceLoaderUseESI; + $modules = (array) $modules; + if ( !count( $modules ) ) { return ''; } if ( count( $modules ) > 1 ) { // Remove duplicate module requests - $modules = array_unique( (array) $modules ); + $modules = array_unique( $modules ); // Sort module names so requests are more uniform sort( $modules ); @@ -2530,7 +2543,7 @@ $templates // Create keyed-by-group list of module objects from modules list $groups = array(); $resourceLoader = $this->getResourceLoader(); - foreach ( (array) $modules as $name ) { + foreach ( $modules as $name ) { $module = $resourceLoader->getModule( $name ); # Check that we're allowed to include this module on this page if ( !$module @@ -2551,7 +2564,7 @@ $templates } $links = ''; - foreach ( $groups as $group => $modules ) { + foreach ( $groups as $group => $grpModules ) { // Special handling for user-specific groups $user = null; if ( ( $group === 'user' || $group === 'private' ) && $this->getUser()->isLoggedIn() ) { @@ -2573,14 +2586,30 @@ $templates $extraQuery ); $context = new ResourceLoaderContext( $resourceLoader, new FauxRequest( $query ) ); - // Drop modules that know they're empty - foreach ( $modules as $key => $module ) { + // Extract modules that know they're empty + $emptyModules = array (); + foreach ( $grpModules as $key => $module ) { if ( $module->isKnownEmpty( $context ) ) { - unset( $modules[$key] ); + $emptyModules[$key] = 'ready'; + unset( $grpModules[$key] ); } } + // Inline empty modules: since they're empty, just mark them as 'ready' + if ( count( $emptyModules ) > 0 && $only !== ResourceLoaderModule::TYPE_STYLES ) { + // If we're only getting the styles, we don't need to do anything for empty modules. + $links .= Html::inlineScript( + + ResourceLoader::makeLoaderConditionalScript( + + ResourceLoader::makeLoaderStateScript( $emptyModules ) + + ) + + ) . "\n"; + } + // If there are no modules left, skip this group - if ( $modules === array() ) { + if ( count( $grpModules ) === 0 ) { continue; } @@ -2591,12 +2620,12 @@ $templates if ( $group === 'private' ) { if ( $only == ResourceLoaderModule::TYPE_STYLES ) { $links .= Html::inlineStyle( - $resourceLoader->makeModuleResponse( $context, $modules ) + $resourceLoader->makeModuleResponse( $context, $grpModules ) ); } else { $links .= Html::inlineScript( ResourceLoader::makeLoaderConditionalScript( - $resourceLoader->makeModuleResponse( $context, $modules ) + $resourceLoader->makeModuleResponse( $context, $grpModules ) ) ); } @@ -2612,7 +2641,7 @@ $templates if ( $group === 'user' ) { // Get the maximum timestamp $timestamp = 1; - foreach ( $modules as $module ) { + foreach ( $grpModules as $module ) { $timestamp = max( $timestamp, $module->getModifiedTime( $context ) ); } // Add a version parameter so cache will break when things change @@ -2620,7 +2649,7 @@ $templates } $url = ResourceLoader::makeLoaderURL( - array_keys( $modules ), + array_keys( $grpModules ), $this->getLanguage()->getCode(), $this->getSkin()->getSkinName(), $user, @@ -2642,7 +2671,7 @@ $templates // Automatically select style/script elements if ( $only === ResourceLoaderModule::TYPE_STYLES ) { $link = Html::linkedStyle( $url ); - } else if ( $loadCall ) { + } else if ( $loadCall ) { $link = Html::inlineScript( ResourceLoader::makeLoaderConditionalScript( Xml::encodeJsCall( 'mw.loader.load', array( $url, 'text/javascript', true ) ) @@ -2663,14 +2692,14 @@ $templates } /** - * JS stuff to put in the <head>. This is the startup module, config + * JS stuff to put in the "<head>". This is the startup module, config * vars and modules marked with position 'top' * * @return String: HTML fragment */ function getHeadScripts() { global $wgResourceLoaderExperimentalAsyncLoading; - + // Startup - this will immediately load jquery and mediawiki modules $scripts = $this->makeResourceLoaderLink( 'startup', ResourceLoaderModule::TYPE_SCRIPTS, true ); @@ -2702,7 +2731,7 @@ $templates ) ); } - + if ( $wgResourceLoaderExperimentalAsyncLoading ) { $scripts .= $this->getScriptsForBottomQueue( true ); } @@ -2711,12 +2740,12 @@ $templates } /** - * JS stuff to put at the 'bottom', which can either be the bottom of the <body> - * or the bottom of the <head> depending on $wgResourceLoaderExperimentalAsyncLoading: + * JS stuff to put at the 'bottom', which can either be the bottom of the "<body>" + * or the bottom of the "<head>" depending on $wgResourceLoaderExperimentalAsyncLoading: * modules marked with position 'bottom', legacy scripts ($this->mScripts), * user preferences, site JS and user JS * - * @param $inHead boolean If true, this HTML goes into the <head>, if false it goes into the <body> + * @param $inHead boolean If true, this HTML goes into the "<head>", if false it goes into the "<body>" * @return string */ function getScriptsForBottomQueue( $inHead ) { @@ -2748,47 +2777,92 @@ $templates // Legacy Scripts $scripts .= "\n" . $this->mScripts; - $userScripts = array(); + $defaultModules = array(); // Add site JS if enabled if ( $wgUseSiteJs ) { $scripts .= $this->makeResourceLoaderLink( 'site', ResourceLoaderModule::TYPE_SCRIPTS, /* $useESI = */ false, /* $extraQuery = */ array(), /* $loadCall = */ $inHead ); - if( $this->getUser()->isLoggedIn() ){ - $userScripts[] = 'user.groups'; - } + $defaultModules['site'] = 'loading'; + } else { + // The wiki is configured to not allow a site module. + $defaultModules['site'] = 'missing'; } // Add user JS if enabled - if ( $wgAllowUserJs && $this->getUser()->isLoggedIn() ) { - if( $this->getTitle() && $this->getTitle()->isJsSubpage() && $this->userCanPreview() ) { - # XXX: additional security check/prompt? - // We're on a preview of a JS subpage - // Exclude this page from the user module in case it's in there (bug 26283) - $scripts .= $this->makeResourceLoaderLink( 'user', ResourceLoaderModule::TYPE_SCRIPTS, false, - array( 'excludepage' => $this->getTitle()->getPrefixedDBkey() ), $inHead - ); - // Load the previewed JS - $scripts .= Html::inlineScript( "\n" . $this->getRequest()->getText( 'wpTextbox1' ) . "\n" ) . "\n"; + if ( $wgAllowUserJs ) { + if ( $this->getUser()->isLoggedIn() ) { + if( $this->getTitle() && $this->getTitle()->isJsSubpage() && $this->userCanPreview() ) { + # XXX: additional security check/prompt? + // We're on a preview of a JS subpage + // Exclude this page from the user module in case it's in there (bug 26283) + $scripts .= $this->makeResourceLoaderLink( 'user', ResourceLoaderModule::TYPE_SCRIPTS, false, + array( 'excludepage' => $this->getTitle()->getPrefixedDBkey() ), $inHead + ); + // Load the previewed JS + $scripts .= Html::inlineScript( "\n" . $this->getRequest()->getText( 'wpTextbox1' ) . "\n" ) . "\n"; + // FIXME: If the user is previewing, say, ./vector.js, his ./common.js will be loaded + // asynchronously and may arrive *after* the inline script here. So the previewed code + // may execute before ./common.js runs. Normally, ./common.js runs before ./vector.js... + } else { + // Include the user module normally, i.e., raw to avoid it being wrapped in a closure. + $scripts .= $this->makeResourceLoaderLink( 'user', ResourceLoaderModule::TYPE_SCRIPTS, + /* $useESI = */ false, /* $extraQuery = */ array(), /* $loadCall = */ $inHead + ); + } + $defaultModules['user'] = 'loading'; } else { - // Include the user module normally - // We can't do $userScripts[] = 'user'; because the user module would end up - // being wrapped in a closure, so load it raw like 'site' - $scripts .= $this->makeResourceLoaderLink( 'user', ResourceLoaderModule::TYPE_SCRIPTS, + // Non-logged-in users have no user module. Treat it as empty and 'ready' to avoid + // blocking default gadgets that might depend on it. Although arguably default-enabled + // gadgets should not depend on the user module, it's harmless and less error-prone to + // handle this case. + $defaultModules['user'] = 'ready'; + } + } else { + // User JS disabled + $defaultModules['user'] = 'missing'; + } + + // Group JS is only enabled if site JS is enabled. + if ( $wgUseSiteJs ) { + if ( $this->getUser()->isLoggedIn() ) { + $scripts .= $this->makeResourceLoaderLink( 'user.groups', ResourceLoaderModule::TYPE_COMBINED, /* $useESI = */ false, /* $extraQuery = */ array(), /* $loadCall = */ $inHead ); + $defaultModules['user.groups'] = 'loading'; + } else { + // Non-logged-in users have no user.groups module. Treat it as empty and 'ready' to + // avoid blocking gadgets that might depend upon the module. + $defaultModules['user.groups'] = 'ready'; } + } else { + // Site (and group JS) disabled + $defaultModules['user.groups'] = 'missing'; } - $scripts .= $this->makeResourceLoaderLink( $userScripts, ResourceLoaderModule::TYPE_COMBINED, - /* $useESI = */ false, /* $extraQuery = */ array(), /* $loadCall = */ $inHead - ); - return $scripts; + $loaderInit = ''; + if ( $inHead ) { + // We generate loader calls anyway, so no need to fix the client-side loader's state to 'loading'. + foreach ( $defaultModules as $m => $state ) { + if ( $state == 'loading' ) { + unset( $defaultModules[$m] ); + } + } + } + if ( count( $defaultModules ) > 0 ) { + $loaderInit = Html::inlineScript( + ResourceLoader::makeLoaderConditionalScript( + ResourceLoader::makeLoaderStateScript( $defaultModules ) + ) + ) . "\n"; + } + return $loaderInit . $scripts; } /** - * JS stuff to put at the bottom of the <body> + * JS stuff to put at the bottom of the "<body>" + * @return string */ function getBottomScripts() { global $wgResourceLoaderExperimentalAsyncLoading; @@ -2802,7 +2876,7 @@ $templates /** * Add one or more variables to be set in mw.config in JavaScript. * - * @param $key {String|Array} Key or array of key/value pars. + * @param $keys {String|Array} Key or array of key/value pairs. * @param $value {Mixed} [optional] Value of the configuration variable. */ public function addJsConfigVars( $keys, $value = null ) { @@ -2830,7 +2904,7 @@ $templates * @return array */ public function getJSVars() { - global $wgUseAjax, $wgEnableMWSuggest; + global $wgUseAjax, $wgContLang; $latestRevID = 0; $pageID = 0; @@ -2885,17 +2959,17 @@ $templates 'wgPageContentLanguage' => $lang->getCode(), 'wgSeparatorTransformTable' => $compactSeparatorTransTable, 'wgDigitTransformTable' => $compactDigitTransTable, + 'wgDefaultDateFormat' => $lang->getDefaultDateFormat(), + 'wgMonthNames' => $lang->getMonthNamesArray(), + 'wgMonthNamesShort' => $lang->getMonthAbbreviationsArray(), 'wgRelevantPageName' => $relevantTitle->getPrefixedDBKey(), ); - if ( $lang->hasVariants() ) { - $vars['wgUserVariant'] = $lang->getPreferredVariant(); + if ( $wgContLang->hasVariants() ) { + $vars['wgUserVariant'] = $wgContLang->getPreferredVariant(); } foreach ( $title->getRestrictionTypes() as $type ) { $vars['wgRestriction' . ucfirst( $type )] = $title->getRestrictions( $type ); } - if ( $wgUseAjax && $wgEnableMWSuggest && !$this->getUser()->getOption( 'disablesuggest', false ) ) { - $vars['wgSearchNamespaces'] = SearchEngine::userNamespaces( $this->getUser() ); - } if ( $title->isMainPage() ) { $vars['wgIsMainPage'] = true; } @@ -2938,12 +3012,11 @@ $templates } /** - * @param $unused Unused - * @param $addContentType bool + * @param $addContentType bool: Whether "<meta>" specifying content type should be returned * - * @return string HTML tag links to be put in the header. + * @return array in format "link name or number => 'link html'". */ - public function getHeadLinks( $unused = null, $addContentType = false ) { + public function getHeadLinksArray( $addContentType = false ) { global $wgUniversalEditButton, $wgFavicon, $wgAppleTouchIcon, $wgEnableAPI, $wgSitename, $wgVersion, $wgHtml5, $wgMimeType, $wgFeed, $wgOverrideSiteFeed, $wgAdvertisedFeedTypes, @@ -2956,20 +3029,20 @@ $templates if ( $wgHtml5 ) { # More succinct than <meta http-equiv=Content-Type>, has the # same effect - $tags[] = Html::element( 'meta', array( 'charset' => 'UTF-8' ) ); + $tags['meta-charset'] = Html::element( 'meta', array( 'charset' => 'UTF-8' ) ); } else { - $tags[] = Html::element( 'meta', array( + $tags['meta-content-type'] = Html::element( 'meta', array( 'http-equiv' => 'Content-Type', 'content' => "$wgMimeType; charset=UTF-8" ) ); - $tags[] = Html::element( 'meta', array( // bug 15835 + $tags['meta-content-style-type'] = Html::element( 'meta', array( // bug 15835 'http-equiv' => 'Content-Style-Type', 'content' => 'text/css' ) ); } } - $tags[] = Html::element( 'meta', array( + $tags['meta-generator'] = Html::element( 'meta', array( 'name' => 'generator', 'content' => "MediaWiki $wgVersion", ) ); @@ -2978,7 +3051,7 @@ $templates if( $p !== 'index,follow' ) { // http://www.robotstxt.org/wc/meta-user.html // Only show if it's different from the default robots policy - $tags[] = Html::element( 'meta', array( + $tags['meta-robots'] = Html::element( 'meta', array( 'name' => 'robots', 'content' => $p, ) ); @@ -2989,7 +3062,7 @@ $templates "/<.*?" . ">/" => '', "/_/" => ' ' ); - $tags[] = Html::element( 'meta', array( + $tags['meta-keywords'] = Html::element( 'meta', array( 'name' => 'keywords', 'content' => preg_replace( array_keys( $strip ), @@ -3006,7 +3079,11 @@ $templates } else { $a = 'name'; } - $tags[] = Html::element( 'meta', + $tagName = "meta-{$tag[0]}"; + if ( isset( $tags[$tagName] ) ) { + $tagName .= $tag[1]; + } + $tags[$tagName] = Html::element( 'meta', array( $a => $tag[0], 'content' => $tag[1] @@ -3025,14 +3102,14 @@ $templates && ( $this->getTitle()->exists() || $this->getTitle()->quickUserCan( 'create', $user ) ) ) { // Original UniversalEditButton $msg = $this->msg( 'edit' )->text(); - $tags[] = Html::element( 'link', array( + $tags['universal-edit-button'] = Html::element( 'link', array( 'rel' => 'alternate', 'type' => 'application/x-wiki', 'title' => $msg, 'href' => $this->getTitle()->getLocalURL( 'action=edit' ) ) ); // Alternate edit link - $tags[] = Html::element( 'link', array( + $tags['alternative-edit'] = Html::element( 'link', array( 'rel' => 'edit', 'title' => $msg, 'href' => $this->getTitle()->getLocalURL( 'action=edit' ) @@ -3045,15 +3122,15 @@ $templates # uses whichever one appears later in the HTML source. Make sure # apple-touch-icon is specified first to avoid this. if ( $wgAppleTouchIcon !== false ) { - $tags[] = Html::element( 'link', array( 'rel' => 'apple-touch-icon', 'href' => $wgAppleTouchIcon ) ); + $tags['apple-touch-icon'] = Html::element( 'link', array( 'rel' => 'apple-touch-icon', 'href' => $wgAppleTouchIcon ) ); } if ( $wgFavicon !== false ) { - $tags[] = Html::element( 'link', array( 'rel' => 'shortcut icon', 'href' => $wgFavicon ) ); + $tags['favicon'] = Html::element( 'link', array( 'rel' => 'shortcut icon', 'href' => $wgFavicon ) ); } # OpenSearch description link - $tags[] = Html::element( 'link', array( + $tags['opensearch'] = Html::element( 'link', array( 'rel' => 'search', 'type' => 'application/opensearchdescription+xml', 'href' => wfScript( 'opensearch_desc' ), @@ -3065,7 +3142,7 @@ $templates # for the MediaWiki API (and potentially additional custom API # support such as WordPress or Twitter-compatible APIs for a # blogging extension, etc) - $tags[] = Html::element( 'link', array( + $tags['rsd'] = Html::element( 'link', array( 'rel' => 'EditURI', 'type' => 'application/rsd+xml', // Output a protocol-relative URL here if $wgServer is protocol-relative @@ -3085,14 +3162,14 @@ $templates if ( !$urlvar ) { $variants = $lang->getVariants(); foreach ( $variants as $_v ) { - $tags[] = Html::element( 'link', array( + $tags["variant-$_v"] = Html::element( 'link', array( 'rel' => 'alternate', 'hreflang' => $_v, 'href' => $this->getTitle()->getLocalURL( array( 'variant' => $_v ) ) ) ); } } else { - $tags[] = Html::element( 'link', array( + $tags['canonical'] = Html::element( 'link', array( 'rel' => 'canonical', 'href' => $this->getTitle()->getCanonicalUrl() ) ); @@ -3115,7 +3192,7 @@ $templates } if ( $copyright ) { - $tags[] = Html::element( 'link', array( + $tags['copyright'] = Html::element( 'link', array( 'rel' => 'copyright', 'href' => $copyright ) ); @@ -3164,11 +3241,21 @@ $templates } } } - return implode( "\n", $tags ); + return $tags; + } + + /** + * @param $unused + * @param $addContentType bool: Whether "<meta>" specifying content type should be returned + * + * @return string HTML tag links to be put in the header. + */ + public function getHeadLinks( $unused = null, $addContentType = false ) { + return implode( "\n", $this->getHeadLinksArray( $addContentType ) ); } /** - * Generate a <link rel/> for a feed. + * Generate a "<link rel/>" for a feed. * * @param $type String: feed type * @param $url String: URL to the feed @@ -3223,7 +3310,7 @@ $templates } /** - * Build a set of <link>s for the stylesheets specified in the $this->styles array. + * Build a set of "<link>" elements for the stylesheets specified in the $this->styles array. * These will be applied to various media & IE conditionals. * * @return string @@ -3259,7 +3346,7 @@ $templates $otherTags .= $this->makeResourceLoaderLink( 'user', ResourceLoaderModule::TYPE_STYLES, false, array( 'excludepage' => $this->getTitle()->getPrefixedDBkey() ) ); - + // Load the previewed CSS // If needed, Janus it first. This is user-supplied CSS, so it's // assumed to be right for the content language directionality. @@ -3421,7 +3508,7 @@ $templates * Add a wikitext-formatted message to the output. * This is equivalent to: * - * $wgOut->addWikiText( wfMsgNoTrans( ... ) ) + * $wgOut->addWikiText( wfMessage( ... )->plain() ) */ public function addWikiMsg( /*...*/ ) { $args = func_get_args(); @@ -3450,9 +3537,6 @@ $templates * message names, or arrays, in which case the first element is the message name, * and subsequent elements are the parameters to that message. * - * The special named parameter 'options' in a message specification array is passed - * through to the $options parameter of wfMsgExt(). - * * Don't use this for messages that are not in users interface language. * * For example: @@ -3461,7 +3545,7 @@ $templates * * Is equivalent to: * - * $wgOut->addWikiText( "<div class='error'>\n" . wfMsgNoTrans( 'some-error' ) . "\n</div>" ); + * $wgOut->addWikiText( "<div class='error'>\n" . wfMessage( 'some-error' )->plain() . "\n</div>" ); * * The newline after opening div is needed in some wikitext. See bug 19226. * @@ -3478,14 +3562,17 @@ $templates $args = $spec; $name = array_shift( $args ); if ( isset( $args['options'] ) ) { - $options = $args['options']; unset( $args['options'] ); + wfDeprecated( + 'Adding "options" to ' . __METHOD__ . ' is no longer supported', + '1.20' + ); } } else { $args = array(); $name = $spec; } - $s = str_replace( '$' . ( $n + 1 ), wfMsgExt( $name, $options, $args ), $s ); + $s = str_replace( '$' . ( $n + 1 ), $this->msg( $name, $args )->plain(), $s ); } $this->addWikiText( $s ); } |