annotate core/modules/ckeditor/src/CKEditorPluginManager.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 4c8ae668cc8c
children
rev   line source
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 }