diff --git a/core/includes/module.inc b/core/includes/module.inc index 429f5db..b45f447 100644 --- a/core/includes/module.inc +++ b/core/includes/module.inc @@ -11,7 +11,7 @@ * * @param $type * The type of list to return: - * - module_enabled: All enabled modules. + * - module: All enabled modules. * - bootstrap: All enabled modules required for bootstrap. * - theme: All themes. * @@ -40,30 +40,26 @@ function system_list($type) { ); // Build a list of themes. $enabled_themes = (array) \Drupal::config('system.theme')->get('enabled'); - // @todo Themes include all themes, including disabled/uninstalled. This - // system.theme.data state will go away entirely as soon as themes have - // a proper installation status. - // @see http://drupal.org/node/1067408 - $theme_data = \Drupal::state()->get('system.theme.data'); - if (empty($theme_data)) { - // @todo: system_list() may be called from _drupal_bootstrap_code(), in + array_walk($enabled_themes, function (&$value, $key) { + $value = 'system.theme.data.' . $key; + }); + $theme_data = (array) \Drupal::state()->getMultiple($enabled_themes); + // @todo Remove me? + if (0 && empty($theme_data)) { + // system_list() may be called from _drupal_bootstrap_code() and // which case system.module is not loaded yet. // Prevent a filesystem scan in drupal_load() and include it directly. - // @see http://drupal.org/node/1067408 require_once DRUPAL_ROOT . '/core/modules/system/system.module'; $theme_data = system_rebuild_theme_data(); } foreach ($theme_data as $name => $theme) { - $theme->status = (int) isset($enabled_themes[$name]); $lists['theme'][$name] = $theme; // Build a list of filenames so drupal_get_filename can use it. - if (isset($enabled_themes[$name])) { - $lists['filepaths'][] = array( - 'type' => 'theme', - 'name' => $name, - 'filepath' => $theme->filename, - ); - } + $lists['filepaths'][] = array( + 'type' => 'theme', + 'name' => $name, + 'filepath' => $theme->filename, + ); } // @todo Move into list_themes(). Read info for a particular requested // theme from state instead. @@ -112,14 +108,12 @@ function system_list_reset() { drupal_static_reset('list_themes'); cache('bootstrap')->delete('system_list'); cache()->delete('system_info'); - // Remove last known theme data state. - // This causes system_list() to call system_rebuild_theme_data() on its next - // invocation. When enabling a module that implements hook_system_info_alter() - // to inject a new (testing) theme or manipulate an existing theme, then that - // will cause system_list_reset() to be called, but theme data is not - // necessarily rebuilt afterwards. - // @todo Obsolete with proper installation status for themes. - \Drupal::state()->delete('system.theme.data'); + // Rebuild theme data to update theme states. + // When enabling a module that implements hook_system_info_alter() to inject a + // new (testing) theme or manipulate an existing theme, then that will cause + // system_list_reset() to be called, but theme data is not necessarily rebuilt + // afterwards. + system_rebuild_theme_data(); } /** diff --git a/core/includes/theme.maintenance.inc b/core/includes/theme.maintenance.inc index 96dcd2f..4e8a1e6 100644 --- a/core/includes/theme.maintenance.inc +++ b/core/includes/theme.maintenance.inc @@ -19,11 +19,6 @@ function _drupal_maintenance_theme() { global $theme, $theme_key, $conf; - // If $theme is already set, assume the others are set too, and do nothing. - if (isset($theme)) { - return; - } - require_once DRUPAL_ROOT . '/' . settings()->get('path_inc', 'core/includes/path.inc'); require_once __DIR__ . '/theme.inc'; require_once __DIR__ . '/common.inc'; @@ -74,6 +69,9 @@ function _drupal_maintenance_theme() { } $themes = list_themes(); + if (empty($themes) || !isset($themes[$custom_theme])) { + $themes = _system_rebuild_theme_data(); + } // list_themes() triggers a drupal_alter() in maintenance mode, but we can't // let themes alter the .info.yml data until we know a theme's base themes. So diff --git a/core/lib/Drupal/Core/Extension/ThemeHandler.php b/core/lib/Drupal/Core/Extension/ThemeHandler.php index c25dcd9..10ee7f2 100644 --- a/core/lib/Drupal/Core/Extension/ThemeHandler.php +++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php @@ -126,11 +126,56 @@ public function __construct(ConfigFactory $config_factory, ModuleHandlerInterfac /** * {@inheritdoc} */ - public function enable(array $theme_list) { + public function enable(array $theme_list, $enable_dependencies = TRUE) { + if ($enable_dependencies) { + // Get all theme data so we can find dependencies and sort. + $theme_data = $this->rebuildThemeData(); + // Create an associative array with weights as values. + $theme_list = array_flip(array_values($theme_list)); + + while (list($theme) = each($theme_list)) { + if (!isset($theme_data[$theme])) { + // This theme is not found in the filesystem, abort. + return FALSE; + } + if ($theme_data[$theme]->status) { + // Skip already enabled themes. + unset($theme_list[$theme]); + continue; + } + $theme_list[$theme] = $theme_data[$theme]->sort; + + // Add dependencies to the list, with a placeholder weight. + // The new themes will be processed as the while loop continues. + foreach (array_keys($theme_data[$theme]->requires) as $dependency) { + if (!isset($theme_list[$dependency])) { + $theme_list[$dependency] = 0; + } + } + + if (!$theme_list) { + // Nothing to do. All themes already enabled. + return TRUE; + } + } + + // Sort the theme list by pre-calculated weights. + arsort($theme_list); + $theme_list = array_keys($theme_list); + } + $this->clearCssCache(); + $themes_installed = array(); + $themes_enabled = array(); $theme_config = $this->configFactory->get('system.theme'); $disabled_themes = $this->configFactory->get('system.theme.disabled'); foreach ($theme_list as $key) { + // Only process themes that are not already enabled. + $enabled = $theme_config->get("enabled.$key") !== NULL; + if ($enabled) { + continue; + } + // Throw an exception if the theme name is too long. if (strlen($key) > DRUPAL_EXTENSION_NAME_MAX_LENGTH) { throw new ExtensionNameLengthException(String::format('Theme name %name is over the maximum allowed length of @max characters.', array( @@ -140,20 +185,66 @@ public function enable(array $theme_list) { } // The value is not used; the weight is ignored for themes currently. - $theme_config->set("enabled.$key", 0)->save(); - $disabled_themes->clear($key)->save(); + $theme_config + ->set("enabled.$key", 0) + ->save(); + $disabled_themes + ->clear($key) + ->save(); // Refresh the theme list as installation of default configuration needs // an updated list to work. $this->reset(); // Install default configuration of the theme. $this->configInstaller->installDefaultConfig('theme', $key); + $this->configInstallDefaultConfig($key); + + // Allow modules to react prior to the installation of a theme. + $this->moduleHandler->invokeAll('themes_preinstall', array($key)); + // Now install the theme if necessary. + if (drupal_get_installed_schema_version($key, TRUE) == SCHEMA_UNINSTALLED) { + $version = SCHEMA_INSTALLED; + + // Install default configuration of the theme. + config_install_default_config('theme', $key); + + drupal_set_installed_schema_version($key, $version); + // Allow the theme to perform install tasks. + $function = $key . '_install'; + if (function_exists($function)) { + $function(); + } + // Record the fact that it was installed. + $themes_installed[] = $key; + watchdog('system', '%theme theme installed.', array('%theme' => $key), WATCHDOG_INFO); + } + + // Allow modules to react prior to the enabling of a theme. + $this->moduleHandler->invokeAll('themes_preenable', array($key)); + + // Enable the theme. + $function = $key . '_enable'; + if (function_exists($function)) { + $function(); + } + + // Record the fact that it was enabled. + $themes_enabled[] = $key; + watchdog('system', '%theme theme enabled.', array('%theme' => $key), WATCHDOG_INFO); } $this->resetSystem(); - // Invoke hook_themes_enabled() after the themes have been enabled. - $this->moduleHandler->invokeAll('themes_enabled', array($theme_list)); + // If any themes were newly installed, invoke hook_themes_installed(). + if (!empty($themes_installed)) { + $this->moduleHandler->invokeAll('themes_installed', $themes_installed); + } + + // If any themes were newly enabled, invoke hook_themes_enabled(). + if (!empty($themes_enabled)) { + $this->moduleHandler->invokeAll('themes_enabled', array($themes_enabled)); + } + return TRUE; } /** @@ -307,6 +398,9 @@ public function rebuildThemeData() { if (!empty($themes[$key]->info['base theme'])) { $sub_themes[] = $key; + // Add the defined base theme as dependency to the theme, in order to + // re-use the dependency graph resolution of _module_build_dependencies(). + $themes[$key]->info['dependencies'][] = $themes[$key]->info['base theme']; } $engine = $themes[$key]->info['engine']; diff --git a/core/lib/Drupal/Core/Extension/ThemeHandlerInterface.php b/core/lib/Drupal/Core/Extension/ThemeHandlerInterface.php index eef3381..98b4139 100644 --- a/core/lib/Drupal/Core/Extension/ThemeHandlerInterface.php +++ b/core/lib/Drupal/Core/Extension/ThemeHandlerInterface.php @@ -17,11 +17,22 @@ * * @param array $theme_list * An array of theme names. + * @param bool $enable_dependencies + * If TRUE, dependencies will automatically be added and enabled in the + * correct order. This incurs a significant performance cost, so use FALSE + * if you know $theme_list is already complete and in the correct order. * * @throws \Drupal\Core\Extension\ExtensionNameLengthException * Thrown when the theme name is to long + * + * @todo D8: This is almost identical to module_enable() now. Only differences: + * - There is no required default .theme PHP file to load. + * - There is no schema to install. + * - System list, state, theme list, and static resets slightly differ. + * @todo hook_themes_installed(), hook_themes_enabled(), etc are not invoked in + * themes, since they are excluded from the hook system. */ - public function enable(array $theme_list); + public function enable(array $theme_list, $enable_dependencies = TRUE); /** * Disables a given list of themes. diff --git a/core/lib/Drupal/Core/Theme/ThemeAccessCheck.php b/core/lib/Drupal/Core/Theme/ThemeAccessCheck.php index 75afddc..06376fd 100644 --- a/core/lib/Drupal/Core/Theme/ThemeAccessCheck.php +++ b/core/lib/Drupal/Core/Theme/ThemeAccessCheck.php @@ -35,7 +35,7 @@ public function access(Route $route, Request $request, AccountInterface $account */ public function checkAccess($theme) { $themes = list_themes(); - return !empty($themes[$theme]->status); + return isset($themes[$theme]) && !empty($themes[$theme]->status); } } diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockAdminThemeTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockAdminThemeTest.php index d553440..181f5b9 100644 --- a/core/modules/block/lib/Drupal/block/Tests/BlockAdminThemeTest.php +++ b/core/modules/block/lib/Drupal/block/Tests/BlockAdminThemeTest.php @@ -37,9 +37,9 @@ function testAdminTheme() { $admin_user = $this->drupalCreateUser(array('administer blocks', 'administer themes')); $this->drupalLogin($admin_user); - // Ensure that access to block admin page is denied when theme is disabled. + // Ensure the block admin page cannot be accessed for a disabled theme. $this->drupalGet('admin/structure/block/list/bartik'); - $this->assertResponse(403); + $this->assertResponse(404); // Enable admin theme and confirm that tab is accessible. theme_enable(array('bartik')); diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php index 9d17cac..ed8d235 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php @@ -795,6 +795,11 @@ protected function setUp() { $this->rebuildContainer(); } + // Re-initialize the theme to ensure that tests do not see an inconsistent + // behavior when calling functions that would initialize the theme if it has + // not been initialized yet. + drupal_theme_initialize(); + // Reset/rebuild all data structures after enabling the modules. $this->resetAll(); diff --git a/core/modules/system/lib/Drupal/system/Controller/ThemeController.php b/core/modules/system/lib/Drupal/system/Controller/ThemeController.php index 852d2b6..14e7e34 100644 --- a/core/modules/system/lib/Drupal/system/Controller/ThemeController.php +++ b/core/modules/system/lib/Drupal/system/Controller/ThemeController.php @@ -103,12 +103,9 @@ public function enable(Request $request) { $theme = $request->get('theme'); if (isset($theme)) { - // Get current list of themes. - $themes = list_themes(TRUE); - - // Check if the specified theme is one recognized by the system. - if (!empty($themes[$theme])) { + if (drupal_get_path('theme', $theme)) { theme_enable(array($theme)); + $themes = list_themes(); drupal_set_message(t('The %theme theme has been enabled.', array('%theme' => $themes[$theme]->info['name']))); } else { diff --git a/core/modules/system/lib/Drupal/system/Tests/Batch/PageTest.php b/core/modules/system/lib/Drupal/system/Tests/Batch/PageTest.php index f268e02..c0824d6 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Batch/PageTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Batch/PageTest.php @@ -35,10 +35,10 @@ public static function getInfo() { function testBatchProgressPageTheme() { // Make sure that the page which starts the batch (an administrative page) // is using a different theme than would normally be used by the batch API. + \Drupal::service('theme_hander')->enable(array('seven', 'bartik')); \Drupal::config('system.theme') ->set('default', 'bartik') ->save(); - theme_enable(array('seven')); \Drupal::config('system.theme')->set('admin', 'seven')->save(); // Log in as an administrator who can see the administrative theme. $admin_user = $this->drupalCreateUser(array('view the administration theme')); diff --git a/core/modules/system/lib/Drupal/system/Tests/System/ThemeTest.php b/core/modules/system/lib/Drupal/system/Tests/System/ThemeTest.php index 554450a..801bc0c 100644 --- a/core/modules/system/lib/Drupal/system/Tests/System/ThemeTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/System/ThemeTest.php @@ -213,7 +213,7 @@ function testAdministrationTheme() { // Reset to the default theme settings. \Drupal::config('system.theme') - ->set('default', 'bartik') + ->set('default', 'stark') ->save(); $edit = array( 'admin_theme' => '0', @@ -222,10 +222,10 @@ function testAdministrationTheme() { $this->drupalPostForm('admin/appearance', $edit, t('Save configuration')); $this->drupalGet('admin'); - $this->assertRaw('core/themes/bartik', 'Site default theme used on administration page.'); + $this->assertRaw('core/themes/stark', 'Site default theme used on administration page.'); $this->drupalGet('node/add'); - $this->assertRaw('core/themes/bartik', 'Site default theme used on the add content page.'); + $this->assertRaw('core/themes/stark', 'Site default theme used on the add content page.'); } /** diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php index 492dfd6..616f928 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php @@ -202,11 +202,11 @@ function testFunctionOverride() { * Test the list_themes() function. */ function testListThemes() { + \Drupal::service('theme_handler')->enable(array('test_subtheme')); $themes = list_themes(); // Check if drupal_theme_access() retrieves enabled themes properly from list_themes(). $this->assertTrue(drupal_theme_access('test_theme'), 'Enabled theme detected'); - // Check if list_themes() returns disabled themes. - $this->assertTrue(array_key_exists('test_basetheme', $themes), 'Disabled theme detected'); + // Check for base theme and subtheme lists. $base_theme_list = array('test_basetheme' => 'Theme test base theme'); $sub_theme_list = array('test_subtheme' => 'Theme test subtheme'); @@ -223,6 +223,7 @@ function testListThemes() { * Test the theme_get_setting() function. */ function testThemeGetSetting() { + theme_enable(array('test_subtheme')); $GLOBALS['theme_key'] = 'test_theme'; $this->assertIdentical(theme_get_setting('theme_test_setting'), 'default value', 'theme_get_setting() uses the default theme automatically.'); $this->assertNotEqual(theme_get_setting('subtheme_override', 'test_basetheme'), theme_get_setting('subtheme_override', 'test_subtheme'), 'Base theme\'s default settings values can be overridden by subtheme.'); diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc index 1ad9b4b..7e972c9 100644 --- a/core/modules/system/system.admin.inc +++ b/core/modules/system/system.admin.inc @@ -10,20 +10,198 @@ use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; /** +<<<<<<< HEAD +======= + * Menu callback; displays a listing of all themes. + * + * @deprecated Use \Drupal\system\Controller\SystemController::themesPage() + */ +function system_themes_page() { + // Get current list of themes. + $themes = system_rebuild_theme_data(); + uasort($themes, 'system_sort_modules_by_info_name'); + + $theme_default = \Drupal::config('system.theme')->get('default'); + $theme_groups = array('enabled' => array(), 'disabled' => array()); + $admin_theme = \Drupal::config('system.theme')->get('admin'); + $admin_theme_options = array(); + + foreach ($themes as &$theme) { + if (!empty($theme->info['hidden'])) { + continue; + } + $theme->is_default = ($theme->name == $theme_default); + + // Identify theme screenshot. + $theme->screenshot = NULL; + // Create a list which includes the current theme and all its base themes. + if (isset($themes[$theme->name]->base_themes)) { + $theme_keys = array_keys($themes[$theme->name]->base_themes); + $theme_keys[] = $theme->name; + } + else { + $theme_keys = array($theme->name); + } + // Look for a screenshot in the current theme or in its closest ancestor. + foreach (array_reverse($theme_keys) as $theme_key) { + if (isset($themes[$theme_key]) && file_exists($themes[$theme_key]->info['screenshot'])) { + $theme->screenshot = array( + 'uri' => $themes[$theme_key]->info['screenshot'], + 'alt' => t('Screenshot for !theme theme', array('!theme' => $theme->info['name'])), + 'title' => t('Screenshot for !theme theme', array('!theme' => $theme->info['name'])), + 'attributes' => array('class' => array('screenshot')), + ); + break; + } + } + + if (empty($theme->status)) { + // Ensure this theme is compatible with this version of core. + // Require the 'content' region to make sure the main page + // content has a common place in all themes. + $theme->incompatible_core = !isset($theme->info['core']) || ($theme->info['core'] != \Drupal::CORE_COMPATIBILITY) || (!isset($theme->info['regions']['content'])); + $theme->incompatible_php = version_compare(phpversion(), $theme->info['php']) < 0; + // Confirmed that the base theme is available. + $theme->incompatible_base = (isset($theme->info['base theme']) && !isset($themes[$theme->info['base theme']])); + // Confirm that the theme engine is available. + $theme->incompatible_engine = (isset($theme->info['engine']) && !isset($theme->owner)); + } + $theme->operations = array(); + if (!empty($theme->status) || !$theme->incompatible_core && !$theme->incompatible_php && !$theme->incompatible_base && !$theme->incompatible_engine) { + // Create the operations links. + $query['theme'] = $theme->name; + if (drupal_theme_access($theme)) { + $theme->operations[] = array( + 'title' => t('Settings'), + 'href' => 'admin/appearance/settings/' . $theme->name, + 'attributes' => array('title' => t('Settings for !theme theme', array('!theme' => $theme->info['name']))), + ); + } + if (!empty($theme->status)) { + if (!$theme->is_default) { + if ($theme->name != $admin_theme) { + $theme->operations[] = array( + 'title' => t('Disable'), + 'route_name' => 'system.theme_disable', + 'query' => $query, + 'attributes' => array('title' => t('Disable !theme theme', array('!theme' => $theme->info['name']))), + ); + } + $theme->operations[] = array( + 'title' => t('Set default'), + 'route_name' => 'system.theme_set_default', + 'query' => $query, + 'attributes' => array('title' => t('Set !theme as default theme', array('!theme' => $theme->info['name']))), + ); + } + $admin_theme_options[$theme->name] = $theme->info['name']; + } + else { + $theme->operations[] = array( + 'title' => t('Enable'), + 'route_name' => 'system.theme_enable', + 'query' => $query, + 'attributes' => array('title' => t('Enable !theme theme', array('!theme' => $theme->info['name']))), + ); + $theme->operations[] = array( + 'title' => t('Enable and set default'), + 'route_name' => 'system.theme_set_default', + 'query' => $query, + 'attributes' => array('title' => t('Enable !theme as default theme', array('!theme' => $theme->info['name']))), + ); + } + } + + // Add notes to default and administration theme. + $theme->notes = array(); + $theme->classes = array(); + if ($theme->is_default) { + $theme->classes[] = 'theme-default'; + $theme->notes[] = t('default theme'); + } + if ($theme->name == $admin_theme || ($theme->is_default && $admin_theme == '0')) { + $theme->classes[] = 'theme-admin'; + $theme->notes[] = t('admin theme'); + } + + // Sort enabled and disabled themes into their own groups. + $theme_groups[$theme->status ? 'enabled' : 'disabled'][] = $theme; + } + + // There are two possible theme groups. + $theme_group_titles = array( + 'enabled' => format_plural(count($theme_groups['enabled']), 'Enabled theme', 'Enabled themes'), + ); + if (!empty($theme_groups['disabled'])) { + $theme_group_titles['disabled'] = format_plural(count($theme_groups['disabled']), 'Disabled theme', 'Disabled themes'); + } + + uasort($theme_groups['enabled'], 'system_sort_themes'); + drupal_alter('system_themes_page', $theme_groups); + + $build = array( + '#sorted' => TRUE, + ); + $build['system_themes_page'] = array( + '#theme' => 'system_themes_page', + '#theme_groups' => $theme_groups, + '#theme_group_titles' => $theme_group_titles, + ); + $build['admin_form'] = drupal_get_form('system_themes_admin_form', $admin_theme_options); + return $build; +} + +/** + * Form to select the administration theme. + * + * @ingroup forms + * @see system_themes_admin_form_submit() + */ +function system_themes_admin_form($form, &$form_state, $theme_options) { + // Administration theme settings. + $form['admin_theme'] = array( + '#type' => 'details', + '#title' => t('Administration theme'), + ); + $form['admin_theme']['admin_theme'] = array( + '#type' => 'select', + '#options' => array(0 => t('Default theme')) + $theme_options, + '#title' => t('Administration theme'), + '#description' => t('Choose "Default theme" to always use the same theme as the rest of the site.'), + '#default_value' => \Drupal::config('system.theme')->get('admin'), + ); + $form['admin_theme']['actions'] = array('#type' => 'actions'); + $form['admin_theme']['actions']['submit'] = array( + '#type' => 'submit', + '#value' => t('Save configuration'), + ); + return $form; +} + +/** + * Process system_themes_admin_form form submissions. + */ +function system_themes_admin_form_submit($form, &$form_state) { + drupal_set_message(t('The configuration options have been saved.')); + \Drupal::config('system.theme')->set('admin', $form_state['values']['admin_theme'])->save(); +} + +/** +>>>>>>> Applying patch from issue 1067408 comment 117 * Menu callback; Set the default theme. */ function system_theme_default() { $request = \Drupal::request(); $theme = $request->get('theme'); if (!empty($theme)) { - // Get current list of themes. - $themes = list_themes(); // Check if the specified theme is one recognized by the system. - if (!empty($themes[$theme])) { + if (drupal_get_path('theme', $theme)) { + $themes = list_themes(); // Enable the theme if it is currently disabled. - if (empty($themes[$theme]->status)) { - theme_enable(array($theme)); + if (!isset($themes[$theme])) { + \Drupal::service('theme_handler')->enable(array($theme)); + $themes = list_themes(); } // Set the default theme. \Drupal::config('system.theme') diff --git a/core/modules/system/system.module b/core/modules/system/system.module index c2cf620..d441753 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -2335,6 +2335,7 @@ function system_get_info($type, $name = NULL) { } } else { + // @todo Migrate theme info to ModuleInfo implementation. $list = system_list($type); foreach ($list as $shortname => $item) { if (!empty($item->status)) { @@ -2360,6 +2361,8 @@ function system_get_info($type, $name = NULL) { * returned. * * @see \Drupal\Core\Utility\ModuleInfo + * + * @todo Merge this helper entirely into system_get_info()? */ function system_get_module_info($property) { static $info; @@ -2499,29 +2502,29 @@ function _system_rebuild_theme_data() { * Array of all available themes and their data. */ function system_rebuild_theme_data() { - $themes = _system_rebuild_theme_data(); + $files = array(); + $themes = \Drupal::service('theme_handler')->rebuildThemeData(); ksort($themes); - // @todo This function has no business in determining/setting the status of - // a theme, but various other functions expect it to return themes with a - // $status property. system_list() stores the return value of this function - // in state, and ensures to set/override the $status property for each theme - // based on the current config. Remove this code when themes have a proper - // installation status. - // @see http://drupal.org/node/1067408 + + // Add name, status, and schema version. $enabled_themes = (array) \Drupal::config('system.theme')->get('enabled'); - $files = array(); + $disabled_themes = (array) \Drupal::config('system.theme.disabled')->get(); + $all_themes = $enabled_themes + $disabled_themes; foreach ($themes as $name => $theme) { + $theme->name = $name; + $theme->weight = isset($all_themes[$name]) ? $all_themes[$name] : 0; $theme->status = (int) isset($enabled_themes[$name]); + $theme->schema_version = SCHEMA_UNINSTALLED; $files[$name] = $theme->filename; + + // Replace last known theme data state. + \Drupal::state()->set('system.theme.data.' . $name, $theme); } - // Replace last known theme data state. - // @todo Obsolete with proper installation status for themes. - \Drupal::state()->set('system.theme.data', $themes); + $themes = \Drupal::moduleHandler()->buildModuleDependencies($themes); // Store filenames to allow system_list() and drupal_get_filename() to // retrieve them without having to rebuild or scan the filesystem. \Drupal::state()->set('system.theme.files', $files); - return $themes; } @@ -2545,25 +2548,26 @@ function _system_default_theme_features() { /** * Get a list of available regions from a specified theme. * - * @param $theme_key - * The name of a theme. + * @param $theme + * A theme object, or the name of a theme. * @param $show * Possible values: REGIONS_ALL or REGIONS_VISIBLE. Visible excludes hidden * regions. * @return * An array of regions in the form $region['name'] = 'description'. */ -function system_region_list($theme_key, $show = REGIONS_ALL) { - $themes = list_themes(); - if (!isset($themes[$theme_key])) { - return array(); +function system_region_list($theme, $show = REGIONS_ALL) { + if (!is_object($theme)) { + $themes = list_themes(); + if (!isset($themes[$theme])) { + return array(); + } + $theme = $themes[$theme]; } - $list = array(); - $info = $themes[$theme_key]->info; // If requested, suppress hidden regions. See block_admin_display_form(). - foreach ($info['regions'] as $name => $label) { - if ($show == REGIONS_ALL || !isset($info['regions_hidden']) || !in_array($name, $info['regions_hidden'])) { + foreach ($theme->info['regions'] as $name => $label) { + if ($show == REGIONS_ALL || !isset($theme->info['regions_hidden']) || !in_array($name, $theme->info['regions_hidden'])) { $list[$name] = t($label); } } diff --git a/theme-1067408.patch b/theme-1067408.patch new file mode 100644 index 0000000..448dd4e --- /dev/null +++ b/theme-1067408.patch @@ -0,0 +1,541 @@ +diff --git a/core/includes/module.inc b/core/includes/module.inc +index 429f5db..b45f447 100644 +--- a/core/includes/module.inc ++++ b/core/includes/module.inc +@@ -11,7 +11,7 @@ + * + * @param $type + * The type of list to return: +- * - module_enabled: All enabled modules. ++ * - module: All enabled modules. + * - bootstrap: All enabled modules required for bootstrap. + * - theme: All themes. + * +@@ -40,30 +40,26 @@ function system_list($type) { + ); + // Build a list of themes. + $enabled_themes = (array) \Drupal::config('system.theme')->get('enabled'); +- // @todo Themes include all themes, including disabled/uninstalled. This +- // system.theme.data state will go away entirely as soon as themes have +- // a proper installation status. +- // @see http://drupal.org/node/1067408 +- $theme_data = \Drupal::state()->get('system.theme.data'); +- if (empty($theme_data)) { +- // @todo: system_list() may be called from _drupal_bootstrap_code(), in ++ array_walk($enabled_themes, function (&$value, $key) { ++ $value = 'system.theme.data.' . $key; ++ }); ++ $theme_data = (array) \Drupal::state()->getMultiple($enabled_themes); ++ // @todo Remove me? ++ if (0 && empty($theme_data)) { ++ // system_list() may be called from _drupal_bootstrap_code() and + // which case system.module is not loaded yet. + // Prevent a filesystem scan in drupal_load() and include it directly. +- // @see http://drupal.org/node/1067408 + require_once DRUPAL_ROOT . '/core/modules/system/system.module'; + $theme_data = system_rebuild_theme_data(); + } + foreach ($theme_data as $name => $theme) { +- $theme->status = (int) isset($enabled_themes[$name]); + $lists['theme'][$name] = $theme; + // Build a list of filenames so drupal_get_filename can use it. +- if (isset($enabled_themes[$name])) { +- $lists['filepaths'][] = array( +- 'type' => 'theme', +- 'name' => $name, +- 'filepath' => $theme->filename, +- ); +- } ++ $lists['filepaths'][] = array( ++ 'type' => 'theme', ++ 'name' => $name, ++ 'filepath' => $theme->filename, ++ ); + } + // @todo Move into list_themes(). Read info for a particular requested + // theme from state instead. +@@ -112,14 +108,12 @@ function system_list_reset() { + drupal_static_reset('list_themes'); + cache('bootstrap')->delete('system_list'); + cache()->delete('system_info'); +- // Remove last known theme data state. +- // This causes system_list() to call system_rebuild_theme_data() on its next +- // invocation. When enabling a module that implements hook_system_info_alter() +- // to inject a new (testing) theme or manipulate an existing theme, then that +- // will cause system_list_reset() to be called, but theme data is not +- // necessarily rebuilt afterwards. +- // @todo Obsolete with proper installation status for themes. +- \Drupal::state()->delete('system.theme.data'); ++ // Rebuild theme data to update theme states. ++ // When enabling a module that implements hook_system_info_alter() to inject a ++ // new (testing) theme or manipulate an existing theme, then that will cause ++ // system_list_reset() to be called, but theme data is not necessarily rebuilt ++ // afterwards. ++ system_rebuild_theme_data(); + } + + /** +diff --git a/core/includes/theme.maintenance.inc b/core/includes/theme.maintenance.inc +index 96dcd2f..4e8a1e6 100644 +--- a/core/includes/theme.maintenance.inc ++++ b/core/includes/theme.maintenance.inc +@@ -19,11 +19,6 @@ + function _drupal_maintenance_theme() { + global $theme, $theme_key, $conf; + +- // If $theme is already set, assume the others are set too, and do nothing. +- if (isset($theme)) { +- return; +- } +- + require_once DRUPAL_ROOT . '/' . settings()->get('path_inc', 'core/includes/path.inc'); + require_once __DIR__ . '/theme.inc'; + require_once __DIR__ . '/common.inc'; +@@ -74,6 +69,9 @@ function _drupal_maintenance_theme() { + } + + $themes = list_themes(); ++ if (empty($themes) || !isset($themes[$custom_theme])) { ++ $themes = _system_rebuild_theme_data(); ++ } + + // list_themes() triggers a drupal_alter() in maintenance mode, but we can't + // let themes alter the .info.yml data until we know a theme's base themes. So +diff --git a/core/lib/Drupal/Core/Extension/ThemeHandler.php b/core/lib/Drupal/Core/Extension/ThemeHandler.php +index 40e59cb..ce69ab6 100644 +--- a/core/lib/Drupal/Core/Extension/ThemeHandler.php ++++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php +@@ -112,11 +112,56 @@ public function __construct(ConfigFactory $config_factory, ModuleHandlerInterfac + /** + * {@inheritdoc} + */ +- public function enable(array $theme_list) { ++ public function enable(array $theme_list, $enable_dependencies = TRUE) { ++ if ($enable_dependencies) { ++ // Get all theme data so we can find dependencies and sort. ++ $theme_data = $this->rebuildThemeData(); ++ // Create an associative array with weights as values. ++ $theme_list = array_flip(array_values($theme_list)); ++ ++ while (list($theme) = each($theme_list)) { ++ if (!isset($theme_data[$theme])) { ++ // This theme is not found in the filesystem, abort. ++ return FALSE; ++ } ++ if ($theme_data[$theme]->status) { ++ // Skip already enabled themes. ++ unset($theme_list[$theme]); ++ continue; ++ } ++ $theme_list[$theme] = $theme_data[$theme]->sort; ++ ++ // Add dependencies to the list, with a placeholder weight. ++ // The new themes will be processed as the while loop continues. ++ foreach (array_keys($theme_data[$theme]->requires) as $dependency) { ++ if (!isset($theme_list[$dependency])) { ++ $theme_list[$dependency] = 0; ++ } ++ } ++ ++ if (!$theme_list) { ++ // Nothing to do. All themes already enabled. ++ return TRUE; ++ } ++ } ++ ++ // Sort the theme list by pre-calculated weights. ++ arsort($theme_list); ++ $theme_list = array_keys($theme_list); ++ } ++ + $this->clearCssCache(); ++ $themes_installed = array(); ++ $themes_enabled = array(); + $theme_config = $this->configFactory->get('system.theme'); + $disabled_themes = $this->configFactory->get('system.theme.disabled'); + foreach ($theme_list as $key) { ++ // Only process themes that are not already enabled. ++ $enabled = $theme_config->get("enabled.$key") !== NULL; ++ if ($enabled) { ++ continue; ++ } ++ + // Throw an exception if the theme name is too long. + if (strlen($key) > DRUPAL_EXTENSION_NAME_MAX_LENGTH) { + throw new ExtensionNameLengthException(String::format('Theme name %name is over the maximum allowed length of @max characters.', array( +@@ -126,20 +171,65 @@ public function enable(array $theme_list) { + } + + // The value is not used; the weight is ignored for themes currently. +- $theme_config->set("enabled.$key", 0)->save(); +- $disabled_themes->clear($key)->save(); ++ $theme_config ++ ->set("enabled.$key", 0) ++ ->save(); ++ $disabled_themes ++ ->clear($key) ++ ->save(); + + // Refresh the theme list as config_install_default_config() needs an + // updated list to work. + $this->reset(); + // Install default configuration of the theme. + $this->configInstallDefaultConfig($key); ++ ++ // Allow modules to react prior to the installation of a theme. ++ $this->moduleHandler->invokeAll('themes_preinstall', array($key)); ++ // Now install the theme if necessary. ++ if (drupal_get_installed_schema_version($key, TRUE) == SCHEMA_UNINSTALLED) { ++ $version = SCHEMA_INSTALLED; ++ ++ // Install default configuration of the theme. ++ config_install_default_config('theme', $key); ++ ++ drupal_set_installed_schema_version($key, $version); ++ // Allow the theme to perform install tasks. ++ $function = $key . '_install'; ++ if (function_exists($function)) { ++ $function(); ++ } ++ // Record the fact that it was installed. ++ $themes_installed[] = $key; ++ watchdog('system', '%theme theme installed.', array('%theme' => $key), WATCHDOG_INFO); ++ } ++ ++ // Allow modules to react prior to the enabling of a theme. ++ $this->moduleHandler->invokeAll('themes_preenable', array($key)); ++ ++ // Enable the theme. ++ $function = $key . '_enable'; ++ if (function_exists($function)) { ++ $function(); ++ } ++ ++ // Record the fact that it was enabled. ++ $themes_enabled[] = $key; ++ watchdog('system', '%theme theme enabled.', array('%theme' => $key), WATCHDOG_INFO); + } + + $this->resetSystem(); + +- // Invoke hook_themes_enabled() after the themes have been enabled. +- $this->moduleHandler->invokeAll('themes_enabled', array($theme_list)); ++ // If any themes were newly installed, invoke hook_themes_installed(). ++ if (!empty($themes_installed)) { ++ $this->moduleHandler->invokeAll('themes_installed', $themes_installed); ++ } ++ ++ // If any themes were newly enabled, invoke hook_themes_enabled(). ++ if (!empty($themes_enabled)) { ++ $this->moduleHandler->invokeAll('themes_enabled', array($themes_enabled)); ++ } ++ return TRUE; + } + + /** +@@ -293,6 +383,9 @@ public function rebuildThemeData() { + + if (!empty($themes[$key]->info['base theme'])) { + $sub_themes[] = $key; ++ // Add the defined base theme as dependency to the theme, in order to ++ // re-use the dependency graph resolution of _module_build_dependencies(). ++ $themes[$key]->info['dependencies'][] = $themes[$key]->info['base theme']; + } + + $engine = $themes[$key]->info['engine']; +diff --git a/core/lib/Drupal/Core/Extension/ThemeHandlerInterface.php b/core/lib/Drupal/Core/Extension/ThemeHandlerInterface.php +index eef3381..98b4139 100644 +--- a/core/lib/Drupal/Core/Extension/ThemeHandlerInterface.php ++++ b/core/lib/Drupal/Core/Extension/ThemeHandlerInterface.php +@@ -17,11 +17,22 @@ + * + * @param array $theme_list + * An array of theme names. ++ * @param bool $enable_dependencies ++ * If TRUE, dependencies will automatically be added and enabled in the ++ * correct order. This incurs a significant performance cost, so use FALSE ++ * if you know $theme_list is already complete and in the correct order. + * + * @throws \Drupal\Core\Extension\ExtensionNameLengthException + * Thrown when the theme name is to long ++ * ++ * @todo D8: This is almost identical to module_enable() now. Only differences: ++ * - There is no required default .theme PHP file to load. ++ * - There is no schema to install. ++ * - System list, state, theme list, and static resets slightly differ. ++ * @todo hook_themes_installed(), hook_themes_enabled(), etc are not invoked in ++ * themes, since they are excluded from the hook system. + */ +- public function enable(array $theme_list); ++ public function enable(array $theme_list, $enable_dependencies = TRUE); + + /** + * Disables a given list of themes. +diff --git a/core/lib/Drupal/Core/Theme/ThemeAccessCheck.php b/core/lib/Drupal/Core/Theme/ThemeAccessCheck.php +index 75afddc..06376fd 100644 +--- a/core/lib/Drupal/Core/Theme/ThemeAccessCheck.php ++++ b/core/lib/Drupal/Core/Theme/ThemeAccessCheck.php +@@ -35,7 +35,7 @@ public function access(Route $route, Request $request, AccountInterface $account + */ + public function checkAccess($theme) { + $themes = list_themes(); +- return !empty($themes[$theme]->status); ++ return isset($themes[$theme]) && !empty($themes[$theme]->status); + } + + } +diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockAdminThemeTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockAdminThemeTest.php +index d553440..181f5b9 100644 +--- a/core/modules/block/lib/Drupal/block/Tests/BlockAdminThemeTest.php ++++ b/core/modules/block/lib/Drupal/block/Tests/BlockAdminThemeTest.php +@@ -37,9 +37,9 @@ function testAdminTheme() { + $admin_user = $this->drupalCreateUser(array('administer blocks', 'administer themes')); + $this->drupalLogin($admin_user); + +- // Ensure that access to block admin page is denied when theme is disabled. ++ // Ensure the block admin page cannot be accessed for a disabled theme. + $this->drupalGet('admin/structure/block/list/bartik'); +- $this->assertResponse(403); ++ $this->assertResponse(404); + + // Enable admin theme and confirm that tab is accessible. + theme_enable(array('bartik')); +diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php +index 265012e..2ee1d39 100644 +--- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php ++++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php +@@ -830,6 +830,11 @@ protected function setUp() { + $this->rebuildContainer(); + } + ++ // Re-initialize the theme to ensure that tests do not see an inconsistent ++ // behavior when calling functions that would initialize the theme if it has ++ // not been initialized yet. ++ drupal_theme_initialize(); ++ + // Reset/rebuild all data structures after enabling the modules. + $this->resetAll(); + +diff --git a/core/modules/system/lib/Drupal/system/Controller/ThemeController.php b/core/modules/system/lib/Drupal/system/Controller/ThemeController.php +index 852d2b6..14e7e34 100644 +--- a/core/modules/system/lib/Drupal/system/Controller/ThemeController.php ++++ b/core/modules/system/lib/Drupal/system/Controller/ThemeController.php +@@ -103,12 +103,9 @@ public function enable(Request $request) { + $theme = $request->get('theme'); + + if (isset($theme)) { +- // Get current list of themes. +- $themes = list_themes(TRUE); +- +- // Check if the specified theme is one recognized by the system. +- if (!empty($themes[$theme])) { ++ if (drupal_get_path('theme', $theme)) { + theme_enable(array($theme)); ++ $themes = list_themes(); + drupal_set_message(t('The %theme theme has been enabled.', array('%theme' => $themes[$theme]->info['name']))); + } + else { +diff --git a/core/modules/system/lib/Drupal/system/Tests/Batch/PageTest.php b/core/modules/system/lib/Drupal/system/Tests/Batch/PageTest.php +index f268e02..c0824d6 100644 +--- a/core/modules/system/lib/Drupal/system/Tests/Batch/PageTest.php ++++ b/core/modules/system/lib/Drupal/system/Tests/Batch/PageTest.php +@@ -35,10 +35,10 @@ public static function getInfo() { + function testBatchProgressPageTheme() { + // Make sure that the page which starts the batch (an administrative page) + // is using a different theme than would normally be used by the batch API. ++ \Drupal::service('theme_hander')->enable(array('seven', 'bartik')); + \Drupal::config('system.theme') + ->set('default', 'bartik') + ->save(); +- theme_enable(array('seven')); + \Drupal::config('system.theme')->set('admin', 'seven')->save(); + // Log in as an administrator who can see the administrative theme. + $admin_user = $this->drupalCreateUser(array('view the administration theme')); +diff --git a/core/modules/system/lib/Drupal/system/Tests/System/ThemeTest.php b/core/modules/system/lib/Drupal/system/Tests/System/ThemeTest.php +index 554450a..801bc0c 100644 +--- a/core/modules/system/lib/Drupal/system/Tests/System/ThemeTest.php ++++ b/core/modules/system/lib/Drupal/system/Tests/System/ThemeTest.php +@@ -213,7 +213,7 @@ function testAdministrationTheme() { + + // Reset to the default theme settings. + \Drupal::config('system.theme') +- ->set('default', 'bartik') ++ ->set('default', 'stark') + ->save(); + $edit = array( + 'admin_theme' => '0', +@@ -222,10 +222,10 @@ function testAdministrationTheme() { + $this->drupalPostForm('admin/appearance', $edit, t('Save configuration')); + + $this->drupalGet('admin'); +- $this->assertRaw('core/themes/bartik', 'Site default theme used on administration page.'); ++ $this->assertRaw('core/themes/stark', 'Site default theme used on administration page.'); + + $this->drupalGet('node/add'); +- $this->assertRaw('core/themes/bartik', 'Site default theme used on the add content page.'); ++ $this->assertRaw('core/themes/stark', 'Site default theme used on the add content page.'); + } + + /** +diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php +index 492dfd6..616f928 100644 +--- a/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php ++++ b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php +@@ -202,11 +202,11 @@ function testFunctionOverride() { + * Test the list_themes() function. + */ + function testListThemes() { ++ \Drupal::service('theme_handler')->enable(array('test_subtheme')); + $themes = list_themes(); + // Check if drupal_theme_access() retrieves enabled themes properly from list_themes(). + $this->assertTrue(drupal_theme_access('test_theme'), 'Enabled theme detected'); +- // Check if list_themes() returns disabled themes. +- $this->assertTrue(array_key_exists('test_basetheme', $themes), 'Disabled theme detected'); ++ + // Check for base theme and subtheme lists. + $base_theme_list = array('test_basetheme' => 'Theme test base theme'); + $sub_theme_list = array('test_subtheme' => 'Theme test subtheme'); +@@ -223,6 +223,7 @@ function testListThemes() { + * Test the theme_get_setting() function. + */ + function testThemeGetSetting() { ++ theme_enable(array('test_subtheme')); + $GLOBALS['theme_key'] = 'test_theme'; + $this->assertIdentical(theme_get_setting('theme_test_setting'), 'default value', 'theme_get_setting() uses the default theme automatically.'); + $this->assertNotEqual(theme_get_setting('subtheme_override', 'test_basetheme'), theme_get_setting('subtheme_override', 'test_subtheme'), 'Base theme\'s default settings values can be overridden by subtheme.'); +diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc +index 3af30c8..c59ad87 100644 +--- a/core/modules/system/system.admin.inc ++++ b/core/modules/system/system.admin.inc +@@ -21,8 +21,9 @@ function system_themes_page() { + uasort($themes, 'system_sort_modules_by_info_name'); + + $theme_default = \Drupal::config('system.theme')->get('default'); +- $theme_groups = array(); ++ $theme_groups = array('enabled' => array(), 'disabled' => array()); + $admin_theme = \Drupal::config('system.theme')->get('admin'); ++ $admin_theme_options = array(); + + foreach ($themes as &$theme) { + if (!empty($theme->info['hidden'])) { +@@ -191,14 +192,14 @@ function system_theme_default() { + $request = \Drupal::request(); + $theme = $request->get('theme'); + if (!empty($theme)) { +- // Get current list of themes. +- $themes = list_themes(); + + // Check if the specified theme is one recognized by the system. +- if (!empty($themes[$theme])) { ++ if (drupal_get_path('theme', $theme)) { ++ $themes = list_themes(); + // Enable the theme if it is currently disabled. +- if (empty($themes[$theme]->status)) { +- theme_enable(array($theme)); ++ if (!isset($themes[$theme])) { ++ \Drupal::service('theme_handler')->enable(array($theme)); ++ $themes = list_themes(); + } + // Set the default theme. + \Drupal::config('system.theme') +diff --git a/core/modules/system/system.module b/core/modules/system/system.module +index fac4c21..426b4f3 100644 +--- a/core/modules/system/system.module ++++ b/core/modules/system/system.module +@@ -2308,6 +2308,7 @@ function system_get_info($type, $name = NULL) { + } + } + else { ++ // @todo Migrate theme info to ModuleInfo implementation. + $list = system_list($type); + foreach ($list as $shortname => $item) { + if (!empty($item->status)) { +@@ -2333,6 +2334,8 @@ function system_get_info($type, $name = NULL) { + * returned. + * + * @see \Drupal\Core\Utility\ModuleInfo ++ * ++ * @todo Merge this helper entirely into system_get_info()? + */ + function system_get_module_info($property) { + static $info; +@@ -2472,29 +2475,29 @@ function _system_rebuild_theme_data() { + * Array of all available themes and their data. + */ + function system_rebuild_theme_data() { +- $themes = _system_rebuild_theme_data(); ++ $files = array(); ++ $themes = \Drupal::service('theme_handler')->rebuildThemeData(); + ksort($themes); +- // @todo This function has no business in determining/setting the status of +- // a theme, but various other functions expect it to return themes with a +- // $status property. system_list() stores the return value of this function +- // in state, and ensures to set/override the $status property for each theme +- // based on the current config. Remove this code when themes have a proper +- // installation status. +- // @see http://drupal.org/node/1067408 ++ ++ // Add name, status, and schema version. + $enabled_themes = (array) \Drupal::config('system.theme')->get('enabled'); +- $files = array(); ++ $disabled_themes = (array) \Drupal::config('system.theme.disabled')->get(); ++ $all_themes = $enabled_themes + $disabled_themes; + foreach ($themes as $name => $theme) { ++ $theme->name = $name; ++ $theme->weight = isset($all_themes[$name]) ? $all_themes[$name] : 0; + $theme->status = (int) isset($enabled_themes[$name]); ++ $theme->schema_version = SCHEMA_UNINSTALLED; + $files[$name] = $theme->filename; ++ ++ // Replace last known theme data state. ++ \Drupal::state()->set('system.theme.data.' . $name, $theme); + } +- // Replace last known theme data state. +- // @todo Obsolete with proper installation status for themes. +- \Drupal::state()->set('system.theme.data', $themes); ++ $themes = \Drupal::moduleHandler()->buildModuleDependencies($themes); + + // Store filenames to allow system_list() and drupal_get_filename() to + // retrieve them without having to rebuild or scan the filesystem. + \Drupal::state()->set('system.theme.files', $files); +- + return $themes; + } + +@@ -2518,25 +2521,26 @@ function _system_default_theme_features() { + /** + * Get a list of available regions from a specified theme. + * +- * @param $theme_key +- * The name of a theme. ++ * @param $theme ++ * A theme object, or the name of a theme. + * @param $show + * Possible values: REGIONS_ALL or REGIONS_VISIBLE. Visible excludes hidden + * regions. + * @return + * An array of regions in the form $region['name'] = 'description'. + */ +-function system_region_list($theme_key, $show = REGIONS_ALL) { +- $themes = list_themes(); +- if (!isset($themes[$theme_key])) { +- return array(); ++function system_region_list($theme, $show = REGIONS_ALL) { ++ if (!is_object($theme)) { ++ $themes = list_themes(); ++ if (!isset($themes[$theme])) { ++ return array(); ++ } ++ $theme = $themes[$theme]; + } +- + $list = array(); +- $info = $themes[$theme_key]->info; + // If requested, suppress hidden regions. See block_admin_display_form(). +- foreach ($info['regions'] as $name => $label) { +- if ($show == REGIONS_ALL || !isset($info['regions_hidden']) || !in_array($name, $info['regions_hidden'])) { ++ foreach ($theme->info['regions'] as $name => $label) { ++ if ($show == REGIONS_ALL || !isset($theme->info['regions_hidden']) || !in_array($name, $theme->info['regions_hidden'])) { + $list[$name] = t($label); + } + }