/** * {@inheritdoc} */ protected function setUp() { // Create a config mock which does not mock the clear(), set() and get() methods. $methods = get_class_methods('Drupal\\Core\\Config\\Config'); unset($methods[array_search('set', $methods)]); unset($methods[array_search('get', $methods)]); unset($methods[array_search('clear', $methods)]); $config_mock = $this->getMockBuilder('Drupal\\Core\\Config\\Config')->disableOriginalConstructor()->setMethods($methods)->getMock(); // Create the config factory we use in the submitForm() function. $this->configFactory = $this->getMock('Drupal\\Core\\Config\\ConfigFactoryInterface'); $this->configFactory->expects($this->any())->method('getEditable')->will($this->returnValue($config_mock)); // Create a MailsystemManager mock. $this->mailManager = $this->getMock('\\Drupal\\mailsystem\\MailsystemManager', array(), array(), '', FALSE); $this->mailManager->expects($this->any())->method('getDefinition')->will($this->returnValueMap(array(array('mailsystem_test', TRUE, array('label' => 'Test Mail-Plugin')), array('mailsystem_demo', TRUE, array('label' => 'Demo Mail-Plugin'))))); $this->mailManager->expects($this->any())->method('getDefinitions')->will($this->returnValue(array(array('id' => 'mailsystem_test', 'label' => 'Test Mail-Plugin'), array('id' => 'mailsystem_demo', 'label' => 'Demo Mail-Plugin')))); // Create a module handler mock. $this->moduleHandler = $this->getMock('\\Drupal\\Core\\Extension\\ModuleHandlerInterface'); $this->moduleHandler->expects($this->any())->method('getImplementations')->with('mail')->will($this->returnValue(array('mailsystem_test', 'mailsystem_demo'))); $this->moduleHandler->expects($this->any())->method('moduleExists')->withAnyParameters()->will($this->returnValue(FALSE)); // Create a theme handler mock. $this->themeHandler = $this->getMock('\\Drupal\\Core\\Extension\\ThemeHandlerInterface'); $this->themeHandler->expects($this->any())->method('listInfo')->will($this->returnValue(array('test_theme' => (object) array('status' => 1, 'info' => array('name' => 'test theme name')), 'demo_theme' => (object) array('status' => 1, 'info' => array('name' => 'test theme name demo')), 'inactive_theme' => (object) array('status' => 0, 'info' => array('name' => 'inactive test theme'))))); // Inject a language-manager into \Drupal. $this->languageManager = $this->getMock('\\Drupal\\Core\\StringTranslation\\TranslationInterface'); $this->languageManager->expects($this->any())->method('translate')->withAnyParameters()->will($this->returnArgument(0)); $container = new ContainerBuilder(); $container->set('string_translation', $this->languageManager); \Drupal::setContainer($container); }
/** * Constructs a TwigEnvironment object and stores cache and storage * internally. */ public function __construct(\Twig_LoaderInterface $loader = NULL, $options = array(), ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler) { // @todo Pass as arguments from the DIC. $this->cache_object = \Drupal::cache(); // Ensure that twig.engine is loaded, given that it is needed to render a // template because functions like twig_drupal_escape_filter are called. require_once DRUPAL_ROOT . '/core/themes/engines/twig/twig.engine'; // Set twig path namespace for themes and modules. $namespaces = array(); foreach ($module_handler->getModuleList() as $name => $extension) { $namespaces[$name] = $extension->getPath(); } foreach ($theme_handler->listInfo() as $name => $extension) { $namespaces[$name] = $extension->getPath(); } foreach ($namespaces as $name => $path) { $templatesDirectory = $path . '/templates'; if (file_exists($templatesDirectory)) { $loader->addPath($templatesDirectory, $name); } } $this->templateClasses = array(); $this->stringLoader = new \Twig_Loader_String(); parent::__construct($loader, $options); }
/** * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state) { $form = parent::buildForm($form, $form_state); $config = $this->config('views.settings'); $options = array(); foreach ($this->themeHandler->listInfo() as $name => $theme) { if ($theme->status) { $options[$name] = $theme->info['name']; } } // This is not currently a fieldset but we may want it to be later, // so this will make it easier to change if we do. $form['basic'] = array(); $form['basic']['ui_show_master_display'] = array('#type' => 'checkbox', '#title' => $this->t('Always show the master (default) display'), '#default_value' => $config->get('ui.show.master_display')); $form['basic']['ui_show_advanced_column'] = array('#type' => 'checkbox', '#title' => $this->t('Always show advanced display settings'), '#default_value' => $config->get('ui.show.advanced_column')); $form['basic']['ui_show_display_embed'] = array('#type' => 'checkbox', '#title' => t('Allow embedded displays'), '#description' => t('Embedded displays can be used in code via views_embed_view().'), '#default_value' => $config->get('ui.show.display_embed')); $form['basic']['ui_exposed_filter_any_label'] = array('#type' => 'select', '#title' => $this->t('Label for "Any" value on non-required single-select exposed filters'), '#options' => array('old_any' => '<Any>', 'new_any' => $this->t('- Any -')), '#default_value' => $config->get('ui.exposed_filter_any_label')); $form['live_preview'] = array('#type' => 'details', '#title' => $this->t('Live preview settings'), '#open' => TRUE); $form['live_preview']['ui_always_live_preview'] = array('#type' => 'checkbox', '#title' => $this->t('Automatically update preview on changes'), '#default_value' => $config->get('ui.always_live_preview')); $form['live_preview']['ui_show_preview_information'] = array('#type' => 'checkbox', '#title' => $this->t('Show information and statistics about the view during live preview'), '#default_value' => $config->get('ui.show.preview_information')); $form['live_preview']['options'] = array('#type' => 'container', '#states' => array('visible' => array(':input[name="ui_show_preview_information"]' => array('checked' => TRUE)))); $form['live_preview']['options']['ui_show_sql_query_enabled'] = array('#type' => 'checkbox', '#title' => $this->t('Show the SQL query'), '#default_value' => $config->get('ui.show.sql_query.enabled')); $form['live_preview']['options']['ui_show_sql_query_where'] = array('#type' => 'radios', '#states' => array('visible' => array(':input[name="ui_show_sql_query_enabled"]' => array('checked' => TRUE))), '#title' => t('Show SQL query'), '#options' => array('above' => $this->t('Above the preview'), 'below' => $this->t('Below the preview')), '#default_value' => $config->get('ui.show.sql_query.where')); $form['live_preview']['options']['ui_show_performance_statistics'] = array('#type' => 'checkbox', '#title' => $this->t('Show performance statistics'), '#default_value' => $config->get('ui.show.performance_statistics')); $form['live_preview']['options']['ui_show_additional_queries'] = array('#type' => 'checkbox', '#title' => $this->t('Show other queries run during render during live preview'), '#description' => $this->t("Drupal has the potential to run many queries while a view is being rendered. Checking this box will display every query run during view render as part of the live preview."), '#default_value' => $config->get('ui.show.additional_queries')); return $form; }
/** * {@inheritdoc} */ public function buildConfigurationForm(array $form, FormStateInterface $form_state) { $form['theme'] = array('#type' => 'select', '#title' => $this->t('Theme'), '#default_value' => $this->configuration['theme'], '#options' => array_map(function ($theme_info) { return $theme_info->info['name']; }, $this->themeHandler->listInfo())); return parent::buildConfigurationForm($form, $form_state); }
/** * Shows the block administration page. * * @param string|null $theme * Theme key of block list. * @param \Symfony\Component\HttpFoundation\Request $request * The current request. * * @return array * A render array as expected by drupal_render(). */ public function listing($theme = NULL, Request $request = NULL) { $theme = $theme ?: $this->config('system.theme')->get('default'); if (!$this->themeHandler->hasUi($theme)) { throw new NotFoundHttpException(); } return $this->entityManager()->getListBuilder('block')->render($theme, $request); }
/** * Tests that the Stable base theme is installed if necessary. */ public function testUpdateHookN() { $this->assertTrue($this->themeHandler->themeExists('test_stable')); $this->assertFalse($this->themeHandler->themeExists('stable')); $this->runUpdates(); // Refresh the theme handler now that Stable has been installed. $this->themeHandler->refreshInfo(); $this->assertTrue($this->themeHandler->themeExists('stable')); }
/** * 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 collect(Request $request, Response $response, \Exception $exception = NULL) { $modules = $this->moduleHandler->getModuleList(); $themes = $this->themeHandler->listInfo(); $this->data['drupal_extension']['count'] = count($modules) + count($themes); $this->data['drupal_extension']['modules'] = $modules; $this->data['drupal_extension']['themes'] = $themes; $this->data['drupal_extension']['installation_path'] = $this->root . '/'; }
/** * {@inheritdoc} */ public function getExtensions() { foreach ($this->moduleHandler->getModuleList() as $module) { (yield $this->wrapCoreExtension($module)); } foreach ($this->themeHandler->listInfo() as $theme) { (yield $this->wrapCoreExtension($theme)); } }
/** * Returns a block theme demo page. * * @param string $theme * The name of the theme. * * @return array * A #type 'page' render array containing the block region demo. */ public function demo($theme) { $page = ['#title' => $this->themeHandler->getName($theme), '#type' => 'page', '#attached' => array('drupalSettings' => ['path' => ['currentPathIsAdmin' => TRUE]], 'library' => array('block/drupal.block.admin'))]; // Show descriptions in each visible page region, nothing else. $visible_regions = $this->getVisibleRegionNames($theme); foreach (array_keys($visible_regions) as $region) { $page[$region]['block_description'] = array('#type' => 'inline_template', '#template' => '<div class="block-region demo-block">{{ region_name }}</div>', '#context' => array('region_name' => $visible_regions[$region])); } return $page; }
/** * {@inheritdoc} */ public function getDerivativeDefinitions($base_plugin_definition) { foreach ($this->themeHandler->listInfo() as $theme_name => $theme) { if ($theme->status) { $this->derivatives[$theme_name] = $base_plugin_definition; $this->derivatives[$theme_name]['title'] = $theme->info['name']; $this->derivatives[$theme_name]['route_parameters'] = array('theme' => $theme_name); } } return $this->derivatives; }
/** * {@inheritdoc} */ public function setUp() { parent::setUp(); $this->directoryList = array('system' => 'core/modules/system'); $this->themeHandler = $this->getMock('Drupal\\Core\\Extension\\ThemeHandlerInterface'); $theme = new Extension('theme', DRUPAL_ROOT . '/core/themes/bartik', 'bartik.info.yml'); $theme->status = 1; $theme->info = array('name' => 'bartik'); $this->themeHandler->expects($this->any())->method('listInfo')->will($this->returnValue(array('bartik' => $theme))); $this->container->set('theme_handler', $this->themeHandler); }
/** * Constructs a new FilesystemLoader object. * * @param string|array $paths * A path or an array of paths to check for templates. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler * The module handler service. * @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler * The theme handler service. */ public function __construct($paths = array(), ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler) { parent::__construct($paths); // Add namespaced paths for modules and themes. $namespaces = array(); foreach ($module_handler->getModuleList() as $name => $extension) { $namespaces[$name] = $extension->getPath(); } foreach ($theme_handler->listInfo() as $name => $extension) { $namespaces[$name] = $extension->getPath(); } foreach ($namespaces as $name => $path) { $this->addPath($path . '/templates', $name); } }
/** * Invalidate the 'rendered' cache tag whenever a theme setting is modified. * * @param \Drupal\Core\Config\ConfigCrudEvent $event * The Event to process. */ public function onSave(ConfigCrudEvent $event) { // Global theme settings. if ($event->getConfig()->getName() === 'system.theme.global') { $this->cacheTagsInvalidator->invalidateTags(['rendered']); } // Theme-specific settings, check if this matches a theme settings // configuration object, in that case, clear the rendered cache tag. foreach (array_keys($this->themeHandler->listInfo()) as $theme_name) { if ($theme_name == $event->getConfig()->getName()) { $this->cacheTagsInvalidator->invalidateTags(['rendered']); break; } } }
/** * Gets all extensions. * * @return array */ protected function getExtensions() { if (!isset($this->extensions)) { $this->extensions = array_merge($this->moduleHandler->getModuleList(), $this->themeHandler->listInfo()); } return $this->extensions; }
/** * Set the default theme. * * @param \Symfony\Component\HttpFoundation\Request $request * A request object containing a theme name. * * @return \Symfony\Component\HttpFoundation\RedirectResponse * Redirects back to the appearance admin page. * * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException * Throws access denied when no theme is set in the request. */ public function setDefaultTheme(Request $request) { $config = $this->configFactory->getEditable('system.theme'); $theme = $request->query->get('theme'); if (isset($theme)) { // Get current list of themes. $themes = $this->themeHandler->listInfo(); // Check if the specified theme is one recognized by the system. // Or try to install the theme. if (isset($themes[$theme]) || $this->themeHandler->install(array($theme))) { $themes = $this->themeHandler->listInfo(); // Set the default theme. $config->set('default', $theme)->save(); $this->routeBuilder->setRebuildNeeded(); // The status message depends on whether an admin theme is currently in // use: a value of 0 means the admin theme is set to be the default // theme. $admin_theme = $config->get('admin'); if ($admin_theme != 0 && $admin_theme != $theme) { drupal_set_message($this->t('Please note that the administration theme is still set to the %admin_theme theme; consequently, the theme on this page remains unchanged. All non-administrative sections of the site, however, will show the selected %selected_theme theme by default.', array('%admin_theme' => $themes[$admin_theme]->info['name'], '%selected_theme' => $themes[$theme]->info['name']))); } else { drupal_set_message($this->t('%theme is now the default theme.', array('%theme' => $themes[$theme]->info['name']))); } } else { drupal_set_message($this->t('The %theme theme was not found.', array('%theme' => $theme)), 'error'); } return $this->redirect('system.themes_page'); } throw new AccessDeniedHttpException(); }
/** * Inject the relevant css for the template. * * You can specify CSS files to be included per entity type and bundle in your * themes css file. This code uses your current theme which is likely to be the * front end theme. * * Examples: * * entity_print: * all: 'yourtheme/all-pdfs', * commerce_order: * all: 'yourtheme/orders' * node: * article: 'yourtheme/article-pdf' * * @param array $render * The renderable array. * @param \Drupal\Core\Entity\ContentEntityInterface $entity * The entity info from entity_get_info(). * * @return array * An array of stylesheets to be used for this template. */ protected function addCss($render, ContentEntityInterface $entity) { $theme = $this->themeHandler->getDefault(); $theme_path = $this->getThemePath($theme); /** @var \Drupal\Core\Extension\InfoParser $parser */ $theme_info = $this->infoParser->parse("{$theme_path}/{$theme}.info.yml"); // Parse out the CSS from the theme info. if (isset($theme_info['entity_print'])) { // See if we have the special "all" key which is added to every PDF. if (isset($theme_info['entity_print']['all'])) { $render['#attached']['library'][] = $theme_info['entity_print']['all']; unset($theme_info['entity_print']['all']); } foreach ($theme_info['entity_print'] as $key => $value) { // If the entity type doesn't match just skip. if ($key !== $entity->getEntityTypeId()) { continue; } // Parse our css files per entity type and bundle. foreach ($value as $css_bundle => $css) { // If it's magic key "all" add it otherwise check the bundle. if ($css_bundle === 'all' || $entity->bundle() === $css_bundle) { $render['#attached']['library'][] = $css; } } } } return $render; }
/** * Gets theme data. * * @return \Drupal\Core\Extension\Extension[] */ protected function getThemeData() { if (!isset($this->themeData)) { $this->themeData = $this->themeHandler->rebuildThemeData(); } return $this->themeData; }
/** * Retrieves the full base/sub-theme ancestry of a theme. * * @param bool $reverse * Whether or not to return the array of themes in reverse order, where the * active theme is the first entry. * * @return \Drupal\bootstrap\Theme[] * An associative array of \Drupal\bootstrap objects (theme), keyed * by machine name. */ public function getAncestry($reverse = FALSE) { $ancestry = $this->themeHandler->getBaseThemes($this->themes, $this->getName()); foreach (array_keys($ancestry) as $name) { $ancestry[$name] = Bootstrap::getTheme($name, $this->themeHandler); } $ancestry[$this->getName()] = $this; return $reverse ? array_reverse($ancestry) : $ancestry; }
/** * Get the path to a theme. * * @param string $theme * The machine name of the theme to get the path for. * * @return string * The path to the given theme. * * @todo replace this with an injectable version of drupal_get_path() when/if * it lands. */ protected function getThemePath($theme) { $info = $this->themeHandler->listInfo(); $path = ''; if (isset($info[$theme])) { $path = dirname($info[$theme]->uri); } return $path; }
/** * Presents the custom block creation form. * * @param \Drupal\block_content\BlockContentTypeInterface $block_content_type * The custom block type to add. * @param \Symfony\Component\HttpFoundation\Request $request * The current request object. * * @return array * A form array as expected by drupal_render(). */ public function addForm(BlockContentTypeInterface $block_content_type, Request $request) { $block = $this->blockContentStorage->create(array('type' => $block_content_type->id())); if (($theme = $request->query->get('theme')) && in_array($theme, array_keys($this->themeHandler->listInfo()))) { // We have navigated to this page from the block library and will keep track // of the theme for redirecting the user to the configuration page for the // newly created block in the given theme. $block->setTheme($theme); } return $this->entityFormBuilder()->getForm($block); }
/** * {@inheritdoc} */ public function getDerivativeDefinitions($base_plugin_definition) { $default_theme = $this->themeHandler->getDefault(); foreach ($this->themeHandler->listInfo() as $theme_name => $theme) { if ($this->themeHandler->hasUi($theme_name)) { $this->derivatives[$theme_name] = $base_plugin_definition; $this->derivatives[$theme_name]['title'] = $theme->info['name']; $this->derivatives[$theme_name]['route_parameters'] = array('theme' => $theme_name); } // Default task! if ($default_theme == $theme_name) { $this->derivatives[$theme_name]['route_name'] = $base_plugin_definition['parent_id']; // Emulate default logic because without the base plugin id we can't // change the base_route. $this->derivatives[$theme_name]['weight'] = -10; unset($this->derivatives[$theme_name]['route_parameters']); } } return $this->derivatives; }
/** * {@inheritdoc} */ public function processDefinition(&$definition, $plugin_id) { parent::processDefinition($definition, $plugin_id); // Add the module or theme path to the 'path'. if ($this->moduleHandler->moduleExists($definition['provider'])) { $definition['provider_type'] = 'module'; $base_path = $this->moduleHandler->getModule($definition['provider'])->getPath(); } elseif ($this->themeHandler->themeExists($definition['provider'])) { $definition['provider_type'] = 'theme'; $base_path = $this->themeHandler->getTheme($definition['provider'])->getPath(); } else { $base_path = ''; } $definition['path'] = !empty($definition['path']) ? $base_path . '/' . $definition['path'] : $base_path; // Add the path to the icon filename. if (!empty($definition['icon'])) { $definition['icon'] = $definition['path'] . '/' . $definition['icon']; } // If 'template' is set, then we'll derive 'template_path' and 'theme'. if (!empty($definition['template'])) { $template_parts = explode('/', $definition['template']); $definition['template'] = array_pop($template_parts); $definition['theme'] = strtr($definition['template'], '-', '_'); $definition['template_path'] = $definition['path']; if (count($template_parts) > 0) { $definition['template_path'] .= '/' . implode('/', $template_parts); } } // If 'css' is set, then we'll derive 'library'. if (!empty($definition['css'])) { $definition['css'] = $definition['path'] . '/' . $definition['css']; $definition['library'] = 'layout_plugin/' . $plugin_id; } // Generate the 'region_names' key from the 'regions' key. $definition['region_names'] = array(); if (!empty($definition['regions']) && is_array($definition['regions'])) { foreach ($definition['regions'] as $region_id => $region_definition) { $definition['region_names'][$region_id] = $region_definition['label']; } } }
/** * Invalidate cache tags when particular system config objects are saved. * * @param \Drupal\Core\Config\ConfigCrudEvent $event * The Event to process. */ public function onSave(ConfigCrudEvent $event) { // Changing the site settings may mean a different route is selected for the // front page. Additionally a change to the site name or similar must // invalidate the render cache since this could be used anywhere. if ($event->getConfig()->getName() === 'system.site') { $this->cacheTagsInvalidator->invalidateTags(['route_match', 'rendered']); } // Theme configuration and global theme settings. if (in_array($event->getConfig()->getName(), ['system.theme', 'system.theme.global'], TRUE)) { $this->cacheTagsInvalidator->invalidateTags(['rendered']); } // Theme-specific settings, check if this matches a theme settings // configuration object, in that case, clear the rendered cache tag. foreach (array_keys($this->themeHandler->listInfo()) as $theme_name) { if ($theme_name == $event->getConfig()->getName()) { $this->cacheTagsInvalidator->invalidateTags(['rendered']); break; } } }
/** * Constructs a TwigEnvironment object and stores cache and storage * internally. */ public function __construct(\Twig_LoaderInterface $loader = NULL, $options = array(), ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler) { // @todo Pass as arguments from the DIC. $this->cache_object = \Drupal::cache(); // Set twig path namespace for themes and modules. $namespaces = array(); foreach ($module_handler->getModuleList() as $name => $extension) { $namespaces[$name] = $extension->getPath(); } foreach ($theme_handler->listInfo() as $name => $extension) { $namespaces[$name] = $extension->getPath(); } foreach ($namespaces as $name => $path) { $templatesDirectory = $path . '/templates'; if (file_exists($templatesDirectory)) { $loader->addPath($templatesDirectory, $name); } } $this->templateClasses = array(); parent::__construct($loader, $options); }
/** * Disables the language switcher blocks. * * @param array $language_types * An array containing all language types whose language switchers need to * be disabled. */ protected function disableLanguageSwitcher(array $language_types) { $theme = $this->themeHandler->getDefault(); $blocks = $this->blockStorage->loadByProperties(array('theme' => $theme)); foreach ($language_types as $language_type) { foreach ($blocks as $block) { if ($block->getPluginId() == 'language_block:' . $language_type) { $block->delete(); } } } }
/** * Disables the language switcher blocks. * * @param array $language_types * An array containing all language types whose language switchers need to * be disabled. */ protected function disableLanguageSwitcher(array $language_types) { $theme = $this->themeHandler->getDefault(); $blocks = $this->blockStorage->loadByProperties(array('theme' => $theme)); foreach ($language_types as $language_type) { foreach ($blocks as $block) { if (strpos($block->id, 'language_switcher_' . substr($language_type, 9)) !== FALSE) { $block->delete(); } } } }
/** * Gets the label for a breakpoint group. * * @param string $group * The breakpoint group. * * @return string * The label. */ protected function getGroupLabel($group) { // Extension names are not translatable. if ($this->moduleHandler->moduleExists($group)) { $label = $this->moduleHandler->getName($group); } elseif ($this->themeHandler->themeExists($group)) { $label = $this->themeHandler->getName($group); } else { // Custom group label that should be translatable. $label = $this->t($group, array(), array('context' => 'breakpoint')); } return $label; }
/** * Returns a list with all themes. * * @return array * Associative array with all enabled themes: * - name: label */ protected function getThemesList() { $theme_options = array('current' => $this->t('Current'), 'default' => $this->t('Default')); if ($this->moduleHandler->moduleExists('domain_theme')) { $theme_options['domain'] = $this->t('Domain Theme'); } foreach ($this->themeHandler->listInfo() as $name => $theme) { if ($theme->status === 1) { $theme_options[$name] = $theme->info['name']; } } return $theme_options; }
/** * {@inheritdoc} */ public function getActiveThemeByName($theme_name) { if ($active_theme = $this->state->get('theme.active_theme.' . $theme_name)) { return $active_theme; } $themes = $this->themeHandler->listInfo(); // If no theme could be negotiated, or if the negotiated theme is not within // the list of installed themes, fall back to the default theme output of // core and modules (like Stark, but without a theme extension at all). This // is possible, because loadActiveTheme() always loads the Twig theme // engine. This is desired, because missing or malformed theme configuration // should not leave the application in a broken state. By falling back to // default output, the user is able to reconfigure the theme through the UI. // Lastly, tests are expected to operate with no theme by default, so as to // only assert the original theme output of modules (unless a test manually // installs a specific theme). if (empty($themes) || !$theme_name || !isset($themes[$theme_name])) { $theme_name = 'core'; // /core/core.info.yml does not actually exist, but is required because // Extension expects a pathname. $active_theme = $this->getActiveTheme(new Extension('theme', 'core/core.info.yml')); // Early-return and do not set state, because the initialized $theme_name // differs from the original $theme_name. return $active_theme; } // Find all our ancestor themes and put them in an array. $base_themes = array(); $ancestor = $theme_name; while ($ancestor && isset($themes[$ancestor]->base_theme)) { $ancestor = $themes[$ancestor]->base_theme; $base_themes[] = $themes[$ancestor]; } $active_theme = $this->getActiveTheme($themes[$theme_name], $base_themes); $this->state->set('theme.active_theme.' . $theme_name, $active_theme); return $active_theme; }