Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 namespace Drupal\breakpoint;
|
Chris@0
|
4
|
Chris@0
|
5 use Drupal\Core\Cache\Cache;
|
Chris@0
|
6 use Drupal\Core\Cache\CacheBackendInterface;
|
Chris@0
|
7 use Drupal\Core\Extension\ModuleHandlerInterface;
|
Chris@0
|
8 use Drupal\Core\Extension\ThemeHandlerInterface;
|
Chris@0
|
9 use Drupal\Core\Plugin\DefaultPluginManager;
|
Chris@0
|
10 use Drupal\Core\Plugin\Discovery\ContainerDerivativeDiscoveryDecorator;
|
Chris@0
|
11 use Drupal\Core\Plugin\Discovery\YamlDiscovery;
|
Chris@0
|
12 use Drupal\Core\Plugin\Factory\ContainerFactory;
|
Chris@0
|
13 use Drupal\Core\StringTranslation\StringTranslationTrait;
|
Chris@0
|
14 use Drupal\Core\StringTranslation\TranslationInterface;
|
Chris@0
|
15
|
Chris@0
|
16 /**
|
Chris@0
|
17 * Defines a breakpoint plugin manager to deal with breakpoints.
|
Chris@0
|
18 *
|
Chris@0
|
19 * Extension can define breakpoints in a EXTENSION_NAME.breakpoints.yml file
|
Chris@0
|
20 * contained in the extension's base directory. Each breakpoint has the
|
Chris@0
|
21 * following structure:
|
Chris@0
|
22 * @code
|
Chris@0
|
23 * MACHINE_NAME:
|
Chris@0
|
24 * label: STRING
|
Chris@0
|
25 * mediaQuery: STRING
|
Chris@0
|
26 * weight: INTEGER
|
Chris@0
|
27 * multipliers:
|
Chris@0
|
28 * - STRING
|
Chris@0
|
29 * @endcode
|
Chris@0
|
30 * For example:
|
Chris@0
|
31 * @code
|
Chris@0
|
32 * bartik.mobile:
|
Chris@0
|
33 * label: mobile
|
Chris@0
|
34 * mediaQuery: '(min-width: 0px)'
|
Chris@0
|
35 * weight: 0
|
Chris@0
|
36 * multipliers:
|
Chris@0
|
37 * - 1x
|
Chris@0
|
38 * - 2x
|
Chris@0
|
39 * @endcode
|
Chris@0
|
40 * Optionally a breakpoint can provide a group key. By default an extensions
|
Chris@0
|
41 * breakpoints will be placed in a group labelled with the extension name.
|
Chris@0
|
42 *
|
Chris@0
|
43 * @see \Drupal\breakpoint\Breakpoint
|
Chris@0
|
44 * @see \Drupal\breakpoint\BreakpointInterface
|
Chris@0
|
45 * @see plugin_api
|
Chris@0
|
46 */
|
Chris@0
|
47 class BreakpointManager extends DefaultPluginManager implements BreakpointManagerInterface {
|
Chris@0
|
48 use StringTranslationTrait;
|
Chris@0
|
49
|
Chris@0
|
50 /**
|
Chris@0
|
51 * {@inheritdoc}
|
Chris@0
|
52 */
|
Chris@0
|
53 protected $defaults = [
|
Chris@0
|
54 // Human readable label for breakpoint.
|
Chris@0
|
55 'label' => '',
|
Chris@0
|
56 // The media query for the breakpoint.
|
Chris@0
|
57 'mediaQuery' => '',
|
Chris@0
|
58 // Weight used for ordering breakpoints.
|
Chris@0
|
59 'weight' => 0,
|
Chris@0
|
60 // Breakpoint multipliers.
|
Chris@0
|
61 'multipliers' => [],
|
Chris@0
|
62 // The breakpoint group.
|
Chris@0
|
63 'group' => '',
|
Chris@0
|
64 // Default class for breakpoint implementations.
|
Chris@0
|
65 'class' => 'Drupal\breakpoint\Breakpoint',
|
Chris@0
|
66 // The plugin id. Set by the plugin system based on the top-level YAML key.
|
Chris@0
|
67 'id' => '',
|
Chris@0
|
68 ];
|
Chris@0
|
69
|
Chris@0
|
70 /**
|
Chris@0
|
71 * The theme handler.
|
Chris@0
|
72 *
|
Chris@0
|
73 * @var \Drupal\Core\Extension\ThemeHandlerInterface
|
Chris@0
|
74 */
|
Chris@0
|
75 protected $themeHandler;
|
Chris@0
|
76
|
Chris@0
|
77 /**
|
Chris@0
|
78 * Static cache of breakpoints keyed by group.
|
Chris@0
|
79 *
|
Chris@0
|
80 * @var array
|
Chris@0
|
81 */
|
Chris@0
|
82 protected $breakpointsByGroup;
|
Chris@0
|
83
|
Chris@0
|
84 /**
|
Chris@0
|
85 * The plugin instances.
|
Chris@0
|
86 *
|
Chris@0
|
87 * @var array
|
Chris@0
|
88 */
|
Chris@0
|
89 protected $instances = [];
|
Chris@0
|
90
|
Chris@0
|
91 /**
|
Chris@0
|
92 * Constructs a new BreakpointManager instance.
|
Chris@0
|
93 *
|
Chris@0
|
94 * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
Chris@0
|
95 * The module handler.
|
Chris@0
|
96 * @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
|
Chris@0
|
97 * The theme handler.
|
Chris@0
|
98 * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
|
Chris@0
|
99 * The cache backend.
|
Chris@0
|
100 * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
|
Chris@0
|
101 * The string translation service.
|
Chris@0
|
102 */
|
Chris@0
|
103 public function __construct(ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler, CacheBackendInterface $cache_backend, TranslationInterface $string_translation) {
|
Chris@0
|
104 $this->factory = new ContainerFactory($this);
|
Chris@0
|
105 $this->moduleHandler = $module_handler;
|
Chris@0
|
106 $this->themeHandler = $theme_handler;
|
Chris@0
|
107 $this->setStringTranslation($string_translation);
|
Chris@0
|
108 $this->alterInfo('breakpoints');
|
Chris@0
|
109 $this->setCacheBackend($cache_backend, 'breakpoints', ['breakpoints']);
|
Chris@0
|
110 }
|
Chris@0
|
111
|
Chris@0
|
112 /**
|
Chris@0
|
113 * {@inheritdoc}
|
Chris@0
|
114 */
|
Chris@0
|
115 protected function getDiscovery() {
|
Chris@0
|
116 if (!isset($this->discovery)) {
|
Chris@0
|
117 $this->discovery = new YamlDiscovery('breakpoints', $this->moduleHandler->getModuleDirectories() + $this->themeHandler->getThemeDirectories());
|
Chris@0
|
118 $this->discovery = new ContainerDerivativeDiscoveryDecorator($this->discovery);
|
Chris@0
|
119 }
|
Chris@0
|
120 return $this->discovery;
|
Chris@0
|
121 }
|
Chris@0
|
122
|
Chris@0
|
123 /**
|
Chris@0
|
124 * {@inheritdoc}
|
Chris@0
|
125 */
|
Chris@0
|
126 public function processDefinition(&$definition, $plugin_id) {
|
Chris@0
|
127 parent::processDefinition($definition, $plugin_id);
|
Chris@0
|
128 // Allow custom groups and therefore more than one group per extension.
|
Chris@0
|
129 if (empty($definition['group'])) {
|
Chris@0
|
130 $definition['group'] = $definition['provider'];
|
Chris@0
|
131 }
|
Chris@0
|
132 // Ensure a 1x multiplier exists.
|
Chris@0
|
133 if (!in_array('1x', $definition['multipliers'])) {
|
Chris@0
|
134 $definition['multipliers'][] = '1x';
|
Chris@0
|
135 }
|
Chris@0
|
136 // Ensure that multipliers are sorted correctly.
|
Chris@0
|
137 sort($definition['multipliers']);
|
Chris@0
|
138 }
|
Chris@0
|
139
|
Chris@0
|
140 /**
|
Chris@0
|
141 * {@inheritdoc}
|
Chris@0
|
142 */
|
Chris@0
|
143 protected function providerExists($provider) {
|
Chris@0
|
144 return $this->moduleHandler->moduleExists($provider) || $this->themeHandler->themeExists($provider);
|
Chris@0
|
145 }
|
Chris@0
|
146
|
Chris@0
|
147 /**
|
Chris@0
|
148 * {@inheritdoc}
|
Chris@0
|
149 */
|
Chris@0
|
150 public function getBreakpointsByGroup($group) {
|
Chris@0
|
151 if (!isset($this->breakpointsByGroup[$group])) {
|
Chris@0
|
152 if ($cache = $this->cacheBackend->get($this->cacheKey . ':' . $group)) {
|
Chris@0
|
153 $this->breakpointsByGroup[$group] = $cache->data;
|
Chris@0
|
154 }
|
Chris@0
|
155 else {
|
Chris@0
|
156 $breakpoints = [];
|
Chris@0
|
157 foreach ($this->getDefinitions() as $plugin_id => $plugin_definition) {
|
Chris@0
|
158 if ($plugin_definition['group'] == $group) {
|
Chris@0
|
159 $breakpoints[$plugin_id] = $plugin_definition;
|
Chris@0
|
160 }
|
Chris@0
|
161 }
|
Chris@0
|
162 uasort($breakpoints, ['Drupal\Component\Utility\SortArray', 'sortByWeightElement']);
|
Chris@0
|
163 $this->cacheBackend->set($this->cacheKey . ':' . $group, $breakpoints, Cache::PERMANENT, ['breakpoints']);
|
Chris@0
|
164 $this->breakpointsByGroup[$group] = $breakpoints;
|
Chris@0
|
165 }
|
Chris@0
|
166 }
|
Chris@0
|
167
|
Chris@0
|
168 $instances = [];
|
Chris@0
|
169 foreach ($this->breakpointsByGroup[$group] as $plugin_id => $definition) {
|
Chris@0
|
170 if (!isset($this->instances[$plugin_id])) {
|
Chris@0
|
171 $this->instances[$plugin_id] = $this->createInstance($plugin_id);
|
Chris@0
|
172 }
|
Chris@0
|
173 $instances[$plugin_id] = $this->instances[$plugin_id];
|
Chris@0
|
174 }
|
Chris@0
|
175 return $instances;
|
Chris@0
|
176 }
|
Chris@0
|
177
|
Chris@0
|
178 /**
|
Chris@0
|
179 * {@inheritdoc}
|
Chris@0
|
180 */
|
Chris@0
|
181 public function getGroups() {
|
Chris@0
|
182 // Use a double colon so as to not clash with the cache for each group.
|
Chris@0
|
183 if ($cache = $this->cacheBackend->get($this->cacheKey . '::groups')) {
|
Chris@0
|
184 $groups = $cache->data;
|
Chris@0
|
185 }
|
Chris@0
|
186 else {
|
Chris@0
|
187 $groups = [];
|
Chris@0
|
188 foreach ($this->getDefinitions() as $plugin_definition) {
|
Chris@0
|
189 if (!isset($groups[$plugin_definition['group']])) {
|
Chris@0
|
190 $groups[$plugin_definition['group']] = $plugin_definition['group'];
|
Chris@0
|
191 }
|
Chris@0
|
192 }
|
Chris@0
|
193 $this->cacheBackend->set($this->cacheKey . '::groups', $groups, Cache::PERMANENT, ['breakpoints']);
|
Chris@0
|
194 }
|
Chris@0
|
195 // Get the labels. This is not cacheable due to translation.
|
Chris@0
|
196 $group_labels = [];
|
Chris@0
|
197 foreach ($groups as $group) {
|
Chris@0
|
198 $group_labels[$group] = $this->getGroupLabel($group);
|
Chris@0
|
199 }
|
Chris@0
|
200 asort($group_labels);
|
Chris@0
|
201 return $group_labels;
|
Chris@0
|
202 }
|
Chris@0
|
203
|
Chris@0
|
204 /**
|
Chris@0
|
205 * {@inheritdoc}
|
Chris@0
|
206 */
|
Chris@0
|
207 public function getGroupProviders($group) {
|
Chris@0
|
208 $providers = [];
|
Chris@0
|
209 $breakpoints = $this->getBreakpointsByGroup($group);
|
Chris@0
|
210 foreach ($breakpoints as $breakpoint) {
|
Chris@0
|
211 $provider = $breakpoint->getProvider();
|
Chris@0
|
212 $extension = FALSE;
|
Chris@0
|
213 if ($this->moduleHandler->moduleExists($provider)) {
|
Chris@0
|
214 $extension = $this->moduleHandler->getModule($provider);
|
Chris@0
|
215 }
|
Chris@0
|
216 elseif ($this->themeHandler->themeExists($provider)) {
|
Chris@0
|
217 $extension = $this->themeHandler->getTheme($provider);
|
Chris@0
|
218 }
|
Chris@0
|
219 if ($extension) {
|
Chris@0
|
220 $providers[$extension->getName()] = $extension->getType();
|
Chris@0
|
221 }
|
Chris@0
|
222 }
|
Chris@0
|
223 return $providers;
|
Chris@0
|
224 }
|
Chris@0
|
225
|
Chris@0
|
226 /**
|
Chris@0
|
227 * {@inheritdoc}
|
Chris@0
|
228 */
|
Chris@0
|
229 public function clearCachedDefinitions() {
|
Chris@0
|
230 parent::clearCachedDefinitions();
|
Chris@0
|
231 $this->breakpointsByGroup = NULL;
|
Chris@0
|
232 $this->instances = [];
|
Chris@0
|
233 }
|
Chris@0
|
234
|
Chris@0
|
235 /**
|
Chris@0
|
236 * Gets the label for a breakpoint group.
|
Chris@0
|
237 *
|
Chris@0
|
238 * @param string $group
|
Chris@0
|
239 * The breakpoint group.
|
Chris@0
|
240 *
|
Chris@0
|
241 * @return string
|
Chris@0
|
242 * The label.
|
Chris@0
|
243 */
|
Chris@0
|
244 protected function getGroupLabel($group) {
|
Chris@0
|
245 // Extension names are not translatable.
|
Chris@0
|
246 if ($this->moduleHandler->moduleExists($group)) {
|
Chris@0
|
247 $label = $this->moduleHandler->getName($group);
|
Chris@0
|
248 }
|
Chris@0
|
249 elseif ($this->themeHandler->themeExists($group)) {
|
Chris@0
|
250 $label = $this->themeHandler->getName($group);
|
Chris@0
|
251 }
|
Chris@0
|
252 else {
|
Chris@0
|
253 // Custom group label that should be translatable.
|
Chris@0
|
254 $label = $this->t($group, [], ['context' => 'breakpoint']);
|
Chris@0
|
255 }
|
Chris@0
|
256 return $label;
|
Chris@0
|
257 }
|
Chris@0
|
258
|
Chris@0
|
259 }
|