Делаем плагины для WordPress. Часть 2 | jenyay.net

Делаем плагины для WordPress. Часть 2

Оглавление

Введение ко второй части
Устанавливаем хуки на нужные фильтры
Делаем плагин к TinyMCE
Оптимизируем JavaScript-плагин
Заключение
Комментарии

Введение ко второй части

В первой части статьи мы начали разрабатывать плагин для оформления ссылок на пользователей и сообщества в ЖЖ так, как это принято на Livejournal.com. На данный момент плагин уже имеет основную функциональность и может преобразовывать строки вида [ljuser]username[/ljuser] и [ljcomm]community[/ljcomm] в такой же HTML-код, который получается, если в ЖЖ использовать теги <lj user="username"> и <lj comm="community"> соответственно.

Не смотря на то, что плагин у нас работает и уже может создавать наглядные ссылки на ЖЖ-юзеров и сообществ, приличный плагин к такой функциональности просто обязан иметь какой-то интерфейс на тот случай, если пользователь забудет имена shortcodes. Поэтому дальнейшим шагом для развития нашего плагина будет добавление двух кнопок на панель визуального редактора. Одна из этих кнопок будет добавлять shortcode [ljuser], а другая - [ljcomm], причем, если в редакторе к этому времени будет выделен какой-то текст, то после нажатия на кнопку именно этот текст и будет считаться именем пользователя или сообщества. Сразу хочу предупредить, что этот раздел будет самым запутанным, и именно здесь понадобятся, хоть и неглубокие, но знания JavaScript. Здесь же мы будем впервые использовать пару фильтров. Тот способ добавления кнопок, который мы будем использовать появился, как и shortcodes, в WordPress 2.5, как добавлять кнопки в редактор более старых версий мы рассматривать не будем.

Устанавливаем хуки на нужные фильтры

В качестве визуального редактора WordPress использует TinyMCE, это настолько вещь в себе, что для него тоже есть отдельные плагины. Чтобы добавить кнопки на панель инструментов в редактор, нам придется установить хуки на два фильтра: mce_buttons_3 (для указания того сколько кнопок мы хотим добавить и их имен) и mce_external_plugins, чтобы указать редактору ссылку на исходный текст плагина для редактора, написанный на JavaScript, который и будет добавлять кнопки и содержать функции, срабатывающие при нажатии кнопок.

Чтобы установить хук на фильтр, необходимо вызвать функцию add_filter(), объявление которой выглядит следующим образом:

add_filter($tag,  $function_to_add,  $priority = 10,  $accepted_args = 1)
  • $tag - имя фильтра, на который мы хотим установить хук.
  • $function_to_add - Функция, которая должна быть вызвана при срабатывании фильтра. Этот параметр полностью аналогичен второму параметру из функции add_shortcode(), которую мы уже использовали в предыдущей части статьи.
  • $priority - необязательный параметр, обозначающий приоритет функции. Этот параметр используется, если надо сделать так, чтобы функции, привязанные к одному и тому же фильтру, вызывались в определенной последовательности. Чем приоритет меньше, тем раньше будет вызвана функция. То есть, если у нас есть две функции func1() и func2(), которые являются хуками для одного фильтра, причем у func1() приоритет равен 10, а у func2() равен 9, то func2() будет вызвана обязательно раньше, чем func1(). Но нам этот параметр сейчас не важен, мы его будет оставлять со значением по умолчанию, то есть 10.
  • $accepted_args - параметр, который устанавливает количество аргументов у функции, обрабатывающей фильтр. Этот параметр мы тоже не будем изменять и оставим его равным 1, как это считается по умолчанию.

Рассмотрим фильтры, которые мы будем использовать более подробно. Начнем с фильтра mce_buttons_3. Как можно догадаться из названия фильтра, он предназначен для работы с кнопками на панели редактора. Заметьте, что фильтры, предназначенные для работы с редактором, начинаются с префикса mce_.

Может возникнуть вопрос, что обозначает цифра "3" в имени фильтра. А цифра эта обозначает номер горизонтального ряда кнопок, с которым мы хотим работать (куда будем добавлять кнопки). Всего есть четыре подобных фильтра: mce_buttons, mce_buttons_2, mce_buttons_3 и mce_buttons_4. Первая панель всегда видна, вторая может убираться с помощью соответственной кнопки на первой панели, а мы будем добавлять кнопки на третью панель, которая по умолчанию пустая, если другие установленные плагины не добавляли на нее свои кнопки.

Чтобы установить хук на обработку фильтра mce_buttons_3 в конструктор класса ljusers, добавим строку, согласно которой обрабатывать этот фильтра будет функция mce_buttons(), расположенная внутри нашего класса.

add_filter( 'mce_buttons_3', array(&$this, 'mce_buttons') );

А вот как выглядит сама функция обработки mce_buttons(), которую мы используем для этого фильтра, она очень простая:

function mce_buttons($buttons)
{
    array_push($buttons, "ljusers", "ljcomm");
    return $buttons;
}

Эта функция принимает один параметр $buttons (вспомните про значение по умолчанию у параметра $accepted_args функции add_filter()), который является массивом кнопок. В конец этого массива мы добавляем две строки, которые обозначают имена для новых кнопок. Обратите внимание на то, что здесь мы только объявляем имена будущих кнопок, а добавлять сами кнопки будет уже отдельный плагин для редактора.

Чтобы добавить плагин к TinyMCE, мы должны обработать фильтр mce_external_plugins, поэтому в конструктор класса ljusers добавляем еще одну строку:

add_filter( 'mce_external_plugins', array(&$this, 'mce_external_plugins') );

Теперь наш конструктор полностью выглядит следующим образом:

function ljusers()
{
    if (function_exists ('add_shortcode') )
    {
        add_shortcode('ljuser', array (&$this, 'user_shortcode') );
        add_shortcode('ljcomm', array (&$this, 'community_shortcode') );

        add_filter( 'mce_buttons_3', array(&$this, 'mce_buttons') );
        add_filter( 'mce_external_plugins', array(&$this, 'mce_external_plugins') );
    }
}

Функция для обработки этого фильтра может выглядеть вот так:

function mce_external_plugins($plugin_array)
{
    if (function_exists (plugins_url) )
        $plugin_array['ljusers'] = plugins_url ('/ljusers/js/editor_plugin.js');
    else
        $plugin_array['ljusers'] = get_option('siteurl') .
            '/wp-content/plugins/ljusers/js/editor_plugin.js';

    return $plugin_array;
}

Аргументом этой функции является массив плагинов для редактора (которые пишутся на языке JavaScript), а точнее пути до них. Согласно рекомендациям скрипты на JavaScript помещаются в поддиректориюjs внутри директории нашего плагина, а имя файла скрипта должно быть editor_plugin.js. Это все не является обязательным требованием, но таким рекомендациям лучше следовать для единообразия. В качестве индекса массива должно быть имя плагина (у нас это будет ljusers).

Добавляться наш плагин может двумя путями в зависимости от того существует ли функция plugins_url(). Дело в том, что эта функция появилась только в WordPress 2.6 (ее еще даже нет в документации), и если мы хотим, чтобы наш плагин работал хотя бы в WordPress 2.5 (если версия старее, то там просто не будет shortcodes), то вынуждены использовать старый, менее красивый, способ, который и используется в ветке else.

В обоих ветках условия мы предполагаем, что плагин будет находиться внутри папки ljusers. Не думаю, что пользователи захотят переименовать эту директорию, поэтому проблем быть не должно.

Функция plugins_url(), как можно понять из примера, строит путь на основе пути до плагинов, прибавляя к нему путь, указанный в качестве аргумента функции.

В ветке else используется функция get_option(), которая возвращает значение параметра, хранящегося в базе данных WordPress, в данном случае параметр siteurl (более подробно с сохранением и чтением параметров из базы мы познакомимся в следующей части статьи). Узнав таким образом путь до блога, добавляем к нему путь до нашего плагина.

Функция mce_external_plugins() должна вернуть тот же массив с плагинами, но уже дополненный плагином ljusers.

Теперь нам надо создать сам JavaScript-плагин, для чего создаем внутри директории ljusers поддиректорию js, в которой создаем файл editor_plugin.js. Кроме того нам понадобятся рисунки для кнопок, их мы положим в поддиректорию img (тоже согласно рекомендациям) внутри директории js. В директорию img копируем наши файлы с картинками для кнопок. Таким образом, дерево каталогов плагина будет выглядеть следующим образом:

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

Делаем плагин к TinyMCE

Плагин к редактору TinyMCE представляет собой особым образом оформленную функцию. Шаблон для плагина можно посмотреть на этой странице документации. Более наглядно его можно записать:

(function()
{
    // Load plugin specific language pack
    tinymce.PluginManager.requireLangPack('ljusers');

    tinymce.create('tinymce.plugins.LjusersPlugin',
        {
            /**
             * Initializes the plugin, this will be executed after the plugin has been created.
             * This call is done before the editor instance has finished it's initialization so use the onInit event
             * of the editor instance to intercept that event.
             *
             * @param {tinymce.Editor} ed Editor instance that the plugin is initialized in.
             * @param {string} url Absolute URL to where the plugin is located.
             */

            init : function(ed, url)
            {
                ...
            },

            /**
             * Creates control instances based in the incomming name. This method is normally not
             * needed since the addButton method of the tinymce.Editor class is a more easy way of adding buttons
             * but you sometimes need to create more complex controls like listboxes, split buttons etc then this
             * method can be used to create those.
             *
             * @param {String} n Name of the control to create.
             * @param {tinymce.ControlManager} cm Control manager to use inorder to create new control.
             * @return {tinymce.ui.Control} New control instance or null if no control was created.
             */

            createControl : function(n, cm)
            {
                return null;
            },            

            /**
             * Returns information about the plugin as a name/value array.
             * The current keys are longname, author, authorurl, infourl and version.
             *
             * @return {Object} Name/value array containing information about the plugin.
             */

            getInfo : function()
            {
                return {
                    longname : 'Ljusers plugin',
                    author : 'Jenyay',
                    authorurl : 'http://jenyay.net',
                    infourl : 'http://jenyay.net',
                    version : "1.0"
                };
            }
        });

    // Register plugin
    tinymce.PluginManager.add('ljusers', tinymce.plugins.LjusersPlugin);
})();

В этом шаблоне создается функция без имени, внутри которой происходит всего три вызова методов объекта tinymce, который и представляет собой редактор.

Сначала вызывается метод requireLangPack(), который загружает файл переводов, или, если быть более точным текстовый домен (Text Domain). Про локализацию плагинов мы будем говорить в одной из следующих частей этой статьи.

tinymce.PluginManager.requireLangPack('ljusers');

Затем вызывается метод tinymce.create(), который создает класс нашего плагина. Про этот метод мы поговорим подробно чуть позже.

После вызова метода tinymce.create() создается экземпляр плагина с помощью вызова метода tinymce.PluginManager.add(). Этот метод принимает два параметра:

  • Первый параметр представляет собой строку, содержащую имя плагина. Это имя должно совпадать с тем, что мы дали плагину в PHP-скрипте внутри функции mce_external_plugins(), то есть у нас этот плагин называется ljusers:
$plugin_array['ljusers'] = plugins_url ('/ljusers/js/editor_plugin.js');
  • Второй параметр представляет собой имя только что созданного с помощью вызова tinymce.create() класса, то есть tinymce.plugins.LjusersPlugin.

Теперь рассмотрим создание класса tinymce.plugins.LjusersPlugin. Классы плагина создаются с помощью метода tinymce.create(), синтаксис которого выглядит следующим образом:

<void> create(<String> s, <Object> o)

В качестве первого параметра s он принимает строку, содержащую имя класса в виде tinymce.ИмяПакета.ИмяКласса. ИмяПакета в нашем случае должно быть plugins, а ИмяКласса - имя класса нашего плагина, то есть в данном случае LjusersPlugin.

Вторым параметром должна быть коллекция, содержащая методы класса. Имя элемента коллекции (ключа) определяет имя будущей функции, а объект функции - ее тело. В дальнейшем будем называть функции из этой коллекции по ее имени, хотя у самой функции имени может и не быть (в нашем случае и не будет). Для создания класса плагина, в нем могут быть реализованы следующие методы, объявленные в классе tinymce.Plugin:

  • init() - вызывается после того как плагин уже был создан, но до того как завершилось создание объекта редактора. Более подробно этот метод мы рассмотрим чуть позже. Именно в нем у нас будет реализована вся функциональность плагина.
  • getInfo() - возвращает коллекцию с информацией о плагине (имя плагина, его версия, автор, сайт автора и страница плагина)
  • createControl() - этот метод используется для создания более сложных объектов, чем кнопки. В нашем плагине мы этот метод использовать не будем.

В нашем плагине всю основную функциональность будет хранить в себе метод init(), рассмотрим его код:

init : function(ed, url)
{
    // Register the command so that it can be invoked by using tinyMCE.activeEditor.execCommand('mceLjusers');
    ed.addCommand('mceLjusers',
        function()
        {
            var content = tinyMCE.activeEditor.selection.getContent({format : 'raw'});
            var newcontent = '[ljuser]' + content + '[/ljuser]';

            tinyMCE.activeEditor.selection.setContent(newcontent);
        }
    );

    ed.addCommand('mceLjcomm',
        function()
        {
            var content = tinyMCE.activeEditor.selection.getContent({format : 'raw'});
            var newcontent = '[ljcomm]' + content + '[/ljcomm]';

            tinyMCE.activeEditor.selection.setContent(newcontent);
        }
    );

    // Register ljusers button
    ed.addButton('ljusers',
        {
            title : 'LJ-user',
            cmd : 'mceLjusers',
            image : url + '/img/userinfo.gif'
        }
    );

    // Register ljcomm button
    ed.addButton('ljcomm',
        {
            title : 'LJ-community',
            cmd : 'mceLjcomm',
            image : url + '/img/community.gif'
        }
    );

}

Эта функция принимает два параметра. Первый - объект редактора, а второй - URL до плагина (без имени файла editor_plugin.js). Тело функции init() состоит из четырех вызовов методов объекта редактора ed. Сначала добавляются две команды, которые будут вызываться при нажатии на кнопки (как вы помните, кнопок у нас тоже две, то есть по одной команде на кнопку). Команды создаются с помощью метода addCommand(), который в общем виде имеет три параметра, из которых мы будем использовать только два:

  • Имя команды, которое в будущем будет использоваться для ссылки на созданную команду, когда будем связывать эту команду с соответствующей кнопкой. У нас команды называются: mceLjusers для добавления shortcode [ljuser] и mceLjcomm для добавления shortcode [ljcomm].
  • Функция, которая будет вызываться при активации команды.

Две команды у нас очень похожи, в каждой из них мы сначала получаем выделенный в редакторе текст с помощью строки:

var content = tinyMCE.activeEditor.selection.getContent({format : 'raw'});

Затем создаем новую строку, окружая выделенный текст нужным shortcode:

var newcontent = '[ljuser]' + content + '[/ljuser]';

И заменяем выделенный текст новой строкой:

tinyMCE.activeEditor.selection.setContent(newcontent);

Более подробно про существующие свойства, методы и события у объекта редактора вы можете узнать на этой странице документации.

После того как команды созданы, мы можем создать наши кнопки и связать их с соответствующими командами. Для этого используется метод addButton() объекта редактора, который принимает два параметра:

  • Имя создаваемой кнопки. Причем это имя должно совпадать с одним из имемен кнопок, которые мы добавляли в массив внутри функции mce_buttons() из скрипта на PHP. У нас кнопки должны называться ljusers и ljcomm.
  • Второй параметр представляет собой коллекцию, с помощью которой описываются свойства кнопки.

Если с первым параметром все должны быть понятно, то возможные свойства кнопки рассмотрим чуть подробнее. Полный список возможных свойств можно найти этой на странице документации, из которых мы будем использовать следующие:

  • title - текст всплывающей подсказки, который появляется при наведении на кнопку мыши.
  • cmd - название команды, которая будет выполняться при нажании на кнопку. Именно с помощью этого свойства и связываются созданные на предыдущем шаге команды с нужной кнопкой. Поэтому здесь мы будем использовать строки mceLjusers и mceLjcomm.
  • image - ссылка на картинку, которая будет изображена на кнопке. Здесь мы как раз и используем параметр url, передаваемый в функцию init().

Полсе того как мы написали функцию init(), осталось заполнить шаблон функции getInfo():

getInfo : function()
{
    return {
        longname : 'Ljusers plugin',
        author : 'Jenyay',
        authorurl : 'http://jenyay.net',
        infourl : 'http://jenyay.net',
        version : "1.0"
    };
}

Думаю, что все имена элементов создаваемой коллекции говорят сами за себя.

Теперь наш плагин готов и его можно проверить в деле. После установки плагина на панели инструментов появятся две кнопки:

Теперь мы можем выделить набранное имя пользователя

и нажать на кнопку "LJ-user", после чего наш текст изменится на следующий:

Точно так же работает и вторая кнопка для вставки ссылки на ЖЖ-сообщество.

Во время отладки плагинов редактора может возникнуть проблема, связанная с тем, что в кеше движка (не браузера) может остаться старая версия плагина. Поэтому после исправлений, сделанных в плагине для редактора, может быть полезно удалять файлы из кэша, который находится в директории wp-content/uploads/js_cache.

Оптимизируем JavaScript-плагин

После того как мы написалии отладили плагин для редактора, в документации рекомендуется сжать исходный текст плагина на JavaScript с помощью специальной онлайновой утилиты Online Javascript compressor, расположенной по адресу http://javascriptcompressor.com/. Эта утилита удаляет "лишние" пробелы и переводы строк в скрипте.

Чтобы сохранить исходный вариант скрипта, сначала переименовываем файл editor_plugin.js в editor_plugin_src.js. Заходим на этот сайт, в верхнее поле копируем содержимое файла editor_plugin_src.js, а полученный результат обработки сохраняем в файл editor_plugin.js.

После обработки скрипта мы получим нечитаемый текст, но сам объем скрипта может уменьшиться в несколько раз, поэтому файл editor_plugin_src.js тоже лучше класть в архив с плагином, чтобы пользователи при необходимости могли бы его подправить под себя.

Заключение

Перечислим кратко основные действия, которые необходимо произвести, чтобы добавить кнопку на панель инструментов:

  • В PHP-скрипте необходимо установить хуки на фильтры mce_buttons_* и mce_external_plugins.
* В обработчике фильтра mce_buttons_* добавить в передаваемый массив имена будущих кнопок.
* В обработчике фильтра mce_external_plugins добавить в передаваемый массив ссылку на плагин к TinyMCE.
  • Написать на JavaScript плагин к TinyMCE.
* В функции init(), которая передается в метод tinymce.create(), создать команды, вызываемые при нажатии на кнопки и создать объекты кнопок.
* Из функции getInfo(), которая передается в метод tinymce.create(), возвратить коллекцию, содержащую информацию о плагине для TinyMCE.

Теперь мы научились добавлять кнопки на панель редактора, а в следующей части статьи мы будем создавать страницу настроек для нашего плагина. Скачать плагин можно здесь.

Продолжение следует...

Делаем плагины для WordPress. Часть 3

Вы можете подписаться на новости сайта через RSS, Группу Вконтакте или Канал в Telegram.
5 stars

Рейтинг 4.6/5. Всего 22 голос(а, ов)



Allpa 19.01.2011 - 06:40

Простите за офтоп

Хотя он кагбе и не совсем офтоп..
Я про TinyMCE: заглянула в его файлы, поняла, что в нём запрограммировано гораздо большее количество кнопок, но в визуальном редакторе показываются далеко не все.
Вопрос: а нельзя ли как-нить разрешить показ всех?
Спасибо :)

 04.06.2012 - 16:55

confused smileyУсилено

Nawata 15.03.2013 - 19:40

Прошу уточнить.

В качестве индекса массива должно быть имя плагина (у нас это будет ljusers).
Пожалуйста, сделайте уточнение. "ljusers" - это имя класса, имя файла или вообще может быть любым набором символов?
Спасибо.

Igor92 18.06.2015 - 16:57

Благодарность

Спасибо за работающий и очень хорошо объясняющий пример, подобного не нашел вообще!

igorkn 23.03.2016 - 16:49

Не паботает

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


Подписаться на комментарии
Автор:
Тема:
 Ваш комментарий
 
 
Введите код 579