36f64b50f4ce90657a85e45b088e7b70c0dde36b
[yaffs-website] / web / themes / contrib / bootstrap / src / Plugin / Alter / ThemeRegistry.php
1 <?php
2
3 namespace Drupal\bootstrap\Plugin\Alter;
4
5 use Drupal\bootstrap\Bootstrap;
6 use Drupal\bootstrap\Plugin\PreprocessManager;
7 use Drupal\Core\Theme\Registry;
8
9 /**
10  * Extends the theme registry to override and use protected functions.
11  *
12  * @todo Refactor into a proper theme.registry service replacement in a
13  * bootstrap_core sub-module once this theme can add it as a dependency.
14  *
15  * @see https://www.drupal.org/node/474684
16  *
17  * @ingroup plugins_alter
18  *
19  * @BootstrapAlter("theme_registry")
20  */
21 class ThemeRegistry extends Registry implements AlterInterface {
22
23   /**
24    * The currently set Bootstrap theme object.
25    *
26    * Cannot use "$theme" because this is the Registry's ActiveTheme object.
27    *
28    * @var \Drupal\bootstrap\Theme
29    */
30   protected $currentTheme;
31
32   /**
33    * {@inheritdoc}
34    */
35   public function __construct(array $configuration, $plugin_id, $plugin_definition) {
36     // This is technically a plugin constructor, but because we wish to use the
37     // protected methods of the Registry class, we must extend from it. Thus,
38     // to properly construct the extended Registry object, we must pass the
39     // arguments it would normally get from the service container to "fake" it.
40     if (!isset($configuration['theme'])) {
41       $configuration['theme'] = Bootstrap::getTheme();
42     }
43     $this->currentTheme = $configuration['theme'];
44     parent::__construct(
45       \Drupal::service('app.root'),
46       \Drupal::service('cache.default'),
47       \Drupal::service('lock'),
48       \Drupal::service('module_handler'),
49       \Drupal::service('theme_handler'),
50       \Drupal::service('theme.initialization'),
51       $this->currentTheme->getName()
52     );
53     $this->setThemeManager(\Drupal::service('theme.manager'));
54     $this->init();
55   }
56
57   /**
58    * {@inheritdoc}
59    */
60   public function alter(&$cache, &$context1 = NULL, &$context2 = NULL) {
61     // Sort the registry alphabetically (for easier debugging).
62     ksort($cache);
63
64     // Add extra variables to all theme hooks.
65     $extra_variables = Bootstrap::extraVariables();
66     foreach (array_keys($cache) as $hook) {
67       // Skip theme hooks that don't set variables.
68       if (!isset($cache[$hook]['variables'])) {
69         continue;
70       }
71       $cache[$hook]['variables'] += $extra_variables;
72     }
73
74     // Ensure paths to templates are set properly. This allows templates to
75     // be moved around in a theme without having to constantly ensuring that
76     // the theme's hook_theme() definitions have the correct static "path" set.
77     foreach ($this->currentTheme->getAncestry() as $ancestor) {
78       $current_theme = $ancestor->getName() === $this->currentTheme->getName();
79       $theme_path = $ancestor->getPath();
80       // Scan entire theme root path.
81       // @see https://www.drupal.org/project/bootstrap/issues/2951575
82       foreach ($ancestor->fileScan('/\.html\.twig$/') as $file) {
83         $hook = str_replace('-', '_', str_replace('.html.twig', '', $file->filename));
84         $path = dirname($file->uri);
85         $incomplete = !isset($cache[$hook]) || strrpos($hook, '__');
86
87         // Create a new theme hook. This primarily happens when theme hook
88         // suggestion templates are created. To prevent the new hook from
89         // inheriting parent hook's "template", it must be manually set here.
90         // @see https://www.drupal.org/node/2871551
91         if (!isset($cache[$hook])) {
92           $cache[$hook] = [
93             'template' => str_replace('.html.twig', '', $file->filename),
94           ];
95         }
96
97         // Always ensure that "path", "type" and "theme path" are properly set.
98         $cache[$hook]['path'] = $path;
99         $cache[$hook]['type'] = $current_theme ? 'theme' : 'base_theme';
100         $cache[$hook]['theme path'] = $theme_path;
101
102         // Flag incomplete.
103         if ($incomplete) {
104           $cache[$hook]['incomplete preprocess functions'] = TRUE;
105         }
106       }
107     }
108
109     // Discover all the theme's preprocess plugins.
110     $preprocess_manager = new PreprocessManager($this->currentTheme);
111     $plugins = $preprocess_manager->getDefinitions();
112     ksort($plugins, SORT_NATURAL);
113
114     // Iterate over the preprocess plugins.
115     foreach ($plugins as $plugin_id => $definition) {
116       $incomplete = !isset($cache[$plugin_id]) || strrpos($plugin_id, '__');
117       if (!isset($cache[$plugin_id])) {
118         $cache[$plugin_id] = [];
119       }
120       array_walk($cache, function (&$info, $hook) use ($plugin_id, $definition) {
121         if ($hook === $plugin_id || strpos($hook, $plugin_id . '__') === 0) {
122           if (!isset($info['preprocess functions'])) {
123             $info['preprocess functions'] = [];
124           }
125           // Due to a limitation in \Drupal\Core\Theme\ThemeManager::render,
126           // callbacks must be functions and not classes. We always specify
127           // "bootstrap_preprocess" here and then assign the plugin ID to a
128           // separate property that we can later intercept and properly invoke.
129           // @todo Revisit if/when preprocess callbacks can be any callable.
130           Bootstrap::addCallback($info['preprocess functions'], 'bootstrap_preprocess', $definition['replace'], $definition['action']);
131           $info['preprocess functions'] = array_unique($info['preprocess functions']);
132           $info['bootstrap preprocess'] = $plugin_id;
133         }
134       });
135
136       if ($incomplete) {
137         $cache[$plugin_id]['incomplete preprocess functions'] = TRUE;
138       }
139     }
140
141     // Allow core to post process.
142     $this->postProcessExtension($cache, $this->theme);
143   }
144
145 }