/** * @param $context ResourceLoaderContext * @return string */ public function getScript(ResourceLoaderContext $context) { global $IP, $wgLoadScript, $wgLegacyJavaScriptGlobals; $out = file_get_contents("{$IP}/resources/startup.js"); if ($context->getOnly() === 'scripts') { // The core modules: $modules = array('jquery', 'mediawiki'); wfRunHooks('ResourceLoaderGetStartupModules', array(&$modules)); // Get the latest version $version = 0; foreach ($modules as $moduleName) { $version = max($version, $context->getResourceLoader()->getModule($moduleName)->getModifiedTime($context)); } // Build load query for StartupModules $query = array('modules' => ResourceLoader::makePackedModulesString($modules), 'only' => 'scripts', 'lang' => $context->getLanguage(), 'skin' => $context->getSkin(), 'debug' => $context->getDebug() ? 'true' : 'false', 'version' => wfTimestamp(TS_ISO_8601_BASIC, $version)); // Ensure uniform query order ksort($query); // Startup function $configuration = $this->getConfig($context); $registrations = self::getModuleRegistrations($context); $out .= "var startUp = function() {\n" . "\tmw.config = new " . Xml::encodeJsCall('mw.Map', array($wgLegacyJavaScriptGlobals)) . "\n" . "\t{$registrations}\n" . "\t" . Xml::encodeJsCall('mw.config.set', array($configuration)) . "\t" . Xml::encodeJsCall('mw.loader.state', array(array('jquery' => 'ready'))) . "};\n"; // Conditional script injection // Wikia change - begin - @author: wladek // $scriptTag = Html::linkedScript( $wgLoadScript . '?' . wfArrayToCGI( $query ) ); // get jquery from CDN if we have wsl and getJqueryUrl loaded $scriptTagJquery = Xml::encodeJsVar(Html::linkedScript(ResourceLoader::makeLoaderURL($modules, $query['lang'], $query['skin'], null, $query['version'], $context->getDebug(), 'scripts'))); $scriptTagNoJquery = Xml::encodeJsVar(Html::linkedScript(ResourceLoader::makeLoaderURL(array('mediawiki'), $query['lang'], $query['skin'], null, $query['version'], $context->getDebug(), 'scripts'))); $scriptTag = <<<ENDSCRIPT ( (window.wsl && window.getJqueryUrl && window.wgJqueryUrl) ? (wsl.buildScript(window.getJqueryUrl()) + {$scriptTagNoJquery}) : ({$scriptTagJquery}) ) ENDSCRIPT; $scriptTag = new XmlJsCode($scriptTag); // Wikia change - end $out .= "if ( isCompatible() ) {\n" . "\t" . Xml::encodeJsCall('document.write', array($scriptTag)) . "}\n" . "delete isCompatible;"; } return $out; }
/** * TODO: Document * @param array|string $modules One or more module names * @param string $only ResourceLoaderModule TYPE_ class constant * @param bool $useESI * @param array $extraQuery Array with extra query parameters to add to each request. array( param => value ) * @param bool $loadCall If true, output an (asynchronous) mw.loader.load() call rather than a "<script src='...'>" tag * @return string The html "<script>", "<link>" and "<style>" tags */ public function makeResourceLoaderLink($modules, $only, $useESI = false, array $extraQuery = array(), $loadCall = false) { global $wgResourceLoaderUseESI; $modules = (array) $modules; $links = array('html' => '', 'states' => array()); if (!count($modules)) { return $links; } if (count($modules) > 1) { // Remove duplicate module requests $modules = array_unique($modules); // Sort module names so requests are more uniform sort($modules); if (ResourceLoader::inDebugMode()) { // Recursively call us for every item foreach ($modules as $name) { $link = $this->makeResourceLoaderLink($name, $only, $useESI); $links['html'] .= $link['html']; $links['states'] += $link['states']; } return $links; } } if (!is_null($this->mTarget)) { $extraQuery['target'] = $this->mTarget; } // Create keyed-by-group list of module objects from modules list $groups = array(); $resourceLoader = $this->getResourceLoader(); foreach ($modules as $name) { $module = $resourceLoader->getModule($name); # Check that we're allowed to include this module on this page if (!$module || $module->getOrigin() > $this->getAllowedModules(ResourceLoaderModule::TYPE_SCRIPTS) && $only == ResourceLoaderModule::TYPE_SCRIPTS || $module->getOrigin() > $this->getAllowedModules(ResourceLoaderModule::TYPE_STYLES) && $only == ResourceLoaderModule::TYPE_STYLES || $this->mTarget && !in_array($this->mTarget, $module->getTargets())) { continue; } $group = $module->getGroup(); if (!isset($groups[$group])) { $groups[$group] = array(); } $groups[$group][$name] = $module; } foreach ($groups as $group => $grpModules) { // Special handling for user-specific groups $user = null; if (($group === 'user' || $group === 'private') && $this->getUser()->isLoggedIn()) { $user = $this->getUser()->getName(); } // Create a fake request based on the one we are about to make so modules return // correct timestamp and emptiness data $query = ResourceLoader::makeLoaderQuery(array(), $this->getLanguage()->getCode(), $this->getSkin()->getSkinName(), $user, null, ResourceLoader::inDebugMode(), $only === ResourceLoaderModule::TYPE_COMBINED ? null : $only, $this->isPrintable(), $this->getRequest()->getBool('handheld'), $extraQuery); $context = new ResourceLoaderContext($resourceLoader, new FauxRequest($query)); // Extract modules that know they're empty foreach ($grpModules as $key => $module) { // Inline empty modules: since they're empty, just mark them as 'ready' (bug 46857) // If we're only getting the styles, we don't need to do anything for empty modules. if ($module->isKnownEmpty($context)) { unset($grpModules[$key]); if ($only !== ResourceLoaderModule::TYPE_STYLES) { $links['states'][$key] = 'ready'; } } } // If there are no non-empty modules, skip this group if (count($grpModules) === 0) { continue; } // Inline private modules. These can't be loaded through load.php for security // reasons, see bug 34907. Note that these modules should be loaded from // getHeadScripts() before the first loader call. Otherwise other modules can't // properly use them as dependencies (bug 30914) if ($group === 'private') { if ($only == ResourceLoaderModule::TYPE_STYLES) { $links['html'] .= Html::inlineStyle($resourceLoader->makeModuleResponse($context, $grpModules)); } else { $links['html'] .= Html::inlineScript(ResourceLoader::makeLoaderConditionalScript($resourceLoader->makeModuleResponse($context, $grpModules))); } $links['html'] .= "\n"; continue; } // Special handling for the user group; because users might change their stuff // on-wiki like user pages, or user preferences; we need to find the highest // timestamp of these user-changeable modules so we can ensure cache misses on change // This should NOT be done for the site group (bug 27564) because anons get that too // and we shouldn't be putting timestamps in Squid-cached HTML $version = null; if ($group === 'user') { // Get the maximum timestamp $timestamp = 1; foreach ($grpModules as $module) { $timestamp = max($timestamp, $module->getModifiedTime($context)); } // Add a version parameter so cache will break when things change $version = wfTimestamp(TS_ISO_8601_BASIC, $timestamp); } $url = ResourceLoader::makeLoaderURL(array_keys($grpModules), $this->getLanguage()->getCode(), $this->getSkin()->getSkinName(), $user, $version, ResourceLoader::inDebugMode(), $only === ResourceLoaderModule::TYPE_COMBINED ? null : $only, $this->isPrintable(), $this->getRequest()->getBool('handheld'), $extraQuery); if ($useESI && $wgResourceLoaderUseESI) { $esi = Xml::element('esi:include', array('src' => $url)); if ($only == ResourceLoaderModule::TYPE_STYLES) { $link = Html::inlineStyle($esi); } else { $link = Html::inlineScript($esi); } } else { // Automatically select style/script elements if ($only === ResourceLoaderModule::TYPE_STYLES) { $link = Html::linkedStyle($url); } elseif ($loadCall) { $link = Html::inlineScript(ResourceLoader::makeLoaderConditionalScript(Xml::encodeJsCall('mw.loader.load', array($url, 'text/javascript', true)))); } else { $link = Html::linkedScript($url); // For modules requested directly in the html via <link> or <script>, // tell mw.loader they are being loading to prevent duplicate requests. foreach ($grpModules as $key => $module) { // Don't output state=loading for the startup module.. if ($key !== 'startup') { $links['states'][$key] = 'loading'; } } } } if ($group == 'noscript') { $links['html'] .= Html::rawElement('noscript', array(), $link) . "\n"; } else { $links['html'] .= $link . "\n"; } } return $links; }
/** * Build a load.php URL using OutputPage instance to get most of the required information * * @param OutputPage $out * @param string|array $modules Module names * @param string $only * @param bool|string $user User name (true to get it from OutputPage) * @param string $version * @param array $extraQuery * @return string */ public static function makeCustomURL(OutputPage $out, $modules, $only = ResourceLoaderModule::TYPE_COMBINED, $user = null, $version = null, $extraQuery = array()) { if ($user === true) { $user = $out->getUser()->getName(); } else { if ($user === false || $user === null) { $user = null; } else { $user = (string) $user; } } $url = ResourceLoader::makeLoaderURL($modules, $out->getLanguage()->getCode(), $out->getSkin()->getSkinName(), $user, $version, ResourceLoader::inDebugMode(), $only === ResourceLoaderModule::TYPE_COMBINED ? null : $only, $out->isPrintable(), $out->getRequest()->getBool('handheld'), $extraQuery); return $url; }
/** * Get the URL or URLs to load for this module's CSS in debug mode. * The default behavior is to return a load.php?only=styles URL for * the module, but file-based modules will want to override this to * load the files directly. See also getScriptURLsForDebug() * * @param $context ResourceLoaderContext: Context object * @return Array: array( mediaType => array( URL1, URL2, ... ), ... ) */ public function getStyleURLsForDebug(ResourceLoaderContext $context) { $url = ResourceLoader::makeLoaderURL(array($this->getName()), $context->getLanguage(), $context->getSkin(), $context->getUser(), $context->getVersion(), true, 'styles', $context->getRequest()->getBool('printable'), $context->getRequest()->getBool('handheld')); return array('all' => array($url)); }
/** * TODO: Document * @param $modules Array/string with the module name(s) * @param $only String ResourceLoaderModule TYPE_ class constant * @param $useESI boolean * @param $extraQuery Array with extra query parameters to add to each request. array( param => value ) * @param $loadCall boolean If true, output an (asynchronous) mw.loader.load() call rather than a <script src="..."> tag * @return string html <script> and <style> tags * // Wikia change -- made this public so we could use it to build asset purge links BAC-895 */ public function makeResourceLoaderLink($modules, $only, $useESI = false, array $extraQuery = array(), $loadCall = false) { global $wgResourceLoaderUseESI; if (!count($modules)) { return ''; } if (count($modules) > 1) { // Remove duplicate module requests $modules = array_unique((array) $modules); // Sort module names so requests are more uniform sort($modules); if (ResourceLoader::inDebugMode()) { // Recursively call us for every item $links = ''; foreach ($modules as $name) { $links .= $this->makeResourceLoaderLink($name, $only, $useESI); } return $links; } } // Create keyed-by-group list of module objects from modules list $groups = array(); $resourceLoader = $this->getResourceLoader(); foreach ((array) $modules as $name) { $module = $resourceLoader->getModule($name); # Check that we're allowed to include this module on this page if (!$module || $module->getOrigin() > $this->getAllowedModules(ResourceLoaderModule::TYPE_SCRIPTS) && $only == ResourceLoaderModule::TYPE_SCRIPTS || $module->getOrigin() > $this->getAllowedModules(ResourceLoaderModule::TYPE_STYLES) && $only == ResourceLoaderModule::TYPE_STYLES) { continue; } $group = $module->getGroup(); if (!isset($groups[$group])) { $groups[$group] = array(); } $groups[$group][$name] = $module; } $links = ''; foreach ($groups as $group => $modules) { // Special handling for user-specific groups $user = null; if (($group === 'user' || $group === 'private') && $this->getUser()->isLoggedIn()) { $user = $this->getUser()->getName(); } // Create a fake request based on the one we are about to make so modules return // correct timestamp and emptiness data $query = ResourceLoader::makeLoaderQuery(array(), $this->getLanguage()->getCode(), $this->getSkin()->getSkinName(), $user, null, ResourceLoader::inDebugMode(), $only === ResourceLoaderModule::TYPE_COMBINED ? null : $only, $this->isPrintable(), $this->getRequest()->getBool('handheld'), $extraQuery); $context = new ResourceLoaderContext($resourceLoader, new FauxRequest($query)); // Drop modules that know they're empty foreach ($modules as $key => $module) { if ($module->isKnownEmpty($context)) { unset($modules[$key]); } } // If there are no modules left, skip this group if ($modules === array()) { continue; } // Inline private modules. These can't be loaded through load.php for security // reasons, see bug 34907. Note that these modules should be loaded from // getHeadScripts() before the first loader call. Otherwise other modules can't // properly use them as dependencies (bug 30914) if ($group === 'private') { // Wikia change - begin - @author: wladek // PER-25:Disabled processing messages in private modules. // Currently private modules don't include any messages as they are more like configuration wrappers. // However the underlying logic still needs to go through some checks which are likely // to cause serious DB spikes when msg_resource table lacks the metadata for these modules. $context->setSkipMessages(true); // Wikia change - end if ($only == ResourceLoaderModule::TYPE_STYLES) { $links .= Html::inlineStyle($resourceLoader->makeModuleResponse($context, $modules)); } else { $links .= Html::inlineScript(ResourceLoader::makeLoaderConditionalScript($resourceLoader->makeModuleResponse($context, $modules))); } $links .= "\n"; continue; } // Special handling for the user group; because users might change their stuff // on-wiki like user pages, or user preferences; we need to find the highest // timestamp of these user-changable modules so we can ensure cache misses on change // This should NOT be done for the site group (bug 27564) because anons get that too // and we shouldn't be putting timestamps in Squid-cached HTML $version = null; if ($group === 'user') { // Get the maximum timestamp $timestamp = 1; foreach ($modules as $module) { $timestamp = max($timestamp, $module->getModifiedTime($context)); } // Add a version parameter so cache will break when things change $version = wfTimestamp(TS_ISO_8601_BASIC, $timestamp); } $url = ResourceLoader::makeLoaderURL(array_keys($modules), $this->getLanguage()->getCode(), $this->getSkin()->getSkinName(), $user, $version, ResourceLoader::inDebugMode(), $only === ResourceLoaderModule::TYPE_COMBINED ? null : $only, $this->isPrintable(), $this->getRequest()->getBool('handheld'), $extraQuery); if ($useESI && $wgResourceLoaderUseESI) { $esi = Xml::element('esi:include', array('src' => $url)); if ($only == ResourceLoaderModule::TYPE_STYLES) { $link = Html::inlineStyle($esi); } else { $link = Html::inlineScript($esi); } } else { // Automatically select style/script elements if ($only === ResourceLoaderModule::TYPE_STYLES) { $link = Html::linkedStyle($url); } else { if ($loadCall) { $link = Html::inlineScript(ResourceLoader::makeLoaderConditionalScript(Xml::encodeJsCall('mw.loader.load', array($url, 'text/javascript', true)))); } else { $link = Html::linkedScript($url); } } } if ($group == 'noscript') { $links .= Html::rawElement('noscript', array(), $link) . "\n"; } else { $links .= $link . "\n"; } } return $links; }