Программное создание плагина WYSIWYG в Drupal 7

№9-1,

технические науки

В статье рассказывается о программном создании фильтров для форматирования текста на примере модуля Emoticon. Данный модуль позволяет добавлять в содержимое сайта изображения в виде смайликов. Реализован фильтр, который заменяет акронимы смайликов на соответствующие изображения.

Похожие материалы

Взаимодействие с пользователем играет решающую роль при работе с пользователем. Именно для решения этой задачи разработаны визуальные редакторы. В этой статье будет рассказываться о самом популярном из них - редакторе CKEditor. По-моему мнению этот визуальный редактор обеспечивает максимум возможностей по сравнению с другими редакторами. Если таких возможностей на данный момент не существует, то их не трудно разработать. В своей статье я расскажу как это реализовать.

Для взаимодействия визуальных редакторов и Drupal существует модуль WYSIWIG. Он позволяет добавить для форм ввода текста функциональность визуальных редакторов. Также он позволяет сторонним разработчикам, таким как мы с вами, добавлять свои плагины для расширения существующих возможностей. Покажу два метода как это можно реализовать.

Разработаем плагин для модуля Смайлики, о котором я рассказывал в своей статье. Он позволит визуально выбирать смайлики, которые нужно добавить в содержимое сайта. Смайлики будут отображаться в редакторе в виде изображений. 

hook_wysiwyg_plugin

Добавим в файл emoticon.module функцию emoticon_wysiwyg_plugin для перехвата hook_wysiwyg_plugin:

/**
 * hook_wysiwyg_plugin().
 */
function emoticon_wysiwyg_plugin($editor, $version) {
  if ($editor == 'ckeditor') {
    return array(
      'emoticon' => array(
        'path' => drupal_get_path('module', 'emoticon') . '/plugins',
        'filename' => 'emoticon_ckeditor.js',
        'buttons' => array(
          'emoticon' => t('Смайлики'),
        ),
        'load' => TRUE,
      ),
    );
  }
}

Этот хук вызывается когда модуль WYSIWIG формирует список доступных плагинов. Выбираем визуальный редактор. Как ранее уже сообщал, мы будем работать с CKEditor. Поэтому в функции выбираем наш редактор и возвращаем результат. Результатом выполнения функции должен быть массив параметров. Нужно указать где располагается наш плагин. Создадим в подкаталоге модуля Emoticon директорию plugins. Здесь будет находиться наш плагин.

Плагином CKEditor является javascript функция, которую мы расположим в отдельном файле. Создадим файл emoticon_ckeditor.js и добавим в неё следующий код:

(function($) {
  CKEDITOR.plugins.add('emoticon', {
    init: function(editor) {
      editor.addCommand('emoticon', new CKEDITOR.dialogCommand('emoticonDialog'));
      editor.ui.addButton('emoticon',  {
        label: Drupal.t('Смайлики'),
        command: 'emoticon',
        icon: this.path + 'icon.png'
      });
    }    
  });
  CKEDITOR.dialog.add('emoticonDialog', function(editor) {
    var g;
    function grid(){
      var icons = Drupal.settings.emoticon.icons, table = '', row;
      do {
        row = icons.splice(0,icons.length>10?10:icons.length);
        table += '<tr><td>' + row.join('</td><td>') + '</td></tr>';
      } while (icons.length > 0)
      return '<table>' + table + '</table>';
    }
    return {
      title: 'Выберите смайлик',
      minWidth: 400,
      minHeight: 400,
       contents:
      [
        {
          id: 'general',
          label: 'Settings',
          elements:
          [
            {
              type: 'html',
              html: grid(),
              onLoad:function(o){
                g=o.sender;
              },
              onClick:function(e){
                var
                  elem=e.data.getTarget(),
                  name=elem.getName();
                  if(name=='td')
                    elem=elem.getChild(0);
                  else if(name!='img')
                    return;
                var
                  src=elem.getAttribute('src'),
                  title=elem.getAttribute('title'),
                  t=editor.document.createElement('img', {attributes:{
                    src:src,
                    title:title,
                    alt:title,
                    width:elem.$.width,
                    height:elem.$.height
                  }});
                  editor.insertElement(t);
                  g.hide();
                  e.data.preventDefault();
              },
            },
          ]
        },
      ],
      buttons:[CKEDITOR.dialog.cancelButton]
    };
  });
})(jQuery);

CKEDITOR.plugins.add

Метод CKEDITOR.plugins.add позволяет нам добавить плагин для визуального редактора CKEditor. Мы должны зарегистрировать диалоговое окно, о котором речь пойдет ниже. Затем добавляем саму кнопку. Метод Drupal.t позволяет нам использовать несколько языков. Применяется в javascript и является аналогом php функции t. Также указываем изображение для нашей кнопки. Файл изображения располагается в директории plugins, которую мы создали выше.

CKEDITOR.dialog.add

Метод CKEDITOR.dialog.add позовляет использовать диалоговое окно при нажатии на кнопку нашего плагина. В начале создаём функцию grid, которая формирует таблицу с нашими смайликами. Данные о смайликах мы получаем из Drupal. Как это реализовать мы поговорим немного позже.

Затем возвращаем результат в виде массива параметров. Указывает тип html-код. Сам код мы реализовали ранее. Указываем его также в параметрах. При клике мыши на диалоговом окне определяем какое изображение смайлика было выбрано. Получаем параметры этого смайлика и вставляем его в текст содержимого. Для этого предназначен метод editor.insertElement.

В завершение закрываем диалоговое окно.

Передача данных в javascript c помощью drupal_add_js

При формировании страницы в Drupal нам необходимо передать данные о смайликах в javascript. Для этого в файл emoticon.module добавим функцию emoticon_preprocess_html:

/**
 * hook_preprocess_html().
 */
function emoticon_preprocess_html(&$vars) {
  $emoticon = new emoticon();
  $package = variable_get('emoticon_package');
  $values = $emoticon->getValues($package);
  $icons = array();
  foreach ($values[1] as $key => $value) {
    $acronym = explode(",", $values[0][$key]);
    $acronym = $acronym[0];
    $icons[] = '<img src="' . $value . '" title="' . $acronym . '" />';
  }
  drupal_add_js(array('emoticon' => array('icons' => $icons)), 'setting');
}

Данный метод использует хук hook_preprocess_html, который предназначен для перехвата действий при формировании страницы. Функция drupal_add_js позволяет передать параметры в javascript. Перед этим получаем данные о используемом пакете смайликов и считываем его.

Это был первый метод, который позволяет реализовать поставленную перед нами задучу. Напомню, что он основан на применении hook_wysiwyg_plugin.

hook_wysiwyg_include_directory

Второй метод использует хук hook_wysiwyg_include_directory, который позволяет указать для WISYWIG дополнительную директорию с плагином. В качестве примера создадим плагин CKEditor, который позволит добавлять в текст содержимого разделители страницы. В файл demo.module добавим функцию demo_wysiwyg_include_directory:

/**
 * hook_wysiwyg_include_directory().
 */
function demo_wysiwyg_include_directory($type) {
  return $type;
}

Далее создадим саму директорию. Для этого в подкаталоге нашего модуля создадим следующую структуру директорий и файлов:

  • plugins
    • wysiwyg
      • demo_pagebreak
        • images
          • pagebreak.gif
        • demo_pagebreak.js
        • demo_pagebreak.css
      • demo_pagebreak.inc

Нужно использовать именно такую структуру для правильной работы плагина. Файл demo_pagebreak.inc использует hook_wysiwyg_plugin для того, чтобы добавить его к нашему визуальному редактору. Добавим в этот файл функцию demo_demo_pagebreak_plugin:

function demo_demo_pagebreak_plugin() {
  $plugins['demo_pagebreak'] = array(
    'title' => t('Page break'),
    'icon file' => 'pagebreak.gif',
    'icon title' => t('Добавить разрыв страницы'),
    'settings' => array(),
  );
  return $plugins;
}

Об этом хуке мы разговаривали ранее. Отличие в том, что здесь нужно вместо wysiwyg указать название нашего плагина demo_pagebreak.

Drupal.wysiwyg.plugins

В javascript файле необходимо добавить методы для нашего нового плагина следующим образом:

(function ($) {
Drupal.wysiwyg.plugins['demo_pagebreak'] = {
  // Описание методов
})(jQuery);

Теперь нужно описать необходимые методы, которые будет использовать Drupal. В отличие от предыдущего метода, в этом можно форматировать текст средствами редактора. Мы можем в визуальном редакторе отображать html комментарий <!--pagebreak--> в виде блока, для которого применим css свойства из файла demo_pagebreak.css:

.pagebreak{
  display:block;
  border:0;
  border-top:3px dotted #ccc;
  margin:1em 0;
  width:100%;
}

При сохранении содержимого, либо при переключении вида редактора в текстовый вид, мы можем заменить форматированный блок на html комментарий. Это очень удобно, так как html комментарии в визуальном редакторе не отображаются. А теперь подробнее...

Метод определения визуального отображения в виде форматированного блока:

  /**
   * Определяет визуальное отображение
   */
  isNode: function(node) {
    return ($(node).is('div.pagebreak'));
  },

Метод для вставки в текст содержимого визуального отображения, либо самого комментария, в зависимости от вида редактора:

  /**
   * Добавляет визуальное отображение, либо 
   */
  invoke: function(data, settings, instanceId) {
    if (data.format == 'html') {
      var content = '<!--pagebreak-->';
    }
    if (typeof content != 'undefined') {
      Drupal.wysiwyg.instances[instanceId].insert(content);
    }
  },

Метод, который будет выполнять замену <!--pagebreak--> на визуальное отображении при загрузке визуального редактора:

  /**
   * Заменяет <!--pagebreak--> на визуальное отображение
   */
  attach: function(content, settings, instanceId) {
    var pagebreak = '<!--pagebreak-->';
    var placeholder = '<div class="pagebreak"></div>';
    var pattern = new RegExp(pagebreak, 'ig');
    content = content.replace(pattern, placeholder);
    return content;
  },

Метод, который будет выполнять обратную замену при выключении визуального редактора:

  /**
   * Заменяет визуальное отображение на <!--pagebreak-->
   */
  detach: function(content, settings, instanceId) {
    var pagebreak = '<!--pagebreak-->';
    newContent = newContent.replace(/<div class="pagebreak"><\/div>/ig, pagebreak);
    return newContent;
  }

На практике оба метода имеют свои плюсы и минусы. Вы должны сами решить какой метод использовать вам.