View source
<?php
namespace Drupal\Core\Extension;
use Drupal\Component\Utility\Html;
use Drupal\Core\Asset\AssetCollectionOptimizerInterface;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Config\ConfigInstallerInterface;
use Drupal\Core\Config\ConfigManagerInterface;
use Drupal\Core\Extension\Exception\UnknownExtensionException;
use Drupal\Core\Routing\RouteBuilderInterface;
use Drupal\Core\State\StateInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\system\ModuleDependencyMessageTrait;
use Psr\Log\LoggerInterface;
class ThemeInstaller implements ThemeInstallerInterface {
use ModuleDependencyMessageTrait;
use StringTranslationTrait;
protected $themeHandler;
protected $configFactory;
protected $configInstaller;
protected $moduleHandler;
protected $state;
protected $configManager;
protected $cssCollectionOptimizer;
protected $routeBuilder;
protected $logger;
protected $moduleExtensionList;
public function __construct(ThemeHandlerInterface $theme_handler, ConfigFactoryInterface $config_factory, ConfigInstallerInterface $config_installer, ModuleHandlerInterface $module_handler, ConfigManagerInterface $config_manager, AssetCollectionOptimizerInterface $css_collection_optimizer, RouteBuilderInterface $route_builder, LoggerInterface $logger, StateInterface $state, ModuleExtensionList $module_extension_list = NULL) {
$this->themeHandler = $theme_handler;
$this->configFactory = $config_factory;
$this->configInstaller = $config_installer;
$this->moduleHandler = $module_handler;
$this->configManager = $config_manager;
$this->cssCollectionOptimizer = $css_collection_optimizer;
$this->routeBuilder = $route_builder;
$this->logger = $logger;
$this->state = $state;
if ($module_extension_list === NULL) {
@trigger_error('The extension.list.module service must be passed to ' . __NAMESPACE__ . '\\ThemeInstaller::__construct(). It was added in drupal:8.9.0 and will be required before drupal:10.0.0.', E_USER_DEPRECATED);
$module_extension_list = \Drupal::service('extension.list.module');
}
$this->moduleExtensionList = $module_extension_list;
}
public function install(array $theme_list, $install_dependencies = TRUE) {
$extension_config = $this->configFactory
->getEditable('core.extension');
$theme_data = $this->themeHandler
->rebuildThemeData();
$installed_themes = $extension_config
->get('theme') ?: [];
$installed_modules = $extension_config
->get('module') ?: [];
if ($install_dependencies) {
$theme_list = array_combine($theme_list, $theme_list);
if ($missing = array_diff_key($theme_list, $theme_data)) {
throw new UnknownExtensionException('Unknown themes: ' . implode(', ', $missing) . '.');
}
if (!($theme_list = array_diff_key($theme_list, $installed_themes))) {
return TRUE;
}
$module_list = $this->moduleExtensionList
->getList();
foreach ($theme_list as $theme => $value) {
$module_dependencies = $theme_data[$theme]->module_dependencies;
$theme_dependencies = array_diff_key($theme_data[$theme]->requires, $module_dependencies);
$unmet_module_dependencies = array_diff_key($module_dependencies, $installed_modules);
if (!empty($unmet_module_dependencies)) {
$unmet_module_dependencies_list = implode(', ', array_keys($unmet_module_dependencies));
throw new MissingDependencyException("Unable to install theme: '{$theme}' due to unmet module dependencies: '{$unmet_module_dependencies_list}'.");
}
foreach ($module_dependencies as $dependency => $dependency_object) {
if ($incompatible = $this
->checkDependencyMessage($module_list, $dependency, $dependency_object)) {
$sanitized_message = Html::decodeEntities(strip_tags($incompatible));
throw new MissingDependencyException("Unable to install theme: {$sanitized_message}");
}
}
foreach (array_keys($theme_dependencies) as $dependency) {
if (!isset($theme_data[$dependency])) {
return FALSE;
}
if (!isset($theme_list[$dependency]) && !isset($installed_themes[$dependency])) {
$theme_list[$dependency] = $dependency;
}
}
}
$theme_list = array_map(function ($theme) use ($theme_data) {
return $theme_data[$theme]->sort;
}, $theme_list);
arsort($theme_list);
$theme_list = array_keys($theme_list);
}
$themes_installed = [];
foreach ($theme_list as $key) {
$installed = $extension_config
->get("theme.{$key}") !== NULL;
if ($installed) {
continue;
}
if (strlen($key) > DRUPAL_EXTENSION_NAME_MAX_LENGTH) {
throw new ExtensionNameLengthException("Theme name {$key} is over the maximum allowed length of " . DRUPAL_EXTENSION_NAME_MAX_LENGTH . ' characters.');
}
$this->configInstaller
->checkConfigurationToInstall('theme', $key);
$extension_config
->set("theme.{$key}", 0)
->save(TRUE);
$theme_settings =& drupal_static('theme_get_setting');
unset($theme_settings[$key]);
$this->themeHandler
->reset();
if (!isset($installed_themes[$key])) {
$this->configInstaller
->installDefaultConfig('theme', $key);
}
$themes_installed[] = $key;
$this->logger
->info('%theme theme installed.', [
'%theme' => $key,
]);
}
$this->cssCollectionOptimizer
->deleteAll();
$this
->resetSystem();
$this->moduleHandler
->invokeAll('themes_installed', [
$themes_installed,
]);
return !empty($themes_installed);
}
public function uninstall(array $theme_list) {
$extension_config = $this->configFactory
->getEditable('core.extension');
$theme_config = $this->configFactory
->getEditable('system.theme');
$list = $this->themeHandler
->listInfo();
foreach ($theme_list as $key) {
if (!isset($list[$key])) {
throw new UnknownExtensionException("Unknown theme: {$key}.");
}
if ($key === $theme_config
->get('default')) {
throw new \InvalidArgumentException("The current default theme {$key} cannot be uninstalled.");
}
if ($key === $theme_config
->get('admin')) {
throw new \InvalidArgumentException("The current administration theme {$key} cannot be uninstalled.");
}
if (!empty($list[$key]->sub_themes)) {
foreach ($list[$key]->sub_themes as $sub_key => $sub_label) {
if (isset($list[$sub_key]) && !in_array($sub_key, $theme_list, TRUE)) {
throw new \InvalidArgumentException("The base theme {$key} cannot be uninstalled, because theme {$sub_key} depends on it.");
}
}
}
}
$this->cssCollectionOptimizer
->deleteAll();
foreach ($theme_list as $key) {
$extension_config
->clear("theme.{$key}");
$theme_settings =& drupal_static('theme_get_setting');
unset($theme_settings[$key]);
$this->configManager
->uninstall('theme', $key);
}
$extension_config
->save(TRUE);
$this
->resetSystem();
$this->themeHandler
->reset();
$this->moduleHandler
->invokeAll('themes_uninstalled', [
$theme_list,
]);
}
protected function resetSystem() {
if ($this->routeBuilder) {
$this->routeBuilder
->setRebuildNeeded();
}
Cache::invalidateTags([
'local_task',
]);
$this
->themeRegistryRebuild();
}
protected function themeRegistryRebuild() {
drupal_theme_rebuild();
}
}