wiki.techinc.nl/includes/resourceloader/ResourceLoaderStartUpModule.php
Roan Kattouw f4f3107209 Fix the fixme on r88053: dependency handling was broken in debug mode in certain cases. More specifically, if A is a file module that depends on B, B is a wiki module that depends on C and C is a file module, the loading order is CBA (correct) in production mode but was BCA (wrong) in debug mode. Fixed this by URL-ifying scripts and styles for those modules in debug mode, as I said to on CR. What this means is that the initial debug=true request for a module will now always return arrays of URLs, never the JS or CSS itself. This was already the case for file modules (which returned arrays of URLs to the raw files), but not for other modules (which returned the JS and CSS itself). So for non-file modules, load.php?modules=foo&debug=true now returns some JS that instructs the loader to fetch the module's JS from load.php?modules=foo&debug=true&only=scripts and the CSS from ...&only=styles .
* Removed the magic behavior where ResourceLoaderModule::getScripts() and getStyles() could return an array of URLs where the documentation said they should return a JS/CSS string. Because I didn't restructure the calling code too much, the old magical behavior should still work.
* Instead, move this behavior to getScriptURLsForDebug() and getStyleURLsForDebug(). The default implementation constructs a single URL for a load.php request for the module with debug=true&only=scripts (or styles). The URL building code duplicates some things from OutputPage::makeResourceLoaderLink(), I'll clean that up later. ResourceLoaderFileModule overrides this method to return URLs to the raw files, using code that I removed from getScripts()/getStyles()
* Add ResourceLoaderModule::supportsURLLoading(), which returns true by default but may return false to indicate that a module does not support loading via a URL. This is needed to respect $this->debugRaw in ResourceLoaderFileModule (set to true for jquery and mediawiki), and obviously for the startup module as well, because we get bootstrapping problems otherwise (can't call mw.loader.implement() when the code for mw.loader isn't loaded yet)
2011-09-13 17:13:53 +00:00

285 lines
10 KiB
PHP

<?php
/**
* 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 Trevor Parscal
* @author Roan Kattouw
*/
class ResourceLoaderStartUpModule extends ResourceLoaderModule {
/* Protected Members */
protected $modifiedTime = array();
/* Protected Methods */
/**
* @param $context ResourceLoaderContext
* @return array
*/
protected function getConfig( $context ) {
global $wgLoadScript, $wgScript, $wgStylePath, $wgScriptExtension,
$wgArticlePath, $wgScriptPath, $wgServer, $wgContLang,
$wgVariantArticlePath, $wgActionPaths, $wgUseAjax, $wgVersion,
$wgEnableAPI, $wgEnableWriteAPI, $wgDBname, $wgEnableMWSuggest,
$wgSitename, $wgFileExtensions, $wgExtensionAssetsPath,
$wgCookiePrefix, $wgResourceLoaderMaxQueryLength, $wgLegacyJavaScriptGlobals;
// Pre-process information
$separatorTransTable = $wgContLang->separatorTransformTable();
$separatorTransTable = $separatorTransTable ? $separatorTransTable : array();
$compactSeparatorTransTable = array(
implode( "\t", array_keys( $separatorTransTable ) ),
implode( "\t", $separatorTransTable ),
);
$digitTransTable = $wgContLang->digitTransformTable();
$digitTransTable = $digitTransTable ? $digitTransTable : array();
$compactDigitTransTable = array(
implode( "\t", array_keys( $digitTransTable ) ),
implode( "\t", $digitTransTable ),
);
$mainPage = Title::newMainPage();
/**
* Namespace related preparation
* - wgNamespaceIds: Key-value pairs of all localized, canonical and aliases for namespaces.
* - wgCaseSensitiveNamespaces: Array of namespaces that are case-sensitive.
*/
$namespaceIds = $wgContLang->getNamespaceIds();
$caseSensitiveNamespaces = array();
foreach( MWNamespace::getCanonicalNamespaces() as $index => $name ) {
$namespaceIds[$wgContLang->lc( $name )] = $index;
if ( !MWNamespace::isCapitalized( $index ) ) {
$caseSensitiveNamespaces[] = $index;
}
}
// Build list of variables
$vars = array(
'wgLoadScript' => $wgLoadScript,
'debug' => $context->getDebug(),
'skin' => $context->getSkin(),
'stylepath' => $wgStylePath,
'wgUrlProtocols' => wfUrlProtocols(),
'wgArticlePath' => $wgArticlePath,
'wgScriptPath' => $wgScriptPath,
'wgScriptExtension' => $wgScriptExtension,
'wgScript' => $wgScript,
'wgVariantArticlePath' => $wgVariantArticlePath,
'wgActionPaths' => $wgActionPaths,
'wgServer' => $wgServer,
'wgUserLanguage' => $context->getLanguage(),
'wgContentLanguage' => $wgContLang->getCode(),
'wgVersion' => $wgVersion,
'wgEnableAPI' => $wgEnableAPI,
'wgEnableWriteAPI' => $wgEnableWriteAPI,
'wgDefaultDateFormat' => $wgContLang->getDefaultDateFormat(),
'wgMonthNames' => $wgContLang->getMonthNamesArray(),
'wgMonthNamesShort' => $wgContLang->getMonthAbbreviationsArray(),
'wgSeparatorTransformTable' => $compactSeparatorTransTable,
'wgDigitTransformTable' => $compactDigitTransTable,
'wgMainPageTitle' => $mainPage ? $mainPage->getPrefixedText() : null,
'wgFormattedNamespaces' => $wgContLang->getFormattedNamespaces(),
'wgNamespaceIds' => $namespaceIds,
'wgSiteName' => $wgSitename,
'wgFileExtensions' => array_values( $wgFileExtensions ),
'wgDBname' => $wgDBname,
// This sucks, it is only needed on Special:Upload, but I could
// not find a way to add vars only for a certain module
'wgFileCanRotate' => BitmapHandler::canRotate(),
'wgAvailableSkins' => Skin::getSkinNames(),
'wgExtensionAssetsPath' => $wgExtensionAssetsPath,
// MediaWiki sets cookies to have this prefix by default
'wgCookiePrefix' => $wgCookiePrefix,
'wgResourceLoaderMaxQueryLength' => $wgResourceLoaderMaxQueryLength,
'wgLegacyJavaScriptGlobals' => $wgLegacyJavaScriptGlobals,
'wgCaseSensitiveNamespaces' => $caseSensitiveNamespaces,
);
if ( $wgUseAjax && $wgEnableMWSuggest ) {
$vars['wgMWSuggestTemplate'] = SearchEngine::getMWSuggestTemplate();
}
wfRunHooks( 'ResourceLoaderGetConfigVars', array( &$vars ) );
return $vars;
}
/**
* Gets registration code for all modules
*
* @param $context ResourceLoaderContext object
* @return String: JavaScript code for registering all modules with the client loader
*/
public static function getModuleRegistrations( ResourceLoaderContext $context ) {
global $wgCacheEpoch;
wfProfileIn( __METHOD__ );
$out = '';
$registrations = array();
$resourceLoader = $context->getResourceLoader();
// Register sources
$out .= ResourceLoader::makeLoaderSourcesScript( $resourceLoader->getSources() );
// Register modules
foreach ( $resourceLoader->getModuleNames() as $name ) {
$module = $resourceLoader->getModule( $name );
// 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 );
}
// Automatically register module
else {
// getModifiedTime() is supposed to return a UNIX timestamp, but it doesn't always
// seem to do that, and custom implementations might forget. Coerce it to TS_UNIX
$moduleMtime = wfTimestamp( TS_UNIX, $module->getModifiedTime( $context ) );
$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' ) ) {
$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() );
}
// Modules with a group but no foreign source pass four arguments (name, timestamp, dependencies, group)
// to mw.loader.register()
else if ( $module->getSource() === 'local' ) {
$registrations[] = array(
$name, $mtime, $module->getDependencies(), $module->getGroup() );
}
// 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() );
}
}
}
$out .= ResourceLoader::makeLoaderRegisterScript( $registrations );
wfProfileOut( __METHOD__ );
return $out;
}
/* Methods */
/**
* @param $context ResourceLoaderContext
* @return string
*/
public function getScript( ResourceLoaderContext $context ) {
global $IP, $wgLoadScript, $wgLegacyJavaScriptGlobals;
$out = file_get_contents( "$IP/resources/startup.js" );
if ( $context->getOnly() === 'scripts' ) {
// The core modules:
$modules = array( 'jquery', 'mediawiki' );
wfRunHooks( 'ResourceLoaderGetStartupModules', array( &$modules ) );
// Get the latest version
$version = 0;
foreach ( $modules as $moduleName ) {
$version = max( $version,
$context->getResourceLoader()->getModule( $moduleName )->getModifiedTime( $context )
);
}
// Build load query for StartupModules
$query = array(
'modules' => ResourceLoader::makePackedModulesString( $modules ),
'only' => 'scripts',
'lang' => $context->getLanguage(),
'skin' => $context->getSkin(),
'debug' => $context->getDebug() ? 'true' : 'false',
'version' => wfTimestamp( TS_ISO_8601_BASIC, $version )
);
// Ensure uniform query order
ksort( $query );
// Startup function
$configuration = $this->getConfig( $context );
$registrations = self::getModuleRegistrations( $context );
$out .= "var startUp = function() {\n" .
"\tmw.config = new " . Xml::encodeJsCall( 'mw.Map', array( $wgLegacyJavaScriptGlobals ) ) . "\n" .
"\t$registrations\n" .
"\t" . Xml::encodeJsCall( 'mw.config.set', array( $configuration ) ) .
"};\n";
// Conditional script injection
$scriptTag = Html::linkedScript( $wgLoadScript . '?' . wfArrayToCGI( $query ) );
$out .= "if ( isCompatible() ) {\n" .
"\t" . Xml::encodeJsCall( 'document.write', array( $scriptTag ) ) .
"}\n" .
"delete isCompatible;";
}
return $out;
}
public function supportsURLLoading() {
return false;
}
/**
* @param $context ResourceLoaderContext
* @return array|mixed
*/
public function getModifiedTime( ResourceLoaderContext $context ) {
global $IP, $wgCacheEpoch;
$hash = $context->getHash();
if ( isset( $this->modifiedTime[$hash] ) ) {
return $this->modifiedTime[$hash];
}
// Call preloadModuleInfo() on ALL modules as we're about
// to call getModifiedTime() on all of them
$loader = $context->getResourceLoader();
$loader->preloadModuleInfo( $loader->getModuleNames(), $context );
$this->modifiedTime[$hash] = filemtime( "$IP/resources/startup.js" );
// ATTENTION!: Because of the line above, this is not going to cause
// infinite recursion - think carefully before making changes to this
// code!
$time = wfTimestamp( TS_UNIX, $wgCacheEpoch );
foreach ( $loader->getModuleNames() as $name ) {
$module = $loader->getModule( $name );
$time = max( $time, $module->getModifiedTime( $context ) );
}
return $this->modifiedTime[$hash] = $time;
}
/* Methods */
/**
* @return string
*/
public function getGroup() {
return 'startup';
}
}