<?php
/**
- * Abstraction for resource loader modules.
+ * Abstraction for ResourceLoader 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
*/
/**
- * Abstraction for resource loader modules, with name registration and maxage functionality.
+ * Abstraction for ResourceLoader modules, with name registration and maxage functionality.
*/
abstract class ResourceLoaderModule {
# Type of resource
// In-object cache for module content
protected $contents = array();
- // Whether the position returned by getPosition() is defined in the module configuration
- // and not a default value
- protected $isPositionDefined = false;
-
/**
* @var Config
*/
return 'bottom';
}
- /**
- * Whether the position returned by getPosition() is a default value or comes from the module
- * definition. This method is meant to be short-lived, and is only useful until classes added
- * via addModuleStyles with a default value define an explicit position. See getModuleStyles()
- * in OutputPage for the related migration warning.
- *
- * @return bool
- * @since 1.26
- */
- public function isPositionDefault() {
- return !$this->isPositionDefined;
- }
-
/**
* 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
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
- */
- public function getLoaderScript() {
- // Stub, override expected
- return false;
- }
-
/**
* Get a list of modules this module depends on.
*
* Dependency information is taken into account when loading a module
* on the client side.
*
- * To add dependencies dynamically on the client side, use a custom
- * loader script, see getLoaderScript()
- *
* Note: It is expected that $context will be made non-optional in the near
* future.
*
*
* These are only image files referenced by the module's stylesheet.
*
- * @param string $skin Skin name
+ * @param ResourceLoaderContext $context
* @return array List of files
*/
- protected function getFileDependencies( $skin ) {
- // Try in-object cache first
- if ( isset( $this->fileDeps[$skin] ) ) {
- return $this->fileDeps[$skin];
- }
+ protected function getFileDependencies( ResourceLoaderContext $context ) {
+ $vary = $context->getSkin() . '|' . $context->getLanguage();
- $dbr = wfGetDB( DB_SLAVE );
- $deps = $dbr->selectField( 'module_deps',
- 'md_deps',
- array(
- 'md_module' => $this->getName(),
- 'md_skin' => $skin,
- ),
- __METHOD__
- );
+ // Try in-object cache first
+ if ( !isset( $this->fileDeps[$vary] ) ) {
+ $dbr = wfGetDB( DB_SLAVE );
+ $deps = $dbr->selectField( 'module_deps',
+ 'md_deps',
+ array(
+ 'md_module' => $this->getName(),
+ 'md_skin' => $vary,
+ ),
+ __METHOD__
+ );
- if ( !is_null( $deps ) ) {
- $this->fileDeps[$skin] = (array)FormatJson::decode( $deps, true );
- } else {
- $this->fileDeps[$skin] = array();
+ if ( !is_null( $deps ) ) {
+ $this->fileDeps[$vary] = self::expandRelativePaths(
+ (array)FormatJson::decode( $deps, true )
+ );
+ } else {
+ $this->fileDeps[$vary] = array();
+ }
}
-
- return $this->fileDeps[$skin];
+ return $this->fileDeps[$vary];
}
/**
* @param string $skin Skin name
* @param array $deps Array of file names
*/
- public function setFileDependencies( $skin, $deps ) {
- $this->fileDeps[$skin] = $deps;
+ public function setFileDependencies( ResourceLoaderContext $context, $files ) {
+ $vary = $context->getSkin() . '|' . $context->getLanguage();
+ $this->fileDeps[$vary] = $files;
}
/**
* Set the files this module depends on indirectly for a given skin.
*
- * @since 1.26
- * @param string $skin Skin name
+ * @since 1.27
+ * @param ResourceLoaderContext $context
* @param array $localFileRefs List of files
*/
- protected function saveFileDependencies( $skin, $localFileRefs ) {
+ protected function saveFileDependencies( ResourceLoaderContext $context, $localFileRefs ) {
+ // Normalise array
+ $localFileRefs = array_values( array_unique( $localFileRefs ) );
+ sort( $localFileRefs );
+
try {
// If the list has been modified since last time we cached it, update the cache
- if ( $localFileRefs !== $this->getFileDependencies( $skin ) ) {
+ if ( $localFileRefs !== $this->getFileDependencies( $context ) ) {
+ $vary = $context->getSkin() . '|' . $context->getLanguage();
$dbw = wfGetDB( DB_MASTER );
$dbw->replace( 'module_deps',
array( array( 'md_module', 'md_skin' ) ), array(
'md_module' => $this->getName(),
- 'md_skin' => $skin,
- 'md_deps' => FormatJson::encode( $localFileRefs ),
+ 'md_skin' => $vary,
+ // Use relative paths to avoid ghost entries when $IP changes (T111481)
+ 'md_deps' => FormatJson::encode( self::getRelativePaths( $localFileRefs ) ),
)
);
}
}
}
+ /**
+ * Make file paths relative to MediaWiki directory.
+ *
+ * This is used to make file paths safe for storing in a database without the paths
+ * becoming stale or incorrect when MediaWiki is moved or upgraded (T111481).
+ *
+ * @since 1.27
+ * @param array $filePaths
+ * @return array
+ */
+ public static function getRelativePaths( Array $filePaths ) {
+ global $IP;
+ return array_map( function ( $path ) use ( $IP ) {
+ return RelPath\getRelativePath( $path, $IP );
+ }, $filePaths );
+ }
+
+ /**
+ * Expand directories relative to $IP.
+ *
+ * @since 1.27
+ * @param array $filePaths
+ * @return array
+ */
+ public static function expandRelativePaths( Array $filePaths ) {
+ global $IP;
+ return array_map( function ( $path ) use ( $IP ) {
+ return RelPath\joinPath( $IP, $path );
+ }, $filePaths );
+ }
+
/**
* Get the last modification timestamp of the messages in this module for a given language.
* @param string $lang Language code
/**
* Get module-specific LESS variables, if any.
*
- * @since 1.26
+ * @since 1.27
* @param ResourceLoaderContext $context
* @return array Module-specific LESS variables.
*/
foreach ( $style as $cssText ) {
if ( is_string( $cssText ) ) {
$stylePairs[$media][] =
- $rl->filter( 'minify-css', $cssText );
+ ResourceLoader::filter( 'minify-css', $cssText );
}
}
} elseif ( is_string( $style ) ) {
- $stylePairs[$media] = $rl->filter( 'minify-css', $style );
+ $stylePairs[$media] = ResourceLoader::filter( 'minify-css', $style );
}
}
}
protected function validateScriptFile( $fileName, $contents ) {
if ( $this->getConfig()->get( 'ResourceLoaderValidateJS' ) ) {
// Try for cache hit
- // Use CACHE_ANYTHING since parsing JS is much slower than a DB query
- $key = wfMemcKey(
+ $cache = ObjectCache::getMainWANInstance();
+ $key = $cache->makeKey(
'resourceloader',
'jsparse',
self::$parseCacheVersion,
md5( $contents )
);
- $cache = wfGetCache( CACHE_ANYTHING );
$cacheEntry = $cache->get( $key );
if ( is_string( $cacheEntry ) ) {
return $cacheEntry;