/**
  * @param $context ResourceLoaderContext
  * @return array|mixed
  */
 public function getModifiedTime(ResourceLoaderContext $context)
 {
     global $IP, $wgCacheEpoch;
     $hash = $context->getHash();
     if (isset($this->modifiedTime[$hash])) {
         return $this->modifiedTime[$hash];
     }
     // Call preloadModuleInfo() on ALL modules as we're about
     // to call getModifiedTime() on all of them
     $loader = $context->getResourceLoader();
     $loader->preloadModuleInfo($loader->getModuleNames(), $context);
     $this->modifiedTime[$hash] = filemtime("{$IP}/resources/startup.js");
     // ATTENTION!: Because of the line above, this is not going to cause
     // infinite recursion - think carefully before making changes to this
     // code!
     $time = wfTimestamp(TS_UNIX, $wgCacheEpoch);
     foreach ($loader->getModuleNames() as $name) {
         $module = $loader->getModule($name);
         $time = max($time, $module->getModifiedTime($context));
     }
     return $this->modifiedTime[$hash] = $time;
 }
 /**
  * @param ResourceLoaderContext $context
  * @return array|mixed
  */
 public function getModifiedTime(ResourceLoaderContext $context)
 {
     global $IP, $wgCacheEpoch;
     $hash = $context->getHash();
     if (isset($this->modifiedTime[$hash])) {
         return $this->modifiedTime[$hash];
     }
     // Call preloadModuleInfo() on ALL modules as we're about
     // to call getModifiedTime() on all of them
     $loader = $context->getResourceLoader();
     $loader->preloadModuleInfo($loader->getModuleNames(), $context);
     $time = max(wfTimestamp(TS_UNIX, $wgCacheEpoch), filemtime("{$IP}/resources/src/startup.js"), $this->getHashMtime($context));
     // ATTENTION!: Because of the line below, this is not going to cause
     // infinite recursion - think carefully before making changes to this
     // code!
     // Pre-populate modifiedTime with something because the the loop over
     // all modules below includes the the startup module (this module).
     $this->modifiedTime[$hash] = 1;
     foreach ($loader->getModuleNames() as $name) {
         $module = $loader->getModule($name);
         $time = max($time, $module->getModifiedTime($context));
     }
     $this->modifiedTime[$hash] = $time;
     return $this->modifiedTime[$hash];
 }
 /**
  * Helper method for getDefinitionSummary().
  *
  * @param ResourceLoaderContext $context
  * @return string SHA-1
  */
 protected function getAllModuleHashes(ResourceLoaderContext $context)
 {
     $rl = $context->getResourceLoader();
     // Preload for getCombinedVersion()
     $rl->preloadModuleInfo($rl->getModuleNames(), $context);
     // ATTENTION: Because of the line below, this is not going to cause infinite recursion.
     // Think carefully before making changes to this code!
     // Pre-populate versionHash with something because the loop over all modules below includes
     // the startup module (this module).
     // See ResourceLoaderModule::getVersionHash() for usage of this cache.
     $this->versionHash[$context->getHash()] = null;
     return $rl->getCombinedVersion($context, $rl->getModuleNames());
 }
 public function getResourceLoader()
 {
     return $this->context->getResourceLoader();
 }
 /**
  * Bundle all resources attached to this module into an array.
  *
  * @since 1.26
  * @param ResourceLoaderContext $context
  * @return array
  */
 protected final function buildContent(ResourceLoaderContext $context)
 {
     $rl = $context->getResourceLoader();
     $stats = RequestContext::getMain()->getStats();
     $statStart = microtime(true);
     // Only include properties that are relevant to this context (e.g. only=scripts)
     // and that are non-empty (e.g. don't include "templates" for modules without
     // templates). This helps prevent invalidating cache for all modules when new
     // optional properties are introduced.
     $content = array();
     // Scripts
     if ($context->shouldIncludeScripts()) {
         // If we are in debug mode, we'll want to return an array of URLs if possible
         // However, we can't do this if the module doesn't support it
         // We also can't do this if there is an only= parameter, because we have to give
         // the module a way to return a load.php URL without causing an infinite loop
         if ($context->getDebug() && !$context->getOnly() && $this->supportsURLLoading()) {
             $scripts = $this->getScriptURLsForDebug($context);
         } else {
             $scripts = $this->getScript($context);
             // rtrim() because there are usually a few line breaks
             // after the last ';'. A new line at EOF, a new line
             // added by ResourceLoaderFileModule::readScriptFiles, etc.
             if (is_string($scripts) && strlen($scripts) && substr(rtrim($scripts), -1) !== ';') {
                 // Append semicolon to prevent weird bugs caused by files not
                 // terminating their statements right (bug 27054)
                 $scripts .= ";\n";
             }
         }
         $content['scripts'] = $scripts;
     }
     // Styles
     if ($context->shouldIncludeStyles()) {
         $styles = array();
         // Don't create empty stylesheets like array( '' => '' ) for modules
         // that don't *have* any stylesheets (bug 38024).
         $stylePairs = $this->getStyles($context);
         if (count($stylePairs)) {
             // If we are in debug mode without &only= set, we'll want to return an array of URLs
             // See comment near shouldIncludeScripts() for more details
             if ($context->getDebug() && !$context->getOnly() && $this->supportsURLLoading()) {
                 $styles = array('url' => $this->getStyleURLsForDebug($context));
             } else {
                 // Minify CSS before embedding in mw.loader.implement call
                 // (unless in debug mode)
                 if (!$context->getDebug()) {
                     foreach ($stylePairs as $media => $style) {
                         // Can be either a string or an array of strings.
                         if (is_array($style)) {
                             $stylePairs[$media] = array();
                             foreach ($style as $cssText) {
                                 if (is_string($cssText)) {
                                     $stylePairs[$media][] = $rl->filter('minify-css', $cssText);
                                 }
                             }
                         } elseif (is_string($style)) {
                             $stylePairs[$media] = $rl->filter('minify-css', $style);
                         }
                     }
                 }
                 // Wrap styles into @media groups as needed and flatten into a numerical array
                 $styles = array('css' => $rl->makeCombinedStyles($stylePairs));
             }
         }
         $content['styles'] = $styles;
     }
     // Messages
     $blobs = $rl->getMessageBlobStore()->get($rl, array($this->getName() => $this), $context->getLanguage());
     if (isset($blobs[$this->getName()])) {
         $content['messagesBlob'] = $blobs[$this->getName()];
     }
     $templates = $this->getTemplates();
     if ($templates) {
         $content['templates'] = $templates;
     }
     $statTiming = microtime(true) - $statStart;
     $statName = strtr($this->getName(), '.', '_');
     $stats->timing("resourceloader_build.all", 1000 * $statTiming);
     $stats->timing("resourceloader_build.{$statName}", 1000 * $statTiming);
     return $content;
 }
 /**
  * @param ResourceLoaderContext $context
  * @return array
  */
 public function getStyles(ResourceLoaderContext $context)
 {
     $this->loadFromDefinition();
     // Build CSS rules
     $rules = [];
     $script = $context->getResourceLoader()->getLoadScript($this->getSource());
     $selectors = $this->getSelectors();
     foreach ($this->getImages($context) as $name => $image) {
         $declarations = $this->getCssDeclarations($image->getDataUri($context, null, 'original'), $image->getUrl($context, $script, null, 'rasterized'));
         $declarations = implode("\n\t", $declarations);
         $selector = strtr($selectors['selectorWithoutVariant'], ['{prefix}' => $this->getPrefix(), '{name}' => $name, '{variant}' => '']);
         $rules[] = "{$selector} {\n\t{$declarations}\n}";
         foreach ($image->getVariants() as $variant) {
             $declarations = $this->getCssDeclarations($image->getDataUri($context, $variant, 'original'), $image->getUrl($context, $script, $variant, 'rasterized'));
             $declarations = implode("\n\t", $declarations);
             $selector = strtr($selectors['selectorWithVariant'], ['{prefix}' => $this->getPrefix(), '{name}' => $name, '{variant}' => $variant]);
             $rules[] = "{$selector} {\n\t{$declarations}\n}";
         }
     }
     $style = implode("\n", $rules);
     return ['all' => $style];
 }
Пример #7
0
 /**
  * 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 ResourceLoaderContext $context
  * @return array Array( mediaType => array( URL1, URL2, ... ), ... )
  */
 public function getStyleURLsForDebug(ResourceLoaderContext $context)
 {
     $resourceLoader = $context->getResourceLoader();
     $derivative = new DerivativeResourceLoaderContext($context);
     $derivative->setModules(array($this->getName()));
     $derivative->setOnly('styles');
     $derivative->setDebug(true);
     $url = $resourceLoader->createLoaderURL($this->getSource(), $derivative);
     return array('all' => array($url));
 }
 /**
  * Explicily load or embed modules on a page.
  *
  * @param ResourceLoaderContext $mainContext
  * @param array $modules One or more module names
  * @param string $only ResourceLoaderModule TYPE_ class constant
  * @param array $extraQuery [optional] Array with extra query parameters for the request
  * @return string|WrappedStringList HTML
  */
 public static function makeLoad(ResourceLoaderContext $mainContext, array $modules, $only, array $extraQuery = [])
 {
     $rl = $mainContext->getResourceLoader();
     $chunks = [];
     if ($mainContext->getDebug() && count($modules) > 1) {
         $chunks = [];
         // Recursively call us for every item
         foreach ($modules as $name) {
             $chunks[] = self::makeLoad($mainContext, [$name], $only, $extraQuery);
         }
         return new WrappedStringList("\n", $chunks);
     }
     // Sort module names so requests are more uniform
     sort($modules);
     // Create keyed-by-source and then keyed-by-group list of module objects from modules list
     $sortedModules = [];
     foreach ($modules as $name) {
         $module = $rl->getModule($name);
         if (!$module) {
             $rl->getLogger()->warning('Unknown module "{module}"', ['module' => $name]);
             continue;
         }
         $sortedModules[$module->getSource()][$module->getGroup()][$name] = $module;
     }
     foreach ($sortedModules as $source => $groups) {
         foreach ($groups as $group => $grpModules) {
             $context = self::makeContext($mainContext, $group, $only, $extraQuery);
             $context->setModules(array_keys($grpModules));
             if ($group === 'private') {
                 // Decide whether to use style or script element
                 if ($only == ResourceLoaderModule::TYPE_STYLES) {
                     $chunks[] = Html::inlineStyle($rl->makeModuleResponse($context, $grpModules));
                 } else {
                     $chunks[] = ResourceLoader::makeInlineScript($rl->makeModuleResponse($context, $grpModules));
                 }
                 continue;
             }
             // See if we have one or more raw modules
             $isRaw = false;
             foreach ($grpModules as $key => $module) {
                 $isRaw |= $module->isRaw();
             }
             // 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 CDN-cached HTML
             if ($group === 'user') {
                 // Must setModules() before makeVersionQuery()
                 $context->setVersion($rl->makeVersionQuery($context));
             }
             $url = $rl->createLoaderURL($source, $context, $extraQuery);
             // Decide whether to use 'style' or 'script' element
             if ($only === ResourceLoaderModule::TYPE_STYLES) {
                 $chunk = Html::linkedStyle($url);
             } else {
                 if ($context->getRaw() || $isRaw) {
                     $chunk = Html::element('script', ['async' => !isset($extraQuery['sync']), 'src' => $url]);
                 } else {
                     $chunk = ResourceLoader::makeInlineScript(Xml::encodeJsCall('mw.loader.load', [$url]));
                 }
             }
             if ($group == 'noscript') {
                 $chunks[] = Html::rawElement('noscript', [], $chunk);
             } else {
                 $chunks[] = $chunk;
             }
         }
     }
     return new WrappedStringList("\n", $chunks);
 }
 /**
  * @since 1.28
  * @param ResourceLoaderContext $context
  * @param IDatabase $db
  * @param string[] $moduleNames
  */
 public static function preloadTitleInfo(ResourceLoaderContext $context, IDatabase $db, array $moduleNames)
 {
     $rl = $context->getResourceLoader();
     // getDB() can be overridden to point to a foreign database.
     // For now, only preload local. In the future, we could preload by wikiID.
     $allPages = [];
     /** @var ResourceLoaderWikiModule[] $wikiModules */
     $wikiModules = [];
     foreach ($moduleNames as $name) {
         $module = $rl->getModule($name);
         if ($module instanceof self) {
             $mDB = $module->getDB();
             // Subclasses may disable getDB and implement getTitleInfo differently
             if ($mDB && $mDB->getWikiID() === $db->getWikiID()) {
                 $wikiModules[] = $module;
                 $allPages += $module->getPages($context);
             }
         }
     }
     $pageNames = array_keys($allPages);
     sort($pageNames);
     $hash = sha1(implode('|', $pageNames));
     // Avoid Zend bug where "static::" does not apply LSB in the closure
     $func = [static::class, 'fetchTitleInfo'];
     $fname = __METHOD__;
     $cache = ObjectCache::getMainWANInstance();
     $allInfo = $cache->getWithSetCallback($cache->makeGlobalKey('resourceloader', 'titleinfo', $db->getWikiID(), $hash), $cache::TTL_HOUR, function ($curVal, &$ttl, array &$setOpts) use($func, $pageNames, $db, $fname) {
         $setOpts += Database::getCacheSetOptions($db);
         return call_user_func($func, $db, $pageNames, $fname);
     }, ['checkKeys' => [$cache->makeGlobalKey('resourceloader', 'titleinfo', $db->getWikiID())]]);
     foreach ($wikiModules as $wikiModule) {
         $pages = $wikiModule->getPages($context);
         // Before we intersect, map the names to canonical form (T145673).
         $intersect = [];
         foreach ($pages as $page => $unused) {
             $title = Title::newFromText($page);
             if ($title) {
                 $intersect[$title->getPrefixedText()] = 1;
             } else {
                 // Page name may be invalid if user-provided (e.g. gadgets)
                 $rl->getLogger()->info('Invalid wiki page title "{title}" in ' . __METHOD__, ['title' => $page]);
             }
         }
         $info = array_intersect_key($allInfo, $intersect);
         $pageNames = array_keys($pages);
         sort($pageNames);
         $key = implode('|', $pageNames);
         $wikiModule->setTitleInfo($key, $info);
     }
 }
 public function getScript(ResourceLoaderContext $context)
 {
     $config = $context->getResourceLoader()->getConfig();
     return ResourceLoader::makeConfigSetScript(['wgUploadDialog' => $config->get('UploadDialog')]);
 }