*/
use MediaWiki\Linker\LinkTarget;
+use Wikimedia\Assert\Assert;
use Wikimedia\Rdbms\Database;
use Wikimedia\Rdbms\IDatabase;
+use MediaWiki\MediaWikiServices;
/**
* Abstraction for ResourceLoader modules which pull from wiki pages
protected $group;
/**
- * @param array $options For back-compat, this can be omitted in favour of overwriting getPages.
+ * @param array|null $options For back-compat, this can be omitted in favour of overwriting
+ * getPages.
*/
public function __construct( array $options = null ) {
if ( is_null( $options ) ) {
/**
* @param string $titleText
+ * @param ResourceLoaderContext|null $context (but passing null is deprecated)
* @return null|string
+ * @since 1.32 added the $context parameter
*/
- protected function getContent( $titleText ) {
+ protected function getContent( $titleText, ResourceLoaderContext $context = null ) {
$title = Title::newFromText( $titleText );
if ( !$title ) {
return null; // Bad title
}
- // If the page is a redirect, follow the redirect.
- if ( $title->isRedirect() ) {
- $content = $this->getContentObj( $title );
- $title = $content ? $content->getUltimateRedirectTarget() : null;
- if ( !$title ) {
- return null; // Dead redirect
- }
+ $content = $this->getContentObj( $title, $context );
+ if ( !$content ) {
+ return null; // No content found
}
- $handler = ContentHandler::getForTitle( $title );
+ $handler = $content->getContentHandler();
if ( $handler->isSupportedFormat( CONTENT_FORMAT_CSS ) ) {
$format = CONTENT_FORMAT_CSS;
} elseif ( $handler->isSupportedFormat( CONTENT_FORMAT_JAVASCRIPT ) ) {
return null; // Bad content model
}
- $content = $this->getContentObj( $title );
- if ( !$content ) {
- return null; // No content found
- }
-
return $content->serialize( $format );
}
/**
* @param Title $title
+ * @param ResourceLoaderContext|null $context (but passing null is deprecated)
+ * @param int|null $maxRedirects Maximum number of redirects to follow. If
+ * null, uses $wgMaxRedirects
* @return Content|null
+ * @since 1.32 added the $context and $maxRedirects parameters
*/
- protected function getContentObj( Title $title ) {
- $revision = Revision::newKnownCurrent( wfGetDB( DB_REPLICA ), $title );
- if ( !$revision ) {
- return null;
+ protected function getContentObj(
+ Title $title, ResourceLoaderContext $context = null, $maxRedirects = null
+ ) {
+ if ( $context === null ) {
+ wfDeprecated( __METHOD__ . ' without a ResourceLoader context', '1.32' );
}
- $content = $revision->getContent( Revision::RAW );
- if ( !$content ) {
- wfDebugLog( 'resourceloader', __METHOD__ . ': failed to load content of JS/CSS page!' );
- return null;
+
+ $overrideCallback = $context ? $context->getContentOverrideCallback() : null;
+ $content = $overrideCallback ? call_user_func( $overrideCallback, $title ) : null;
+ if ( $content ) {
+ if ( !$content instanceof Content ) {
+ $this->getLogger()->error(
+ 'Bad content override for "{title}" in ' . __METHOD__,
+ [ 'title' => $title->getPrefixedText() ]
+ );
+ return null;
+ }
+ } else {
+ $revision = Revision::newKnownCurrent( wfGetDB( DB_REPLICA ), $title );
+ if ( !$revision ) {
+ return null;
+ }
+ $content = $revision->getContent( Revision::RAW );
+
+ if ( !$content ) {
+ $this->getLogger()->error(
+ 'Failed to load content of JS/CSS page "{title}" in ' . __METHOD__,
+ [ 'title' => $title->getPrefixedText() ]
+ );
+ return null;
+ }
+ }
+
+ if ( $content && $content->isRedirect() ) {
+ if ( $maxRedirects === null ) {
+ $maxRedirects = $this->getConfig()->get( 'MaxRedirects' ) ?: 0;
+ }
+ if ( $maxRedirects > 0 ) {
+ $newTitle = $content->getRedirectTarget();
+ return $newTitle ? $this->getContentObj( $newTitle, $context, $maxRedirects - 1 ) : null;
+ }
}
+
return $content;
}
+ /**
+ * @param ResourceLoaderContext $context
+ * @return bool
+ */
+ public function shouldEmbedModule( ResourceLoaderContext $context ) {
+ $overrideCallback = $context->getContentOverrideCallback();
+ if ( $overrideCallback && $this->getSource() === 'local' ) {
+ foreach ( $this->getPages( $context ) as $page => $info ) {
+ $title = Title::newFromText( $page );
+ if ( $title && call_user_func( $overrideCallback, $title ) !== null ) {
+ return true;
+ }
+ }
+ }
+
+ return parent::shouldEmbedModule( $context );
+ }
+
/**
* @param ResourceLoaderContext $context
* @return string JavaScript code
if ( $options['type'] !== 'script' ) {
continue;
}
- $script = $this->getContent( $titleText );
+ $script = $this->getContent( $titleText, $context );
if ( strval( $script ) !== '' ) {
$script = $this->validateScriptFile( $titleText, $script );
$scripts .= ResourceLoader::makeComment( $titleText ) . $script . "\n";
if ( $options['type'] !== 'style' ) {
continue;
}
- $media = isset( $options['media'] ) ? $options['media'] : 'all';
- $style = $this->getContent( $titleText );
+ $media = $options['media'] ?? 'all';
+ $style = $this->getContent( $titleText, $context );
if ( strval( $style ) === '' ) {
continue;
}
if ( !isset( $this->titleInfo[$batchKey] ) ) {
$this->titleInfo[$batchKey] = static::fetchTitleInfo( $dbr, $pageNames, __METHOD__ );
}
- return $this->titleInfo[$batchKey];
+
+ $titleInfo = $this->titleInfo[$batchKey];
+
+ // Override the title info from the overrides, if any
+ $overrideCallback = $context->getContentOverrideCallback();
+ if ( $overrideCallback ) {
+ foreach ( $pageNames as $page ) {
+ $title = Title::newFromText( $page );
+ $content = $title ? call_user_func( $overrideCallback, $title ) : null;
+ if ( $content !== null ) {
+ $titleInfo[$title->getPrefixedText()] = [
+ 'page_len' => $content->getSize(),
+ 'page_latest' => 'TBD', // None available
+ 'page_touched' => wfTimestamp( TS_MW ),
+ ];
+ }
+ }
+ }
+
+ return $titleInfo;
}
protected static function fetchTitleInfo( IDatabase $db, array $pages, $fname = __METHOD__ ) {
$func = [ static::class, 'fetchTitleInfo' ];
$fname = __METHOD__;
- $cache = ObjectCache::getMainWANInstance();
+ $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
$allInfo = $cache->getWithSetCallback(
$cache->makeGlobalKey( 'resourceloader', 'titleinfo', $db->getDomainID(), $hash ),
$cache::TTL_HOUR,
* @param Title $title
* @param Revision|null $old Prior page revision
* @param Revision|null $new New page revision
- * @param string $wikiId
+ * @param string $domain Database domain ID
* @since 1.28
*/
public static function invalidateModuleCache(
- Title $title, Revision $old = null, Revision $new = null, $wikiId
+ Title $title, Revision $old = null, Revision $new = null, $domain
) {
static $formats = [ CONTENT_FORMAT_CSS, CONTENT_FORMAT_JAVASCRIPT ];
+ Assert::parameterType( 'string', $domain, '$domain' );
+
+ // TODO: MCR: differentiate between page functionality and content model!
+ // Not all pages containing CSS or JS have to be modules! [PageType]
if ( $old && in_array( $old->getContentFormat(), $formats ) ) {
$purge = true;
} elseif ( $new && in_array( $new->getContentFormat(), $formats ) ) {
}
if ( $purge ) {
- $cache = ObjectCache::getMainWANInstance();
- $key = $cache->makeGlobalKey( 'resourceloader', 'titleinfo', $wikiId );
+ $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
+ $key = $cache->makeGlobalKey( 'resourceloader', 'titleinfo', $domain );
$cache->touchCheckKey( $key );
}
}