diff options
Diffstat (limited to 'includes/resourceloader')
15 files changed, 420 insertions, 145 deletions
diff --git a/includes/resourceloader/ResourceLoader.php b/includes/resourceloader/ResourceLoader.php index 9175b10d..3b48a266 100644 --- a/includes/resourceloader/ResourceLoader.php +++ b/includes/resourceloader/ResourceLoader.php @@ -1,5 +1,7 @@ <?php /** + * Base class for resource loading system. + * * 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 @@ -161,11 +163,11 @@ class ResourceLoader { $wgResourceLoaderMinifierStatementsOnOwnLine, $wgResourceLoaderMinifierMaxLineLength ); - $result .= "\n\n/* cache key: $key */\n"; + $result .= "\n/* cache key: $key */"; break; case 'minify-css': $result = CSSMin::minify( $data ); - $result .= "\n\n/* cache key: $key */\n"; + $result .= "\n/* cache key: $key */"; break; } @@ -215,7 +217,7 @@ class ResourceLoader { * Registers a module with the ResourceLoader system. * * @param $name Mixed: Name of module as a string or List of name/object pairs as an array - * @param $info Module info array. For backwards compatibility with 1.17alpha, + * @param $info array Module info array. For backwards compatibility with 1.17alpha, * this may also be a ResourceLoaderModule object. Optional when using * multiple-registration calling style. * @throws MWException: If a duplicate module registration is attempted @@ -239,9 +241,9 @@ class ResourceLoader { ); } - // Check $name for illegal characters - if ( preg_match( '/[|,!]/', $name ) ) { - throw new MWException( "ResourceLoader module name '$name' is invalid. Names may not contain pipes (|), commas (,) or exclamation marks (!)" ); + // Check $name for validity + if ( !self::isValidModuleName( $name ) ) { + throw new MWException( "ResourceLoader module name '$name' is invalid, see ResourceLoader::isValidModuleName()" ); } // Attach module @@ -354,6 +356,7 @@ class ResourceLoader { * @return Array */ public function getTestModuleNames( $framework = 'all' ) { + /// @TODO: api siteinfo prop testmodulenames modulenames if ( $framework == 'all' ) { return $this->testModuleNames; } elseif ( isset( $this->testModuleNames[$framework] ) && is_array( $this->testModuleNames[$framework] ) ) { @@ -499,10 +502,6 @@ class ResourceLoader { $response = $this->makeComment( $warnings ) . $response; } - // Remove the output buffer and output the response - ob_end_clean(); - echo $response; - // Save response to file cache unless there are errors if ( isset( $fileCache ) && !$errors && !$missing ) { // Cache single modules...and other requests if there are enough hits @@ -515,6 +514,10 @@ class ResourceLoader { } } + // Remove the output buffer and output the response + ob_end_clean(); + echo $response; + wfProfileOut( __METHOD__ ); } @@ -598,7 +601,7 @@ class ResourceLoader { /** * Send out code for a response from file cache if possible * - * @param $fileCache ObjectFileCache: Cache object for this request URL + * @param $fileCache ResourceFileCache: Cache object for this request URL * @param $context ResourceLoaderContext: Context in which to generate a response * @return bool If this found a cache file and handled the response */ @@ -682,6 +685,7 @@ class ResourceLoader { } // Generate output + $isRaw = false; foreach ( $modules as $name => $module ) { /** * @var $module ResourceLoaderModule @@ -699,7 +703,7 @@ class ResourceLoader { $scripts = $module->getScriptURLsForDebug( $context ); } else { $scripts = $module->getScript( $context ); - if ( is_string( $scripts ) ) { + if ( is_string( $scripts ) && strlen( $scripts ) && substr( $scripts, -1 ) !== ';' ) { // bug 27054: Append semicolon to prevent weird bugs // caused by files not terminating their statements right $scripts .= ";\n"; @@ -709,12 +713,39 @@ class ResourceLoader { // Styles $styles = array(); if ( $context->shouldIncludeStyles() ) { - // If we are in debug mode, we'll want to return an array of URLs - // See comment near shouldIncludeScripts() for more details - if ( $context->getDebug() && !$context->getOnly() && $module->supportsURLLoading() ) { - $styles = $module->getStyleURLsForDebug( $context ); - } else { - $styles = $module->getStyles( $context ); + // Don't create empty stylesheets like array( '' => '' ) for modules + // that don't *have* any stylesheets (bug 38024). + $stylePairs = $module->getStyles( $context ); + if ( count ( $stylePairs ) ) { + // If we are in debug mode without &only= set, we'll want to return an array of URLs + // See comment near shouldIncludeScripts() for more details + if ( $context->getDebug() && !$context->getOnly() && $module->supportsURLLoading() ) { + $styles = array( + 'url' => $module->getStyleURLsForDebug( $context ) + ); + } else { + // Minify CSS before embedding in mw.loader.implement call + // (unless in debug mode) + if ( !$context->getDebug() ) { + foreach ( $stylePairs as $media => $style ) { + // Can be either a string or an array of strings. + if ( is_array( $style ) ) { + $stylePairs[$media] = array(); + foreach ( $style as $cssText ) { + if ( is_string( $cssText ) ) { + $stylePairs[$media][] = $this->filter( 'minify-css', $cssText ); + } + } + } elseif ( is_string( $style ) ) { + $stylePairs[$media] = $this->filter( 'minify-css', $style ); + } + } + } + // Wrap styles into @media groups as needed and flatten into a numerical array + $styles = array( + 'css' => self::makeCombinedStyles( $stylePairs ) + ); + } } } @@ -733,23 +764,21 @@ class ResourceLoader { } break; case 'styles': - $out .= self::makeCombinedStyles( $styles ); + // We no longer seperate into media, they are all combined now with + // custom media type groups into @media .. {} sections as part of the css string. + // Module returns either an empty array or a numerical array with css strings. + $out .= isset( $styles['css'] ) ? implode( '', $styles['css'] ) : ''; break; case 'messages': $out .= self::makeMessageSetScript( new XmlJsCode( $messagesBlob ) ); break; default: - // Minify CSS before embedding in mw.loader.implement call - // (unless in debug mode) - if ( !$context->getDebug() ) { - foreach ( $styles as $media => $style ) { - if ( is_string( $style ) ) { - $styles[$media] = $this->filter( 'minify-css', $style ); - } - } - } - $out .= self::makeLoaderImplementScript( $name, $scripts, $styles, - new XmlJsCode( $messagesBlob ) ); + $out .= self::makeLoaderImplementScript( + $name, + $scripts, + $styles, + new XmlJsCode( $messagesBlob ) + ); break; } } catch ( Exception $e ) { @@ -760,15 +789,14 @@ class ResourceLoader { $missing[] = $name; unset( $modules[$name] ); } + $isRaw |= $module->isRaw(); wfProfileOut( __METHOD__ . '-' . $name ); } // Update module states - if ( $context->shouldIncludeScripts() ) { + if ( $context->shouldIncludeScripts() && !$context->getRaw() && !$isRaw ) { // Set the state of modules loaded as only scripts to ready - if ( count( $modules ) && $context->getOnly() === 'scripts' - && !isset( $modules['startup'] ) ) - { + if ( count( $modules ) && $context->getOnly() === 'scripts' ) { $out .= self::makeLoaderStateScript( array_fill_keys( array_keys( $modules ), 'ready' ) ); } @@ -796,9 +824,9 @@ class ResourceLoader { * Returns JS code to call to mw.loader.implement for a module with * given properties. * - * @param $name Module name + * @param $name string Module name * @param $scripts Mixed: List of URLs to JavaScript files or String of JavaScript code - * @param $styles Mixed: List of CSS strings keyed by media type, or list of lists of URLs to + * @param $styles Mixed: Array of CSS strings keyed by media type, or an array of lists of URLs to * CSS files keyed by media type * @param $messages Mixed: List of messages associated with this module. May either be an * associative array mapping message key to value, or a JSON-encoded message blob containing @@ -808,7 +836,7 @@ class ResourceLoader { */ public static function makeLoaderImplementScript( $name, $scripts, $styles, $messages ) { if ( is_string( $scripts ) ) { - $scripts = new XmlJsCode( "function( $ ) {{$scripts}}" ); + $scripts = new XmlJsCode( "function () {\n{$scripts}\n}" ); } elseif ( !is_array( $scripts ) ) { throw new MWException( 'Invalid scripts error. Array of URLs or string of code expected.' ); } @@ -817,6 +845,11 @@ class ResourceLoader { array( $name, $scripts, + // Force objects. mw.loader.implement requires them to be javascript objects. + // Although these variables are associative arrays, which become javascript + // objects through json_encode. In many cases they will be empty arrays, and + // PHP/json_encode() consider empty arrays to be numerical arrays and + // output javascript "[]" instead of "{}". This fixes that. (object)$styles, (object)$messages ) ); @@ -836,26 +869,34 @@ class ResourceLoader { /** * Combines an associative array mapping media type to CSS into a - * single stylesheet with @media blocks. + * single stylesheet with "@media" blocks. * - * @param $styles Array: List of CSS strings keyed by media type + * @param $styles Array: Array keyed by media type containing (arrays of) CSS strings. * - * @return string + * @return Array */ - public static function makeCombinedStyles( array $styles ) { - $out = ''; - foreach ( $styles as $media => $style ) { - // Transform the media type based on request params and config - // The way that this relies on $wgRequest to propagate request params is slightly evil - $media = OutputPage::transformCssMedia( $media ); - - if ( $media === null ) { - // Skip - } elseif ( $media === '' || $media == 'all' ) { - // Don't output invalid or frivolous @media statements - $out .= "$style\n"; - } else { - $out .= "@media $media {\n" . str_replace( "\n", "\n\t", "\t" . $style ) . "\n}\n"; + private static function makeCombinedStyles( array $stylePairs ) { + $out = array(); + foreach ( $stylePairs as $media => $styles ) { + // ResourceLoaderFileModule::getStyle can return the styles + // as a string or an array of strings. This is to allow separation in + // the front-end. + $styles = (array) $styles; + foreach ( $styles as $style ) { + $style = trim( $style ); + // Don't output an empty "@media print { }" block (bug 40498) + if ( $style !== '' ) { + // Transform the media type based on request params and config + // The way that this relies on $wgRequest to propagate request params is slightly evil + $media = OutputPage::transformCssMedia( $media ); + + if ( $media === '' || $media == 'all' ) { + $out[] = $style; + } else if ( is_string( $media ) ) { + $out[] = "@media $media {\n" . str_replace( "\n", "\n\t", "\t" . $style ) . "}"; + } + // else: skip + } } } return $out; @@ -902,7 +943,7 @@ class ResourceLoader { public static function makeCustomLoaderScript( $name, $version, $dependencies, $group, $source, $script ) { $script = str_replace( "\n", "\n\t", trim( $script ) ); return Xml::encodeJsCall( - "( function( name, version, dependencies, group, source ) {\n\t$script\n} )", + "( function ( name, version, dependencies, group, source ) {\n\t$script\n} )", array( $name, $version, $dependencies, $group, $source ) ); } @@ -975,7 +1016,7 @@ class ResourceLoader { * @return string */ public static function makeLoaderConditionalScript( $script ) { - return "if(window.mw){\n".trim( $script )."\n}"; + return "if(window.mw){\n" . trim( $script ) . "\n}"; } /** @@ -1091,4 +1132,17 @@ class ResourceLoader { ksort( $query ); return $query; } + + /** + * Check a module name for validity. + * + * Module names may not contain pipes (|), commas (,) or exclamation marks (!) and can be + * at most 255 bytes. + * + * @param $moduleName string Module name to check + * @return bool Whether $moduleName is a valid module name + */ + public static function isValidModuleName( $moduleName ) { + return !preg_match( '/[|,!]/', $moduleName ) && strlen( $moduleName ) <= 255; + } } diff --git a/includes/resourceloader/ResourceLoaderContext.php b/includes/resourceloader/ResourceLoaderContext.php index dd69bb01..0e96c6c8 100644 --- a/includes/resourceloader/ResourceLoaderContext.php +++ b/includes/resourceloader/ResourceLoaderContext.php @@ -1,5 +1,7 @@ <?php /** + * Context for resource loader modules. + * * 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 @@ -39,6 +41,7 @@ class ResourceLoaderContext { protected $only; protected $version; protected $hash; + protected $raw; /* Methods */ @@ -62,6 +65,7 @@ class ResourceLoaderContext { $this->debug = $request->getFuzzyBool( 'debug', $wgResourceLoaderDebug ); $this->only = $request->getVal( 'only' ); $this->version = $request->getVal( 'version' ); + $this->raw = $request->getFuzzyBool( 'raw' ); $skinnames = Skin::getSkinNames(); // If no skin is specified, or we don't recognize the skin, use the default skin @@ -157,7 +161,7 @@ class ResourceLoaderContext { $this->direction = $this->request->getVal( 'dir' ); if ( !$this->direction ) { # directionality based on user language (see bug 6100) - $this->direction = Language::factory( $this->language )->getDir(); + $this->direction = Language::factory( $this->getLanguage() )->getDir(); } } return $this->direction; @@ -201,6 +205,13 @@ class ResourceLoaderContext { /** * @return bool */ + public function getRaw() { + return $this->raw; + } + + /** + * @return bool + */ public function shouldIncludeScripts() { return is_null( $this->only ) || $this->only === 'scripts'; } diff --git a/includes/resourceloader/ResourceLoaderFileModule.php b/includes/resourceloader/ResourceLoaderFileModule.php index 3d657e1c..d0c56ae8 100644 --- a/includes/resourceloader/ResourceLoaderFileModule.php +++ b/includes/resourceloader/ResourceLoaderFileModule.php @@ -1,5 +1,7 @@ <?php /** + * Resource loader module based on local JavaScript/CSS files. + * * 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 @@ -109,6 +111,8 @@ class ResourceLoaderFileModule extends ResourceLoaderModule { protected $position = 'bottom'; /** Boolean: Link to raw files in debug mode */ protected $debugRaw = true; + /** Boolean: Whether mw.loader.state() call should be omitted */ + protected $raw = false; /** * Array: Cache for mtime * @par Usage: @@ -238,6 +242,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule { break; // Single booleans case 'debugRaw': + case 'raw': $this->{$member} = (bool) $option; break; } @@ -366,6 +371,13 @@ class ResourceLoaderFileModule extends ResourceLoaderModule { } /** + * @return bool + */ + public function isRaw() { + return $this->raw; + } + + /** * Get the last modified timestamp of this module. * * Last modified timestamps are calculated from the highest last modified @@ -622,7 +634,8 @@ class ResourceLoaderFileModule extends ResourceLoaderModule { // Get and register local file references $this->localFileRefs = array_merge( $this->localFileRefs, - CSSMin::getLocalFileReferences( $style, $dir ) ); + CSSMin::getLocalFileReferences( $style, $dir ) + ); return CSSMin::remap( $style, $dir, $remoteDir, true ); diff --git a/includes/resourceloader/ResourceLoaderFilePageModule.php b/includes/resourceloader/ResourceLoaderFilePageModule.php index e3b719bb..61ed5206 100644 --- a/includes/resourceloader/ResourceLoaderFilePageModule.php +++ b/includes/resourceloader/ResourceLoaderFilePageModule.php @@ -1,5 +1,26 @@ <?php /** + * Resource loader module for MediaWiki:Filepage.css + * + * 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 + */ + +/** * ResourceLoader definition for MediaWiki:Filepage.css */ class ResourceLoaderFilePageModule extends ResourceLoaderWikiModule { diff --git a/includes/resourceloader/ResourceLoaderLanguageDataModule.php b/includes/resourceloader/ResourceLoaderLanguageDataModule.php new file mode 100644 index 00000000..c916c4a5 --- /dev/null +++ b/includes/resourceloader/ResourceLoaderLanguageDataModule.php @@ -0,0 +1,122 @@ +<?php +/** + * Resource loader module for populating language specific data. + * + * 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 + * @author Santhosh Thottingal + * @author Timo Tijhof + */ + +/** + * ResourceLoader module for populating language specific data. + */ +class ResourceLoaderLanguageDataModule extends ResourceLoaderModule { + + protected $language; + /** + * Get the grammar forms for the site content language. + * + * @return array + */ + protected function getSiteLangGrammarForms() { + return $this->language->getGrammarForms(); + } + + /** + * Get the plural forms for the site content language. + * + * @return array + */ + protected function getPluralRules() { + return $this->language->getPluralRules(); + } + + /** + * Get the digit transform table for the content language + * Seperator transform table also required here to convert + * the . and , sign to appropriate forms in content language. + * + * @return array + */ + protected function getDigitTransformTable() { + $digitTransformTable = $this->language->digitTransformTable(); + $separatorTransformTable = $this->language->separatorTransformTable(); + if ( $digitTransformTable ) { + array_merge( $digitTransformTable, (array)$separatorTransformTable ); + } else { + return $separatorTransformTable; + } + return $digitTransformTable; + } + + /** + * Get all the dynamic data for the content language to an array + * + * @return array + */ + protected function getData() { + return array( + 'digitTransformTable' => $this->getDigitTransformTable(), + 'grammarForms' => $this->getSiteLangGrammarForms(), + 'pluralRules' => $this->getPluralRules(), + ); + } + + /** + * @param $context ResourceLoaderContext + * @return string: JavaScript code + */ + public function getScript( ResourceLoaderContext $context ) { + $this->language = Language::factory( $context->getLanguage() ); + return Xml::encodeJsCall( 'mw.language.setData', array( + $this->language->getCode(), + $this->getData() + ) ); + } + + /** + * @param $context ResourceLoaderContext + * @return array|int|Mixed + */ + public function getModifiedTime( ResourceLoaderContext $context ) { + $this->language = Language::factory( $context ->getLanguage() ); + $cache = wfGetCache( CACHE_ANYTHING ); + $key = wfMemcKey( 'resourceloader', 'langdatamodule', 'changeinfo' ); + + $data = $this->getData(); + $hash = md5( serialize( $data ) ); + + $result = $cache->get( $key ); + if ( is_array( $result ) && $result['hash'] === $hash ) { + return $result['timestamp']; + } + $timestamp = wfTimestamp(); + $cache->set( $key, array( + 'hash' => $hash, + 'timestamp' => $timestamp, + ) ); + return $timestamp; + } + + /** + * @return array + */ + public function getDependencies() { + return array( 'mediawiki.language.init' ); + } +} diff --git a/includes/resourceloader/ResourceLoaderModule.php b/includes/resourceloader/ResourceLoaderModule.php index 1a232ec2..9c49c45f 100644 --- a/includes/resourceloader/ResourceLoaderModule.php +++ b/includes/resourceloader/ResourceLoaderModule.php @@ -1,5 +1,7 @@ <?php /** + * Abstraction for resource loader modules. + * * 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 @@ -170,7 +172,9 @@ abstract class ResourceLoaderModule { * Get all CSS for this module for a given skin. * * @param $context ResourceLoaderContext: Context object - * @return Array: List of CSS strings keyed by media type + * @return Array: List of CSS strings or array of CSS strings keyed by media type. + * like array( 'screen' => '.foo { width: 0 }' ); + * or array( 'screen' => array( '.foo { width: 0 }' ) ); */ public function getStyles( ResourceLoaderContext $context ) { // Stub, override expected @@ -235,8 +239,8 @@ abstract class ResourceLoaderModule { /** * Where on the HTML page should this module's JS be loaded? - * 'top': in the <head> - * 'bottom': at the bottom of the <body> + * - 'top': in the "<head>" + * - 'bottom': at the bottom of the "<body>" * * @return string */ @@ -245,6 +249,17 @@ abstract class ResourceLoaderModule { } /** + * Whether this module's JS expects to work without the client-side ResourceLoader module. + * Returning true from this function will prevent mw.loader.state() call from being + * appended to the bottom of the script. + * + * @return bool + */ + public function isRaw() { + return false; + } + + /** * Get the loader JS for this module, if set. * * @return Mixed: JavaScript loader code as a string or boolean false if no custom loader set diff --git a/includes/resourceloader/ResourceLoaderNoscriptModule.php b/includes/resourceloader/ResourceLoaderNoscriptModule.php index 28f629a2..8e81c8d9 100644 --- a/includes/resourceloader/ResourceLoaderNoscriptModule.php +++ b/includes/resourceloader/ResourceLoaderNoscriptModule.php @@ -1,5 +1,7 @@ <?php /** + * Resource loader for site customizations for users without JavaScript enabled. + * * 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 diff --git a/includes/resourceloader/ResourceLoaderSiteModule.php b/includes/resourceloader/ResourceLoaderSiteModule.php index 2527a0a3..03fe1fe5 100644 --- a/includes/resourceloader/ResourceLoaderSiteModule.php +++ b/includes/resourceloader/ResourceLoaderSiteModule.php @@ -1,5 +1,7 @@ <?php /** + * Resource loader module for site customizations. + * * 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 diff --git a/includes/resourceloader/ResourceLoaderStartUpModule.php b/includes/resourceloader/ResourceLoaderStartUpModule.php index 5dbce439..20ee83f9 100644 --- a/includes/resourceloader/ResourceLoaderStartUpModule.php +++ b/includes/resourceloader/ResourceLoaderStartUpModule.php @@ -1,5 +1,7 @@ <?php /** + * Module for resource loader initialization. + * * 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 @@ -35,8 +37,8 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule { protected function getConfig( $context ) { global $wgLoadScript, $wgScript, $wgStylePath, $wgScriptExtension, $wgArticlePath, $wgScriptPath, $wgServer, $wgContLang, - $wgVariantArticlePath, $wgActionPaths, $wgUseAjax, $wgVersion, - $wgEnableAPI, $wgEnableWriteAPI, $wgDBname, $wgEnableMWSuggest, + $wgVariantArticlePath, $wgActionPaths, $wgVersion, + $wgEnableAPI, $wgEnableWriteAPI, $wgDBname, $wgSitename, $wgFileExtensions, $wgExtensionAssetsPath, $wgCookiePrefix, $wgResourceLoaderMaxQueryLength; @@ -77,10 +79,7 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule { 'wgVersion' => $wgVersion, 'wgEnableAPI' => $wgEnableAPI, 'wgEnableWriteAPI' => $wgEnableWriteAPI, - 'wgDefaultDateFormat' => $wgContLang->getDefaultDateFormat(), - 'wgMonthNames' => $wgContLang->getMonthNamesArray(), - 'wgMonthNamesShort' => $wgContLang->getMonthAbbreviationsArray(), - 'wgMainPageTitle' => $mainPage ? $mainPage->getPrefixedText() : null, + 'wgMainPageTitle' => $mainPage->getPrefixedText(), 'wgFormattedNamespaces' => $wgContLang->getFormattedNamespaces(), 'wgNamespaceIds' => $namespaceIds, 'wgSiteName' => $wgSitename, @@ -96,9 +95,6 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule { 'wgResourceLoaderMaxQueryLength' => $wgResourceLoaderMaxQueryLength, 'wgCaseSensitiveNamespaces' => $caseSensitiveNamespaces, ); - if ( $wgUseAjax && $wgEnableMWSuggest ) { - $vars['wgMWSuggestTemplate'] = SearchEngine::getMWSuggestTemplate(); - } wfRunHooks( 'ResourceLoaderGetConfigVars', array( &$vars ) ); @@ -125,12 +121,12 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule { // Register modules foreach ( $resourceLoader->getModuleNames() as $name ) { $module = $resourceLoader->getModule( $name ); + $deps = $module->getDependencies(); + $group = $module->getGroup(); + $source = $module->getSource(); // Support module loader scripts $loader = $module->getLoaderScript(); if ( $loader !== false ) { - $deps = $module->getDependencies(); - $group = $module->getGroup(); - $source = $module->getSource(); $version = wfTimestamp( TS_ISO_8601_BASIC, $module->getModifiedTime( $context ) ); $out .= ResourceLoader::makeCustomLoaderScript( $name, $version, $deps, $group, $source, $loader ); @@ -143,26 +139,23 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule { $mtime = max( $moduleMtime, wfTimestamp( TS_UNIX, $wgCacheEpoch ) ); // Modules without dependencies, a group or a foreign source pass two arguments (name, timestamp) to // mw.loader.register() - if ( !count( $module->getDependencies() && $module->getGroup() === null && $module->getSource() === 'local' ) ) { + if ( !count( $deps ) && $group === null && $source === 'local' ) { $registrations[] = array( $name, $mtime ); } // Modules with dependencies but no group or foreign source pass three arguments // (name, timestamp, dependencies) to mw.loader.register() - elseif ( $module->getGroup() === null && $module->getSource() === 'local' ) { - $registrations[] = array( - $name, $mtime, $module->getDependencies() ); + elseif ( $group === null && $source === 'local' ) { + $registrations[] = array( $name, $mtime, $deps ); } // Modules with a group but no foreign source pass four arguments (name, timestamp, dependencies, group) // to mw.loader.register() - elseif ( $module->getSource() === 'local' ) { - $registrations[] = array( - $name, $mtime, $module->getDependencies(), $module->getGroup() ); + elseif ( $source === 'local' ) { + $registrations[] = array( $name, $mtime, $deps, $group ); } // Modules with a foreign source pass five arguments (name, timestamp, dependencies, group, source) // to mw.loader.register() else { - $registrations[] = array( - $name, $mtime, $module->getDependencies(), $module->getGroup(), $module->getSource() ); + $registrations[] = array( $name, $mtime, $deps, $group, $source ); } } } @@ -175,6 +168,13 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule { /* Methods */ /** + * @return bool + */ + public function isRaw() { + return true; + } + + /** * @param $context ResourceLoaderContext * @return string */ @@ -185,19 +185,20 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule { if ( $context->getOnly() === 'scripts' ) { // The core modules: - $modules = array( 'jquery', 'mediawiki' ); - wfRunHooks( 'ResourceLoaderGetStartupModules', array( &$modules ) ); + $moduleNames = array( 'jquery', 'mediawiki' ); + wfRunHooks( 'ResourceLoaderGetStartupModules', array( &$moduleNames ) ); // Get the latest version + $loader = $context->getResourceLoader(); $version = 0; - foreach ( $modules as $moduleName ) { + foreach ( $moduleNames as $moduleName ) { $version = max( $version, - $context->getResourceLoader()->getModule( $moduleName )->getModifiedTime( $context ) + $loader->getModule( $moduleName )->getModifiedTime( $context ) ); } // Build load query for StartupModules $query = array( - 'modules' => ResourceLoader::makePackedModulesString( $modules ), + 'modules' => ResourceLoader::makePackedModulesString( $moduleNames ), 'only' => 'scripts', 'lang' => $context->getLanguage(), 'skin' => $context->getSkin(), @@ -210,6 +211,7 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule { // Startup function $configuration = $this->getConfig( $context ); $registrations = self::getModuleRegistrations( $context ); + $registrations = str_replace( "\n", "\n\t", trim( $registrations ) ); // fix indentation $out .= "var startUp = function() {\n" . "\tmw.config = new " . Xml::encodeJsCall( 'mw.Map', array( $wgLegacyJavaScriptGlobals ) ) . "\n" . "\t$registrations\n" . diff --git a/includes/resourceloader/ResourceLoaderUserCSSPrefsModule.php b/includes/resourceloader/ResourceLoaderUserCSSPrefsModule.php index 02693d3e..0e95d964 100644 --- a/includes/resourceloader/ResourceLoaderUserCSSPrefsModule.php +++ b/includes/resourceloader/ResourceLoaderUserCSSPrefsModule.php @@ -1,5 +1,7 @@ <?php /** + * Resource loader module for user preference customizations. + * * 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 @@ -69,13 +71,6 @@ class ResourceLoaderUserCSSPrefsModule extends ResourceLoaderModule { $rules[] = 'a:lang(ar), a:lang(ckb), a:lang(fa),a:lang(kk-arab), ' . 'a:lang(mzn), a:lang(ps), a:lang(ur) { text-decoration: none; }'; } - if ( $options['highlightbroken'] ) { - $rules[] = "a.new, #quickbar a.new { color: #ba0000; }\n"; - } else { - $rules[] = "a.new, #quickbar a.new, a.stub, #quickbar a.stub { color: inherit; }"; - $rules[] = "a.new:after, #quickbar a.new:after { content: '?'; color: #ba0000; }"; - $rules[] = "a.stub:after, #quickbar a.stub:after { content: '!'; color: #772233; }"; - } if ( $options['justify'] ) { $rules[] = "#article, #bodyContent, #mw_content { text-align: justify; }\n"; } @@ -86,7 +81,10 @@ class ResourceLoaderUserCSSPrefsModule extends ResourceLoaderModule { $rules[] = ".editsection { display: none; }\n"; } if ( $options['editfont'] !== 'default' ) { - $rules[] = "textarea { font-family: {$options['editfont']}; }\n"; + // Double-check that $options['editfont'] consists of safe characters only + if ( preg_match( '/^[a-zA-Z0-9_, -]+$/', $options['editfont'] ) ) { + $rules[] = "textarea { font-family: {$options['editfont']}; }\n"; + } } $style = implode( "\n", $rules ); if ( $this->getFlip( $context ) ) { diff --git a/includes/resourceloader/ResourceLoaderUserGroupsModule.php b/includes/resourceloader/ResourceLoaderUserGroupsModule.php index 733dfa04..1316f423 100644 --- a/includes/resourceloader/ResourceLoaderUserGroupsModule.php +++ b/includes/resourceloader/ResourceLoaderUserGroupsModule.php @@ -1,5 +1,7 @@ <?php /** + * Resource loader module for user customizations. + * * 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 @@ -31,21 +33,32 @@ class ResourceLoaderUserGroupsModule extends ResourceLoaderWikiModule { * @return array */ protected function getPages( ResourceLoaderContext $context ) { - if ( $context->getUser() ) { - $user = User::newFromName( $context->getUser() ); - if ( $user instanceof User ) { - $pages = array(); - foreach( $user->getEffectiveGroups() as $group ) { - if ( in_array( $group, array( '*', 'user' ) ) ) { - continue; - } - $pages["MediaWiki:Group-$group.js"] = array( 'type' => 'script' ); - $pages["MediaWiki:Group-$group.css"] = array( 'type' => 'style' ); - } - return $pages; + global $wgUser; + + $userName = $context->getUser(); + if ( $userName === null ) { + return array(); + } + + // Use $wgUser is possible; allows to skip a lot of code + if ( is_object( $wgUser ) && $wgUser->getName() == $userName ) { + $user = $wgUser; + } else { + $user = User::newFromName( $userName ); + if ( !$user instanceof User ) { + return array(); + } + } + + $pages = array(); + foreach( $user->getEffectiveGroups() as $group ) { + if ( in_array( $group, array( '*', 'user' ) ) ) { + continue; } + $pages["MediaWiki:Group-$group.js"] = array( 'type' => 'script' ); + $pages["MediaWiki:Group-$group.css"] = array( 'type' => 'style' ); } - return array(); + return $pages; } /* Methods */ diff --git a/includes/resourceloader/ResourceLoaderUserModule.php b/includes/resourceloader/ResourceLoaderUserModule.php index 338b6322..177302c5 100644 --- a/includes/resourceloader/ResourceLoaderUserModule.php +++ b/includes/resourceloader/ResourceLoaderUserModule.php @@ -1,5 +1,7 @@ <?php /** + * Resource loader module for user customizations. + * * 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 @@ -33,33 +35,40 @@ class ResourceLoaderUserModule extends ResourceLoaderWikiModule { * @return array */ protected function getPages( ResourceLoaderContext $context ) { - if ( $context->getUser() ) { - // Get the normalized title of the user's user page - $username = $context->getUser(); - $userpageTitle = Title::makeTitleSafe( NS_USER, $username ); - $userpage = $userpageTitle->getPrefixedDBkey(); // Needed so $excludepages works + $username = $context->getUser(); + + if ( $username === null ) { + return array(); + } + + // Get the normalized title of the user's user page + $userpageTitle = Title::makeTitleSafe( NS_USER, $username ); + + if ( !$userpageTitle instanceof Title ) { + return array(); + } + + $userpage = $userpageTitle->getPrefixedDBkey(); // Needed so $excludepages works - $pages = array( - "$userpage/common.js" => array( 'type' => 'script' ), - "$userpage/" . $context->getSkin() . '.js' => - array( 'type' => 'script' ), - "$userpage/common.css" => array( 'type' => 'style' ), - "$userpage/" . $context->getSkin() . '.css' => - array( 'type' => 'style' ), - ); + $pages = array( + "$userpage/common.js" => array( 'type' => 'script' ), + "$userpage/" . $context->getSkin() . '.js' => + array( 'type' => 'script' ), + "$userpage/common.css" => array( 'type' => 'style' ), + "$userpage/" . $context->getSkin() . '.css' => + array( 'type' => 'style' ), + ); - // Hack for bug 26283: if we're on a preview page for a CSS/JS page, - // we need to exclude that page from this module. In that case, the excludepage - // parameter will be set to the name of the page we need to exclude. - $excludepage = $context->getRequest()->getVal( 'excludepage' ); - if ( isset( $pages[$excludepage] ) ) { - // This works because $excludepage is generated with getPrefixedDBkey(), - // just like the keys in $pages[] above - unset( $pages[$excludepage] ); - } - return $pages; + // Hack for bug 26283: if we're on a preview page for a CSS/JS page, + // we need to exclude that page from this module. In that case, the excludepage + // parameter will be set to the name of the page we need to exclude. + $excludepage = $context->getRequest()->getVal( 'excludepage' ); + if ( isset( $pages[$excludepage] ) ) { + // This works because $excludepage is generated with getPrefixedDBkey(), + // just like the keys in $pages[] above + unset( $pages[$excludepage] ); } - return array(); + return $pages; } /* Methods */ diff --git a/includes/resourceloader/ResourceLoaderUserOptionsModule.php b/includes/resourceloader/ResourceLoaderUserOptionsModule.php index 7b162205..4624cbce 100644 --- a/includes/resourceloader/ResourceLoaderUserOptionsModule.php +++ b/includes/resourceloader/ResourceLoaderUserOptionsModule.php @@ -1,5 +1,7 @@ <?php /** + * Resource loader module for user preference customizations. + * * 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 @@ -42,7 +44,7 @@ class ResourceLoaderUserOptionsModule extends ResourceLoaderModule { if ( isset( $this->modifiedTime[$hash] ) ) { return $this->modifiedTime[$hash]; } - + global $wgUser; return $this->modifiedTime[$hash] = wfTimestamp( TS_UNIX, $wgUser->getTouched() ); } @@ -58,6 +60,13 @@ class ResourceLoaderUserOptionsModule extends ResourceLoaderModule { } /** + * @return bool + */ + public function supportsURLLoading() { + return false; + } + + /** * @return string */ public function getGroup() { diff --git a/includes/resourceloader/ResourceLoaderUserTokensModule.php b/includes/resourceloader/ResourceLoaderUserTokensModule.php index e1a52388..62d096a6 100644 --- a/includes/resourceloader/ResourceLoaderUserTokensModule.php +++ b/includes/resourceloader/ResourceLoaderUserTokensModule.php @@ -1,5 +1,7 @@ <?php /** + * Resource loader module for user tokens. + * * 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 @@ -55,6 +57,13 @@ class ResourceLoaderUserTokensModule extends ResourceLoaderModule { } /** + * @return bool + */ + public function supportsURLLoading() { + return false; + } + + /** * @return string */ public function getGroup() { diff --git a/includes/resourceloader/ResourceLoaderWikiModule.php b/includes/resourceloader/ResourceLoaderWikiModule.php index 91a51f89..ee8dd1e5 100644 --- a/includes/resourceloader/ResourceLoaderWikiModule.php +++ b/includes/resourceloader/ResourceLoaderWikiModule.php @@ -1,5 +1,7 @@ <?php /** + * Abstraction for resource loader modules which pull from wiki pages. + * * 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 @@ -20,8 +22,6 @@ * @author Roan Kattouw */ -defined( 'MEDIAWIKI' ) || die( 1 ); - /** * Abstraction for resource loader modules which pull from wiki pages * @@ -42,7 +42,6 @@ abstract class ResourceLoaderWikiModule extends ResourceLoaderModule { /* Abstract Protected Methods */ /** - * @abstract * @param $context ResourceLoaderContext */ abstract protected function getPages( ResourceLoaderContext $context ); @@ -69,14 +68,10 @@ abstract class ResourceLoaderWikiModule extends ResourceLoaderModule { * @return null|string */ protected function getContent( $title ) { - if ( $title->getNamespace() === NS_MEDIAWIKI ) { - $message = wfMessage( $title->getDBkey() )->inContentLanguage(); - return $message->exists() ? $message->plain() : ''; - } if ( !$title->isCssJsSubpage() && !$title->isCssOrJsPage() ) { return null; } - $revision = Revision::newFromTitle( $title ); + $revision = Revision::newFromTitle( $title, false, Revision::READ_NORMAL ); if ( !$revision ) { return null; } @@ -137,12 +132,12 @@ abstract class ResourceLoaderWikiModule extends ResourceLoaderModule { } $style = CSSMin::remap( $style, false, $wgScriptPath, true ); if ( !isset( $styles[$media] ) ) { - $styles[$media] = ''; + $styles[$media] = array(); } if ( strpos( $titleText, '*/' ) === false ) { - $styles[$media] .= "/* $titleText */\n"; + $style = "/* $titleText */\n" . $style; } - $styles[$media] .= $style . "\n"; + $styles[$media][] = $style; } return $styles; } @@ -181,7 +176,7 @@ abstract class ResourceLoaderWikiModule extends ResourceLoaderModule { // 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]; |