c5c9fb6709703f6947c72ff1549f8b1006b74581
[yaffs-website] / web / themes / contrib / bootstrap / src / ThemeSettings.php
1 <?php
2 /**
3  * @file
4  * Contains \Drupal\bootstrap\ThemeSettings.
5  */
6
7 namespace Drupal\bootstrap;
8
9 use Drupal\Component\Utility\DiffArray;
10 use Drupal\Component\Utility\NestedArray;
11 use Drupal\Core\Config\Config;
12 use Drupal\Core\Config\StorageException;
13
14 /**
15  * Provides a configuration API wrapper for runtime merged theme settings.
16  *
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.
19  *
20  * @ingroup utility
21  */
22 class ThemeSettings extends Config {
23
24   /**
25    * The default settings.
26    *
27    * @var array
28    */
29   protected $defaults;
30
31   /**
32    * The current theme object.
33    *
34    * @var \Drupal\bootstrap\Theme
35    */
36   protected $theme;
37
38   /**
39    * {@inheritdoc}
40    */
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;
44
45     // Retrieve cache.
46     $cache = $theme->getCache('settings');
47
48     // Use cached settings.
49     if ($defaults = $cache->get('defaults')) {
50       $this->defaults = $defaults;
51       $this->initWithData($cache->get('data', []));
52       return;
53     }
54
55     // Retrieve the global settings from configuration.
56     $this->defaults = \Drupal::config('system.theme.global')->get();
57
58     // Retrieve the theme setting plugin discovery defaults (code).
59     foreach ($theme->getSettingPlugin() as $name => $setting) {
60       $this->defaults[$name] = $setting->getDefaultValue();
61     }
62
63     // Retrieve the theme ancestry.
64     $ancestry = $theme->getAncestry();
65
66     // Remove the active theme from the ancestry.
67     $active_theme = array_pop($ancestry);
68
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);
72     }
73
74     // Merge the active theme config.
75     $this->initWithData($this->getThemeConfig($active_theme, TRUE));
76
77     // Cache the data and defaults.
78     $cache->set('data', $this->data);
79     $cache->set('defaults', $this->defaults);
80   }
81
82   /**
83    * {@inheritdoc}
84    */
85   public function getCacheTags() {
86     return ['rendered'];
87   }
88
89   /**
90    * {@inheritdoc}
91    */
92   public function get($key = '') {
93     if (empty($key)) {
94       return NestedArray::mergeDeepArray([$this->defaults, $this->data], TRUE);
95     }
96     else {
97       $value = parent::get($key);
98       if (!isset($value)) {
99         $value = $this->getOriginal($key);
100       }
101     }
102     return $value;
103   }
104
105   /**
106    * {@inheritdoc}
107    */
108   public function getOriginal($key = '', $apply_overrides = TRUE) {
109     $original_data = $this->defaults;
110     if ($apply_overrides) {
111       // Apply overrides.
112       if (isset($this->moduleOverrides) && is_array($this->moduleOverrides)) {
113         $original_data = NestedArray::mergeDeepArray(array($original_data, $this->moduleOverrides), TRUE);
114       }
115       if (isset($this->settingsOverrides) && is_array($this->settingsOverrides)) {
116         $original_data = NestedArray::mergeDeepArray(array($original_data, $this->settingsOverrides), TRUE);
117       }
118     }
119
120     if (empty($key)) {
121       return $original_data;
122     }
123     else {
124       $parts = explode('.', $key);
125       if (count($parts) == 1) {
126         return isset($original_data[$key]) ? $original_data[$key] : NULL;
127       }
128       else {
129         $value = NestedArray::getValue($original_data, $parts, $key_exists);
130         return $key_exists ? $value : NULL;
131       }
132     }
133   }
134
135   /**
136    * Retrieves a specific theme's stored config settings.
137    *
138    * @param \Drupal\bootstrap\Theme $theme
139    *   A theme object.
140    * @param bool $active_theme
141    *   Flag indicating whether or not $theme is the active theme.
142    *
143    * @return array
144    *   A array diff of overridden config theme settings.
145    */
146   public function getThemeConfig(Theme $theme, $active_theme = FALSE) {
147     $config = new \Drupal\Core\Theme\ThemeSettings($theme->getName());
148
149     // Retrieve configured theme-specific settings, if any.
150     try {
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']);
155         }
156         $config->merge($theme_settings);
157       }
158     }
159     catch (StorageException $e) {
160     }
161
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);
169         }
170       }
171     }
172
173     // Generate the path to the logo image.
174     if ($config->get('features.logo')) {
175       $logo_url = FALSE;
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");
179           break;
180         }
181       }
182       if ($config->get('logo.use_default') && $logo_url) {
183         $config->set('logo.url', $logo_url);
184       }
185       elseif (($logo_path = $config->get('logo.path')) && file_exists($logo_path)) {
186         $config->set('logo.url', file_create_url($logo_path));
187       }
188     }
189
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));
195       }
196       elseif ($favicon_path = $config->get('favicon.path')) {
197         $config->set('favicon.url', file_create_url($favicon_path));
198       }
199     }
200
201     // Retrieve the config data.
202     $data = $config->get();
203
204     // Retrieve a diff of settings that override the defaults.
205     $diff = DiffArray::diffAssocRecursive($data, $this->defaults);
206
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) {
211       $arrays = [];
212       $arrays[] = isset($this->defaults[$key]) ? $this->defaults[$key] : [];
213       $arrays[] = isset($data[$key]) ? $data[$key] : [];
214       $diff[$key] = NestedArray::mergeDeepArray($arrays, TRUE);
215     }
216
217     return $diff;
218   }
219
220   /**
221    * Determines if a setting overrides the default value.
222    *
223    * @param string $name
224    *   The name of the setting to check.
225    * @param mixed $value
226    *   The new value to check.
227    *
228    * @return bool
229    *   TRUE or FALSE
230    */
231   public function overridesValue($name, $value) {
232     // Retrieve the currently stored value for comparison purposes.
233     $current_value = $this->get($name);
234
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 !== []) {
240       return TRUE;
241     }
242
243     // Otherwise, determine if value is overridden by any array differences.
244     return !!DiffArray::diffAssocRecursive([$name => $value], [$name => $current_value]);
245   }
246
247   /**
248    * {@inheritdoc}
249    */
250   public function save($has_trusted_data = FALSE) {
251     parent::save($has_trusted_data);
252     $this->theme->getCache('settings')->deleteAll();
253     return $this;
254   }
255
256 }