MediaWiki
REL1_23
|
00001 <?php 00025 class ResourceLoaderStartUpModule extends ResourceLoaderModule { 00026 00027 /* Protected Members */ 00028 00029 protected $modifiedTime = array(); 00030 protected $configVars = array(); 00031 protected $targets = array( 'desktop', 'mobile' ); 00032 00033 /* Protected Methods */ 00034 00039 protected function getConfig( $context ) { 00040 00041 $hash = $context->getHash(); 00042 if ( isset( $this->configVars[$hash] ) ) { 00043 return $this->configVars[$hash]; 00044 } 00045 00046 global $wgLoadScript, $wgScript, $wgStylePath, $wgScriptExtension, 00047 $wgArticlePath, $wgScriptPath, $wgServer, $wgContLang, 00048 $wgVariantArticlePath, $wgActionPaths, $wgVersion, 00049 $wgEnableAPI, $wgEnableWriteAPI, $wgDBname, 00050 $wgSitename, $wgFileExtensions, $wgExtensionAssetsPath, 00051 $wgCookiePrefix, $wgResourceLoaderMaxQueryLength, 00052 $wgResourceLoaderStorageEnabled, $wgResourceLoaderStorageVersion, 00053 $wgSearchType; 00054 00055 $mainPage = Title::newMainPage(); 00056 00062 $namespaceIds = $wgContLang->getNamespaceIds(); 00063 $caseSensitiveNamespaces = array(); 00064 foreach ( MWNamespace::getCanonicalNamespaces() as $index => $name ) { 00065 $namespaceIds[$wgContLang->lc( $name )] = $index; 00066 if ( !MWNamespace::isCapitalized( $index ) ) { 00067 $caseSensitiveNamespaces[] = $index; 00068 } 00069 } 00070 00071 // Build list of variables 00072 $vars = array( 00073 'wgLoadScript' => $wgLoadScript, 00074 'debug' => $context->getDebug(), 00075 'skin' => $context->getSkin(), 00076 'stylepath' => $wgStylePath, 00077 'wgUrlProtocols' => wfUrlProtocols(), 00078 'wgArticlePath' => $wgArticlePath, 00079 'wgScriptPath' => $wgScriptPath, 00080 'wgScriptExtension' => $wgScriptExtension, 00081 'wgScript' => $wgScript, 00082 'wgSearchType' => $wgSearchType, 00083 'wgVariantArticlePath' => $wgVariantArticlePath, 00084 // Force object to avoid "empty" associative array from 00085 // becoming [] instead of {} in JS (bug 34604) 00086 'wgActionPaths' => (object)$wgActionPaths, 00087 'wgServer' => $wgServer, 00088 'wgUserLanguage' => $context->getLanguage(), 00089 'wgContentLanguage' => $wgContLang->getCode(), 00090 'wgVersion' => $wgVersion, 00091 'wgEnableAPI' => $wgEnableAPI, 00092 'wgEnableWriteAPI' => $wgEnableWriteAPI, 00093 'wgMainPageTitle' => $mainPage->getPrefixedText(), 00094 'wgFormattedNamespaces' => $wgContLang->getFormattedNamespaces(), 00095 'wgNamespaceIds' => $namespaceIds, 00096 'wgContentNamespaces' => MWNamespace::getContentNamespaces(), 00097 'wgSiteName' => $wgSitename, 00098 'wgFileExtensions' => array_values( array_unique( $wgFileExtensions ) ), 00099 'wgDBname' => $wgDBname, 00100 // This sucks, it is only needed on Special:Upload, but I could 00101 // not find a way to add vars only for a certain module 00102 'wgFileCanRotate' => BitmapHandler::canRotate(), 00103 'wgAvailableSkins' => Skin::getSkinNames(), 00104 'wgExtensionAssetsPath' => $wgExtensionAssetsPath, 00105 // MediaWiki sets cookies to have this prefix by default 00106 'wgCookiePrefix' => $wgCookiePrefix, 00107 'wgResourceLoaderMaxQueryLength' => $wgResourceLoaderMaxQueryLength, 00108 'wgCaseSensitiveNamespaces' => $caseSensitiveNamespaces, 00109 'wgLegalTitleChars' => Title::convertByteClassToUnicodeClass( Title::legalChars() ), 00110 'wgResourceLoaderStorageVersion' => $wgResourceLoaderStorageVersion, 00111 'wgResourceLoaderStorageEnabled' => $wgResourceLoaderStorageEnabled, 00112 ); 00113 00114 wfRunHooks( 'ResourceLoaderGetConfigVars', array( &$vars ) ); 00115 00116 $this->configVars[$hash] = $vars; 00117 return $this->configVars[$hash]; 00118 } 00119 00126 public static function getModuleRegistrations( ResourceLoaderContext $context ) { 00127 global $wgCacheEpoch; 00128 wfProfileIn( __METHOD__ ); 00129 00130 $resourceLoader = $context->getResourceLoader(); 00131 $target = $context->getRequest()->getVal( 'target', 'desktop' ); 00132 00133 $out = ''; 00134 $registryData = array(); 00135 00136 // Get registry data 00137 foreach ( $resourceLoader->getModuleNames() as $name ) { 00138 $module = $resourceLoader->getModule( $name ); 00139 $moduleTargets = $module->getTargets(); 00140 if ( !in_array( $target, $moduleTargets ) ) { 00141 continue; 00142 } 00143 00144 // getModifiedTime() is supposed to return a UNIX timestamp, but it doesn't always 00145 // seem to do that, and custom implementations might forget. Coerce it to TS_UNIX 00146 $moduleMtime = wfTimestamp( TS_UNIX, $module->getModifiedTime( $context ) ); 00147 $mtime = max( $moduleMtime, wfTimestamp( TS_UNIX, $wgCacheEpoch ) ); 00148 00149 // FIXME: Convert to numbers, wfTimestamp always gives us stings, even for TS_UNIX 00150 00151 $registryData[ $name ] = array( 00152 'version' => $mtime, 00153 'dependencies' => $module->getDependencies(), 00154 'group' => $module->getGroup(), 00155 'source' => $module->getSource(), 00156 'loader' => $module->getLoaderScript(), 00157 ); 00158 } 00159 00160 // Register sources 00161 $out .= ResourceLoader::makeLoaderSourcesScript( $resourceLoader->getSources() ); 00162 00163 // Concatenate module loader scripts and figure out the different call 00164 // signatures for mw.loader.register 00165 $registrations = array(); 00166 foreach ( $registryData as $name => $data ) { 00167 if ( $data['loader'] !== false ) { 00168 $out .= ResourceLoader::makeCustomLoaderScript( 00169 $name, 00170 wfTimestamp( TS_ISO_8601_BASIC, $data['version'] ), 00171 $data['dependencies'], 00172 $data['group'], 00173 $data['source'], 00174 $data['loader'] 00175 ); 00176 continue; 00177 } 00178 00179 if ( 00180 !count( $data['dependencies'] ) && 00181 $data['group'] === null && 00182 $data['source'] === 'local' 00183 ) { 00184 // Modules without dependencies, a group or a foreign source; 00185 // call mw.loader.register(name, timestamp) 00186 $registrations[] = array( $name, $data['version'] ); 00187 } elseif ( $data['group'] === null && $data['source'] === 'local' ) { 00188 // Modules with dependencies but no group or foreign source; 00189 // call mw.loader.register(name, timestamp, dependencies) 00190 $registrations[] = array( $name, $data['version'], $data['dependencies'] ); 00191 } elseif ( $data['source'] === 'local' ) { 00192 // Modules with a group but no foreign source; 00193 // call mw.loader.register(name, timestamp, dependencies, group) 00194 $registrations[] = array( 00195 $name, 00196 $data['version'], 00197 $data['dependencies'], 00198 $data['group'] 00199 ); 00200 } else { 00201 // Modules with a foreign source; 00202 // call mw.loader.register(name, timestamp, dependencies, group, source) 00203 $registrations[] = array( 00204 $name, 00205 $data['version'], 00206 $data['dependencies'], 00207 $data['group'], 00208 $data['source'] 00209 ); 00210 } 00211 } 00212 00213 // Register modules 00214 $out .= ResourceLoader::makeLoaderRegisterScript( $registrations ); 00215 00216 wfProfileOut( __METHOD__ ); 00217 return $out; 00218 } 00219 00220 /* Methods */ 00221 00225 public function isRaw() { 00226 return true; 00227 } 00228 00238 public static function getStartupModulesUrl( ResourceLoaderContext $context ) { 00239 // The core modules: 00240 $moduleNames = array( 'jquery', 'mediawiki' ); 00241 wfRunHooks( 'ResourceLoaderGetStartupModules', array( &$moduleNames ), '1.23' ); 00242 00243 // Get the latest version 00244 $loader = $context->getResourceLoader(); 00245 $version = 0; 00246 foreach ( $moduleNames as $moduleName ) { 00247 $version = max( $version, 00248 $loader->getModule( $moduleName )->getModifiedTime( $context ) 00249 ); 00250 } 00251 00252 $query = array( 00253 'modules' => ResourceLoader::makePackedModulesString( $moduleNames ), 00254 'only' => 'scripts', 00255 'lang' => $context->getLanguage(), 00256 'skin' => $context->getSkin(), 00257 'debug' => $context->getDebug() ? 'true' : 'false', 00258 'version' => wfTimestamp( TS_ISO_8601_BASIC, $version ) 00259 ); 00260 // Ensure uniform query order 00261 ksort( $query ); 00262 return wfAppendQuery( wfScript( 'load' ), $query ); 00263 } 00264 00269 public function getScript( ResourceLoaderContext $context ) { 00270 global $IP, $wgLegacyJavaScriptGlobals; 00271 00272 $out = file_get_contents( "$IP/resources/src/startup.js" ); 00273 if ( $context->getOnly() === 'scripts' ) { 00274 00275 // Startup function 00276 $configuration = $this->getConfig( $context ); 00277 $registrations = self::getModuleRegistrations( $context ); 00278 // Fix indentation 00279 $registrations = str_replace( "\n", "\n\t", trim( $registrations ) ); 00280 $out .= "var startUp = function () {\n" . 00281 "\tmw.config = new " . 00282 Xml::encodeJsCall( 'mw.Map', array( $wgLegacyJavaScriptGlobals ) ) . "\n" . 00283 "\t$registrations\n" . 00284 "\t" . Xml::encodeJsCall( 'mw.config.set', array( $configuration ) ) . 00285 "};\n"; 00286 00287 // Conditional script injection 00288 $scriptTag = Html::linkedScript( self::getStartupModulesUrl( $context ) ); 00289 $out .= "if ( isCompatible() ) {\n" . 00290 "\t" . Xml::encodeJsCall( 'document.write', array( $scriptTag ) ) . 00291 "}"; 00292 } 00293 00294 return $out; 00295 } 00296 00300 public function supportsURLLoading() { 00301 return false; 00302 } 00303 00308 public function getModifiedTime( ResourceLoaderContext $context ) { 00309 global $IP, $wgCacheEpoch; 00310 00311 $hash = $context->getHash(); 00312 if ( isset( $this->modifiedTime[$hash] ) ) { 00313 return $this->modifiedTime[$hash]; 00314 } 00315 00316 // Call preloadModuleInfo() on ALL modules as we're about 00317 // to call getModifiedTime() on all of them 00318 $loader = $context->getResourceLoader(); 00319 $loader->preloadModuleInfo( $loader->getModuleNames(), $context ); 00320 00321 $time = max( 00322 wfTimestamp( TS_UNIX, $wgCacheEpoch ), 00323 filemtime( "$IP/resources/src/startup.js" ), 00324 $this->getHashMtime( $context ) 00325 ); 00326 00327 // ATTENTION!: Because of the line below, this is not going to cause 00328 // infinite recursion - think carefully before making changes to this 00329 // code! 00330 // Pre-populate modifiedTime with something because the the loop over 00331 // all modules below includes the the startup module (this module). 00332 $this->modifiedTime[$hash] = 1; 00333 00334 foreach ( $loader->getModuleNames() as $name ) { 00335 $module = $loader->getModule( $name ); 00336 $time = max( $time, $module->getModifiedTime( $context ) ); 00337 } 00338 00339 $this->modifiedTime[$hash] = $time; 00340 return $this->modifiedTime[$hash]; 00341 } 00342 00351 public function getModifiedHash( ResourceLoaderContext $context ) { 00352 global $wgLegacyJavaScriptGlobals; 00353 00354 $data = array( 00355 'vars' => $this->getConfig( $context ), 00356 'wgLegacyJavaScriptGlobals' => $wgLegacyJavaScriptGlobals, 00357 ); 00358 00359 return md5( serialize( $data ) ); 00360 } 00361 00365 public function getGroup() { 00366 return 'startup'; 00367 } 00368 }