Version 1
[yaffs-website] / web / core / modules / ckeditor / src / CKEditorPluginManager.php
1 <?php
2
3 namespace Drupal\ckeditor;
4
5 use Drupal\Component\Utility\NestedArray;
6 use Drupal\Core\Form\FormStateInterface;
7 use Drupal\Core\Plugin\DefaultPluginManager;
8 use Drupal\Core\Cache\CacheBackendInterface;
9 use Drupal\Core\Extension\ModuleHandlerInterface;
10 use Drupal\editor\Entity\Editor;
11
12 /**
13  * Provides a CKEditor Plugin plugin manager.
14  *
15  * @see \Drupal\ckeditor\CKEditorPluginInterface
16  * @see \Drupal\ckeditor\CKEditorPluginButtonsInterface
17  * @see \Drupal\ckeditor\CKEditorPluginContextualInterface
18  * @see \Drupal\ckeditor\CKEditorPluginConfigurableInterface
19  * @see \Drupal\ckeditor\CKEditorPluginCssInterface
20  * @see \Drupal\ckeditor\CKEditorPluginBase
21  * @see \Drupal\ckeditor\Annotation\CKEditorPlugin
22  * @see plugin_api
23  */
24 class CKEditorPluginManager extends DefaultPluginManager {
25
26   /**
27    * Constructs a CKEditorPluginManager object.
28    *
29    * @param \Traversable $namespaces
30    *   An object that implements \Traversable which contains the root paths
31    *   keyed by the corresponding namespace to look for plugin implementations.
32    * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
33    *   Cache backend instance to use.
34    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
35    *   The module handler to invoke the alter hook with.
36    */
37   public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
38     parent::__construct('Plugin/CKEditorPlugin', $namespaces, $module_handler, 'Drupal\ckeditor\CKEditorPluginInterface', 'Drupal\ckeditor\Annotation\CKEditorPlugin');
39     $this->alterInfo('ckeditor_plugin_info');
40     $this->setCacheBackend($cache_backend, 'ckeditor_plugins');
41   }
42
43   /**
44    * Retrieves enabled plugins' files, keyed by plugin ID.
45    *
46    * For CKEditor plugins that implement:
47    *  - CKEditorPluginButtonsInterface, not CKEditorPluginContextualInterface,
48    *     a plugin is enabled if at least one of its buttons is in the toolbar;
49    *  - CKEditorPluginContextualInterface, not CKEditorPluginButtonsInterface,
50    *     a plugin is enabled if its isEnabled() method returns TRUE
51    *  - both of these interfaces, a plugin is enabled if either is the case.
52    *
53    * Internal plugins (those that are part of the bundled build of CKEditor) are
54    * excluded by default, since they are loaded implicitly. If you need to know
55    * even implicitly loaded (i.e. internal) plugins, then set the optional
56    * second parameter.
57    *
58    * @param \Drupal\editor\Entity\Editor $editor
59    *   A configured text editor object.
60    * @param bool $include_internal_plugins
61    *   Defaults to FALSE. When set to TRUE, plugins whose isInternal() method
62    *   returns TRUE will also be included.
63    * @return array
64    *   A list of the enabled CKEditor plugins, with the plugin IDs as keys and
65    *   the Drupal root-relative plugin files as values.
66    *   For internal plugins, the value is NULL.
67    */
68   public function getEnabledPluginFiles(Editor $editor, $include_internal_plugins = FALSE) {
69     $plugins = array_keys($this->getDefinitions());
70     $toolbar_buttons = $this->getEnabledButtons($editor);
71     $enabled_plugins = [];
72     $additional_plugins = [];
73
74     foreach ($plugins as $plugin_id) {
75       $plugin = $this->createInstance($plugin_id);
76
77       if (!$include_internal_plugins && $plugin->isInternal()) {
78         continue;
79       }
80
81       $enabled = FALSE;
82       // Enable this plugin if it provides a button that has been enabled.
83       if ($plugin instanceof CKEditorPluginButtonsInterface) {
84         $plugin_buttons = array_keys($plugin->getButtons());
85         $enabled = (count(array_intersect($toolbar_buttons, $plugin_buttons)) > 0);
86       }
87       // Otherwise enable this plugin if it declares itself as enabled.
88       if (!$enabled && $plugin instanceof CKEditorPluginContextualInterface) {
89         $enabled = $plugin->isEnabled($editor);
90       }
91
92       if ($enabled) {
93         $enabled_plugins[$plugin_id] = ($plugin->isInternal()) ? NULL : $plugin->getFile();
94         // Check if this plugin has dependencies that also need to be enabled.
95         $additional_plugins = array_merge($additional_plugins, array_diff($plugin->getDependencies($editor), $additional_plugins));
96       }
97     }
98
99     // Add the list of dependent plugins.
100     foreach ($additional_plugins as $plugin_id) {
101       $plugin = $this->createInstance($plugin_id);
102       $enabled_plugins[$plugin_id] = ($plugin->isInternal()) ? NULL : $plugin->getFile();
103     }
104
105     // Always return plugins in the same order.
106     asort($enabled_plugins);
107
108     return $enabled_plugins;
109   }
110
111   /**
112    * Gets the enabled toolbar buttons in the given text editor instance.
113    *
114    * @param \Drupal\editor\Entity\Editor $editor
115    *   A configured text editor object.
116    *
117    * @return string[]
118    *   A list of the toolbar buttons enabled in the given text editor instance.
119    */
120   public static function getEnabledButtons(Editor $editor) {
121     $toolbar_rows = [];
122     $settings = $editor->getSettings();
123     foreach ($settings['toolbar']['rows'] as $row_number => $row) {
124       $toolbar_rows[] = array_reduce($settings['toolbar']['rows'][$row_number], function (&$result, $button_group) {
125         return array_merge($result, $button_group['items']);
126       }, []);
127     }
128     return array_unique(NestedArray::mergeDeepArray($toolbar_rows));
129   }
130
131   /**
132    * Retrieves all available CKEditor buttons, keyed by plugin ID.
133    *
134    * @return array
135    *   All available CKEditor buttons, with plugin IDs as keys and button
136    *   metadata (as implemented by getButtons()) as values.
137    *
138    * @see \Drupal\ckeditor\CKEditorPluginButtonsInterface::getButtons()
139    */
140   public function getButtons() {
141     $plugins = array_keys($this->getDefinitions());
142     $buttons_plugins = [];
143
144     foreach ($plugins as $plugin_id) {
145       $plugin = $this->createInstance($plugin_id);
146       if ($plugin instanceof CKEditorPluginButtonsInterface) {
147         $buttons_plugins[$plugin_id] = $plugin->getButtons();
148       }
149     }
150
151     return $buttons_plugins;
152   }
153
154   /**
155    * Injects the CKEditor plugins settings forms as a vertical tabs subform.
156    *
157    * @param array &$form
158    *   A reference to an associative array containing the structure of the form.
159    * @param \Drupal\Core\Form\FormStateInterface $form_state
160    *   The current state of the form.
161    * @param \Drupal\editor\Entity\Editor $editor
162    *   A configured text editor object.
163    */
164   public function injectPluginSettingsForm(array &$form, FormStateInterface $form_state, Editor $editor) {
165     $definitions = $this->getDefinitions();
166
167     foreach (array_keys($definitions) as $plugin_id) {
168       $plugin = $this->createInstance($plugin_id);
169       if ($plugin instanceof CKEditorPluginConfigurableInterface) {
170         $plugin_settings_form = [];
171         $form['plugins'][$plugin_id] = [
172           '#type' => 'details',
173           '#title' => $definitions[$plugin_id]['label'],
174           '#open' => TRUE,
175           '#group' => 'editor][settings][plugin_settings',
176           '#attributes' => [
177             'data-ckeditor-plugin-id' => $plugin_id,
178           ],
179         ];
180         // Provide enough metadata for the drupal.ckeditor.admin library to
181         // allow it to automatically show/hide the vertical tab containing the
182         // settings for this plugin. Only do this if it's a CKEditor plugin that
183         // just provides buttons, don't do this if it's a contextually enabled
184         // CKEditor plugin. After all, in the latter case, we can't know when
185         // its settings should be shown!
186         if ($plugin instanceof CKEditorPluginButtonsInterface && !$plugin instanceof CKEditorPluginContextualInterface) {
187           $form['plugins'][$plugin_id]['#attributes']['data-ckeditor-buttons'] = implode(' ', array_keys($plugin->getButtons()));
188         }
189         $form['plugins'][$plugin_id] += $plugin->settingsForm($plugin_settings_form, $form_state, $editor);
190       }
191     }
192   }
193
194   /**
195    * Retrieves enabled plugins' iframe instance CSS files, keyed by plugin ID.
196    *
197    * @param \Drupal\editor\Entity\Editor $editor
198    *   A configured text editor object.
199    *
200    * @return string[]
201    *   Enabled plugins CKEditor CSS files, with plugin IDs as keys and CSS file
202    *   paths relative to the Drupal root (as implemented by getCssFiles()) as
203    *   values.
204    *
205    * @see \Drupal\ckeditor\CKEditorPluginCssInterface::getCssFiles()
206    */
207   public function getCssFiles(Editor $editor) {
208     $enabled_plugins = array_keys($this->getEnabledPluginFiles($editor, TRUE));
209     $css_files = [];
210
211     foreach ($enabled_plugins as $plugin_id) {
212       $plugin = $this->createInstance($plugin_id);
213       if ($plugin instanceof CKEditorPluginCssInterface) {
214         $css_files[$plugin_id] = $plugin->getCssFiles($editor);
215       }
216     }
217
218     return $css_files;
219   }
220
221 }