wiki.techinc.nl/includes/resourceloader/ResourceLoaderWikiModule.php
Roan Kattouw 880f09b10c (bug 27302) Avoid unnecessary requests for user and site modules if the relevant wiki pages don't exist.
Done by adding isKnownEmpty() to ResourceLoaderModule and overriding it to check for page existence in ResourceLoaderWikiModule. Needed to rearrange some code in OutputPage::makeResourceLoaderLink() to have the emptiness check and dropping of modules work properly. Also factored the page_touched check in ResourceLoaderWikiModule::getModifiedTime() out to a separate method (getTitleMtimes()) and moved in-object caching there as well, so getModifiedTime() and isKnownEmpty() share code and caching for their timestamp/existence checks.

This does not account for the case where e.g. a user has user CSS but no user JS: I had implemented this by checking for $context->getOnly() in getTitleMtimes(), but then realized it's not safe to do this in a function called by getModifiedTime(): it causes the timestamp list in the startup module to only take scripts in account for wiki modules, because the startup module has &only=scripts set
2011-02-19 17:07:05 +00:00

178 lines
5 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
*/
defined( 'MEDIAWIKI' ) || die( 1 );
/**
* Abstraction for resource loader modules which pull from wiki pages
*
* This can only be used for wiki pages in the MediaWiki and User namespaces,
* because of its dependence on the functionality of
* Title::isValidCssJsSubpage.
*/
abstract class ResourceLoaderWikiModule extends ResourceLoaderModule {
/* Protected Members */
# Origin is user-supplied code
protected $origin = self::ORIGIN_USER_SITEWIDE;
// In-object cache for title mtimes
protected $titleMtimes = array();
/* Abstract Protected Methods */
abstract protected function getPages( ResourceLoaderContext $context );
/* Protected Methods */
/**
* @param $title Title
* @return null|string
*/
protected function getContent( $title ) {
if ( $title->getNamespace() === NS_MEDIAWIKI ) {
$dbkey = $title->getDBkey();
return wfEmptyMsg( $dbkey ) ? '' : wfMsgExt( $dbkey, 'content' );
}
if ( !$title->isCssJsSubpage() ) {
return null;
}
$revision = Revision::newFromTitle( $title );
if ( !$revision ) {
return null;
}
return $revision->getRawText();
}
/* Methods */
public function getScript( ResourceLoaderContext $context ) {
$scripts = '';
foreach ( $this->getPages( $context ) as $titleText => $options ) {
if ( $options['type'] !== 'script' ) {
continue;
}
$title = Title::newFromText( $titleText );
if ( !$title ) {
continue;
}
$script = $this->getContent( $title );
if ( strval( $script ) !== '' ) {
if ( strpos( $titleText, '*/' ) === false ) {
$scripts .= "/* $titleText */\n";
}
$scripts .= $script . "\n";
}
}
return $scripts;
}
public function getStyles( ResourceLoaderContext $context ) {
global $wgScriptPath;
$styles = array();
foreach ( $this->getPages( $context ) as $titleText => $options ) {
if ( $options['type'] !== 'style' ) {
continue;
}
$title = Title::newFromText( $titleText );
if ( !$title ) {
continue;
}
$media = isset( $options['media'] ) ? $options['media'] : 'all';
$style = $this->getContent( $title );
if ( strval( $style ) === '' ) {
continue;
}
if ( $this->getFlip( $context ) ) {
$style = CSSJanus::transform( $style, true, false );
}
$style = CSSMin::remap( $style, false, $wgScriptPath, true );
if ( !isset( $styles[$media] ) ) {
$styles[$media] = '';
}
if ( strpos( $titleText, '*/' ) === false ) {
$styles[$media] .= "/* $titleText */\n";
}
$styles[$media] .= $style . "\n";
}
return $styles;
}
public function getModifiedTime( ResourceLoaderContext $context ) {
$modifiedTime = 1; // wfTimestamp() interprets 0 as "now"
$mtimes = $this->getTitleMtimes( $context );
if ( count( $mtimes ) ) {
$modifiedTime = max( $modifiedTime, max( $mtimes ) );
}
return $modifiedTime;
}
public function isKnownEmpty( ResourceLoaderContext $context ) {
return count( $this->getTitleMtimes( $context ) ) == 0;
}
/**
* @param $context ResourceLoaderContext
* @return bool
*/
public function getFlip( $context ) {
global $wgContLang;
return $wgContLang->getDir() !== $context->getDirection();
}
/**
* Get the modification times of all titles that would be loaded for
* a given context.
* @param $context ResourceLoaderContext: Context object
* @return array( prefixed DB key => UNIX timestamp ), nonexistent titles are dropped
*/
protected function getTitleMtimes( ResourceLoaderContext $context ) {
$hash = $context->getHash();
if ( isset( $this->titleMtimes[$hash] ) ) {
return $this->titleMtimes[$hash];
}
$this->titleMtimes[$hash] = array();
$batch = new LinkBatch;
foreach ( $this->getPages( $context ) as $titleText => $options ) {
$batch->addObj( Title::newFromText( $titleText ) );
}
if ( !$batch->isEmpty() ) {
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( 'page',
array( 'page_namespace', 'page_title', 'page_touched' ),
$batch->constructSet( 'page', $dbr ),
__METHOD__
);
foreach ( $res as $row ) {
$title = Title::makeTitle( $row->page_namespace, $row->page_title );
$this->titleMtimes[$hash][$title->getPrefixedDBkey()] =
wfTimestamp( TS_UNIX, $row->page_touched );
}
}
return $this->titleMtimes[$hash];
}
}