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