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