Published:


Módulo ckeditor drupal Permalink

As vezes é muito útil injetar um botão nosso no ckeditor do Drupal, segue-se um exemplo de um botão customizado ao ser clicado abre um modal com ajax que recebe um texto e o injeta no corpo do texto. Isso em si não é nada útil, mas a possibilidade de interceptamos e fazermos qualquer modificação com esse botão.

Criando um módulo chamado ckeditor_generic com ckeditor_generic.info.yml:

name: CKEditor Button Generic Example
type: module
description: "CKEditor Button"
package: CKEditor
core: 8.x
core_version_requirement: ^8 || ^9

dependencies:
  - drupal:ckeditor

O plugin que implementa CKEditorPluginBase colocamos em src/Plugin/CKEditorPlugin/CkeditorGeneric.php:

<?php

namespace Drupal\ckeditor_generic\Plugin\CKEditorPlugin;

use Drupal\ckeditor\CKEditorPluginBase;
use Drupal\editor\Entity\Editor;


/**
 * Defines the "ckeditor_generic" plugin.
 *
 * @CKEditorPlugin(
 *   id = "ckeditor_generic",
 *   label = @Translation("CKEditor Button")
 * )
 */
class CkeditorGeneric extends CKEditorPluginBase {

  /**
   * {@inheritdoc}
   */
  public function getFile() {
    return drupal_get_path('module', 'ckeditor_generic') . '/js/plugin.js';
  }

  /**
   * {@inheritdoc}
   */
  public function getConfig(Editor $editor) {
    return array(
      'dialog_title_insert' => $this->t('Insert a Text'),
    );
  }

  /**
   * Implements CKEditorPluginButtonsInterface::getButtons().
   */
  public function getButtons() {
    return array(
      'ckeditor_generic_button' => array(
        'label' => $this->t('Ckediton Button'),
        'image' => drupal_get_path('module', 'ckeditor_generic') . '/images/icone.png',
      ),
    );
  }

}

em /images/icone.png coloque uma imagem qualquer para o ícone do seu botão e crie /js/plugin.js por enquanto vazio.

Agora criemos um arquivo para o formulário em src/Form/CkeditorGenericDialog.php:

<?php

namespace Drupal\ckeditor_generic\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\filter\Entity\FilterFormat;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\HtmlCommand;
use Drupal\editor\Ajax\EditorDialogSave;
use Drupal\Core\Ajax\CloseModalDialogCommand;

class CkeditorGenericDialog extends FormBase {

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'editor_ckeditor_generic_dialog';
  }

  /**
   * {@inheritdoc}
   *
   * @param \Drupal\filter\Entity\FilterFormat $filter_format
   *   The filter format for which this dialog corresponds.
   */
  public function buildForm(array $form, FormStateInterface $form_state, FilterFormat $filter_format = NULL) {

    $user_input = $form_state->getUserInput();
    $input = isset($user_input['editor_object']) ? $user_input['editor_object'] : array();

    $form['#tree'] = TRUE;
    $form['#attached']['library'][] = 'editor/drupal.editor.dialog';
    $form['#attached']['library'][] = 'ckeditor_generic/ckeditor_generic.dialog';

    $form['#prefix'] = '<div id="editor-ckeditor-generic-dialog-form">';
    $form['#suffix'] = '</div>';

    $form['attributes']['body'] = array(
      '#type' => 'textarea',
      '#title' => $this->t('Body'),
      '#default_value' => isset($input['body']) ? $input['body'] : '',
      '#size' => 50,
    );

    $form['actions'] = array(
      '#type' => 'actions',
    );
    $form['actions']['save_modal'] = array(
      '#type' => 'submit',
      '#value' => $this->t('Insert'),
      // No regular submit-handler. This form only works via JavaScript.
      '#submit' => array(),
      '#ajax' => array(
        'callback' => '::submitForm',
        'event' => 'click',
      ),
    );

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $response = new AjaxResponse();

    if ($form_state->getErrors()) {
      unset($form['#prefix'], $form['#suffix']);
      $form['status_messages'] = [
        '#type' => 'status_messages',
        '#weight' => -10,
      ];
      $response->addCommand(new HtmlCommand('#editor-ckeditor-generic-dialog-form', $form));
    }
    else {
      $response->addCommand(new EditorDialogSave($form_state->getValues()));
      $response->addCommand(new CloseModalDialogCommand());
    }

    return $response;
  }

}

E definimos a rota correspondente do formulário, no caso ajax ckeditor_generic.routing.yml:

ckeditor_generic.dialog:
  path: '/ckeditor_generic/dialog'
  defaults:
    _form: '\Drupal\ckeditor_generic\Form\CkeditorGenericDialog'
    _title: 'ckeditor_generic'
  options:
    _theme: ajax_base_page
  requirements:
    _permission: 'access in-place editing'

Vamos injetar código css/js tanto no modal quanto dentro do conteúdo inserido no ckeditor, assim implementemos ckeditor_generic.libraries.yml:

ckeditor_generic:
  version: VERSION
  css:
    theme:
      css/ckeditor_generic.css: {}

ckeditor_generic.dialog:
  version: VERSION
  css:
    theme:
      css/dialog.css: {}

Em css/dialog.css eu injetei esse css:

#editor-ckeditor-generic-dialog-form .form-item-attributes-body {
  clear: both;
  background-color: yellow;
}

E em ckeditor_generic.css:

.ckeditor-generic{
    background-color: red;
    color: blue;
}

h1.ckeditor-generic-header {
    color: green;
}

Claro, essas cores foram usada só para verificar se o código está chegando onde deveria estar chegando.

Por fim o plugin.js que trata da ação do botão em si:

/**
 * @file
 * ckeditor_generic plugin.
 *
 * @ignore
 */

(function ($, Drupal, drupalSettings, CKEDITOR) {

    'use strict';
  
    CKEDITOR.plugins.add('ckeditor_generic', {
      init: function (editor) {
        editor.addCommand('ckeditor_generic', {
          modes: {wysiwyg: 1},
          canUndo: true,
          exec: function (editor) {

            // Prepare a save callback to be used upon saving the dialog.
            var saveCallback = function (returnValues) {
              editor.fire('saveSnapshot');
  
              if (returnValues.attributes.body) {
                var selection = editor.getSelection();
                var range = selection.getRanges(1)[0];
  
                if (range.collapsed) {
                  var values = returnValues.attributes;

                  var container = editor.document.createElement('div');
                  container.setAttribute('class', 'ckeditor-generic');
  
                  var header = editor.document.createElement('h1');
                  header.setAttribute('class', 'ckeditor-generic-header');

                  header.setHtml('parte de cima');
  
                  var body = editor.document.createElement('p');
                  body.setHtml(values.body);
  
                  container.append(header);
                  container.append(body);
  
                  editor.insertElement(container);

                }
                
              }
  
              // Save snapshot for undo support.
              editor.fire('saveSnapshot');
            };
            // Drupal.t() will not work inside CKEditor plugins because CKEditor
            // loads the JavaScript file instead of Drupal. Pull translated
            // strings from the plugin settings that are translated server-side.
            var dialogSettings = {
              title: editor.config.dialog_title_insert,
              dialogClass: 'ckeditor_generic-dialog'
            };
  
            // Open the dialog for the edit form.
            var existingValues = {};
            Drupal.ckeditor.openDialog(editor, Drupal.url('ckeditor_generic/dialog'), existingValues, saveCallback, dialogSettings);
          }
        });

        // Add button
        if (editor.ui.addButton) {
          editor.ui.addButton('ckeditor_generic_button', {
            label: Drupal.t('Insert ckeditor_generic'),
            command: 'ckeditor_generic',
            icon: this.path +  '../images/icone.png'
          });
        }
  
        // If the "menu" plugin is loaded, register the menu items.
        if (editor.addMenuItems) {
          editor.addMenuItems({
            ckeditor_generic: {
              label: Drupal.t('ckeditor_generic'),
              command: 'ckeditor_generic',
              group: 'tools',
              order: 1
            }
          });
        }
      }
    });
  
  })(jQuery, Drupal, drupalSettings, CKEDITOR);