CKEditor.php 8.53 KB
Newer Older
1 2 3 4
<?php

/**
 * @file
5
 * Contains \Drupal\ckeditor\Plugin\Editor\CKEditor.
6 7
 */

8
namespace Drupal\ckeditor\Plugin\Editor;
9

10
use Drupal\Core\Language\Language;
11
use Drupal\editor\Plugin\EditorBase;
12
use Drupal\editor\Annotation\Editor;
13
use Drupal\Core\Annotation\Translation;
14
use Drupal\editor\Plugin\Core\Entity\Editor as EditorEntity;
15 16 17 18

/**
 * Defines a CKEditor-based text editor for Drupal.
 *
19
 * @Editor(
20 21
 *   id = "ckeditor",
 *   label = @Translation("CKEditor"),
22
 *   supports_inline_editing = TRUE
23 24 25 26 27
 * )
 */
class CKEditor extends EditorBase {

  /**
28
   * Implements \Drupal\editor\Plugin\EditPluginInterface::getDefaultSettings().
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
   */
  public function getDefaultSettings() {
    return array(
      'toolbar' => array(
        'buttons' => array(
          array(
            'Bold', 'Italic',
            '|', 'Link', 'Unlink',
            '|', 'BulletedList', 'NumberedList',
            '|', 'Blockquote', 'Image',
            '|', 'Source',
          ),
        ),
      ),
      'plugins' => array(),
    );
  }

  /**
48
   * Implements \Drupal\editor\Plugin\EditPluginInterface::settingsForm().
49
   */
50
  public function settingsForm(array $form, array &$form_state, EditorEntity $editor) {
51 52
    $module_path = drupal_get_path('module', 'ckeditor');
    $manager = drupal_container()->get('plugin.manager.ckeditor.plugin');
53 54 55
    $ckeditor_settings_toolbar = array(
      '#theme' => 'ckeditor_settings_toolbar',
      '#editor' => $editor,
56
      '#plugins' => $manager->getButtonsPlugins(),
57
    );
58 59 60 61 62 63 64 65
    $form['toolbar'] = array(
      '#type' => 'container',
      '#attached' => array(
        'library' => array(array('ckeditor', 'drupal.ckeditor.admin')),
        'js' => array(
          array(
            'type' => 'setting',
            'data' => array('ckeditor' => array(
66
              'toolbarAdmin' => drupal_render($ckeditor_settings_toolbar),
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
            )),
          )
        ),
      ),
      '#attributes' => array('class' => array('ckeditor-toolbar-configuration')),
    );
    $form['toolbar']['buttons'] = array(
      '#type' => 'textarea',
      '#title' => t('Toolbar buttons'),
      '#default_value' => json_encode($editor->settings['toolbar']['buttons']),
      '#attributes' => array('class' => array('ckeditor-toolbar-textarea')),
    );

    // CKEditor plugin settings, if any.
    $form['plugin_settings'] = array(
      '#type' => 'vertical_tabs',
    );
    $manager->injectPluginSettingsForm($form, $form_state, $editor);
    if (count(element_children($form['plugins'])) === 0) {
      unset($form['plugins']);
      unset($form['plugin_settings']);
    }

90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
    // Hidden CKEditor instance. We need a hidden CKEditor instance with all
    // plugins enabled, so we can retrieve CKEditor's per-feature metadata (on
    // which tags, attributes, styles and classes are enabled). This metadata is
    // necessary for certain filters' (e.g. the html_filter filter) settings to
    // be updated accordingly.
    // Get a list of all external plugins and their corresponding files.
    $plugins = array_keys($manager->getDefinitions());
    $all_external_plugins = array();
    foreach ($plugins as $plugin_id) {
      $plugin = $manager->createInstance($plugin_id);
      if (!$plugin->isInternal()) {
        $all_external_plugins[$plugin_id] = $plugin->getFile();
      }
    }
    // Get a list of all buttons that are provided by all plugins.
    $all_buttons = array_reduce($manager->getButtonsPlugins(), function($result, $item) {
      return array_merge($result, array_keys($item));
    }, array());
    // Build a fake Editor object, which we'll use to generate JavaScript
    // settings for this fake Editor instance.
    $fake_editor = entity_create('editor', array(
      'format' => '',
      'editor' => 'ckeditor',
      'settings' => array(
        // Single toolbar row that contains all existing buttons.
        'toolbar' => array('buttons' => array(0 => $all_buttons)),
        'plugins' => $editor->settings['plugins'],
      ),
    ));
119 120 121 122
    $config = $this->getJSSettings($fake_editor);
    // Remove the ACF configuration that is generated based on filter settings,
    // because otherwise we cannot retrieve per-feature metadata.
    unset($config['allowedContent']);
123 124 125 126 127 128 129
    $form['hidden_ckeditor'] = array(
      '#markup' => '<div id="ckeditor-hidden" class="element-hidden"></div>',
      '#attached' => array(
        'js' => array(
          array(
            'type' => 'setting',
            'data' => array('ckeditor' => array(
130
              'hiddenCKEditorConfig' => $config,
131 132 133 134 135 136
            )),
          ),
        ),
      ),
    );

137 138 139 140
    return $form;
  }

  /**
141
   * Implements \Drupal\editor\Plugin\EditPluginInterface::settingsFormSubmit().
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
   */
  public function settingsFormSubmit(array $form, array &$form_state) {
    // Modify the toolbar settings by reference. The values in
    // $form_state['values']['editor']['settings'] will be saved directly by
    // editor_form_filter_admin_format_submit().
    $toolbar_settings = &$form_state['values']['editor']['settings']['toolbar'];

    $toolbar_settings['buttons'] = json_decode($toolbar_settings['buttons'], FALSE);

    // Remove the plugin settings' vertical tabs state; no need to save that.
    if (isset($form_state['values']['editor']['settings']['plugins'])) {
      unset($form_state['values']['editor']['settings']['plugin_settings']);
    }
  }

  /**
158
   * Implements \Drupal\editor\Plugin\EditPluginInterface::getJSSettings().
159
   */
160
  public function getJSSettings(EditorEntity $editor) {
161
    $language_interface = language(Language::TYPE_INTERFACE);
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179

    $settings = array();
    $manager = drupal_container()->get('plugin.manager.ckeditor.plugin');

    // Get the settings for all enabled plugins, even the internal ones.
    $enabled_plugins = array_keys($manager->getEnabledPlugins($editor, TRUE));
    foreach ($enabled_plugins as $plugin_id) {
      $plugin = $manager->createInstance($plugin_id);
      $settings += $plugin->getConfig($editor);
    }

    // Next, set the most fundamental CKEditor settings.
    $external_plugins = $manager->getEnabledPlugins($editor);
    $settings += array(
      'toolbar' => $this->buildToolbarJSSetting($editor),
      'contentsCss' => $this->buildContentsCssJSSetting($editor),
      'extraPlugins' => implode(',', array_keys($external_plugins)),
      'language' => $language_interface->langcode,
180 181 182 183 184 185
      // Configure CKEditor to not load styles.js. The StylesCombo plugin will
      // set stylesSet according to the user's settings, if the "Styles" button
      // is enabled. We cannot get rid of this until CKEditor will stop loading
      // styles.js by default.
      // See http://dev.ckeditor.com/ticket/9992#comment:9.
      'stylesSet' => FALSE,
186 187 188 189 190 191 192
    );

    // Finally, set Drupal-specific CKEditor settings.
    $settings += array(
      'drupalExternalPlugins' => array_map('file_create_url', $external_plugins),
    );

193 194
    ksort($settings);

195 196 197 198
    return $settings;
  }

  /**
199
   * Implements \Drupal\editor\Plugin\EditPluginInterface::getLibraries().
200
   */
201
  public function getLibraries(EditorEntity $editor) {
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
    return array(
      array('ckeditor', 'drupal.ckeditor'),
    );
  }

  /**
   * Builds the "toolbar" configuration part of the CKEditor JS settings.
   *
   * @see getJSSettings()
   *
   * @param \Drupal\editor\Plugin\Core\Entity\Editor $editor
   *   A configured text editor object.
   * @return array
   *   An array containing the "toolbar" configuration.
   */
217
  public function buildToolbarJSSetting(EditorEntity $editor) {
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
    $toolbar = array();
    foreach ($editor->settings['toolbar']['buttons'] as $row_number => $row) {
      $button_group = array();
      foreach ($row as $button_name) {
        // Change the toolbar separators into groups.
        if ($button_name === '|') {
          $toolbar[] = $button_group;
          $button_group = array();
        }
        else {
          $button_group['items'][] = $button_name;
        }
      }
      $toolbar[] = $button_group;
      $toolbar[] = '/';
    }

    return $toolbar;
  }

  /**
   * Builds the "contentsCss" configuration part of the CKEditor JS settings.
   *
   * @see getJSSettings()
   *
   * @param \Drupal\editor\Plugin\Core\Entity\Editor $editor
   *   A configured text editor object.
   * @return array
   *   An array containing the "contentsCss" configuration.
   */
248
  public function buildContentsCssJSSetting(EditorEntity $editor) {
249 250 251 252 253 254 255 256 257 258 259
    $css = array(
      drupal_get_path('module', 'ckeditor') . '/css/ckeditor-iframe.css',
    );
    $css = array_merge($css, _ckeditor_theme_css());
    drupal_alter('ckeditor_css', $css, $editor);
    $css = array_map('file_create_url', $css);

    return array_values($css);
  }

}