/** * Generate code for a response. * * @param ResourceLoaderContext $context Context in which to generate a response * @param array $modules List of module objects keyed by module name * @param array $missing List of requested module names that are unregistered (optional) * @return string Response data */ public function makeModuleResponse(ResourceLoaderContext $context, array $modules, array $missing = array()) { $out = ''; $states = array(); if (!count($modules) && !count($missing)) { return <<<MESSAGE /* This file is the Web entry point for MediaWiki's ResourceLoader: <https://www.mediawiki.org/wiki/ResourceLoader>. In this request, no modules were requested. Max made me put this here. */ MESSAGE; } $image = $context->getImageObj(); if ($image) { $data = $image->getImageData($context); if ($data === false) { $data = ''; $this->errors[] = 'Image generation failed'; } return $data; } // Pre-fetch blobs if ($context->shouldIncludeMessages()) { try { $this->blobStore->get($this, $modules, $context->getLanguage()); } catch (Exception $e) { MWExceptionHandler::logException($e); $this->logger->warning('Prefetching MessageBlobStore failed: {exception}', array('exception' => $e)); $this->errors[] = self::formatExceptionNoComment($e); } } foreach ($missing as $name) { $states[$name] = 'missing'; } // Generate output $isRaw = false; foreach ($modules as $name => $module) { try { $content = $module->getModuleContent($context); // Append output switch ($context->getOnly()) { case 'scripts': $scripts = $content['scripts']; if (is_string($scripts)) { // Load scripts raw... $out .= $scripts; } elseif (is_array($scripts)) { // ...except when $scripts is an array of URLs $out .= self::makeLoaderImplementScript($name, $scripts, array(), array()); } break; case 'styles': $styles = $content['styles']; // We no longer seperate into media, they are all combined now with // custom media type groups into @media .. {} sections as part of the css string. // Module returns either an empty array or a numerical array with css strings. $out .= isset($styles['css']) ? implode('', $styles['css']) : ''; break; default: $out .= self::makeLoaderImplementScript($name, isset($content['scripts']) ? $content['scripts'] : '', isset($content['styles']) ? $content['styles'] : array(), isset($content['messagesBlob']) ? new XmlJsCode($content['messagesBlob']) : array(), isset($content['templates']) ? $content['templates'] : array()); break; } } catch (Exception $e) { MWExceptionHandler::logException($e); $this->logger->warning('Generating module package failed: {exception}', array('exception' => $e)); $this->errors[] = self::formatExceptionNoComment($e); // Respond to client with error-state instead of module implementation $states[$name] = 'error'; unset($modules[$name]); } $isRaw |= $module->isRaw(); } // Update module states if ($context->shouldIncludeScripts() && !$context->getRaw() && !$isRaw) { if (count($modules) && $context->getOnly() === 'scripts') { // Set the state of modules loaded as only scripts to ready as // they don't have an mw.loader.implement wrapper that sets the state foreach ($modules as $name => $module) { $states[$name] = 'ready'; } } // Set the state of modules we didn't respond to with mw.loader.implement if (count($states)) { $out .= self::makeLoaderStateScript($states); } } else { if (count($states)) { $this->errors[] = 'Problematic modules: ' . FormatJson::encode($states, ResourceLoader::inDebugMode()); } } $enableFilterCache = true; if (count($modules) === 1 && reset($modules) instanceof ResourceLoaderUserTokensModule) { // If we're building the embedded user.tokens, don't cache (T84960) $enableFilterCache = false; } if (!$context->getDebug()) { if ($context->getOnly() === 'styles') { $out = $this->filter('minify-css', $out); } else { $out = $this->filter('minify-js', $out, array('cache' => $enableFilterCache)); } } return $out; }
/** * Generate code for a response. * * @param ResourceLoaderContext $context Context in which to generate a response * @param array $modules List of module objects keyed by module name * @param array $missing List of requested module names that are unregistered (optional) * @return string Response data */ public function makeModuleResponse(ResourceLoaderContext $context, array $modules, array $missing = array()) { $out = ''; $states = array(); if (!count($modules) && !count($missing)) { return <<<MESSAGE /* This file is the Web entry point for MediaWiki's ResourceLoader: <https://www.mediawiki.org/wiki/ResourceLoader>. In this request, no modules were requested. Max made me put this here. */ MESSAGE; } $image = $context->getImageObj(); if ($image) { $data = $image->getImageData($context); if ($data === false) { $data = ''; $this->errors[] = 'Image generation failed'; } return $data; } // Pre-fetch blobs if ($context->shouldIncludeMessages()) { try { $blobs = $this->blobStore->get($this, $modules, $context->getLanguage()); } catch (Exception $e) { MWExceptionHandler::logException($e); wfDebugLog('resourceloader', __METHOD__ . ": pre-fetching blobs from MessageBlobStore failed: {$e}"); $this->errors[] = self::formatExceptionNoComment($e); } } else { $blobs = array(); } foreach ($missing as $name) { $states[$name] = 'missing'; } // Generate output $isRaw = false; foreach ($modules as $name => $module) { /** * @var $module ResourceLoaderModule */ try { $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() && $module->supportsURLLoading()) { $scripts = $module->getScriptURLsForDebug($context); } else { $scripts = $module->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"; } } } // Styles $styles = array(); if ($context->shouldIncludeStyles()) { // Don't create empty stylesheets like array( '' => '' ) for modules // that don't *have* any stylesheets (bug 38024). $stylePairs = $module->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() && $module->supportsURLLoading()) { $styles = array('url' => $module->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][] = $this->filter('minify-css', $cssText); } } } elseif (is_string($style)) { $stylePairs[$media] = $this->filter('minify-css', $style); } } } // Wrap styles into @media groups as needed and flatten into a numerical array $styles = array('css' => self::makeCombinedStyles($stylePairs)); } } } // Messages $messagesBlob = isset($blobs[$name]) ? $blobs[$name] : '{}'; // Append output switch ($context->getOnly()) { case 'scripts': if (is_string($scripts)) { // Load scripts raw... $out .= $scripts; } elseif (is_array($scripts)) { // ...except when $scripts is an array of URLs $out .= self::makeLoaderImplementScript($name, $scripts, array(), array()); } break; case 'styles': // We no longer seperate into media, they are all combined now with // custom media type groups into @media .. {} sections as part of the css string. // Module returns either an empty array or a numerical array with css strings. $out .= isset($styles['css']) ? implode('', $styles['css']) : ''; break; case 'messages': $out .= self::makeMessageSetScript(new XmlJsCode($messagesBlob)); break; case 'templates': $out .= Xml::encodeJsCall('mw.templates.set', array($name, (object) $module->getTemplates()), ResourceLoader::inDebugMode()); break; default: $out .= self::makeLoaderImplementScript($name, $scripts, $styles, new XmlJsCode($messagesBlob), $module->getTemplates()); break; } } catch (Exception $e) { MWExceptionHandler::logException($e); wfDebugLog('resourceloader', __METHOD__ . ": generating module package failed: {$e}"); $this->errors[] = self::formatExceptionNoComment($e); // Respond to client with error-state instead of module implementation $states[$name] = 'error'; unset($modules[$name]); } $isRaw |= $module->isRaw(); } // Update module states if ($context->shouldIncludeScripts() && !$context->getRaw() && !$isRaw) { if (count($modules) && $context->getOnly() === 'scripts') { // Set the state of modules loaded as only scripts to ready as // they don't have an mw.loader.implement wrapper that sets the state foreach ($modules as $name => $module) { $states[$name] = 'ready'; } } // Set the state of modules we didn't respond to with mw.loader.implement if (count($states)) { $out .= self::makeLoaderStateScript($states); } } else { if (count($states)) { $this->errors[] = 'Problematic modules: ' . FormatJson::encode($states, ResourceLoader::inDebugMode()); } } if (!$context->getDebug()) { if ($context->getOnly() === 'styles') { $out = $this->filter('minify-css', $out); } else { $out = $this->filter('minify-js', $out); } } return $out; }
/** * Generates code for a response * * @param $context ResourceLoaderContext: Context in which to generate a response * @param $modules Array: List of module objects keyed by module name * @param $missing Array: List of unavailable modules (optional) * @return String: Response data */ public function makeModuleResponse(ResourceLoaderContext $context, array $modules, $missing = array()) { $out = ''; $exceptions = ''; if ($modules === array() && $missing === array()) { return '/* No modules requested. Max made me put this here */'; } wfProfileIn(__METHOD__); // Pre-fetch blobs if ($context->shouldIncludeMessages()) { try { $blobs = MessageBlobStore::get($this, $modules, $context->getLanguage()); } catch (Exception $e) { // Add exception to the output as a comment $exceptions .= $this->formatException($e); } } else { $blobs = array(); } // Generate output foreach ($modules as $name => $module) { /** * @var $module ResourceLoaderModule */ wfProfileIn(__METHOD__ . '-' . $name); try { $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() && $module->supportsURLLoading()) { $scripts = $module->getScriptURLsForDebug($context); } else { $scripts = $module->getScript($context); if (is_string($scripts)) { // bug 27054: Append semicolon to prevent weird bugs // caused by files not terminating their statements right $scripts .= ";\n"; } } } // Styles $styles = array(); if ($context->shouldIncludeStyles()) { // If we are in debug mode, we'll want to return an array of URLs // See comment near shouldIncludeScripts() for more details if ($context->getDebug() && !$context->getOnly() && $module->supportsURLLoading()) { $styles = $module->getStyleURLsForDebug($context); } else { $styles = $module->getStyles($context); } } // Messages $messagesBlob = isset($blobs[$name]) ? $blobs[$name] : '{}'; // Append output switch ($context->getOnly()) { case 'scripts': if (is_string($scripts)) { // Load scripts raw... $out .= $scripts; } elseif (is_array($scripts)) { // ...except when $scripts is an array of URLs $out .= self::makeLoaderImplementScript($name, $scripts, array(), array()); } break; case 'styles': $out .= self::makeCombinedStyles($styles); break; case 'messages': $out .= self::makeMessageSetScript(new XmlJsCode($messagesBlob)); break; default: // Minify CSS before embedding in mw.loader.implement call // (unless in debug mode) if (!$context->getDebug()) { foreach ($styles as $media => $style) { if (is_string($style)) { $styles[$media] = $this->filter('minify-css', $style); } } } $out .= self::makeLoaderImplementScript($name, $scripts, $styles, new XmlJsCode($messagesBlob)); break; } } catch (Exception $e) { // Add exception to the output as a comment $exceptions .= $this->formatException($e); // Register module as missing $missing[] = $name; unset($modules[$name]); } wfProfileOut(__METHOD__ . '-' . $name); } // Update module states if ($context->shouldIncludeScripts()) { // Set the state of modules loaded as only scripts to ready if (count($modules) && $context->getOnly() === 'scripts' && !isset($modules['startup'])) { $out .= self::makeLoaderStateScript(array_fill_keys(array_keys($modules), 'ready')); } // Set the state of modules which were requested but unavailable as missing if (is_array($missing) && count($missing)) { $out .= self::makeLoaderStateScript(array_fill_keys($missing, 'missing')); } } if (!$context->getDebug()) { if ($context->getOnly() === 'styles') { $out = $this->filter('minify-css', $out); } else { $out = $this->filter('minify-js', $out); } } wfProfileOut(__METHOD__); return $exceptions . $out; }
/** * Generates code for a response * * @param $context ResourceLoaderContext: Context in which to generate a response * @param $modules Array: List of module objects keyed by module name * @param $missing Array: List of unavailable modules (optional) * @return String: Response data */ public function makeModuleResponse(ResourceLoaderContext $context, array $modules, $missing = array()) { $out = ''; $exceptions = ''; if ($modules === array() && $missing === array()) { return '/* No modules requested. Max made me put this here */'; } wfProfileIn(__METHOD__); // Pre-fetch blobs if ($context->shouldIncludeMessages()) { try { $blobs = MessageBlobStore::get($this, $modules, $context->getLanguage()); } catch (Exception $e) { // Add exception to the output as a comment $exceptions .= $this->makeComment($e->__toString()); } } else { $blobs = array(); } // Generate output $isRaw = false; foreach ($modules as $name => $module) { /** * @var $module ResourceLoaderModule */ wfProfileIn(__METHOD__ . '-' . $name); try { $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() && $module->supportsURLLoading()) { $scripts = $module->getScriptURLsForDebug($context); } else { $scripts = $module->getScript($context); if (is_string($scripts) && strlen($scripts) && substr($scripts, -1) !== ';') { // bug 27054: Append semicolon to prevent weird bugs // caused by files not terminating their statements right $scripts .= ";\n"; } } } // Styles $styles = array(); if ($context->shouldIncludeStyles()) { // Don't create empty stylesheets like array( '' => '' ) for modules // that don't *have* any stylesheets (bug 38024). $stylePairs = $module->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() && $module->supportsURLLoading()) { $styles = array('url' => $module->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][] = $this->filter('minify-css', $cssText); } } } elseif (is_string($style)) { $stylePairs[$media] = $this->filter('minify-css', $style); } } } // Wrap styles into @media groups as needed and flatten into a numerical array $styles = array('css' => self::makeCombinedStyles($stylePairs)); } } } // Messages $messagesBlob = isset($blobs[$name]) ? $blobs[$name] : '{}'; // Append output switch ($context->getOnly()) { case 'scripts': if (is_string($scripts)) { // Load scripts raw... $out .= $scripts; } elseif (is_array($scripts)) { // ...except when $scripts is an array of URLs $out .= self::makeLoaderImplementScript($name, $scripts, array(), array()); } break; case 'styles': // We no longer seperate into media, they are all combined now with // custom media type groups into @media .. {} sections as part of the css string. // Module returns either an empty array or a numerical array with css strings. $out .= isset($styles['css']) ? implode('', $styles['css']) : ''; break; case 'messages': $out .= self::makeMessageSetScript(new XmlJsCode($messagesBlob)); break; default: $out .= self::makeLoaderImplementScript($name, $scripts, $styles, new XmlJsCode($messagesBlob)); break; } } catch (Exception $e) { // Add exception to the output as a comment $exceptions .= $this->makeComment($e->__toString()); // Register module as missing $missing[] = $name; unset($modules[$name]); } $isRaw |= $module->isRaw(); wfProfileOut(__METHOD__ . '-' . $name); } // Update module states if ($context->shouldIncludeScripts() && !$context->getRaw() && !$isRaw) { // Set the state of modules loaded as only scripts to ready if (count($modules) && $context->getOnly() === 'scripts') { $out .= self::makeLoaderStateScript(array_fill_keys(array_keys($modules), 'ready')); } // Set the state of modules which were requested but unavailable as missing if (is_array($missing) && count($missing)) { $out .= self::makeLoaderStateScript(array_fill_keys($missing, 'missing')); } } if (!$context->getDebug()) { if ($context->getOnly() === 'styles') { $out = $this->filter('minify-css', $out); } else { $out = $this->filter('minify-js', $out); } } wfProfileOut(__METHOD__); return $exceptions . $out; }
/** * Generates code for a response * * @param $context ResourceLoaderContext: Context in which to generate a response * @param $modules Array: List of module objects keyed by module name * @param $missing Array: List of unavailable modules (optional) * @return String: Response data */ public function makeModuleResponse(ResourceLoaderContext $context, array $modules, $missing = array()) { $out = ''; $exceptions = ''; if ($modules === array() && $missing === array()) { return '/* No modules requested. Max made me put this here */'; } wfProfileIn(__METHOD__); // Pre-fetch blobs if ($context->shouldIncludeMessages()) { try { $blobs = MessageBlobStore::get($this, $modules, $context->getLanguage()); } catch (Exception $e) { // Add exception to the output as a comment $exceptions .= "/*\n{$e->__toString()}\n*/\n"; } } else { $blobs = array(); } // Generate output foreach ($modules as $name => $module) { wfProfileIn(__METHOD__ . '-' . $name); try { // Scripts $scripts = ''; if ($context->shouldIncludeScripts()) { // bug 27054: Append semicolon to prevent weird bugs // caused by files not terminating their statements right $scripts .= $module->getScript($context) . ";\n"; } // Styles $styles = array(); if ($context->shouldIncludeStyles()) { $styles = $module->getStyles($context); } // Messages $messagesBlob = isset($blobs[$name]) ? $blobs[$name] : '{}'; // Append output switch ($context->getOnly()) { case 'scripts': $out .= $scripts; break; case 'styles': $out .= self::makeCombinedStyles($styles); break; case 'messages': $out .= self::makeMessageSetScript(new XmlJsCode($messagesBlob)); break; default: // Minify CSS before embedding in mediaWiki.loader.implement call // (unless in debug mode) if (!$context->getDebug()) { foreach ($styles as $media => $style) { $styles[$media] = $this->filter('minify-css', $style); } } $out .= self::makeLoaderImplementScript($name, $scripts, $styles, new XmlJsCode($messagesBlob)); break; } } catch (Exception $e) { // Add exception to the output as a comment $exceptions .= "/*\n{$e->__toString()}\n*/\n"; // Register module as missing $missing[] = $name; unset($modules[$name]); } wfProfileOut(__METHOD__ . '-' . $name); } // Update module states if ($context->shouldIncludeScripts()) { // Set the state of modules loaded as only scripts to ready if (count($modules) && $context->getOnly() === 'scripts' && !isset($modules['startup'])) { $out .= self::makeLoaderStateScript(array_fill_keys(array_keys($modules), 'ready')); } // Set the state of modules which were requested but unavailable as missing if (is_array($missing) && count($missing)) { $out .= self::makeLoaderStateScript(array_fill_keys($missing, 'missing')); } } if (!$context->getDebug()) { if ($context->getOnly() === 'styles') { $out = $this->filter('minify-css', $out); } else { $out = $this->filter('minify-js', $out); } } wfProfileOut(__METHOD__); return $exceptions . $out; }