4 * Contains \Drupal\bootstrap\ThemeSettings.
7 namespace Drupal\bootstrap;
9 use Drupal\Component\Utility\DiffArray;
10 use Drupal\Component\Utility\NestedArray;
11 use Drupal\Core\Config\Config;
12 use Drupal\Core\Config\StorageException;
15 * Provides a configuration API wrapper for runtime merged theme settings.
17 * This is a wrapper around theme_get_setting() since it does not inherit
18 * base theme config nor handle default/overridden values very well.
22 class ThemeSettings extends Config {
25 * The default settings.
32 * The current theme object.
34 * @var \Drupal\bootstrap\Theme
41 public function __construct(Theme $theme) {
42 parent::__construct($theme->getName() . '.settings', \Drupal::service('config.storage'), \Drupal::service('event_dispatcher'), \Drupal::service('config.typed'));
43 $this->theme = $theme;
46 $cache = $theme->getCache('settings');
48 // Use cached settings.
49 if ($defaults = $cache->get('defaults')) {
50 $this->defaults = $defaults;
51 $this->initWithData($cache->get('data', []));
55 // Retrieve the global settings from configuration.
56 $this->defaults = \Drupal::config('system.theme.global')->get();
58 // Retrieve the theme setting plugin discovery defaults (code).
59 foreach ($theme->getSettingPlugin() as $name => $setting) {
60 $this->defaults[$name] = $setting->getDefaultValue();
63 // Retrieve the theme ancestry.
64 $ancestry = $theme->getAncestry();
66 // Remove the active theme from the ancestry.
67 $active_theme = array_pop($ancestry);
69 // Iterate and merge all ancestor theme config into the defaults.
70 foreach ($ancestry as $ancestor) {
71 $this->defaults = NestedArray::mergeDeepArray([$this->defaults, $this->getThemeConfig($ancestor)], TRUE);
74 // Merge the active theme config.
75 $this->initWithData($this->getThemeConfig($active_theme, TRUE));
77 // Cache the data and defaults.
78 $cache->set('data', $this->data);
79 $cache->set('defaults', $this->defaults);
85 public function getCacheTags() {
92 public function get($key = '') {
94 return NestedArray::mergeDeepArray([$this->defaults, $this->data], TRUE);
97 $value = parent::get($key);
99 $value = $this->getOriginal($key);
108 public function getOriginal($key = '', $apply_overrides = TRUE) {
109 $original_data = $this->defaults;
110 if ($apply_overrides) {
112 if (isset($this->moduleOverrides) && is_array($this->moduleOverrides)) {
113 $original_data = NestedArray::mergeDeepArray(array($original_data, $this->moduleOverrides), TRUE);
115 if (isset($this->settingsOverrides) && is_array($this->settingsOverrides)) {
116 $original_data = NestedArray::mergeDeepArray(array($original_data, $this->settingsOverrides), TRUE);
121 return $original_data;
124 $parts = explode('.', $key);
125 if (count($parts) == 1) {
126 return isset($original_data[$key]) ? $original_data[$key] : NULL;
129 $value = NestedArray::getValue($original_data, $parts, $key_exists);
130 return $key_exists ? $value : NULL;
136 * Retrieves a specific theme's stored config settings.
138 * @param \Drupal\bootstrap\Theme $theme
140 * @param bool $active_theme
141 * Flag indicating whether or not $theme is the active theme.
144 * A array diff of overridden config theme settings.
146 public function getThemeConfig(Theme $theme, $active_theme = FALSE) {
147 $config = new \Drupal\Core\Theme\ThemeSettings($theme->getName());
149 // Retrieve configured theme-specific settings, if any.
151 if ($theme_settings = \Drupal::config($theme->getName() . '.settings')->get()) {
152 // Remove schemas if not the active theme.
153 if (!$active_theme) {
154 unset($theme_settings['schemas']);
156 $config->merge($theme_settings);
159 catch (StorageException $e) {
162 // If the theme does not support a particular feature, override the
163 // global setting and set the value to NULL.
164 $info = $theme->getInfo();
165 if (!empty($info['features'])) {
166 foreach (_system_default_theme_features() as $feature) {
167 if (!in_array($feature, $info['features'])) {
168 $config->set('features.' . $feature, NULL);
173 // Generate the path to the logo image.
174 if ($config->get('features.logo')) {
176 foreach (['svg', 'png', 'jpg'] as $type) {
177 if (file_exists($theme->getPath() . "/logo.$type")) {
178 $logo_url = file_create_url($theme->getPath() . "/logo.$type");
182 if ($config->get('logo.use_default') && $logo_url) {
183 $config->set('logo.url', $logo_url);
185 elseif (($logo_path = $config->get('logo.path')) && file_exists($logo_path)) {
186 $config->set('logo.url', file_create_url($logo_path));
190 // Generate the path to the favicon.
191 if ($config->get('features.favicon')) {
192 $favicon_url = $theme->getPath() . '/favicon.ico';
193 if ($config->get('favicon.use_default') && file_exists($favicon_url)) {
194 $config->set('favicon.url', file_create_url($favicon_url));
196 elseif ($favicon_path = $config->get('favicon.path')) {
197 $config->set('favicon.url', file_create_url($favicon_path));
201 // Retrieve the config data.
202 $data = $config->get();
204 // Retrieve a diff of settings that override the defaults.
205 $diff = DiffArray::diffAssocRecursive($data, $this->defaults);
207 // Ensure core features are always present in the diff. The theme settings
208 // form will not work properly otherwise.
209 // @todo Just rebuild the features section of the form?
210 foreach (['favicon', 'features', 'logo'] as $key) {
212 $arrays[] = isset($this->defaults[$key]) ? $this->defaults[$key] : [];
213 $arrays[] = isset($data[$key]) ? $data[$key] : [];
214 $diff[$key] = NestedArray::mergeDeepArray($arrays, TRUE);
221 * Determines if a setting overrides the default value.
223 * @param string $name
224 * The name of the setting to check.
225 * @param mixed $value
226 * The new value to check.
231 public function overridesValue($name, $value) {
232 // Retrieve the currently stored value for comparison purposes.
233 $current_value = $this->get($name);
235 // Due to the nature of DiffArray::diffAssocRecursive, if the provided
236 // value is an empty array, it cannot be iterated over to determine if
237 // the values are different. Instead, it must be checked explicitly.
238 // @see https://www.drupal.org/node/2771121
239 if ($value === [] && $current_value !== []) {
243 // Otherwise, determine if value is overridden by any array differences.
244 return !!DiffArray::diffAssocRecursive([$name => $value], [$name => $current_value]);
250 public function save($has_trusted_data = FALSE) {
251 parent::save($has_trusted_data);
252 $this->theme->getCache('settings')->deleteAll();