Делаем плагины для WordPress. Часть 2
ОглавлениеВведение ко второй части Введение ко второй частиВ первой части статьи мы начали разрабатывать плагин для оформления ссылок на пользователей и сообщества в ЖЖ так, как это принято на 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)
Рассмотрим фильтры, которые мы будем использовать более подробно. Начнем с фильтра 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(). Этот метод принимает два параметра:
$plugin_array['ljusers'] = plugins_url ('/ljusers/js/editor_plugin.js');
Теперь рассмотрим создание класса tinymce.plugins.LjusersPlugin. Классы плагина создаются с помощью метода tinymce.create(), синтаксис которого выглядит следующим образом: <void> create(<String> s, <Object> o) В качестве первого параметра s он принимает строку, содержащую имя класса в виде tinymce.ИмяПакета.ИмяКласса. ИмяПакета в нашем случае должно быть plugins, а ИмяКласса - имя класса нашего плагина, то есть в данном случае LjusersPlugin. Вторым параметром должна быть коллекция, содержащая методы класса. Имя элемента коллекции (ключа) определяет имя будущей функции, а объект функции - ее тело. В дальнейшем будем называть функции из этой коллекции по ее имени, хотя у самой функции имени может и не быть (в нашем случае и не будет). Для создания класса плагина, в нем могут быть реализованы следующие методы, объявленные в классе tinymce.Plugin:
В нашем плагине всю основную функциональность будет хранить в себе метод 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(), который в общем виде имеет три параметра, из которых мы будем использовать только два:
Две команды у нас очень похожи, в каждой из них мы сначала получаем выделенный в редакторе текст с помощью строки: var content = tinyMCE.activeEditor.selection.getContent({format : 'raw'}); Затем создаем новую строку, окружая выделенный текст нужным shortcode: var newcontent = '[ljuser]' + content + '[/ljuser]'; И заменяем выделенный текст новой строкой: tinyMCE.activeEditor.selection.setContent(newcontent); Более подробно про существующие свойства, методы и события у объекта редактора вы можете узнать на этой странице документации. После того как команды созданы, мы можем создать наши кнопки и связать их с соответствующими командами. Для этого используется метод addButton() объекта редактора, который принимает два параметра:
Если с первым параметром все должны быть понятно, то возможные свойства кнопки рассмотрим чуть подробнее. Полный список возможных свойств можно найти этой на странице документации, из которых мы будем использовать следующие:
Полсе того как мы написали функцию 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 тоже лучше класть в архив с плагином, чтобы пользователи при необходимости могли бы его подправить под себя. ЗаключениеПеречислим кратко основные действия, которые необходимо произвести, чтобы добавить кнопку на панель инструментов:
* В обработчике фильтра mce_buttons_* добавить в передаваемый массив имена будущих кнопок.
* В обработчике фильтра mce_external_plugins добавить в передаваемый массив ссылку на плагин к TinyMCE.
* В функции init(), которая передается в метод tinymce.create(), создать команды, вызываемые при нажатии на кнопки и создать объекты кнопок.
* Из функции getInfo(), которая передается в метод tinymce.create(), возвратить коллекцию, содержащую информацию о плагине для TinyMCE.
Теперь мы научились добавлять кнопки на панель редактора, а в следующей части статьи мы будем создавать страницу настроек для нашего плагина. Скачать плагин можно здесь. Продолжение следует... Делаем плагины для WordPress. Часть 3 Пожалуйста, оцените материал
|