Mercurial > hg > isophonics-drupal-site
comparison core/lib/Drupal/Core/Extension/ThemeInstaller.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | 7a779792577d |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\Core\Extension; | |
4 | |
5 use Drupal\Core\Asset\AssetCollectionOptimizerInterface; | |
6 use Drupal\Core\Cache\Cache; | |
7 use Drupal\Core\Config\ConfigFactoryInterface; | |
8 use Drupal\Core\Config\ConfigInstallerInterface; | |
9 use Drupal\Core\Config\ConfigManagerInterface; | |
10 use Drupal\Core\Routing\RouteBuilderInterface; | |
11 use Drupal\Core\State\StateInterface; | |
12 use Psr\Log\LoggerInterface; | |
13 | |
14 /** | |
15 * Manages theme installation/uninstallation. | |
16 */ | |
17 class ThemeInstaller implements ThemeInstallerInterface { | |
18 | |
19 /** | |
20 * @var \Drupal\Core\Extension\ThemeHandlerInterface | |
21 */ | |
22 protected $themeHandler; | |
23 | |
24 /** | |
25 * @var \Drupal\Core\Config\ConfigFactoryInterface | |
26 */ | |
27 protected $configFactory; | |
28 | |
29 /** | |
30 * @var \Drupal\Core\Config\ConfigInstallerInterface | |
31 */ | |
32 protected $configInstaller; | |
33 | |
34 /** | |
35 * @var \Drupal\Core\Extension\ModuleHandlerInterface | |
36 */ | |
37 protected $moduleHandler; | |
38 | |
39 /** | |
40 * @var \Drupal\Core\State\StateInterface | |
41 */ | |
42 protected $state; | |
43 | |
44 /** | |
45 * @var \Drupal\Core\Config\ConfigManagerInterface | |
46 */ | |
47 protected $configManager; | |
48 | |
49 /** | |
50 * @var \Drupal\Core\Asset\AssetCollectionOptimizerInterface | |
51 */ | |
52 protected $cssCollectionOptimizer; | |
53 | |
54 /** | |
55 * @var \Drupal\Core\Routing\RouteBuilderInterface | |
56 */ | |
57 protected $routeBuilder; | |
58 | |
59 /** | |
60 * @var \Psr\Log\LoggerInterface | |
61 */ | |
62 protected $logger; | |
63 | |
64 /** | |
65 * Constructs a new ThemeInstaller. | |
66 * | |
67 * @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler | |
68 * The theme handler. | |
69 * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory | |
70 * The config factory to get the installed themes. | |
71 * @param \Drupal\Core\Config\ConfigInstallerInterface $config_installer | |
72 * (optional) The config installer to install configuration. This optional | |
73 * to allow the theme handler to work before Drupal is installed and has a | |
74 * database. | |
75 * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler | |
76 * The module handler to fire themes_installed/themes_uninstalled hooks. | |
77 * @param \Drupal\Core\Config\ConfigManagerInterface $config_manager | |
78 * The config manager used to uninstall a theme. | |
79 * @param \Drupal\Core\Asset\AssetCollectionOptimizerInterface $css_collection_optimizer | |
80 * The CSS asset collection optimizer service. | |
81 * @param \Drupal\Core\Routing\RouteBuilderInterface $route_builder | |
82 * (optional) The route builder service to rebuild the routes if a theme is | |
83 * installed. | |
84 * @param \Psr\Log\LoggerInterface $logger | |
85 * A logger instance. | |
86 * @param \Drupal\Core\State\StateInterface $state | |
87 * The state store. | |
88 */ | |
89 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) { | |
90 $this->themeHandler = $theme_handler; | |
91 $this->configFactory = $config_factory; | |
92 $this->configInstaller = $config_installer; | |
93 $this->moduleHandler = $module_handler; | |
94 $this->configManager = $config_manager; | |
95 $this->cssCollectionOptimizer = $css_collection_optimizer; | |
96 $this->routeBuilder = $route_builder; | |
97 $this->logger = $logger; | |
98 $this->state = $state; | |
99 } | |
100 | |
101 /** | |
102 * {@inheritdoc} | |
103 */ | |
104 public function install(array $theme_list, $install_dependencies = TRUE) { | |
105 $extension_config = $this->configFactory->getEditable('core.extension'); | |
106 | |
107 $theme_data = $this->themeHandler->rebuildThemeData(); | |
108 | |
109 if ($install_dependencies) { | |
110 $theme_list = array_combine($theme_list, $theme_list); | |
111 | |
112 if ($missing = array_diff_key($theme_list, $theme_data)) { | |
113 // One or more of the given themes doesn't exist. | |
114 throw new \InvalidArgumentException('Unknown themes: ' . implode(', ', $missing) . '.'); | |
115 } | |
116 | |
117 // Only process themes that are not installed currently. | |
118 $installed_themes = $extension_config->get('theme') ?: []; | |
119 if (!$theme_list = array_diff_key($theme_list, $installed_themes)) { | |
120 // Nothing to do. All themes already installed. | |
121 return TRUE; | |
122 } | |
123 | |
124 while (list($theme) = each($theme_list)) { | |
125 // Add dependencies to the list. The new themes will be processed as | |
126 // the while loop continues. | |
127 foreach (array_keys($theme_data[$theme]->requires) as $dependency) { | |
128 if (!isset($theme_data[$dependency])) { | |
129 // The dependency does not exist. | |
130 return FALSE; | |
131 } | |
132 | |
133 // Skip already installed themes. | |
134 if (!isset($theme_list[$dependency]) && !isset($installed_themes[$dependency])) { | |
135 $theme_list[$dependency] = $dependency; | |
136 } | |
137 } | |
138 } | |
139 | |
140 // Set the actual theme weights. | |
141 $theme_list = array_map(function ($theme) use ($theme_data) { | |
142 return $theme_data[$theme]->sort; | |
143 }, $theme_list); | |
144 | |
145 // Sort the theme list by their weights (reverse). | |
146 arsort($theme_list); | |
147 $theme_list = array_keys($theme_list); | |
148 } | |
149 else { | |
150 $installed_themes = $extension_config->get('theme') ?: []; | |
151 } | |
152 | |
153 $themes_installed = []; | |
154 foreach ($theme_list as $key) { | |
155 // Only process themes that are not already installed. | |
156 $installed = $extension_config->get("theme.$key") !== NULL; | |
157 if ($installed) { | |
158 continue; | |
159 } | |
160 | |
161 // Throw an exception if the theme name is too long. | |
162 if (strlen($key) > DRUPAL_EXTENSION_NAME_MAX_LENGTH) { | |
163 throw new ExtensionNameLengthException("Theme name $key is over the maximum allowed length of " . DRUPAL_EXTENSION_NAME_MAX_LENGTH . ' characters.'); | |
164 } | |
165 | |
166 // Validate default configuration of the theme. If there is existing | |
167 // configuration then stop installing. | |
168 $this->configInstaller->checkConfigurationToInstall('theme', $key); | |
169 | |
170 // The value is not used; the weight is ignored for themes currently. Do | |
171 // not check schema when saving the configuration. | |
172 $extension_config | |
173 ->set("theme.$key", 0) | |
174 ->save(TRUE); | |
175 | |
176 // Add the theme to the current list. | |
177 // @todo Remove all code that relies on $status property. | |
178 $theme_data[$key]->status = 1; | |
179 $this->themeHandler->addTheme($theme_data[$key]); | |
180 | |
181 // Update the current theme data accordingly. | |
182 $current_theme_data = $this->state->get('system.theme.data', []); | |
183 $current_theme_data[$key] = $theme_data[$key]; | |
184 $this->state->set('system.theme.data', $current_theme_data); | |
185 | |
186 // Reset theme settings. | |
187 $theme_settings = &drupal_static('theme_get_setting'); | |
188 unset($theme_settings[$key]); | |
189 | |
190 // @todo Remove system_list(). | |
191 $this->systemListReset(); | |
192 | |
193 // Only install default configuration if this theme has not been installed | |
194 // already. | |
195 if (!isset($installed_themes[$key])) { | |
196 // Install default configuration of the theme. | |
197 $this->configInstaller->installDefaultConfig('theme', $key); | |
198 } | |
199 | |
200 $themes_installed[] = $key; | |
201 | |
202 // Record the fact that it was installed. | |
203 $this->logger->info('%theme theme installed.', ['%theme' => $key]); | |
204 } | |
205 | |
206 $this->cssCollectionOptimizer->deleteAll(); | |
207 $this->resetSystem(); | |
208 | |
209 // Invoke hook_themes_installed() after the themes have been installed. | |
210 $this->moduleHandler->invokeAll('themes_installed', [$themes_installed]); | |
211 | |
212 return !empty($themes_installed); | |
213 } | |
214 | |
215 /** | |
216 * {@inheritdoc} | |
217 */ | |
218 public function uninstall(array $theme_list) { | |
219 $extension_config = $this->configFactory->getEditable('core.extension'); | |
220 $theme_config = $this->configFactory->getEditable('system.theme'); | |
221 $list = $this->themeHandler->listInfo(); | |
222 foreach ($theme_list as $key) { | |
223 if (!isset($list[$key])) { | |
224 throw new \InvalidArgumentException("Unknown theme: $key."); | |
225 } | |
226 if ($key === $theme_config->get('default')) { | |
227 throw new \InvalidArgumentException("The current default theme $key cannot be uninstalled."); | |
228 } | |
229 if ($key === $theme_config->get('admin')) { | |
230 throw new \InvalidArgumentException("The current administration theme $key cannot be uninstalled."); | |
231 } | |
232 // Base themes cannot be uninstalled if sub themes are installed, and if | |
233 // they are not uninstalled at the same time. | |
234 // @todo https://www.drupal.org/node/474684 and | |
235 // https://www.drupal.org/node/1297856 themes should leverage the module | |
236 // dependency system. | |
237 if (!empty($list[$key]->sub_themes)) { | |
238 foreach ($list[$key]->sub_themes as $sub_key => $sub_label) { | |
239 if (isset($list[$sub_key]) && !in_array($sub_key, $theme_list, TRUE)) { | |
240 throw new \InvalidArgumentException("The base theme $key cannot be uninstalled, because theme $sub_key depends on it."); | |
241 } | |
242 } | |
243 } | |
244 } | |
245 | |
246 $this->cssCollectionOptimizer->deleteAll(); | |
247 $current_theme_data = $this->state->get('system.theme.data', []); | |
248 foreach ($theme_list as $key) { | |
249 // The value is not used; the weight is ignored for themes currently. | |
250 $extension_config->clear("theme.$key"); | |
251 | |
252 // Update the current theme data accordingly. | |
253 unset($current_theme_data[$key]); | |
254 | |
255 // Reset theme settings. | |
256 $theme_settings = &drupal_static('theme_get_setting'); | |
257 unset($theme_settings[$key]); | |
258 | |
259 // Remove all configuration belonging to the theme. | |
260 $this->configManager->uninstall('theme', $key); | |
261 | |
262 } | |
263 // Don't check schema when uninstalling a theme since we are only clearing | |
264 // keys. | |
265 $extension_config->save(TRUE); | |
266 $this->state->set('system.theme.data', $current_theme_data); | |
267 | |
268 | |
269 // @todo Remove system_list(). | |
270 $this->themeHandler->refreshInfo(); | |
271 $this->resetSystem(); | |
272 | |
273 $this->moduleHandler->invokeAll('themes_uninstalled', [$theme_list]); | |
274 } | |
275 | |
276 /** | |
277 * Resets some other systems like rebuilding the route information or caches. | |
278 */ | |
279 protected function resetSystem() { | |
280 if ($this->routeBuilder) { | |
281 $this->routeBuilder->setRebuildNeeded(); | |
282 } | |
283 $this->systemListReset(); | |
284 | |
285 // @todo It feels wrong to have the requirement to clear the local tasks | |
286 // cache here. | |
287 Cache::invalidateTags(['local_task']); | |
288 $this->themeRegistryRebuild(); | |
289 } | |
290 | |
291 /** | |
292 * Wraps drupal_theme_rebuild(). | |
293 */ | |
294 protected function themeRegistryRebuild() { | |
295 drupal_theme_rebuild(); | |
296 } | |
297 | |
298 /** | |
299 * Wraps system_list_reset(). | |
300 */ | |
301 protected function systemListReset() { | |
302 system_list_reset(); | |
303 } | |
304 | |
305 } |