/** * {@inheritdoc} */ public function getVisibleBlocksPerRegion(array &$cacheable_metadata = []) { $active_theme = $this->themeManager->getActiveTheme(); // Build an array of the region names in the right order. $empty = array_fill_keys($active_theme->getRegions(), array()); $full = array(); foreach ($this->blockStorage->loadByProperties(array('theme' => $active_theme->getName())) as $block_id => $block) { /** @var \Drupal\block\BlockInterface $block */ $access = $block->access('view', NULL, TRUE); $region = $block->getRegion(); if (!isset($cacheable_metadata[$region])) { $cacheable_metadata[$region] = CacheableMetadata::createFromObject($access); } else { $cacheable_metadata[$region] = $cacheable_metadata[$region]->merge(CacheableMetadata::createFromObject($access)); } // Set the contexts on the block before checking access. if ($access->isAllowed()) { $full[$region][$block_id] = $block; } } // Merge it with the actual values to maintain the region ordering. $assignments = array_intersect_key(array_merge($empty, $full), $empty); foreach ($assignments as &$assignment) { // Suppress errors because PHPUnit will indirectly modify the contents, // triggering https://bugs.php.net/bug.php?id=50688. @uasort($assignment, 'Drupal\\block\\Entity\\Block::sort'); } return $assignments; }
/** * {@inheritdoc} */ protected function getCid() { if (!isset($this->cid)) { $this->cid = 'library_info:' . $this->themeManager->getActiveTheme()->getName(); } return $this->cid; }
/** * {@inheritdoc} */ public function collect(Request $request, Response $response, \Exception $exception = NULL) { $activeTheme = $this->themeManager->getActiveTheme(); $this->data['activeTheme'] = ['name' => $activeTheme->getName(), 'path' => $activeTheme->getPath(), 'engine' => $activeTheme->getEngine(), 'owner' => $activeTheme->getOwner(), 'baseThemes' => $activeTheme->getBaseThemes(), 'extension' => $activeTheme->getExtension(), 'styleSheetsRemove' => $activeTheme->getStyleSheetsRemove(), 'libraries' => $activeTheme->getLibraries(), 'regions' => $activeTheme->getRegions()]; if ($this->themeNegotiator instanceof ThemeNegotiatorWrapper) { $this->data['negotiator'] = ['class' => $this->getMethodData($this->themeNegotiator->getNegotiator(), 'determineActiveTheme'), 'id' => $this->themeNegotiator->getNegotiator()->_serviceId]; } }
/** * Tests opting out of Stable by setting the base theme to false. */ public function testWildWest() { $this->themeHandler->install(['test_wild_west']); $this->config('system.theme')->set('default', 'test_wild_west')->save(); $theme = $this->themeManager->getActiveTheme(); /** @var \Drupal\Core\Theme\ActiveTheme $base_theme */ $base_themes = $theme->getBaseThemes(); $this->assertTrue(empty($base_themes), 'No base theme is set when a theme has opted out of using Stable.'); }
/** * {@inheritdoc} */ public function getInfo($type) { $theme_name = $this->themeManager->getActiveTheme()->getName(); if (!isset($this->elementInfo[$theme_name])) { $this->elementInfo[$theme_name] = $this->buildInfo($theme_name); } $info = isset($this->elementInfo[$theme_name][$type]) ? $this->elementInfo[$theme_name][$type] : array(); $info['#defaults_loaded'] = TRUE; return $info; }
/** * Tests that changes to the info file are picked up. */ public function testChanges() { $this->themeHandler->install(array('test_theme')); $this->themeHandler->setDefault('test_theme'); $this->themeManager->resetActiveTheme(); $active_theme = $this->themeManager->getActiveTheme(); // Make sure we are not testing the wrong theme. $this->assertEqual('test_theme', $active_theme->getName()); $this->assertEqual(['classy/base', 'core/normalize', 'test_theme/global-styling'], $active_theme->getLibraries()); // @see theme_test_system_info_alter() $this->state->set('theme_test.modify_info_files', TRUE); drupal_flush_all_caches(); $active_theme = $this->themeManager->getActiveTheme(); $this->assertEqual(['classy/base', 'core/normalize', 'test_theme/global-styling', 'core/backbone'], $active_theme->getLibraries()); }
/** * {@inheritdoc} */ public function evaluate() { if (!$this->configuration['theme']) { return TRUE; } return $this->themeManager->getActiveTheme()->getName() == $this->configuration['theme']; }
/** * Gets the name of the theme used for this block listing. * * @return string * The name of the theme. */ protected function getThemeName() { // If no theme was specified, use the current theme. if (!$this->theme) { $this->theme = $this->themeManager->getActiveTheme()->getName(); } return $this->theme; }
/** * Page callback: Tests the theme negotiation functionality. * * @param bool $inherited * TRUE when the requested page is intended to inherit * the theme of its parent. * * @return string * A string describing the requested custom theme and actual * theme being used * for the current page request. */ public function themePage($inherited) { $theme_key = $this->themeManager->getActiveTheme()->getName(); // Now we check what the theme negotiator service returns. $active_theme = $this->themeNegotiator->determineActiveTheme($this->routeMatch); $output = "Active theme: {$active_theme}. Actual theme: {$theme_key}."; if ($inherited) { $output .= ' Theme negotiation inheritance is being tested.'; } return ['#markup' => $output]; }
/** * {@inheritdoc} */ public function build() { $build = parent::build(); $active_theme = $this->themeManager->getActiveTheme(); $theme_name = $active_theme->getName(); $destination = $this->redirectDestination->get(); $visible_regions = $this->getVisibleRegionNames($theme_name); // Build an array of the region names in the right order. $build += array_fill_keys(array_keys($visible_regions), []); foreach ($visible_regions as $region => $region_name) { $query = ['region' => $region]; if ($destination) { $query['destination'] = $destination; } $title = $this->t('<span class="visually-hidden">Place block in the %region region</span>', ['%region' => $region_name]); $operations['block_description'] = ['#type' => 'inline_template', '#template' => '<div class="block-place-region">{{ link }}</div>', '#context' => ['link' => Link::createFromRoute($title, 'block.admin_library', ['theme' => $theme_name], ['query' => $query, 'attributes' => ['title' => $title, 'class' => ['use-ajax', 'button', 'button--small'], 'data-dialog-type' => 'modal', 'data-dialog-options' => Json::encode(['width' => 700])]])]]; $build[$region] = ['block_place_operations' => $operations] + $build[$region]; } $build['#attached']['library'][] = 'block_place/drupal.block_place'; return $build; }
/** * Apply libraries overrides specified for the current active theme. * * @param array $libraries * The libraries definitions. * @param string $extension * The extension in which these libraries are defined. * * @return array * The modified libraries definitions. */ protected function applyLibrariesOverride($libraries, $extension) { $active_theme = $this->themeManager->getActiveTheme(); // ActiveTheme::getLibrariesOverride() returns libraries-overrides for the // current theme as well as all its base themes. $all_libraries_overrides = $active_theme->getLibrariesOverride(); foreach ($all_libraries_overrides as $theme_path => $libraries_overrides) { foreach ($libraries as $library_name => $library) { // Process libraries overrides. if (isset($libraries_overrides["{$extension}/{$library_name}"])) { // Active theme defines an override for this library. $override_definition = $libraries_overrides["{$extension}/{$library_name}"]; if (is_string($override_definition) || $override_definition === FALSE) { // A string or boolean definition implies an override (or removal) // for the whole library. Use the override key to specify that this // library will be overridden when it is called. // @see \Drupal\Core\Asset\LibraryDiscovery::getLibraryByName() if ($override_definition) { $libraries[$library_name]['override'] = $override_definition; } else { $libraries[$library_name]['override'] = FALSE; } } elseif (is_array($override_definition)) { // An array definition implies an override for an asset within this // library. foreach ($override_definition as $sub_key => $value) { // Throw an exception if the asset is not properly specified. if (!is_array($value)) { throw new InvalidLibrariesOverrideSpecificationException(sprintf('Library asset %s is not correctly specified. It should be in the form "extension/library_name/sub_key/path/to/asset.js".', "{$extension}/{$library_name}/{$sub_key}")); } if ($sub_key === 'drupalSettings') { // drupalSettings may not be overridden. throw new InvalidLibrariesOverrideSpecificationException(sprintf('drupalSettings may not be overridden in libraries-override. Trying to override %s. Use hook_library_info_alter() instead.', "{$extension}/{$library_name}/{$sub_key}")); } elseif ($sub_key === 'css') { // SMACSS category should be incorporated into the asset name. foreach ($value as $category => $overrides) { $this->setOverrideValue($libraries[$library_name], [$sub_key, $category], $overrides, $theme_path); } } else { $this->setOverrideValue($libraries[$library_name], [$sub_key], $value, $theme_path); } } } } } } return $libraries; }
/** * {@inheritdoc} */ public function getCssAssets(AttachedAssetsInterface $assets, $optimize) { $theme_info = $this->themeManager->getActiveTheme(); $css = []; $default_options = ['type' => 'file', 'group' => CSS_AGGREGATE_DEFAULT, 'weight' => 0, 'every_page' => FALSE, 'media' => 'all', 'preprocess' => TRUE, 'browsers' => []]; foreach ($this->getLibrariesToLoad($assets) as $library) { list($extension, $name) = explode('/', $library, 2); $definition = $this->libraryDiscovery->getLibraryByName($extension, $name); if (isset($definition['css'])) { foreach ($definition['css'] as $options) { $options += $default_options; $options['browsers'] += ['IE' => TRUE, '!IE' => TRUE]; // Files with a query string cannot be preprocessed. if ($options['type'] === 'file' && $options['preprocess'] && strpos($options['data'], '?') !== FALSE) { $options['preprocess'] = FALSE; } // Always add a tiny value to the weight, to conserve the insertion // order. $options['weight'] += count($css) / 1000; // CSS files are being keyed by the full path. $css[$options['data']] = $options; } } } // Allow modules and themes to alter the CSS assets. $this->moduleHandler->alter('css', $css, $assets); $this->themeManager->alter('css', $css, $assets); // Sort CSS items, so that they appear in the correct order. uasort($css, 'static::sort'); // Allow themes to remove CSS files by CSS files full path and file name. if ($stylesheet_remove = $theme_info->getStyleSheetsRemove()) { foreach ($css as $key => $options) { if (isset($stylesheet_remove[$key])) { unset($css[$key]); } } } if ($optimize) { $css = \Drupal::service('asset.css.collection_optimizer')->optimize($css); } return $css; }
/** * Applies the libraries-extend specified by the active theme. * * This extends the library definitions with the those specified by the * libraries-extend specifications for the active theme. * * @param string $extension * The name of the extension for which library definitions will be extended. * @param string $library_name * The name of the library whose definitions is to be extended. * @param $library_definition * The library definition to be extended. * * @return array * The library definition extended as specified by libraries-extend. * * @throws \Drupal\Core\Asset\Exception\InvalidLibrariesExtendSpecificationException */ protected function applyLibrariesExtend($extension, $library_name, $library_definition) { $libraries_extend = $this->themeManager->getActiveTheme()->getLibrariesExtend(); if (!empty($libraries_extend["{$extension}/{$library_name}"])) { foreach ($libraries_extend["{$extension}/{$library_name}"] as $library_extend_name) { if (!is_string($library_extend_name)) { // Only string library names are allowed. throw new InvalidLibrariesExtendSpecificationException('The libraries-extend specification for each library must be a list of strings.'); } list($new_extension, $new_library_name) = explode('/', $library_extend_name, 2); $new_libraries = $this->get($new_extension); if (isset($new_libraries[$new_library_name])) { $library_definition = NestedArray::mergeDeep($library_definition, $new_libraries[$new_library_name]); } else { throw new InvalidLibrariesExtendSpecificationException(sprintf('The specified library "%s" does not exist.', $library_extend_name)); } } } return $library_definition; }
/** * Retrieves the key of the theme used to render the emails. */ public function getMailTheme() { $theme = $this->mailsystemConfig->get('theme'); switch ($theme) { case 'default': $theme = $this->configFactory->get('system.theme')->get('default'); break; case 'current': $theme = $this->themeManager->getActiveTheme()->getName(); break; case 'domain': // Fetch the theme for the current domain. // @todo: Reimplement this as soon as module port or similar module is around. if (FALSE && \Drupal::moduleHandler()->moduleExists('domain_theme')) { // Assign the selected theme, based on the active domain. global $_domain; $domain_theme = domain_theme_lookup($_domain['domain_id']); // The above returns -1 on failure. $theme = $domain_theme != -1 ? $domain_theme['theme'] : $this->themeManager->getActiveTheme()->getName(); } break; } return $theme; }
/** * Gets the current theme for this page. * * @return string * The current theme. */ protected function getTheme() { return $this->themeManager->getActiveTheme()->getName(); }
/** * {@inheritdoc} */ public function getContext() { return $this->themeManager->getActiveTheme()->getName() ?: 'stark'; }
/** * {@inheritdoc} */ public function getJsAssets(AttachedAssetsInterface $assets, $optimize) { $theme_info = $this->themeManager->getActiveTheme(); // Add the theme name to the cache key since themes may implement // hook_library_info_alter(). Additionally add the current language to // support translation of JavaScript files via hook_js_alter(). $libraries_to_load = $this->getLibrariesToLoad($assets); $cid = 'js:' . $theme_info->getName() . ':' . $this->languageManager->getCurrentLanguage()->getId() . ':' . Crypt::hashBase64(serialize($libraries_to_load)) . (int) (count($assets->getSettings()) > 0) . (int) $optimize; if ($cached = $this->cache->get($cid)) { list($js_assets_header, $js_assets_footer, $settings, $settings_in_header) = $cached->data; } else { $javascript = []; $default_options = ['type' => 'file', 'group' => JS_DEFAULT, 'weight' => 0, 'cache' => TRUE, 'preprocess' => TRUE, 'attributes' => [], 'version' => NULL, 'browsers' => []]; // Collect all libraries that contain JS assets and are in the header. $header_js_libraries = []; foreach ($libraries_to_load as $library) { list($extension, $name) = explode('/', $library, 2); $definition = $this->libraryDiscovery->getLibraryByName($extension, $name); if (isset($definition['js']) && !empty($definition['header'])) { $header_js_libraries[] = $library; } } // The current list of header JS libraries are only those libraries that // are in the header, but their dependencies must also be loaded for them // to function correctly, so update the list with those. $header_js_libraries = $this->libraryDependencyResolver->getLibrariesWithDependencies($header_js_libraries); foreach ($libraries_to_load as $library) { list($extension, $name) = explode('/', $library, 2); $definition = $this->libraryDiscovery->getLibraryByName($extension, $name); if (isset($definition['js'])) { foreach ($definition['js'] as $options) { $options += $default_options; // 'scope' is a calculated option, based on which libraries are // marked to be loaded from the header (see above). $options['scope'] = in_array($library, $header_js_libraries) ? 'header' : 'footer'; // Preprocess can only be set if caching is enabled and no // attributes are set. $options['preprocess'] = $options['cache'] && empty($options['attributes']) ? $options['preprocess'] : FALSE; // Always add a tiny value to the weight, to conserve the insertion // order. $options['weight'] += count($javascript) / 1000; // Local and external files must keep their name as the associative // key so the same JavaScript file is not added twice. $javascript[$options['data']] = $options; } } } // Allow modules and themes to alter the JavaScript assets. $this->moduleHandler->alter('js', $javascript, $assets); $this->themeManager->alter('js', $javascript, $assets); // Sort JavaScript assets, so that they appear in the correct order. uasort($javascript, 'static::sort'); // Prepare the return value: filter JavaScript assets per scope. $js_assets_header = []; $js_assets_footer = []; foreach ($javascript as $key => $item) { if ($item['scope'] == 'header') { $js_assets_header[$key] = $item; } elseif ($item['scope'] == 'footer') { $js_assets_footer[$key] = $item; } } if ($optimize) { $collection_optimizer = \Drupal::service('asset.js.collection_optimizer'); $js_assets_header = $collection_optimizer->optimize($js_assets_header); $js_assets_footer = $collection_optimizer->optimize($js_assets_footer); } // If the core/drupalSettings library is being loaded or is already // loaded, get the JavaScript settings assets, and convert them into a // single "regular" JavaScript asset. $libraries_to_load = $this->getLibrariesToLoad($assets); $settings_required = in_array('core/drupalSettings', $libraries_to_load) || in_array('core/drupalSettings', $this->libraryDependencyResolver->getLibrariesWithDependencies($assets->getAlreadyLoadedLibraries())); $settings_have_changed = count($libraries_to_load) > 0 || count($assets->getSettings()) > 0; // Initialize settings to FALSE since they are not needed by default. This // distinguishes between an empty array which must still allow // hook_js_settings_alter() to be run. $settings = FALSE; if ($settings_required && $settings_have_changed) { $settings = $this->getJsSettingsAssets($assets); // Allow modules to add cached JavaScript settings. foreach ($this->moduleHandler->getImplementations('js_settings_build') as $module) { $function = $module . '_' . 'js_settings_build'; $function($settings, $assets); } } $settings_in_header = in_array('core/drupalSettings', $header_js_libraries); $this->cache->set($cid, [$js_assets_header, $js_assets_footer, $settings, $settings_in_header], CacheBackendInterface::CACHE_PERMANENT, ['library_info']); } if ($settings !== FALSE) { // Attached settings override both library definitions and // hook_js_settings_build(). $settings = NestedArray::mergeDeepArray([$settings, $assets->getSettings()], TRUE); // Allow modules and themes to alter the JavaScript settings. $this->moduleHandler->alter('js_settings', $settings, $assets); $this->themeManager->alter('js_settings', $settings, $assets); // Update the $assets object accordingly, so that it reflects the final // settings. $assets->setSettings($settings); $settings_as_inline_javascript = ['type' => 'setting', 'group' => JS_SETTING, 'weight' => 0, 'browsers' => [], 'data' => $settings]; $settings_js_asset = ['drupalSettings' => $settings_as_inline_javascript]; // Prepend to the list of JS assets, to render it first. Preferably in // the footer, but in the header if necessary. if ($settings_in_header) { $js_assets_header = $settings_js_asset + $js_assets_header; } else { $js_assets_footer = $settings_js_asset + $js_assets_footer; } } return [$js_assets_header, $js_assets_footer]; }
/** * Gets the path of the active theme. * * @return string * The path to the active theme. */ public function getActiveThemePath() { return $this->themeManager->getActiveTheme()->getPath(); }
/** * Initializes a theme with a certain name. * * This function does to much magic, so it should be replaced by another * services which holds the current active theme information. * * @param string $theme_name * (optional) The name of the theme for which to construct the registry. */ protected function init($theme_name = NULL) { if ($this->initialized) { return; } // Unless instantiated for a specific theme, use globals. if (!isset($theme_name)) { $this->theme = $this->themeManager->getActiveTheme(); } else { $this->theme = $this->themeInitialization->getActiveThemeByName($theme_name); $this->themeInitialization->loadActiveTheme($this->theme); } }
/** * {@inheritdoc} */ public function getCssAssets(AttachedAssetsInterface $assets, $optimize) { $theme_info = $this->themeManager->getActiveTheme(); $css = []; foreach ($this->getLibrariesToLoad($assets) as $library) { list($extension, $name) = explode('/', $library, 2); $definition = $this->libraryDiscovery->getLibraryByName($extension, $name); if (isset($definition['css'])) { foreach ($definition['css'] as $options) { $options += array('type' => 'file', 'group' => CSS_AGGREGATE_DEFAULT, 'weight' => 0, 'every_page' => FALSE, 'media' => 'all', 'preprocess' => TRUE, 'browsers' => array()); $options['browsers'] += array('IE' => TRUE, '!IE' => TRUE); // Files with a query string cannot be preprocessed. if ($options['type'] === 'file' && $options['preprocess'] && strpos($options['data'], '?') !== FALSE) { $options['preprocess'] = FALSE; } // Always add a tiny value to the weight, to conserve the insertion // order. $options['weight'] += count($css) / 1000; // Add the data to the CSS array depending on the type. switch ($options['type']) { case 'file': // Local CSS files are keyed by basename; if a file with the same // basename is added more than once, it gets overridden. // By default, take over the filename as basename. if (!isset($options['basename'])) { $options['basename'] = drupal_basename($options['data']); } $css[$options['basename']] = $options; break; default: // External files are keyed by their full URI, so the same CSS // file is not added twice. $css[$options['data']] = $options; } } } } // Allow modules and themes to alter the CSS assets. $this->moduleHandler->alter('css', $css, $assets); $this->themeManager->alter('css', $css, $assets); // Sort CSS items, so that they appear in the correct order. uasort($css, 'static::sort'); // Allow themes to remove CSS files by basename. if ($stylesheet_remove = $theme_info->getStyleSheetsRemove()) { foreach ($css as $key => $options) { if (isset($options['basename']) && isset($stylesheet_remove[$options['basename']])) { unset($css[$key]); } } } // Allow themes to conditionally override CSS files by basename. if ($stylesheet_override = $theme_info->getStyleSheetsOverride()) { foreach ($css as $key => $options) { if (isset($options['basename']) && isset($stylesheet_override[$options['basename']])) { $css[$key]['data'] = $stylesheet_override[$options['basename']]; } } } if ($optimize) { $css = \Drupal::service('asset.css.collection_optimizer')->optimize($css); } return $css; }