2010-10-19 18:25:42 +00:00
|
|
|
<?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
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
2010-10-20 00:22:25 +00:00
|
|
|
* ResourceLoader module based on local JavaScript/CSS files.
|
2010-10-19 18:25:42 +00:00
|
|
|
*/
|
|
|
|
|
class ResourceLoaderFileModule extends ResourceLoaderModule {
|
2010-10-19 20:59:23 +00:00
|
|
|
|
2010-10-19 18:25:42 +00:00
|
|
|
/* Protected Members */
|
2010-10-19 23:02:15 +00:00
|
|
|
|
2010-11-05 18:33:50 +00:00
|
|
|
/** String: Local base path, see __construct() */
|
2010-10-27 22:22:10 +00:00
|
|
|
protected $localBasePath = '';
|
2010-11-05 18:33:50 +00:00
|
|
|
/** String: Remote base path, see __construct() */
|
2010-10-27 22:22:10 +00:00
|
|
|
protected $remoteBasePath = '';
|
2010-10-20 20:43:30 +00:00
|
|
|
/**
|
2010-11-05 18:33:50 +00:00
|
|
|
* Array: List of paths to JavaScript files to always include
|
|
|
|
|
* @example array( [file-path], [file-path], ... )
|
2010-10-20 20:43:30 +00:00
|
|
|
*/
|
2010-10-19 18:25:42 +00:00
|
|
|
protected $scripts = array();
|
2010-10-20 20:43:30 +00:00
|
|
|
/**
|
2010-11-05 18:33:50 +00:00
|
|
|
* Array: List of JavaScript files to include when using a specific language
|
|
|
|
|
* @example array( [language-code] => array( [file-path], [file-path], ... ), ... )
|
2010-10-20 20:43:30 +00:00
|
|
|
*/
|
2010-10-19 18:25:42 +00:00
|
|
|
protected $languageScripts = array();
|
2010-10-20 20:43:30 +00:00
|
|
|
/**
|
2010-11-05 18:33:50 +00:00
|
|
|
* Array: List of JavaScript files to include when using a specific skin
|
|
|
|
|
* @example array( [skin-name] => array( [file-path], [file-path], ... ), ... )
|
2010-10-20 20:43:30 +00:00
|
|
|
*/
|
2010-10-19 18:25:42 +00:00
|
|
|
protected $skinScripts = array();
|
2010-10-20 20:43:30 +00:00
|
|
|
/**
|
2010-11-05 18:33:50 +00:00
|
|
|
* Array: List of paths to JavaScript files to include in debug mode
|
|
|
|
|
* @example array( [skin-name] => array( [file-path], [file-path], ... ), ... )
|
2010-10-20 20:43:30 +00:00
|
|
|
*/
|
2010-10-19 22:48:52 +00:00
|
|
|
protected $debugScripts = array();
|
2010-10-20 20:43:30 +00:00
|
|
|
/**
|
2010-11-05 18:33:50 +00:00
|
|
|
* Array: List of paths to JavaScript files to include in the startup module
|
|
|
|
|
* @example array( [file-path], [file-path], ... )
|
2010-10-20 20:43:30 +00:00
|
|
|
*/
|
2010-10-19 22:48:52 +00:00
|
|
|
protected $loaderScripts = array();
|
2010-10-20 20:43:30 +00:00
|
|
|
/**
|
2010-11-05 18:33:50 +00:00
|
|
|
* Array: List of paths to CSS files to always include
|
|
|
|
|
* @example array( [file-path], [file-path], ... )
|
2010-10-20 20:43:30 +00:00
|
|
|
*/
|
2010-10-19 22:48:52 +00:00
|
|
|
protected $styles = array();
|
2010-10-20 20:43:30 +00:00
|
|
|
/**
|
2010-11-05 18:33:50 +00:00
|
|
|
* Array: List of paths to CSS files to include when using specific skins
|
|
|
|
|
* @example array( [file-path], [file-path], ... )
|
2010-10-20 20:43:30 +00:00
|
|
|
*/
|
2010-10-19 18:25:42 +00:00
|
|
|
protected $skinStyles = array();
|
2010-10-20 20:43:30 +00:00
|
|
|
/**
|
2010-11-05 18:33:50 +00:00
|
|
|
* Array: List of modules this module depends on
|
|
|
|
|
* @example array( [file-path], [file-path], ... )
|
2010-10-20 20:43:30 +00:00
|
|
|
*/
|
2010-10-19 22:48:52 +00:00
|
|
|
protected $dependencies = array();
|
2010-10-20 20:43:30 +00:00
|
|
|
/**
|
2010-11-05 18:33:50 +00:00
|
|
|
* Array: List of message keys used by this module
|
|
|
|
|
* @example array( [message-key], [message-key], ... )
|
2010-10-20 20:43:30 +00:00
|
|
|
*/
|
2010-10-26 20:17:32 +00:00
|
|
|
protected $messages = array();
|
2010-11-05 18:33:50 +00:00
|
|
|
/** String: Name of group to load this module in */
|
2010-10-19 22:48:52 +00:00
|
|
|
protected $group;
|
2011-04-07 12:07:25 +00:00
|
|
|
/** String: Position on the page to load this module at */
|
|
|
|
|
protected $position = 'bottom';
|
2010-11-05 18:33:50 +00:00
|
|
|
/** Boolean: Link to raw files in debug mode */
|
2010-10-21 01:03:46 +00:00
|
|
|
protected $debugRaw = true;
|
2010-10-20 20:43:30 +00:00
|
|
|
/**
|
2010-11-05 18:33:50 +00:00
|
|
|
* Array: Cache for mtime
|
|
|
|
|
* @example array( [hash] => [mtime], [hash] => [mtime], ... )
|
2010-10-20 20:43:30 +00:00
|
|
|
*/
|
2010-10-19 18:25:42 +00:00
|
|
|
protected $modifiedTime = array();
|
2010-10-27 19:59:10 +00:00
|
|
|
/**
|
2010-11-05 18:33:50 +00:00
|
|
|
* Array: Place where readStyleFile() tracks file dependencies
|
|
|
|
|
* @example array( [file-path], [file-path], ... )
|
2010-10-27 19:59:10 +00:00
|
|
|
*/
|
|
|
|
|
protected $localFileRefs = array();
|
2010-10-19 18:25:42 +00:00
|
|
|
|
|
|
|
|
/* Methods */
|
|
|
|
|
|
|
|
|
|
/**
|
2010-10-20 00:22:25 +00:00
|
|
|
* Constructs a new module from an options array.
|
2011-06-17 16:05:05 +00:00
|
|
|
*
|
2010-11-05 18:33:50 +00:00
|
|
|
* @param $options Array: List of options; if not given or empty, an empty module will be
|
|
|
|
|
* constructed
|
|
|
|
|
* @param $localBasePath String: Base path to prepend to all local paths in $options. Defaults
|
|
|
|
|
* to $IP
|
|
|
|
|
* @param $remoteBasePath String: Base path to prepend to all remote paths in $options. Defaults
|
|
|
|
|
* to $wgScriptPath
|
2011-03-02 20:28:32 +00:00
|
|
|
*
|
2011-06-17 16:05:05 +00:00
|
|
|
* Below is a description for the $options array:
|
2011-03-02 20:28:32 +00:00
|
|
|
* @code
|
2010-10-19 20:59:23 +00:00
|
|
|
* array(
|
* Made Resources.php return a pure-data array instead of an ugly mix of data and code. This allows the class code to be lazy-loaded with the autoloader, for a performance advantage especially on non-APC installs. And using the convention where if the class is omitted, ResourceLoaderFileModule is assumed, the registration code becomes shorter and simpler.
* Modified ResourceLoader to lazy-initialise module objects, for a further performance advantage.
* Deleted ResourceLoader::getModules(), provided getModuleNames() instead. Although the startup module needs this functionality, it's slow to generate, so to avoid misuse, it's better to provide a foolproof fast interface and let the startup module do the slow thing itself.
* Modified ResourceLoader::register() to optionally accept an info array instead of an object.
* Added $wgResourceModules, allowing extensions to efficiently define their own resource loader modules. The trouble with hooks is that they contain code, and code is slow. We've been through all this before with i18n. Hooks are useful as a performance tool only if you call them very rarely.
* Moved ResourceLoader settings to their own section in DefaultSettings.php
* Added options to ResourceLoaderFileModule equivalent to the $localBasePath and $remoteBasePath parameters, to allow it to be instantiated via the new array style. Also added remoteExtPath, which allows modules to be registered before $wgExtensionAssetsPath is known.
* Added OutputPage::getResourceLoader(), mostly for debugging.
* The time saving at the moment is about 5ms per request with no extensions, which is significant already with 6 load.php requests for a cold cache page view. This is a much more scalable interface; the relative saving will grow as more extensions are added which use this interface, especially for non-APC installs.
Although the interface is backwards compatible, extension updates will follow in a subsequent commit.
2010-11-19 10:41:06 +00:00
|
|
|
* // Base path to prepend to all local paths in $options. Defaults to $IP
|
|
|
|
|
* 'localBasePath' => [base path],
|
|
|
|
|
* // Base path to prepend to all remote paths in $options. Defaults to $wgScriptPath
|
2010-12-03 13:04:01 +00:00
|
|
|
* 'remoteBasePath' => [base path],
|
|
|
|
|
* // Equivalent of remoteBasePath, but relative to $wgExtensionAssetsPath
|
|
|
|
|
* 'remoteExtPath' => [base path],
|
2010-10-19 20:59:23 +00:00
|
|
|
* // Scripts to always include
|
|
|
|
|
* 'scripts' => [file path string or array of file path strings],
|
|
|
|
|
* // Scripts to include in specific language contexts
|
2010-10-19 18:25:42 +00:00
|
|
|
* 'languageScripts' => array(
|
2010-10-19 20:59:23 +00:00
|
|
|
* [language code] => [file path string or array of file path strings],
|
2010-10-19 18:25:42 +00:00
|
|
|
* ),
|
2010-10-19 20:59:23 +00:00
|
|
|
* // Scripts to include in specific skin contexts
|
|
|
|
|
* 'skinScripts' => array(
|
|
|
|
|
* [skin name] => [file path string or array of file path strings],
|
|
|
|
|
* ),
|
|
|
|
|
* // Scripts to include in debug contexts
|
|
|
|
|
* 'debugScripts' => [file path string or array of file path strings],
|
|
|
|
|
* // Scripts to include in the startup module
|
2010-10-19 22:48:52 +00:00
|
|
|
* 'loaderScripts' => [file path string or array of file path strings],
|
2010-10-19 20:59:23 +00:00
|
|
|
* // Modules which must be loaded before this module
|
|
|
|
|
* 'dependencies' => [modile name string or array of module name strings],
|
|
|
|
|
* // Styles to always load
|
|
|
|
|
* 'styles' => [file path string or array of file path strings],
|
|
|
|
|
* // Styles to include in specific skin contexts
|
2010-10-19 18:25:42 +00:00
|
|
|
* 'skinStyles' => array(
|
2010-10-19 20:59:23 +00:00
|
|
|
* [skin name] => [file path string or array of file path strings],
|
2010-10-19 18:25:42 +00:00
|
|
|
* ),
|
2010-10-19 20:59:23 +00:00
|
|
|
* // Messages to always load
|
|
|
|
|
* 'messages' => [array of message key strings],
|
|
|
|
|
* // Group which this module should be loaded together with
|
|
|
|
|
* 'group' => [group name string],
|
2011-04-07 12:07:25 +00:00
|
|
|
* // Position on the page to load this module at
|
|
|
|
|
* 'position' => ['bottom' (default) or 'top']
|
2010-10-19 18:25:42 +00:00
|
|
|
* )
|
2011-03-02 20:28:32 +00:00
|
|
|
* @endcode
|
2010-10-19 18:25:42 +00:00
|
|
|
*/
|
2011-06-17 16:05:05 +00:00
|
|
|
public function __construct( $options = array(), $localBasePath = null,
|
|
|
|
|
$remoteBasePath = null )
|
2010-11-05 06:53:14 +00:00
|
|
|
{
|
2010-10-27 22:22:10 +00:00
|
|
|
global $IP, $wgScriptPath;
|
|
|
|
|
$this->localBasePath = $localBasePath === null ? $IP : $localBasePath;
|
|
|
|
|
$this->remoteBasePath = $remoteBasePath === null ? $wgScriptPath : $remoteBasePath;
|
* Made Resources.php return a pure-data array instead of an ugly mix of data and code. This allows the class code to be lazy-loaded with the autoloader, for a performance advantage especially on non-APC installs. And using the convention where if the class is omitted, ResourceLoaderFileModule is assumed, the registration code becomes shorter and simpler.
* Modified ResourceLoader to lazy-initialise module objects, for a further performance advantage.
* Deleted ResourceLoader::getModules(), provided getModuleNames() instead. Although the startup module needs this functionality, it's slow to generate, so to avoid misuse, it's better to provide a foolproof fast interface and let the startup module do the slow thing itself.
* Modified ResourceLoader::register() to optionally accept an info array instead of an object.
* Added $wgResourceModules, allowing extensions to efficiently define their own resource loader modules. The trouble with hooks is that they contain code, and code is slow. We've been through all this before with i18n. Hooks are useful as a performance tool only if you call them very rarely.
* Moved ResourceLoader settings to their own section in DefaultSettings.php
* Added options to ResourceLoaderFileModule equivalent to the $localBasePath and $remoteBasePath parameters, to allow it to be instantiated via the new array style. Also added remoteExtPath, which allows modules to be registered before $wgExtensionAssetsPath is known.
* Added OutputPage::getResourceLoader(), mostly for debugging.
* The time saving at the moment is about 5ms per request with no extensions, which is significant already with 6 load.php requests for a cold cache page view. This is a much more scalable interface; the relative saving will grow as more extensions are added which use this interface, especially for non-APC installs.
Although the interface is backwards compatible, extension updates will follow in a subsequent commit.
2010-11-19 10:41:06 +00:00
|
|
|
|
|
|
|
|
if ( isset( $options['remoteExtPath'] ) ) {
|
|
|
|
|
global $wgExtensionAssetsPath;
|
|
|
|
|
$this->remoteBasePath = $wgExtensionAssetsPath . '/' . $options['remoteExtPath'];
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-19 20:59:23 +00:00
|
|
|
foreach ( $options as $member => $option ) {
|
|
|
|
|
switch ( $member ) {
|
|
|
|
|
// Lists of file paths
|
2010-10-19 18:25:42 +00:00
|
|
|
case 'scripts':
|
|
|
|
|
case 'debugScripts':
|
2010-10-19 22:48:52 +00:00
|
|
|
case 'loaderScripts':
|
2010-10-19 18:25:42 +00:00
|
|
|
case 'styles':
|
2010-10-27 22:22:10 +00:00
|
|
|
$this->{$member} = (array) $option;
|
2010-10-19 20:59:23 +00:00
|
|
|
break;
|
|
|
|
|
// Collated lists of file paths
|
|
|
|
|
case 'languageScripts':
|
|
|
|
|
case 'skinScripts':
|
2010-10-19 18:25:42 +00:00
|
|
|
case 'skinStyles':
|
2010-10-21 22:34:26 +00:00
|
|
|
if ( !is_array( $option ) ) {
|
|
|
|
|
throw new MWException(
|
2011-06-17 16:05:05 +00:00
|
|
|
"Invalid collated file path list error. " .
|
2010-11-05 06:53:14 +00:00
|
|
|
"'$option' given, array expected."
|
2010-10-21 22:34:26 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
foreach ( $option as $key => $value ) {
|
|
|
|
|
if ( !is_string( $key ) ) {
|
|
|
|
|
throw new MWException(
|
2011-06-17 16:05:05 +00:00
|
|
|
"Invalid collated file path list key error. " .
|
2010-11-05 06:53:14 +00:00
|
|
|
"'$key' given, string expected."
|
2010-10-21 22:34:26 +00:00
|
|
|
);
|
|
|
|
|
}
|
2010-10-27 22:22:10 +00:00
|
|
|
$this->{$member}[$key] = (array) $value;
|
2010-10-19 18:25:42 +00:00
|
|
|
}
|
2010-10-21 22:34:26 +00:00
|
|
|
break;
|
2010-10-19 20:59:23 +00:00
|
|
|
// Lists of strings
|
2010-10-19 18:25:42 +00:00
|
|
|
case 'dependencies':
|
|
|
|
|
case 'messages':
|
2010-10-19 20:59:23 +00:00
|
|
|
$this->{$member} = (array) $option;
|
2010-10-19 18:25:42 +00:00
|
|
|
break;
|
2010-10-19 20:59:23 +00:00
|
|
|
// Single strings
|
2010-10-19 18:25:42 +00:00
|
|
|
case 'group':
|
2011-04-07 12:07:25 +00:00
|
|
|
case 'position':
|
* Made Resources.php return a pure-data array instead of an ugly mix of data and code. This allows the class code to be lazy-loaded with the autoloader, for a performance advantage especially on non-APC installs. And using the convention where if the class is omitted, ResourceLoaderFileModule is assumed, the registration code becomes shorter and simpler.
* Modified ResourceLoader to lazy-initialise module objects, for a further performance advantage.
* Deleted ResourceLoader::getModules(), provided getModuleNames() instead. Although the startup module needs this functionality, it's slow to generate, so to avoid misuse, it's better to provide a foolproof fast interface and let the startup module do the slow thing itself.
* Modified ResourceLoader::register() to optionally accept an info array instead of an object.
* Added $wgResourceModules, allowing extensions to efficiently define their own resource loader modules. The trouble with hooks is that they contain code, and code is slow. We've been through all this before with i18n. Hooks are useful as a performance tool only if you call them very rarely.
* Moved ResourceLoader settings to their own section in DefaultSettings.php
* Added options to ResourceLoaderFileModule equivalent to the $localBasePath and $remoteBasePath parameters, to allow it to be instantiated via the new array style. Also added remoteExtPath, which allows modules to be registered before $wgExtensionAssetsPath is known.
* Added OutputPage::getResourceLoader(), mostly for debugging.
* The time saving at the moment is about 5ms per request with no extensions, which is significant already with 6 load.php requests for a cold cache page view. This is a much more scalable interface; the relative saving will grow as more extensions are added which use this interface, especially for non-APC installs.
Although the interface is backwards compatible, extension updates will follow in a subsequent commit.
2010-11-19 10:41:06 +00:00
|
|
|
case 'localBasePath':
|
|
|
|
|
case 'remoteBasePath':
|
2010-10-21 01:03:46 +00:00
|
|
|
$this->{$member} = (string) $option;
|
|
|
|
|
break;
|
|
|
|
|
// Single booleans
|
|
|
|
|
case 'debugRaw':
|
|
|
|
|
$this->{$member} = (bool) $option;
|
2010-10-19 18:25:42 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-02-12 23:20:32 +00:00
|
|
|
// Make sure the remote base path is a complete valid url
|
|
|
|
|
$this->remoteBasePath = wfExpandUrl( $this->remoteBasePath );
|
2010-10-19 18:25:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2010-10-20 00:22:25 +00:00
|
|
|
* Gets all scripts for a given context concatenated together.
|
2011-06-17 16:05:05 +00:00
|
|
|
*
|
2010-11-05 18:33:50 +00:00
|
|
|
* @param $context ResourceLoaderContext: Context in which to generate script
|
|
|
|
|
* @return String: JavaScript code for $context
|
2010-10-19 18:25:42 +00:00
|
|
|
*/
|
2010-10-19 22:48:52 +00:00
|
|
|
public function getScript( ResourceLoaderContext $context ) {
|
2011-05-14 12:15:58 +00:00
|
|
|
$files = $this->getScriptFiles( $context );
|
|
|
|
|
if ( $context->getDebug() && $this->debugRaw ) {
|
|
|
|
|
$urls = array();
|
|
|
|
|
foreach ( $this->getScriptFiles( $context ) as $file ) {
|
|
|
|
|
$urls[] = $this->getRemotePath( $file );
|
2010-10-21 01:03:46 +00:00
|
|
|
}
|
2011-05-14 12:15:58 +00:00
|
|
|
return $urls;
|
2010-10-19 22:48:52 +00:00
|
|
|
}
|
2010-10-27 22:22:10 +00:00
|
|
|
return $this->readScriptFiles( $files );
|
2010-10-19 18:25:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2010-10-20 00:22:25 +00:00
|
|
|
* Gets loader script.
|
2011-06-17 16:05:05 +00:00
|
|
|
*
|
2010-11-05 18:33:50 +00:00
|
|
|
* @return String: JavaScript code to be added to startup module
|
2010-10-19 18:25:42 +00:00
|
|
|
*/
|
2010-10-19 22:48:52 +00:00
|
|
|
public function getLoaderScript() {
|
2010-10-19 23:00:52 +00:00
|
|
|
if ( count( $this->loaderScripts ) == 0 ) {
|
2010-10-19 22:48:52 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
2010-10-27 22:22:10 +00:00
|
|
|
return $this->readScriptFiles( $this->loaderScripts );
|
2010-10-19 18:25:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2010-10-20 00:22:25 +00:00
|
|
|
* Gets all styles for a given context concatenated together.
|
2011-06-17 16:05:05 +00:00
|
|
|
*
|
2010-11-05 18:33:50 +00:00
|
|
|
* @param $context ResourceLoaderContext: Context in which to generate styles
|
|
|
|
|
* @return String: CSS code for $context
|
2010-10-19 18:25:42 +00:00
|
|
|
*/
|
|
|
|
|
public function getStyles( ResourceLoaderContext $context ) {
|
2011-05-14 12:15:58 +00:00
|
|
|
$styles = $this->readStyleFiles(
|
|
|
|
|
$this->getStyleFiles( $context ),
|
2010-12-16 19:31:48 +00:00
|
|
|
$this->getFlip( $context )
|
|
|
|
|
);
|
2011-05-14 12:15:58 +00:00
|
|
|
if ( !$context->getOnly() && $context->getDebug() && $this->debugRaw ) {
|
|
|
|
|
$urls = array();
|
|
|
|
|
foreach ( $this->getStyleFiles( $context ) as $mediaType => $list ) {
|
|
|
|
|
$urls[$mediaType] = array();
|
|
|
|
|
foreach ( $list as $file ) {
|
|
|
|
|
$urls[$mediaType][] = $this->getRemotePath( $file );
|
|
|
|
|
}
|
2010-10-19 18:25:42 +00:00
|
|
|
}
|
2011-05-14 12:15:58 +00:00
|
|
|
return $urls;
|
2010-10-19 18:25:42 +00:00
|
|
|
}
|
|
|
|
|
// Collect referenced files
|
2010-10-27 19:59:10 +00:00
|
|
|
$this->localFileRefs = array_unique( $this->localFileRefs );
|
2010-10-19 22:48:52 +00:00
|
|
|
// If the list has been modified since last time we cached it, update the cache
|
2010-10-27 19:59:10 +00:00
|
|
|
if ( $this->localFileRefs !== $this->getFileDependencies( $context->getSkin() ) ) {
|
2010-10-19 18:25:42 +00:00
|
|
|
$dbw = wfGetDB( DB_MASTER );
|
|
|
|
|
$dbw->replace( 'module_deps',
|
|
|
|
|
array( array( 'md_module', 'md_skin' ) ), array(
|
|
|
|
|
'md_module' => $this->getName(),
|
|
|
|
|
'md_skin' => $context->getSkin(),
|
2010-10-27 19:59:10 +00:00
|
|
|
'md_deps' => FormatJson::encode( $this->localFileRefs ),
|
2010-10-19 18:25:42 +00:00
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return $styles;
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-19 22:48:52 +00:00
|
|
|
/**
|
2010-10-20 00:22:25 +00:00
|
|
|
* Gets list of message keys used by this module.
|
2011-06-17 16:05:05 +00:00
|
|
|
*
|
2010-11-05 18:33:50 +00:00
|
|
|
* @return Array: List of message keys
|
2010-10-19 22:48:52 +00:00
|
|
|
*/
|
2010-10-19 18:25:42 +00:00
|
|
|
public function getMessages() {
|
|
|
|
|
return $this->messages;
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-19 22:48:52 +00:00
|
|
|
/**
|
2010-10-20 00:22:25 +00:00
|
|
|
* Gets the name of the group this module should be loaded in.
|
2011-06-17 16:05:05 +00:00
|
|
|
*
|
2010-11-05 18:33:50 +00:00
|
|
|
* @return String: Group name
|
2010-10-19 22:48:52 +00:00
|
|
|
*/
|
2010-10-19 18:25:42 +00:00
|
|
|
public function getGroup() {
|
|
|
|
|
return $this->group;
|
|
|
|
|
}
|
2011-05-21 17:45:20 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return string
|
|
|
|
|
*/
|
2011-04-07 12:07:25 +00:00
|
|
|
public function getPosition() {
|
|
|
|
|
return $this->position;
|
|
|
|
|
}
|
2010-10-19 18:25:42 +00:00
|
|
|
|
2010-10-19 22:48:52 +00:00
|
|
|
/**
|
2010-10-20 00:22:25 +00:00
|
|
|
* Gets list of names of modules this module depends on.
|
2011-06-17 16:05:05 +00:00
|
|
|
*
|
2010-11-05 18:33:50 +00:00
|
|
|
* @return Array: List of module names
|
2010-10-19 22:48:52 +00:00
|
|
|
*/
|
2010-10-19 18:25:42 +00:00
|
|
|
public function getDependencies() {
|
|
|
|
|
return $this->dependencies;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2010-10-20 00:22:25 +00:00
|
|
|
* Get the last modified timestamp of this module.
|
2011-06-17 16:05:05 +00:00
|
|
|
*
|
|
|
|
|
* Last modified timestamps are calculated from the highest last modified
|
|
|
|
|
* timestamp of this module's constituent files as well as the files it
|
|
|
|
|
* depends on. This function is context-sensitive, only performing
|
|
|
|
|
* calculations on files relevant to the given language, skin and debug
|
2010-11-05 06:53:14 +00:00
|
|
|
* mode.
|
2011-06-17 16:05:05 +00:00
|
|
|
*
|
|
|
|
|
* @param $context ResourceLoaderContext: Context in which to calculate
|
2010-11-05 06:53:14 +00:00
|
|
|
* the modified time
|
2010-11-05 18:33:50 +00:00
|
|
|
* @return Integer: UNIX timestamp
|
|
|
|
|
* @see ResourceLoaderModule::getFileDependencies
|
2010-10-19 18:25:42 +00:00
|
|
|
*/
|
|
|
|
|
public function getModifiedTime( ResourceLoaderContext $context ) {
|
|
|
|
|
if ( isset( $this->modifiedTime[$context->getHash()] ) ) {
|
|
|
|
|
return $this->modifiedTime[$context->getHash()];
|
|
|
|
|
}
|
|
|
|
|
wfProfileIn( __METHOD__ );
|
2011-06-17 16:05:05 +00:00
|
|
|
|
2010-10-19 23:47:56 +00:00
|
|
|
$files = array();
|
2011-06-17 16:05:05 +00:00
|
|
|
|
2010-10-19 23:47:56 +00:00
|
|
|
// Flatten style files into $files
|
|
|
|
|
$styles = self::collateFilePathListByOption( $this->styles, 'media', 'all' );
|
|
|
|
|
foreach ( $styles as $styleFiles ) {
|
|
|
|
|
$files = array_merge( $files, $styleFiles );
|
2010-10-19 18:25:42 +00:00
|
|
|
}
|
2010-10-19 22:48:52 +00:00
|
|
|
$skinFiles = self::tryForKey(
|
2011-06-17 16:05:05 +00:00
|
|
|
self::collateFilePathListByOption( $this->skinStyles, 'media', 'all' ),
|
|
|
|
|
$context->getSkin(),
|
2010-11-05 06:53:14 +00:00
|
|
|
'default'
|
2010-10-19 18:25:42 +00:00
|
|
|
);
|
|
|
|
|
foreach ( $skinFiles as $styleFiles ) {
|
2010-10-19 23:47:56 +00:00
|
|
|
$files = array_merge( $files, $styleFiles );
|
2010-10-19 18:25:42 +00:00
|
|
|
}
|
2011-06-17 16:05:05 +00:00
|
|
|
|
2010-10-19 18:25:42 +00:00
|
|
|
// Final merge, this should result in a master list of dependent files
|
|
|
|
|
$files = array_merge(
|
2010-10-19 23:47:56 +00:00
|
|
|
$files,
|
2010-10-19 18:25:42 +00:00
|
|
|
$this->scripts,
|
|
|
|
|
$context->getDebug() ? $this->debugScripts : array(),
|
2010-10-19 22:48:52 +00:00
|
|
|
self::tryForKey( $this->languageScripts, $context->getLanguage() ),
|
|
|
|
|
self::tryForKey( $this->skinScripts, $context->getSkin(), 'default' ),
|
2010-10-27 22:22:10 +00:00
|
|
|
$this->loaderScripts
|
2010-10-19 18:25:42 +00:00
|
|
|
);
|
2010-10-27 22:22:10 +00:00
|
|
|
$files = array_map( array( $this, 'getLocalPath' ), $files );
|
|
|
|
|
// File deps need to be treated separately because they're already prefixed
|
|
|
|
|
$files = array_merge( $files, $this->getFileDependencies( $context->getSkin() ) );
|
2011-06-17 16:05:05 +00:00
|
|
|
|
|
|
|
|
// If a module is nothing but a list of dependencies, we need to avoid
|
2010-11-05 06:53:14 +00:00
|
|
|
// giving max() an empty array
|
2010-10-20 20:43:30 +00:00
|
|
|
if ( count( $files ) === 0 ) {
|
2011-02-10 16:39:53 +00:00
|
|
|
wfProfileOut( __METHOD__ );
|
2010-10-20 20:43:30 +00:00
|
|
|
return $this->modifiedTime[$context->getHash()] = 1;
|
|
|
|
|
}
|
2011-06-17 16:05:05 +00:00
|
|
|
|
2010-10-19 18:25:42 +00:00
|
|
|
wfProfileIn( __METHOD__.'-filemtime' );
|
2010-10-27 22:22:10 +00:00
|
|
|
$filesMtime = max( array_map( 'filemtime', $files ) );
|
2010-10-19 18:25:42 +00:00
|
|
|
wfProfileOut( __METHOD__.'-filemtime' );
|
2011-06-17 16:05:05 +00:00
|
|
|
$this->modifiedTime[$context->getHash()] = max(
|
|
|
|
|
$filesMtime,
|
2010-11-05 06:53:14 +00:00
|
|
|
$this->getMsgBlobMtime( $context->getLanguage() ) );
|
2011-02-10 16:39:53 +00:00
|
|
|
|
2010-10-19 18:25:42 +00:00
|
|
|
wfProfileOut( __METHOD__ );
|
|
|
|
|
return $this->modifiedTime[$context->getHash()];
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-14 12:15:58 +00:00
|
|
|
/* Protected Methods */
|
2010-10-19 18:25:42 +00:00
|
|
|
|
2011-05-21 17:45:20 +00:00
|
|
|
/**
|
|
|
|
|
* @param $path string
|
|
|
|
|
* @return string
|
|
|
|
|
*/
|
2010-10-27 22:22:10 +00:00
|
|
|
protected function getLocalPath( $path ) {
|
|
|
|
|
return "{$this->localBasePath}/$path";
|
|
|
|
|
}
|
2011-05-21 17:45:20 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $path string
|
|
|
|
|
* @return string
|
|
|
|
|
*/
|
2010-10-27 22:22:10 +00:00
|
|
|
protected function getRemotePath( $path ) {
|
|
|
|
|
return "{$this->remoteBasePath}/$path";
|
2010-10-19 20:59:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2010-10-20 00:22:25 +00:00
|
|
|
* Collates file paths by option (where provided).
|
2011-06-17 16:05:05 +00:00
|
|
|
*
|
|
|
|
|
* @param $list Array: List of file paths in any combination of index/path
|
2010-11-05 06:53:14 +00:00
|
|
|
* or path/options pairs
|
2010-11-08 10:19:28 +00:00
|
|
|
* @param $option String: option name
|
|
|
|
|
* @param $default Mixed: default value if the option isn't set
|
2010-11-05 18:33:50 +00:00
|
|
|
* @return Array: List of file paths, collated by $option
|
2010-10-19 20:59:23 +00:00
|
|
|
*/
|
|
|
|
|
protected static function collateFilePathListByOption( array $list, $option, $default ) {
|
|
|
|
|
$collatedFiles = array();
|
|
|
|
|
foreach ( (array) $list as $key => $value ) {
|
|
|
|
|
if ( is_int( $key ) ) {
|
|
|
|
|
// File name as the value
|
|
|
|
|
if ( !isset( $collatedFiles[$default] ) ) {
|
|
|
|
|
$collatedFiles[$default] = array();
|
|
|
|
|
}
|
|
|
|
|
$collatedFiles[$default][] = $value;
|
2011-06-17 16:05:05 +00:00
|
|
|
} elseif ( is_array( $value ) ) {
|
2010-10-19 20:59:23 +00:00
|
|
|
// File name as the key, options array as the value
|
2010-10-20 20:43:30 +00:00
|
|
|
$optionValue = isset( $value[$option] ) ? $value[$option] : $default;
|
|
|
|
|
if ( !isset( $collatedFiles[$optionValue] ) ) {
|
|
|
|
|
$collatedFiles[$optionValue] = array();
|
2010-10-19 20:59:23 +00:00
|
|
|
}
|
2010-10-20 20:43:30 +00:00
|
|
|
$collatedFiles[$optionValue][] = $key;
|
2010-10-19 20:59:23 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return $collatedFiles;
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-19 18:25:42 +00:00
|
|
|
/**
|
2010-10-20 00:22:25 +00:00
|
|
|
* Gets a list of element that match a key, optionally using a fallback key.
|
2011-06-17 16:05:05 +00:00
|
|
|
*
|
2010-11-05 18:33:50 +00:00
|
|
|
* @param $list Array: List of lists to select from
|
|
|
|
|
* @param $key String: Key to look for in $map
|
|
|
|
|
* @param $fallback String: Key to look for in $list if $key doesn't exist
|
2011-06-17 16:05:05 +00:00
|
|
|
* @return Array: List of elements from $map which matched $key or $fallback,
|
2010-11-05 06:53:14 +00:00
|
|
|
* or an empty list in case of no match
|
2010-10-19 18:25:42 +00:00
|
|
|
*/
|
2010-10-20 20:56:33 +00:00
|
|
|
protected static function tryForKey( array $list, $key, $fallback = null ) {
|
2010-10-19 22:48:52 +00:00
|
|
|
if ( isset( $list[$key] ) && is_array( $list[$key] ) ) {
|
2010-10-20 20:56:33 +00:00
|
|
|
return $list[$key];
|
2011-06-17 16:05:05 +00:00
|
|
|
} elseif ( is_string( $fallback )
|
|
|
|
|
&& isset( $list[$fallback] )
|
|
|
|
|
&& is_array( $list[$fallback] ) )
|
2010-11-05 06:53:14 +00:00
|
|
|
{
|
2010-10-20 20:56:33 +00:00
|
|
|
return $list[$fallback];
|
2010-10-19 22:48:52 +00:00
|
|
|
}
|
|
|
|
|
return array();
|
2010-10-19 18:25:42 +00:00
|
|
|
}
|
|
|
|
|
|
2011-05-14 12:15:58 +00:00
|
|
|
/**
|
|
|
|
|
* Gets a list of file paths for all scripts in this module, in order of propper execution.
|
2011-06-17 16:05:05 +00:00
|
|
|
*
|
2011-05-14 12:15:58 +00:00
|
|
|
* @param $context ResourceLoaderContext: Context
|
|
|
|
|
* @return Array: List of file paths
|
|
|
|
|
*/
|
|
|
|
|
protected function getScriptFiles( ResourceLoaderContext $context ) {
|
|
|
|
|
$files = array_merge(
|
|
|
|
|
$this->scripts,
|
|
|
|
|
self::tryForKey( $this->languageScripts, $context->getLanguage() ),
|
|
|
|
|
self::tryForKey( $this->skinScripts, $context->getSkin(), 'default' )
|
|
|
|
|
);
|
|
|
|
|
if ( $context->getDebug() ) {
|
|
|
|
|
$files = array_merge( $files, $this->debugScripts );
|
|
|
|
|
}
|
|
|
|
|
return $files;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Gets a list of file paths for all styles in this module, in order of propper inclusion.
|
2011-06-17 16:05:05 +00:00
|
|
|
*
|
2011-05-14 12:15:58 +00:00
|
|
|
* @param $context ResourceLoaderContext: Context
|
|
|
|
|
* @return Array: List of file paths
|
|
|
|
|
*/
|
|
|
|
|
protected function getStyleFiles( ResourceLoaderContext $context ) {
|
|
|
|
|
return array_merge_recursive(
|
|
|
|
|
self::collateFilePathListByOption( $this->styles, 'media', 'all' ),
|
|
|
|
|
self::collateFilePathListByOption(
|
|
|
|
|
self::tryForKey( $this->skinStyles, $context->getSkin(), 'default' ), 'media', 'all'
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-19 18:25:42 +00:00
|
|
|
/**
|
2010-10-20 00:22:25 +00:00
|
|
|
* Gets the contents of a list of JavaScript files.
|
2011-06-17 16:05:05 +00:00
|
|
|
*
|
2010-11-05 18:33:50 +00:00
|
|
|
* @param $scripts Array: List of file paths to scripts to read, remap and concetenate
|
|
|
|
|
* @return String: Concatenated and remapped JavaScript data from $scripts
|
2010-10-19 18:25:42 +00:00
|
|
|
*/
|
2010-10-27 22:22:10 +00:00
|
|
|
protected function readScriptFiles( array $scripts ) {
|
2011-07-11 21:39:06 +00:00
|
|
|
global $wgResourceLoaderValidateStaticJS;
|
2010-10-19 22:48:52 +00:00
|
|
|
if ( empty( $scripts ) ) {
|
2010-10-19 18:25:42 +00:00
|
|
|
return '';
|
|
|
|
|
}
|
2010-11-05 06:53:14 +00:00
|
|
|
$js = '';
|
|
|
|
|
foreach ( array_unique( $scripts ) as $fileName ) {
|
|
|
|
|
$localPath = $this->getLocalPath( $fileName );
|
|
|
|
|
$contents = file_get_contents( $localPath );
|
|
|
|
|
if ( $contents === false ) {
|
|
|
|
|
throw new MWException( __METHOD__.": script file not found: \"$localPath\"" );
|
|
|
|
|
}
|
2011-07-11 21:39:06 +00:00
|
|
|
if ( $wgResourceLoaderValidateStaticJS ) {
|
|
|
|
|
// Static files don't really need to be checked as often; unlike
|
|
|
|
|
// on-wiki module they shouldn't change unexpectedly without
|
|
|
|
|
// admin interference.
|
|
|
|
|
$contents = $this->validateScriptFile( $fileName, $contents );
|
|
|
|
|
}
|
2010-11-05 06:53:14 +00:00
|
|
|
$js .= $contents . "\n";
|
|
|
|
|
}
|
|
|
|
|
return $js;
|
2010-10-19 18:25:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2010-10-20 00:22:25 +00:00
|
|
|
* Gets the contents of a list of CSS files.
|
2011-06-17 16:05:05 +00:00
|
|
|
*
|
2011-05-14 12:15:58 +00:00
|
|
|
* @param $styles Array: List of media type/list of file paths pairs, to read, remap and
|
|
|
|
|
* concetenate
|
2011-05-21 17:45:20 +00:00
|
|
|
*
|
|
|
|
|
* @param $flip bool
|
|
|
|
|
*
|
2011-06-17 16:05:05 +00:00
|
|
|
* @return Array: List of concatenated and remapped CSS data from $styles,
|
2010-11-05 06:53:14 +00:00
|
|
|
* keyed by media type
|
2010-10-19 18:25:42 +00:00
|
|
|
*/
|
2010-12-16 19:31:48 +00:00
|
|
|
protected function readStyleFiles( array $styles, $flip ) {
|
2010-10-19 22:48:52 +00:00
|
|
|
if ( empty( $styles ) ) {
|
|
|
|
|
return array();
|
2010-10-19 18:25:42 +00:00
|
|
|
}
|
|
|
|
|
foreach ( $styles as $media => $files ) {
|
2011-01-09 16:24:09 +00:00
|
|
|
$uniqueFiles = array_unique( $files );
|
2010-10-19 22:48:52 +00:00
|
|
|
$styles[$media] = implode(
|
2010-12-16 19:31:48 +00:00
|
|
|
"\n",
|
|
|
|
|
array_map(
|
|
|
|
|
array( $this, 'readStyleFile' ),
|
2011-01-09 16:24:09 +00:00
|
|
|
$uniqueFiles,
|
|
|
|
|
array_fill( 0, count( $uniqueFiles ), $flip )
|
2010-12-16 19:31:48 +00:00
|
|
|
)
|
2010-10-19 22:48:52 +00:00
|
|
|
);
|
2010-10-19 18:25:42 +00:00
|
|
|
}
|
|
|
|
|
return $styles;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2010-10-20 00:22:25 +00:00
|
|
|
* Reads a style file.
|
2011-06-17 16:05:05 +00:00
|
|
|
*
|
2010-10-19 22:48:52 +00:00
|
|
|
* This method can be used as a callback for array_map()
|
2011-06-17 16:05:05 +00:00
|
|
|
*
|
2010-11-05 18:33:50 +00:00
|
|
|
* @param $path String: File path of script file to read
|
2011-05-21 17:45:20 +00:00
|
|
|
* @param $flip bool
|
|
|
|
|
*
|
2010-11-05 18:33:50 +00:00
|
|
|
* @return String: CSS data in script file
|
2010-10-19 18:25:42 +00:00
|
|
|
*/
|
2011-06-17 16:05:05 +00:00
|
|
|
protected function readStyleFile( $path, $flip ) {
|
2010-11-05 06:53:14 +00:00
|
|
|
$localPath = $this->getLocalPath( $path );
|
|
|
|
|
$style = file_get_contents( $localPath );
|
|
|
|
|
if ( $style === false ) {
|
|
|
|
|
throw new MWException( __METHOD__.": style file not found: \"$localPath\"" );
|
|
|
|
|
}
|
2010-12-16 19:31:48 +00:00
|
|
|
if ( $flip ) {
|
|
|
|
|
$style = CSSJanus::transform( $style, true, false );
|
|
|
|
|
}
|
2011-01-09 12:48:30 +00:00
|
|
|
$dirname = dirname( $path );
|
|
|
|
|
if ( $dirname == '.' ) {
|
|
|
|
|
// If $path doesn't have a directory component, don't prepend a dot
|
|
|
|
|
$dirname = '';
|
|
|
|
|
}
|
|
|
|
|
$dir = $this->getLocalPath( $dirname );
|
|
|
|
|
$remoteDir = $this->getRemotePath( $dirname );
|
2010-10-27 19:59:10 +00:00
|
|
|
// Get and register local file references
|
2011-06-17 16:05:05 +00:00
|
|
|
$this->localFileRefs = array_merge(
|
|
|
|
|
$this->localFileRefs,
|
2010-11-05 06:53:14 +00:00
|
|
|
CSSMin::getLocalFileReferences( $style, $dir ) );
|
2010-10-19 18:25:42 +00:00
|
|
|
return CSSMin::remap(
|
2010-10-27 22:22:10 +00:00
|
|
|
$style, $dir, $remoteDir, true
|
2010-10-19 18:25:42 +00:00
|
|
|
);
|
|
|
|
|
}
|
2011-03-25 11:15:40 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get whether CSS for this module should be flipped
|
|
|
|
|
* @param $context ResourceLoaderContext
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public function getFlip( $context ) {
|
|
|
|
|
return $context->getDirection() === 'rtl';
|
|
|
|
|
}
|
2010-10-19 18:25:42 +00:00
|
|
|
}
|