public function getPages(ResourceLoaderContext $context) { $pages = array(); $missingCallback = $context->getRequest()->getVal('missingCallback'); if ($this->type) { foreach ($this->articles as $article) { $pageKey = $article['title']; $type = isset($article['type']) ? $article['type'] : $this->type; $pageInfo = array('type' => $type); if (isset($article['originalName'])) { $pageInfo['originalName'] = $article['originalName']; } if ($missingCallback) { $pageInfo['missingCallback'] = $missingCallback; } if (!empty($article['cityId'])) { $pageIndex = 'fakename' . $this->id++; $pageInfo['city_id'] = intval($article['cityId']); $pageInfo['title'] = $article['title']; // Unable to resolve wiki to cityId } else { if (isset($article['cityId'])) { $pageInfo['missing'] = true; } } $pages[$pageKey] = $pageInfo; } } return $pages; }
/** * @param ResourceLoaderContext $context * @return array List of pages */ protected function getPages(ResourceLoaderContext $context) { $config = $this->getConfig(); $user = $context->getUserObj(); if ($user->isAnon()) { return []; } // Use localised/normalised variant to ensure $excludepage matches $userPage = $user->getUserPage()->getPrefixedDBkey(); $pages = []; if ($config->get('AllowUserCss')) { $pages["{$userPage}/common.css"] = ['type' => 'style']; $pages["{$userPage}/" . $context->getSkin() . '.css'] = ['type' => 'style']; } // User group pages are maintained site-wide and enabled with site JS/CSS. if ($config->get('UseSiteCss')) { foreach ($user->getEffectiveGroups() as $group) { if ($group == '*') { continue; } $pages["MediaWiki:Group-{$group}.css"] = ['type' => 'style']; } } // Hack for T28283: Allow excluding pages for preview on a CSS/JS page. // The excludepage parameter is set by OutputPage. $excludepage = $context->getRequest()->getVal('excludepage'); if (isset($pages[$excludepage])) { unset($pages[$excludepage]); } return $pages; }
/** * @param $context ResourceLoaderContext * @return array */ protected function getPages(ResourceLoaderContext $context) { $username = $context->getUser(); if ($username === null) { return array(); } // Get the normalized title of the user's user page $userpageTitle = Title::makeTitleSafe(NS_USER, $username); if (!$userpageTitle instanceof Title) { return array(); } $userpage = $userpageTitle->getPrefixedDBkey(); // Needed so $excludepages works $pages = array("{$userpage}/common.js" => array('type' => 'script'), "{$userpage}/" . $context->getSkin() . '.js' => array('type' => 'script'), "{$userpage}/common.css" => array('type' => 'style'), "{$userpage}/" . $context->getSkin() . '.css' => array('type' => 'style')); // Hack for bug 26283: if we're on a preview page for a CSS/JS page, // we need to exclude that page from this module. In that case, the excludepage // parameter will be set to the name of the page we need to exclude. $excludepage = $context->getRequest()->getVal('excludepage'); if (isset($pages[$excludepage])) { // This works because $excludepage is generated with getPrefixedDBkey(), // just like the keys in $pages[] above unset($pages[$excludepage]); } return $pages; }
/** * Check if an RL request can be cached. * Caller is responsible for checking if any modules are private. * @param ResourceLoaderContext $context * @return bool */ public static function useFileCache(ResourceLoaderContext $context) { global $wgUseFileCache, $wgDefaultSkin, $wgLanguageCode; if (!$wgUseFileCache) { return false; } // Get all query values $queryVals = $context->getRequest()->getValues(); foreach ($queryVals as $query => $val) { if (in_array($query, array('modules', 'image', 'variant', 'version', '*'))) { // Use file cache regardless of the value of this parameter continue; // note: &* added as IE fix } elseif ($query === 'skin' && $val === $wgDefaultSkin) { continue; } elseif ($query === 'lang' && $val === $wgLanguageCode) { continue; } elseif ($query === 'only' && in_array($val, array('styles', 'scripts'))) { continue; } elseif ($query === 'debug' && $val === 'false') { continue; } elseif ($query === 'format' && $val === 'rasterized') { continue; } return false; } return true; // cacheable }
/** * Check if an RL request can be cached. * Caller is responsible for checking if any modules are private. * @param $context ResourceLoaderContext * @return bool */ public static function useFileCache(ResourceLoaderContext $context) { global $wgUseFileCache, $wgDefaultSkin, $wgLanguageCode; if (!$wgUseFileCache) { return false; } // Get all query values $queryVals = $context->getRequest()->getValues(); foreach ($queryVals as $query => $val) { if ($query === 'modules' || $query === 'version' || $query === '*') { continue; // note: &* added as IE fix } elseif ($query === 'skin' && $val === $wgDefaultSkin) { continue; } elseif ($query === 'lang' && $val === $wgLanguageCode) { continue; } elseif ($query === 'only' && in_array($val, array('styles', 'scripts'))) { continue; } elseif ($query === 'debug' && $val === 'false') { continue; } return false; } return true; // cacheable }
/** * Get list of pages used by this module * * @param ResourceLoaderContext $context * @return array List of pages */ protected function getPages(ResourceLoaderContext $context) { $allowUserJs = $this->getConfig()->get('AllowUserJs'); $allowUserCss = $this->getConfig()->get('AllowUserCss'); if (!$allowUserJs && !$allowUserCss) { return array(); } $user = $context->getUserObj(); if (!$user || $user->isAnon()) { return array(); } // Needed so $excludepages works $userPage = $user->getUserPage()->getPrefixedDBkey(); $pages = array(); if ($allowUserJs) { $pages["{$userPage}/common.js"] = array('type' => 'script'); $pages["{$userPage}/" . $context->getSkin() . '.js'] = array('type' => 'script'); } if ($allowUserCss) { $pages["{$userPage}/common.css"] = array('type' => 'style'); $pages["{$userPage}/" . $context->getSkin() . '.css'] = array('type' => 'style'); } // Hack for bug 26283: if we're on a preview page for a CSS/JS page, // we need to exclude that page from this module. In that case, the excludepage // parameter will be set to the name of the page we need to exclude. $excludepage = $context->getRequest()->getVal('excludepage'); if (isset($pages[$excludepage])) { // This works because $excludepage is generated with getPrefixedDBkey(), // just like the keys in $pages[] above unset($pages[$excludepage]); } return $pages; }
/** * Get registration code for all modules. * * @param ResourceLoaderContext $context object * @return string JavaScript code for registering all modules with the client loader */ public static function getModuleRegistrations(ResourceLoaderContext $context) { global $wgCacheEpoch; wfProfileIn(__METHOD__); $resourceLoader = $context->getResourceLoader(); $target = $context->getRequest()->getVal('target', 'desktop'); $out = ''; $registryData = array(); // Get registry data foreach ($resourceLoader->getModuleNames() as $name) { $module = $resourceLoader->getModule($name); $moduleTargets = $module->getTargets(); if (!in_array($target, $moduleTargets)) { continue; } // getModifiedTime() is supposed to return a UNIX timestamp, but it doesn't always // seem to do that, and custom implementations might forget. Coerce it to TS_UNIX $moduleMtime = wfTimestamp(TS_UNIX, $module->getModifiedTime($context)); $mtime = max($moduleMtime, wfTimestamp(TS_UNIX, $wgCacheEpoch)); // FIXME: Convert to numbers, wfTimestamp always gives us stings, even for TS_UNIX $registryData[$name] = array('version' => $mtime, 'dependencies' => $module->getDependencies(), 'group' => $module->getGroup(), 'source' => $module->getSource(), 'loader' => $module->getLoaderScript()); } // Register sources $out .= ResourceLoader::makeLoaderSourcesScript($resourceLoader->getSources()); // Concatenate module loader scripts and figure out the different call // signatures for mw.loader.register $registrations = array(); foreach ($registryData as $name => $data) { if ($data['loader'] !== false) { $out .= ResourceLoader::makeCustomLoaderScript($name, wfTimestamp(TS_ISO_8601_BASIC, $data['version']), $data['dependencies'], $data['group'], $data['source'], $data['loader']); continue; } if (!count($data['dependencies']) && $data['group'] === null && $data['source'] === 'local') { // Modules without dependencies, a group or a foreign source; // call mw.loader.register(name, timestamp) $registrations[] = array($name, $data['version']); } elseif ($data['group'] === null && $data['source'] === 'local') { // Modules with dependencies but no group or foreign source; // call mw.loader.register(name, timestamp, dependencies) $registrations[] = array($name, $data['version'], $data['dependencies']); } elseif ($data['source'] === 'local') { // Modules with a group but no foreign source; // call mw.loader.register(name, timestamp, dependencies, group) $registrations[] = array($name, $data['version'], $data['dependencies'], $data['group']); } else { // Modules with a foreign source; // call mw.loader.register(name, timestamp, dependencies, group, source) $registrations[] = array($name, $data['version'], $data['dependencies'], $data['group'], $data['source']); } } // Register modules $out .= ResourceLoader::makeLoaderRegisterScript($registrations); wfProfileOut(__METHOD__); return $out; }
/** * Gets registration code for all modules * * @param $context ResourceLoaderContext object * @return String: JavaScript code for registering all modules with the client loader */ public static function getModuleRegistrations(ResourceLoaderContext $context) { global $wgCacheEpoch; wfProfileIn(__METHOD__); $out = ''; $registrations = array(); $resourceLoader = $context->getResourceLoader(); $target = $context->getRequest()->getVal('target', 'desktop'); // Register sources $out .= ResourceLoader::makeLoaderSourcesScript($resourceLoader->getSources()); // Register modules foreach ($resourceLoader->getModuleNames() as $name) { $module = $resourceLoader->getModule($name); $moduleTargets = $module->getTargets(); if (!in_array($target, $moduleTargets)) { continue; } $deps = $module->getDependencies(); $group = $module->getGroup(); $source = $module->getSource(); // Support module loader scripts $loader = $module->getLoaderScript(); if ($loader !== false) { $version = wfTimestamp(TS_ISO_8601_BASIC, $module->getModifiedTime($context)); $out .= ResourceLoader::makeCustomLoaderScript($name, $version, $deps, $group, $source, $loader); continue; } // Automatically register module // getModifiedTime() is supposed to return a UNIX timestamp, but it doesn't always // seem to do that, and custom implementations might forget. Coerce it to TS_UNIX $moduleMtime = wfTimestamp(TS_UNIX, $module->getModifiedTime($context)); $mtime = max($moduleMtime, wfTimestamp(TS_UNIX, $wgCacheEpoch)); // Modules without dependencies, a group or a foreign source pass two arguments (name, timestamp) to // mw.loader.register() if (!count($deps) && $group === null && $source === 'local') { $registrations[] = array($name, $mtime); } elseif ($group === null && $source === 'local') { $registrations[] = array($name, $mtime, $deps); } elseif ($source === 'local') { $registrations[] = array($name, $mtime, $deps, $group); } else { $registrations[] = array($name, $mtime, $deps, $group, $source); } } $out .= ResourceLoader::makeLoaderRegisterScript($registrations); wfProfileOut(__METHOD__); return $out; }
/** * Get list of pages used by this module * * @param ResourceLoaderContext $context * @return array List of pages */ protected function getPages(ResourceLoaderContext $context) { $config = $this->getConfig(); $user = $context->getUserObj(); if ($user->isAnon()) { return []; } // Use localised/normalised variant to ensure $excludepage matches $userPage = $user->getUserPage()->getPrefixedDBkey(); $pages = []; if ($config->get('AllowUserJs')) { $pages["{$userPage}/common.js"] = ['type' => 'script']; $pages["{$userPage}/" . $context->getSkin() . '.js'] = ['type' => 'script']; } if ($config->get('AllowUserCss')) { $pages["{$userPage}/common.css"] = ['type' => 'style']; $pages["{$userPage}/" . $context->getSkin() . '.css'] = ['type' => 'style']; } $useSiteJs = $config->get('UseSiteJs'); $useSiteCss = $config->get('UseSiteCss'); // User group pages are maintained site-wide and enabled with site JS/CSS. if ($useSiteJs || $useSiteCss) { foreach ($user->getEffectiveGroups() as $group) { if ($group == '*') { continue; } if ($useSiteJs) { $pages["MediaWiki:Group-{$group}.js"] = ['type' => 'script']; } if ($useSiteCss) { $pages["MediaWiki:Group-{$group}.css"] = ['type' => 'style']; } } } // Hack for bug 26283: if we're on a preview page for a CSS/JS page, // we need to exclude that page from this module. In that case, the excludepage // parameter will be set to the name of the page we need to exclude. $excludepage = $context->getRequest()->getVal('excludepage'); if (isset($pages[$excludepage])) { // This works because $excludepage is generated with getPrefixedDBkey(), // just like the keys in $pages[] above unset($pages[$excludepage]); } return $pages; }
/** * Customize caching policy for RL modules * * * cache "static" modules for 30 days when cb param in the URL matches $wgStyleVersion * * cache "dynamic" modules for 30 days when version param is present in the URL and matches $mtime timestamp * * otherwise fallback to caching for 5 minutes * * @see BAC-1241 * * @param ResourceLoader $rl * @param ResourceLoaderContext $context * @param $mtime int UNIX timestamp from module(s) calculated from filesystem * @param $maxage int UNIX timestamp for maxage * @param $smaxage int UNIX timestamp for smaxage * @return bool it's a hook */ public static function onResourceLoaderModifyMaxAge(ResourceLoader $rl, ResourceLoaderContext $context, $mtime, &$maxage, &$smaxage) { global $wgStyleVersion, $wgResourceLoaderMaxage; // parse cb and version provided as URL parameters // version%3D123456-20140220T090000Z // cb%3D123456%26 $version = explode('-', (string) $context->getVersion(), 2); if (count($version) === 2) { list($cb, $ts) = $version; $ts = strtotime($ts); // convert MW to UNIX timestamp } else { $cb = $context->getRequest()->getVal('cb', false); $ts = false; } // check if at least one of required modules serves dynamic content $hasDynamicModule = false; $modules = $context->getModules(); foreach ($modules as $moduleName) { if (!$rl->getModule($moduleName) instanceof ResourceLoaderFileModule) { $hasDynamicModule = true; break; } } if ($hasDynamicModule) { // use long TTL when version value matches $mtime passed to the hook $useLongTTL = !empty($ts) && $ts <= $mtime; } else { // use long TTL when cache buster value from URL matches $wgStyleVersion $useLongTTL = !empty($cb) && $cb <= $wgStyleVersion; } // modify caching times if (!$useLongTTL) { WikiaLogger::instance()->info('rl.shortTTL', ['modules' => join(',', $modules), 'cb' => $cb, 'ts' => $ts]); } $cachingTimes = $wgResourceLoaderMaxage[$useLongTTL ? 'versioned' : 'unversioned']; $maxage = $cachingTimes['client']; $smaxage = $cachingTimes['server']; return true; }
/** * Get registration code for all modules. * * @param ResourceLoaderContext $context * @return string JavaScript code for registering all modules with the client loader */ public function getModuleRegistrations(ResourceLoaderContext $context) { $resourceLoader = $context->getResourceLoader(); $target = $context->getRequest()->getVal('target', 'desktop'); // Bypass target filter if this request is Special:JavaScriptTest. // To prevent misuse in production, this is only allowed if testing is enabled server-side. $byPassTargetFilter = $this->getConfig()->get('EnableJavaScriptTest') && $target === 'test'; $out = ''; $registryData = []; // Get registry data foreach ($resourceLoader->getModuleNames() as $name) { $module = $resourceLoader->getModule($name); $moduleTargets = $module->getTargets(); if (!$byPassTargetFilter && !in_array($target, $moduleTargets)) { continue; } if ($module->isRaw()) { // Don't register "raw" modules (like 'jquery' and 'mediawiki') client-side because // depending on them is illegal anyway and would only lead to them being reloaded // causing any state to be lost (like jQuery plugins, mw.config etc.) continue; } $versionHash = $module->getVersionHash($context); if (strlen($versionHash) !== 8) { $context->getLogger()->warning("Module '{module}' produced an invalid version hash: '{version}'.", ['module' => $name, 'version' => $versionHash]); // Module implementation either broken or deviated from ResourceLoader::makeHash // Asserted by tests/phpunit/structure/ResourcesTest. $versionHash = ResourceLoader::makeHash($versionHash); } $skipFunction = $module->getSkipFunction(); if ($skipFunction !== null && !ResourceLoader::inDebugMode()) { $skipFunction = ResourceLoader::filter('minify-js', $skipFunction); } $registryData[$name] = ['version' => $versionHash, 'dependencies' => $module->getDependencies($context), 'group' => $module->getGroup(), 'source' => $module->getSource(), 'skip' => $skipFunction]; } self::compileUnresolvedDependencies($registryData); // Register sources $out .= ResourceLoader::makeLoaderSourcesScript($resourceLoader->getSources()); // Figure out the different call signatures for mw.loader.register $registrations = []; foreach ($registryData as $name => $data) { // Call mw.loader.register(name, version, dependencies, group, source, skip) $registrations[] = [$name, $data['version'], $data['dependencies'], $data['group'], $data['source'] === 'local' ? null : $data['source'], $data['skip']]; } // Register modules $out .= "\n" . ResourceLoader::makeLoaderRegisterScript($registrations); return $out; }
/** * Add X-Served-By and X-Backend-Response-Time response headers to ResourceLoader * * See BAC-1319 for details * * @param ResourceLoader $rl * @param ResourceLoaderContext $context * @return bool * @author macbre */ static function onResourceLoaderAfterRespond(ResourceLoader $rl, ResourceLoaderContext $context) { self::addExtraHeaders($context->getRequest()->response()); return true; }
/** * Respond with 304 Last Modified if appropiate. * * If there's an If-Modified-Since header, respond with a 304 appropriately * and clear out the output buffer. If the client cache is too old then do nothing. * * @param $context ResourceLoaderContext * @param string $mtime The TS_MW timestamp to check the header against * @return bool True if 304 header sent and output handled */ protected function tryRespondLastModified(ResourceLoaderContext $context, $mtime) { // If there's an If-Modified-Since header, respond with a 304 appropriately // Some clients send "timestamp;length=123". Strip the part after the first ';' // so we get a valid timestamp. $ims = $context->getRequest()->getHeader('If-Modified-Since'); // Never send 304s in debug mode if ($ims !== false && !$context->getDebug()) { $imsTS = strtok($ims, ';'); if ($mtime <= wfTimestamp(TS_UNIX, $imsTS)) { // There's another bug in ob_gzhandler (see also the comment at // the top of this function) that causes it to gzip even empty // responses, meaning it's impossible to produce a truly empty // response (because the gzip header is always there). This is // a problem because 304 responses have to be completely empty // per the HTTP spec, and Firefox behaves buggily when they're not. // See also http://bugs.php.net/bug.php?id=51579 // To work around this, we tear down all output buffering before // sending the 304. wfResetOutputBuffers(true); header('HTTP/1.0 304 Not Modified'); header('Status: 304 Not Modified'); return true; } } return false; }
public function getRequest() { return $this->context->getRequest(); }
private static function makeContext(ResourceLoaderContext $mainContext, $group, $type, array $extraQuery = []) { // Create new ResourceLoaderContext so that $extraQuery may trigger isRaw(). $req = new FauxRequest(array_merge($mainContext->getRequest()->getValues(), $extraQuery)); // Set 'only' if not combined $req->setVal('only', $type === ResourceLoaderModule::TYPE_COMBINED ? null : $type); // Remove user parameter in most cases if ($group !== 'user' && $group !== 'private') { $req->setVal('user', null); } $context = new ResourceLoaderContext($mainContext->getResourceLoader(), $req); // Allow caller to setVersion() and setModules() return new DerivativeResourceLoaderContext($context); }
/** * Get registration code for all modules. * * @param ResourceLoaderContext $context * @return string JavaScript code for registering all modules with the client loader */ public function getModuleRegistrations(ResourceLoaderContext $context) { wfProfileIn(__METHOD__); $resourceLoader = $context->getResourceLoader(); $target = $context->getRequest()->getVal('target', 'desktop'); $out = ''; $registryData = array(); // Get registry data foreach ($resourceLoader->getModuleNames() as $name) { $module = $resourceLoader->getModule($name); $moduleTargets = $module->getTargets(); if (!in_array($target, $moduleTargets)) { continue; } if ($module->isRaw()) { // Don't register "raw" modules (like 'jquery' and 'mediawiki') client-side because // depending on them is illegal anyway and would only lead to them being reloaded // causing any state to be lost (like jQuery plugins, mw.config etc.) continue; } // getModifiedTime() is supposed to return a UNIX timestamp, but it doesn't always // seem to do that, and custom implementations might forget. Coerce it to TS_UNIX $moduleMtime = wfTimestamp(TS_UNIX, $module->getModifiedTime($context)); $mtime = max($moduleMtime, wfTimestamp(TS_UNIX, $this->getConfig()->get('CacheEpoch'))); // FIXME: Convert to numbers, wfTimestamp always gives us stings, even for TS_UNIX $skipFunction = $module->getSkipFunction(); if ($skipFunction !== null && !ResourceLoader::inDebugMode()) { $skipFunction = $resourceLoader->filter('minify-js', $skipFunction, false); } $registryData[$name] = array('version' => $mtime, 'dependencies' => $module->getDependencies(), 'group' => $module->getGroup(), 'source' => $module->getSource(), 'loader' => $module->getLoaderScript(), 'skip' => $skipFunction); } self::compileUnresolvedDependencies($registryData); // Register sources $out .= ResourceLoader::makeLoaderSourcesScript($resourceLoader->getSources()); // Concatenate module loader scripts and figure out the different call // signatures for mw.loader.register $registrations = array(); foreach ($registryData as $name => $data) { if ($data['loader'] !== false) { $out .= ResourceLoader::makeCustomLoaderScript($name, wfTimestamp(TS_ISO_8601_BASIC, $data['version']), $data['dependencies'], $data['group'], $data['source'], $data['loader']); continue; } if (!count($data['dependencies']) && $data['group'] === null && $data['source'] === 'local' && $data['skip'] === null) { // Modules with no dependencies, group, foreign source or skip function; // call mw.loader.register(name, timestamp) $registrations[] = array($name, $data['version']); } elseif ($data['group'] === null && $data['source'] === 'local' && $data['skip'] === null) { // Modules with dependencies but no group, foreign source or skip function; // call mw.loader.register(name, timestamp, dependencies) $registrations[] = array($name, $data['version'], $data['dependencies']); } elseif ($data['source'] === 'local' && $data['skip'] === null) { // Modules with a group but no foreign source or skip function; // call mw.loader.register(name, timestamp, dependencies, group) $registrations[] = array($name, $data['version'], $data['dependencies'], $data['group']); } elseif ($data['skip'] === null) { // Modules with a foreign source but no skip function; // call mw.loader.register(name, timestamp, dependencies, group, source) $registrations[] = array($name, $data['version'], $data['dependencies'], $data['group'], $data['source']); } else { // Modules with a skip function; // call mw.loader.register(name, timestamp, dependencies, group, source, skip) $registrations[] = array($name, $data['version'], $data['dependencies'], $data['group'], $data['source'], $data['skip']); } } // Register modules $out .= ResourceLoader::makeLoaderRegisterScript($registrations); wfProfileOut(__METHOD__); return $out; }
/** * Hook handler. * * If mode equals 'articles' in the request, bootstraps fake module and reinitialize * ResourceLoaderContext object to include the just-defined fake module. * * @param $resourceLoader ResourceLoader * @param $context ResourceLoaderContext * @return bool */ public function onResourceLoaderBeforeRespond($resourceLoader, ResourceLoaderContext &$context) { /* @var $request WebRequest */ $request = $context->getRequest(); if ($request->getVal('mode') !== 'articles') { return true; } $only = $context->getOnly(); $type = $this->getTypeByOnly($only); if (empty($type)) { return true; } $articles = $request->getVal('articles'); $articles = explode('|', $articles); if (empty($articles)) { return true; } // prepare fake ResourceLoader module metadata $moduleName = md5(serialize(array($articles, $only, $context->getHash()))); $moduleFullName = 'wikia.fake.articles.' . $moduleName; $moduleInfo = array('class' => 'ResourceLoaderCustomWikiModule', 'articles' => $this->parseArticleNames($articles), 'type' => $type); // register new fake module $resourceLoader->register($moduleFullName, $moduleInfo); // reinitialize ResourceLoader context $request->setVal('modules', $moduleFullName); $context = new ResourceLoaderContext($resourceLoader, $request); return true; }
/** * 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)); }
protected function getFileContents($fileName, ResourceLoaderContext $context) { switch (self::getFileType($fileName)) { case self::FILE_TYPE_REGULAR: return file_get_contents($fileName); break; case self::FILE_TYPE_SASS: $sass = new SassService($fileName); $params = $context->getRequest()->getVal('sass_params'); $params = !empty($params) ? FormatJson::decode($params, true) : array(); return $sass->getContents($params); break; } }
public function testAccessors() { $ctx = new ResourceLoaderContext($this->getResourceLoader(), new FauxRequest([])); $this->assertInstanceOf(WebRequest::class, $ctx->getRequest()); $this->assertInstanceOf(\Psr\Log\LoggerInterface::class, $ctx->getLogger()); }
/** * If there's an If-Modified-Since header, respond with a 304 appropriately * and clear out the output buffer. If the client cache is too old then do nothing. * @param $context ResourceLoaderContext * @param $mtime string The TS_MW timestamp to check the header against * @return bool True iff 304 header sent and output handled */ protected function tryRespondLastModified(ResourceLoaderContext $context, $mtime) { // If there's an If-Modified-Since header, respond with a 304 appropriately // Some clients send "timestamp;length=123". Strip the part after the first ';' // so we get a valid timestamp. $ims = $context->getRequest()->getHeader('If-Modified-Since'); // Never send 304s in debug mode if ($ims !== false && !$context->getDebug()) { $imsTS = strtok($ims, ';'); if ($mtime <= wfTimestamp(TS_UNIX, $imsTS)) { // There's another bug in ob_gzhandler (see also the comment at // the top of this function) that causes it to gzip even empty // responses, meaning it's impossible to produce a truly empty // response (because the gzip header is always there). This is // a problem because 304 responses have to be completely empty // per the HTTP spec, and Firefox behaves buggily when they're not. // See also http://bugs.php.net/bug.php?id=51579 // To work around this, we tear down all output buffering before // sending the 304. // On some setups, ob_get_level() doesn't seem to go down to zero // no matter how often we call ob_get_clean(), so instead of doing // the more intuitive while ( ob_get_level() > 0 ) ob_get_clean(); // we have to be safe here and avoid an infinite loop. for ($i = 0; $i < ob_get_level(); $i++) { ob_end_clean(); } header('HTTP/1.0 304 Not Modified'); header('Status: 304 Not Modified'); return true; } } return false; }
/** * Get registration code for all modules. * * @param ResourceLoaderContext $context * @return string JavaScript code for registering all modules with the client loader */ public function getModuleRegistrations(ResourceLoaderContext $context) { $resourceLoader = $context->getResourceLoader(); $target = $context->getRequest()->getVal('target', 'desktop'); $out = ''; $registryData = array(); // Get registry data foreach ($resourceLoader->getModuleNames() as $name) { $module = $resourceLoader->getModule($name); $moduleTargets = $module->getTargets(); if (!in_array($target, $moduleTargets)) { continue; } if ($module->isRaw()) { // Don't register "raw" modules (like 'jquery' and 'mediawiki') client-side because // depending on them is illegal anyway and would only lead to them being reloaded // causing any state to be lost (like jQuery plugins, mw.config etc.) continue; } $versionHash = $module->getVersionHash($context); if (strlen($versionHash) !== 8) { // Module implementation either broken or deviated from ResourceLoader::makeHash // Asserted by tests/phpunit/structure/ResourcesTest. $versionHash = ResourceLoader::makeHash($versionHash); } $skipFunction = $module->getSkipFunction(); if ($skipFunction !== null && !ResourceLoader::inDebugMode()) { $skipFunction = $resourceLoader->filter('minify-js', $skipFunction, false); } $registryData[$name] = array('version' => $versionHash, 'dependencies' => $module->getDependencies($context), 'group' => $module->getGroup(), 'source' => $module->getSource(), 'loader' => $module->getLoaderScript(), 'skip' => $skipFunction); } self::compileUnresolvedDependencies($registryData); // Register sources $out .= ResourceLoader::makeLoaderSourcesScript($resourceLoader->getSources()); // Concatenate module loader scripts and figure out the different call // signatures for mw.loader.register $registrations = array(); foreach ($registryData as $name => $data) { if ($data['loader'] !== false) { $out .= ResourceLoader::makeCustomLoaderScript($name, $data['version'], $data['dependencies'], $data['group'], $data['source'], $data['loader']); continue; } // Call mw.loader.register(name, version, dependencies, group, source, skip) $registrations[] = array($name, $data['version'], $data['dependencies'], $data['group'], $data['source'] === 'local' ? null : $data['source'], $data['skip']); } // Register modules $out .= "\n" . ResourceLoader::makeLoaderRegisterScript($registrations); return $out; }
/** * Helper for createLoaderURL() * * @since 1.24 * @see makeLoaderQuery * @param ResourceLoaderContext $context * @param array $extraQuery * @return array */ public static function createLoaderQuery(ResourceLoaderContext $context, $extraQuery = array()) { return self::makeLoaderQuery($context->getModules(), $context->getLanguage(), $context->getSkin(), $context->getUser(), $context->getVersion(), $context->getDebug(), $context->getOnly(), $context->getRequest()->getBool('printable'), $context->getRequest()->getBool('handheld'), $extraQuery); }
/** * Get registration code for all modules. * * @param ResourceLoaderContext $context * @return string JavaScript code for registering all modules with the client loader */ public function getModuleRegistrations(ResourceLoaderContext $context) { $resourceLoader = $context->getResourceLoader(); $target = $context->getRequest()->getVal('target', 'desktop'); $out = ''; $registryData = array(); // Get registry data foreach ($resourceLoader->getModuleNames() as $name) { $module = $resourceLoader->getModule($name); $moduleTargets = $module->getTargets(); if (!in_array($target, $moduleTargets)) { continue; } if ($module->isRaw()) { // Don't register "raw" modules (like 'jquery' and 'mediawiki') client-side because // depending on them is illegal anyway and would only lead to them being reloaded // causing any state to be lost (like jQuery plugins, mw.config etc.) continue; } // Coerce module timestamp to UNIX timestamp. // getModifiedTime() is supposed to return a UNIX timestamp, but custom implementations // might forget. TODO: Maybe emit warning? $moduleMtime = wfTimestamp(TS_UNIX, $module->getModifiedTime($context)); $skipFunction = $module->getSkipFunction(); if ($skipFunction !== null && !ResourceLoader::inDebugMode()) { $skipFunction = $resourceLoader->filter('minify-js', $skipFunction, false); } $mtime = max($moduleMtime, wfTimestamp(TS_UNIX, $this->getConfig()->get('CacheEpoch'))); $registryData[$name] = array('version' => (int) $mtime, 'dependencies' => $module->getDependencies(), 'group' => $module->getGroup(), 'source' => $module->getSource(), 'loader' => $module->getLoaderScript(), 'skip' => $skipFunction); } self::compileUnresolvedDependencies($registryData); // Register sources $out .= ResourceLoader::makeLoaderSourcesScript($resourceLoader->getSources()); // Concatenate module loader scripts and figure out the different call // signatures for mw.loader.register $registrations = array(); foreach ($registryData as $name => $data) { if ($data['loader'] !== false) { $out .= ResourceLoader::makeCustomLoaderScript($name, $data['version'], $data['dependencies'], $data['group'], $data['source'], $data['loader']); continue; } // Call mw.loader.register(name, timestamp, dependencies, group, source, skip) $registrations[] = array($name, $data['version'], $data['dependencies'], $data['group'], $data['source'] === 'local' ? null : $data['source'], $data['skip']); } // Register modules $out .= ResourceLoader::makeLoaderRegisterScript($registrations); return $out; }