MediaWiki  REL1_21
ResourceLoaderModule.php
Go to the documentation of this file.
00001 <?php
00028 abstract class ResourceLoaderModule {
00029 
00030         # Type of resource
00031         const TYPE_SCRIPTS = 'scripts';
00032         const TYPE_STYLES = 'styles';
00033         const TYPE_MESSAGES = 'messages';
00034         const TYPE_COMBINED = 'combined';
00035 
00036         # sitewide core module like a skin file or jQuery component
00037         const ORIGIN_CORE_SITEWIDE = 1;
00038 
00039         # per-user module generated by the software
00040         const ORIGIN_CORE_INDIVIDUAL = 2;
00041 
00042         # sitewide module generated from user-editable files, like MediaWiki:Common.js, or
00043         # modules accessible to multiple users, such as those generated by the Gadgets extension.
00044         const ORIGIN_USER_SITEWIDE = 3;
00045 
00046         # per-user module generated from user-editable files, like User:Me/vector.js
00047         const ORIGIN_USER_INDIVIDUAL = 4;
00048 
00049         # an access constant; make sure this is kept as the largest number in this group
00050         const ORIGIN_ALL = 10;
00051 
00052         # script and style modules form a hierarchy of trustworthiness, with core modules like
00053         # skins and jQuery as most trustworthy, and user scripts as least trustworthy.  We can
00054         # limit the types of scripts and styles we allow to load on, say, sensitive special
00055         # pages like Special:UserLogin and Special:Preferences
00056         protected $origin = self::ORIGIN_CORE_SITEWIDE;
00057 
00058         /* Protected Members */
00059 
00060         protected $name = null;
00061         protected $targets = array( 'desktop' );
00062 
00063         // In-object cache for file dependencies
00064         protected $fileDeps = array();
00065         // In-object cache for message blob mtime
00066         protected $msgBlobMtime = array();
00067 
00068         /* Methods */
00069 
00076         public function getName() {
00077                 return $this->name;
00078         }
00079 
00086         public function setName( $name ) {
00087                 $this->name = $name;
00088         }
00089 
00097         public function getOrigin() {
00098                 return $this->origin;
00099         }
00100 
00107         public function setOrigin( $origin ) {
00108                 $this->origin = $origin;
00109         }
00110 
00115         public function getFlip( $context ) {
00116                 global $wgContLang;
00117 
00118                 return $wgContLang->getDir() !== $context->getDirection();
00119         }
00120 
00128         public function getScript( ResourceLoaderContext $context ) {
00129                 // Stub, override expected
00130                 return '';
00131         }
00132 
00147         public function getScriptURLsForDebug( ResourceLoaderContext $context ) {
00148                 $url = ResourceLoader::makeLoaderURL(
00149                         array( $this->getName() ),
00150                         $context->getLanguage(),
00151                         $context->getSkin(),
00152                         $context->getUser(),
00153                         $context->getVersion(),
00154                         true, // debug
00155                         'scripts', // only
00156                         $context->getRequest()->getBool( 'printable' ),
00157                         $context->getRequest()->getBool( 'handheld' )
00158                 );
00159                 return array( $url );
00160         }
00161 
00168         public function supportsURLLoading() {
00169                 return true;
00170         }
00171 
00180         public function getStyles( ResourceLoaderContext $context ) {
00181                 // Stub, override expected
00182                 return array();
00183         }
00184 
00194         public function getStyleURLsForDebug( ResourceLoaderContext $context ) {
00195                 $url = ResourceLoader::makeLoaderURL(
00196                         array( $this->getName() ),
00197                         $context->getLanguage(),
00198                         $context->getSkin(),
00199                         $context->getUser(),
00200                         $context->getVersion(),
00201                         true, // debug
00202                         'styles', // only
00203                         $context->getRequest()->getBool( 'printable' ),
00204                         $context->getRequest()->getBool( 'handheld' )
00205                 );
00206                 return array( 'all' => array( $url ) );
00207         }
00208 
00216         public function getMessages() {
00217                 // Stub, override expected
00218                 return array();
00219         }
00220 
00226         public function getGroup() {
00227                 // Stub, override expected
00228                 return null;
00229         }
00230 
00236         public function getSource() {
00237                 // Stub, override expected
00238                 return 'local';
00239         }
00240 
00248         public function getPosition() {
00249                 return 'bottom';
00250         }
00251 
00259         public function isRaw() {
00260                 return false;
00261         }
00262 
00268         public function getLoaderScript() {
00269                 // Stub, override expected
00270                 return false;
00271         }
00272 
00288         public function getDependencies() {
00289                 // Stub, override expected
00290                 return array();
00291         }
00292 
00298         public function getTargets() {
00299                 return $this->targets;
00300         }
00301 
00309         public function getFileDependencies( $skin ) {
00310                 // Try in-object cache first
00311                 if ( isset( $this->fileDeps[$skin] ) ) {
00312                         return $this->fileDeps[$skin];
00313                 }
00314 
00315                 $dbr = wfGetDB( DB_SLAVE );
00316                 $deps = $dbr->selectField( 'module_deps', 'md_deps', array(
00317                                 'md_module' => $this->getName(),
00318                                 'md_skin' => $skin,
00319                         ), __METHOD__
00320                 );
00321                 if ( !is_null( $deps ) ) {
00322                         $this->fileDeps[$skin] = (array) FormatJson::decode( $deps, true );
00323                 } else {
00324                         $this->fileDeps[$skin] = array();
00325                 }
00326                 return $this->fileDeps[$skin];
00327         }
00328 
00335         public function setFileDependencies( $skin, $deps ) {
00336                 $this->fileDeps[$skin] = $deps;
00337         }
00338 
00345         public function getMsgBlobMtime( $lang ) {
00346                 if ( !isset( $this->msgBlobMtime[$lang] ) ) {
00347                         if ( !count( $this->getMessages() ) ) {
00348                                 return 0;
00349                         }
00350 
00351                         $dbr = wfGetDB( DB_SLAVE );
00352                         $msgBlobMtime = $dbr->selectField( 'msg_resource', 'mr_timestamp', array(
00353                                         'mr_resource' => $this->getName(),
00354                                         'mr_lang' => $lang
00355                                 ), __METHOD__
00356                         );
00357                         // If no blob was found, but the module does have messages, that means we need
00358                         // to regenerate it. Return NOW
00359                         if ( $msgBlobMtime === false ) {
00360                                 $msgBlobMtime = wfTimestampNow();
00361                         }
00362                         $this->msgBlobMtime[$lang] = wfTimestamp( TS_UNIX, $msgBlobMtime );
00363                 }
00364                 return $this->msgBlobMtime[$lang];
00365         }
00366 
00373         public function setMsgBlobMtime( $lang, $mtime ) {
00374                 $this->msgBlobMtime[$lang] = $mtime;
00375         }
00376 
00377         /* Abstract Methods */
00378 
00393         public function getModifiedTime( ResourceLoaderContext $context ) {
00394                 // 0 would mean now
00395                 return 1;
00396         }
00397 
00407         public function isKnownEmpty( ResourceLoaderContext $context ) {
00408                 return false;
00409         }
00410 
00412         private static $jsParser;
00413         private static $parseCacheVersion = 1;
00414 
00423         protected function validateScriptFile( $fileName, $contents ) {
00424                 global $wgResourceLoaderValidateJS;
00425                 if ( $wgResourceLoaderValidateJS ) {
00426                         // Try for cache hit
00427                         // Use CACHE_ANYTHING since filtering is very slow compared to DB queries
00428                         $key = wfMemcKey( 'resourceloader', 'jsparse', self::$parseCacheVersion, md5( $contents ) );
00429                         $cache = wfGetCache( CACHE_ANYTHING );
00430                         $cacheEntry = $cache->get( $key );
00431                         if ( is_string( $cacheEntry ) ) {
00432                                 return $cacheEntry;
00433                         }
00434 
00435                         $parser = self::javaScriptParser();
00436                         try {
00437                                 $parser->parse( $contents, $fileName, 1 );
00438                                 $result = $contents;
00439                         } catch ( Exception $e ) {
00440                                 // We'll save this to cache to avoid having to validate broken JS over and over...
00441                                 $err = $e->getMessage();
00442                                 $result = "throw new Error(" . Xml::encodeJsVar( "JavaScript parse error: $err" ) . ");";
00443                         }
00444 
00445                         $cache->set( $key, $result );
00446                         return $result;
00447                 } else {
00448                         return $contents;
00449                 }
00450         }
00451 
00455         protected static function javaScriptParser() {
00456                 if ( !self::$jsParser ) {
00457                         self::$jsParser = new JSParser();
00458                 }
00459                 return self::$jsParser;
00460         }
00461 
00468         protected static function safeFilemtime( $filename ) {
00469                 if ( file_exists( $filename ) ) {
00470                         return filemtime( $filename );
00471                 } else {
00472                         // We only ever map this function on an array if we're gonna call max() after,
00473                         // so return our standard minimum timestamps here. This is 1, not 0, because
00474                         // wfTimestamp(0) == NOW
00475                         return 1;
00476                 }
00477         }
00478 }