Руководство по созданию плагинов в Joomla 1.5
Плагины позволяют нам редактировать функциональность системы, без непосредственного редактирования существующего кода. Например, плагин можно использовать для обработки контента, перед выводом его на экран, расширить возможности поиска, или создать свой механизм авторизации. В этом уроке мы рассмотрим на примере, как заменить определенную стоку в статье с картинкой.
Введение
Когда мы начинаем создавать новый плагин, мы разбиваем создание на несколько частей. Так нам будет проще вникать в суть и удобнее тестировать плагин. В идеале, мы должны иметь более одного сервера, чтобы тестировать плагин (в частности его установку).
Сначала мы создадим простейщий установщик. XML установщика опишит плагин, названный Foobar - My Extension.
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE install SYSTEM "http://dev.joomla.org/xml/1.5/plugin-install.dtd">
<install version="1.5" type="plugin" group="foobar">
<name>Foobar - My Extension</name>
<author>Имя автора</author>
<authorEmail>Email автора</authorEmail>
<authorUrl>Сайт автора</authorUrl>
<creationDate>Дата создания</creationDate>
<copyright>Копирайты</copyright>
<license>Лицензия</license>
<version>Версия плагина</version>
<description>Описание плагина</description>
<files>
<filename plugin="myextension">myextension.php</filename>
</files>
<params/>
</install>
Очень важно заметить параметр группы плагина. Вообще все плагины разделяются на логические группы. Вот список групп ядра Joomla:
- authentication
- content
- editors
- editors-xtd
- search
- system
- user
- xmlrpc
Также как вы заметили, мы можем указать свою группу.
Также в XMl-файле указывается очень важной информацией является параметр plugin. Этот параметр является уникальным в группе и идентифицирует наш плагин. Далее в таблице видны имена плагинов, которые уже заняты под ядро системы.
| Группа | Зарезервированные имена |
|---|---|
| authentication | gmail |
| joomla | |
| ldap | |
| openid | |
| content | emailcloak |
| geshi | |
| loadmodule | |
| pagebreak | |
| pagenavigation | |
| sef | |
| vote | |
| editors | none |
| tinymce | |
| xstandard | |
| editors-xtd | image |
| pagebreak | |
| readmore | |
| search | categories |
| contacts | |
| content | |
| newsfeeds | |
| sections | |
| weblinks | |
| system | cache |
| debug | |
| legacy | |
| log | |
| remember | |
| user | joomla |
| xmlrpc | blogger |
| joomla |
После того как вы создали XML файл myextension.xml, создайте файл myextension.php и положите их в каталог foobar. После чего заархивируйте в gz, .tar, .tar.gz, или zip (лучше все-так в zip).
Заметьте, что файлы одного плагина не делятся на каталоги, потому что плагины состоят всего из двух файлов. После этого вы можете установить плагин. Но пока он не выполняет никаких действий.
События
Итак у нас должно быть создано событие, которое в дальнейшем будет обрабатываться плагином.
Приложения Joomla используют глобальный объект, названный диспетчер событий (event dispatcher), для отправки событий зарегестрированным слушателям (listeners). Глобальный диспетчер событий - это объект JEventDispatcher, который является расширением абстрактного класса JObservable.
В Joomla, слушателем может быть класс или функция. Когда мы используем класс слушателя, то этот класс должен быть расширением класса JPlugin. Именно расширением, для того чтобы наследовать основные методы класса.
Итак, представьте что у нас есть компонент Foobar, отображающий некоторые записи из БД. Мы можем использовать событие onPrepareFoobar, которое происходит перед выводом записей.
Чтобы добавить событие, мы записываем его с помощью метода triggerEvent() в диспетчер событий, который уже передает его слушателям.
Метод triggerEvent() содержит два параметра: название события и массив аргументов переданных слушателям. Допустим мы добавляем событие onPrepareFoobar:
$dispatcher =& JDispatcher::getInstance();
JPluginHelper::importPlugin('Foobar');
$arguments = array(&$foobarData);
$result = $dispatcher->trigger('onPrepareFoobar', $arguments);
Заметьте, что мы передаем $foobarData в виде ссылки, а второй параметр функции обязательно должен быть массивом. Теперь мы можем написать плагины, которые будут обрабатывать $foobarData.
Слушатель
Это одно из важнейших понятий. Вообще слушателями являются плагины, которые обрабатывают события переданные из диспетчера событий.
Регестрирование слушателей
Когда мы создаем новый плагин, если мы используем функции, мы должны информировать приложение о каждой функции и событии. Для этого нужно использовать метод приложений registerEvent(). Метод содержит два параметра: имя события и имя обработчика.
Технически имя обработчика может быть именем класса.
Например, в ядре Joomla компонент поиска использует плагины для поиска результата. Этот плагин ищет в статьях с помощью функции plgSearchContent() которая обрабатывает событие onSearch:
$mainframe->registerEvent('onSearch', 'plgSearchContent');
Обработка событий
Как уже говорилось ранее, мы можем использовать как функции, так и классы для обработки событий. Начнем мы наше изучение обработки событий с использования функций.
Мы уже сделали заготовку плагина, названного My Plugin в группе Foobar и мы хотим обработать событие названное onPrepareFoobar.
Перед тем как мы начнем создавать нашу функцию, мы должны дать ей название. Важно использовать следующее правило именований: слово plg, группа плагина, имя плагина, событие. Например, наша функция будет называться plgFoobarMyPluginPrepareFoobar.
Например эту функцию мы можем использовать для обработки события:
$mainframe->registerEvent('onPrepareFoobar','plgFoobarMyPluginPrepareFoobar');
/**
* Переводит переданный параметр в верхний регистр.
*
* @param Foobar Reference to a Foobar object
*/
function plgFoobarMyPluginPrepareFoobar(&$foobar)
{
$foobar->name = strtoupper($foobar->name);
}
Самая важная часть в этой функции - это переданный параметр. Ранее мы передали параметр в виде массива. Каждый элемент массива обрабатывается плагином отдельно.
Плагин состоящий из функции, так же может обрабатывать множественные события.
Если мы хотим создать слушателя используя класс, для этого мы должны расширить класс JPlugin. Сначала мы должны разобраться как именовать класс слушателя. JPlugin требует специальное именование: слово plg, имя группы плагина, имя плагина.
Например, плагин с именем myplugin в группе foobar должен называться plgFoobarMyplugin.
Этот пример обрабатывает два события: onPrepareFoobar и onAfterDisplayFoobar.
// Импортируем класс JPlugin
jimport('joomla.event.plugin');
/**
* My Plugin event listener
*/
class plgFoobarMyplugin extends JPlugin
{
/**
* Обрабатываем событие onPrepareFoobar
*
* @param object Foobar to prepare
*/
function onPrepareFoobar(&$foobar)
{
$foobar->name = JString::strtoupper($foobar->name);
}
/**
* Обрабатываем событие onAfterDisplayFoobar
*
* @param object Foobar which is being displayed
* @return string XHTML to display after the Foobar
*/
function onAfterDisplayFoobar(&$foobar)
{
return '<p>'.JText::('Foobar Name converted to upper case by
My Plugin').'<p>';
}
}
В этом примере, как видите, вам не приходится самим регестрировать события. За вас все сделает фрэймворк Joomla. Главное правильно назвать класс и события.
Когда мы импортируем плагин в Joomla, глобальный диспетчер событий автоматически смотрит классы слушателей и регестрирует их.
Метод onAfterDisplayFoobar() вернет значение. Вы должны помнить, что ранее мы передали на обработку плагину массив.
В этом примере видно как получить обратно уже обработанный массив.
$dispatcher =& JDispatcher::getInstance();
JPluginHelper::importPlugin('Foobar');
$arguments = array(&$foobarData);
$result = $dispatcher->trigger('onPrepareFoobar', $arguments);
$foobar->onAfterDisplayFoobar = trim(implode("\n", $result));
Это очень простой пример. Далее мы рассмотрим более сложные вещи.
Группы плагинов
Плагины делятся на различные группы. Каждая группа плагинов обрабатывает определенный набор событий. В ядре Joomla содержится 8 групп: authentication
content
editors
editors-xtd
search
system
user
xmlrpc
Далее мы рассмотрим каждую группу подробнее.
Authentication
Joomla поддерживает 4 различных методов авторизации: GMail
Joomla!
LDAP
OpenID
Также можно создать свои методы авторизации пользователя.
В этой группе существует только один метод onAuthenticate.
В плагине мы можем установить значение следующих свойств:
| Свойство | Описание |
|---|---|
| birthdate | Дата рождения пользователя |
| country | Страна пользователя |
| E-mail адрес пользователя | |
| error_message | Сообщение об ошибке или отмена авторизации |
| fullname | ФИО |
| gender | Пол |
| language | Язык |
| postcode | Почтовый индекс |
| status | Статус авторизации |
| timezone | Часовой пояс |
| username | Логин |
Свойство status используется для определения результата авторизации. Таблица содержит три константы, которые может содержать свойство status:
| Константа | Описание |
|---|---|
| JAUTHENTICATE_STATUS_CANCEL | Авторизация отменена |
| JAUTHENTICATE_STATUS_FAILURE | Ошибка авторизации |
| JAUTHENTICATE_STATUS_SUCCESS | Авторизация прошла успешно |
По-умолчанию в Joomla опубликован только один плагин авторизации - Joomla!. Также вы можете опубликовать LDAP, GMAIL и OpenId.
onAuthenticate
| Описание | Происходит, когда пользователь авторизируется на сайте | |
|---|---|---|
| Параметры | username | Логин |
| password | Пароль | |
| response | Ссылка на объект JAuthenticationResponse | |
Content
Плагины группы content, позволяют обрабатывать элементы контента, прежде чем вывести его на экран. Наиболее часто используемое событие - это onPrepareContent. Это событие запускается самым первым.
Поставим такую задачу- заменить все символы ":)" на картинку улыбающегося смайлика. Вот решение этой задачи:
defined('_JEXEC') or die('Restricted access');
// регестрируем обработчик
$mainframe->registerEvent('onPrepareContent',
'plgContentSmiley');
/**
* Заменим :) на смайлик.
*
* @param object Content item
* @param JParameter Content parameters
* @param int Page number
*/
function plgContentSmiley(&$row, &$params, $page)
{
$pattern = '/\:\)/';
$icon = '<img src="/plugins/content/smiley.gif" />';
$row->text = preg_replace($pattern, $icon, $row->text);
}
Заметьте, что итоговые значения не нужно возвращаться, и параметр $row передается по ссылке.
Рассмотрим подробнее атрибуты контента:
| Атрибут | Описание |
|---|---|
| created | дата создания в формате 0000-00-00 00:00:00. |
| modified | Дата последнего изменения в формате 0000-00-00 00:00:00 |
| text | основной контент элемента |
| title | заголовок элемента контента |
| toc | таблица контента |
Теперь рассмотрим происходящие события.
onAfterDisplayContent
| Описание | Создается xhtml строка после обработки контента | |
|---|---|---|
| Параметры | row | Ссылка на объект контента |
| params | ссылка на объект JParameter контента | |
| page | Номер страницы | |
| Возвращает | XHTML после отображения самого контента | |
onAfterDisplayTitle
| Описание | Создается xhtml строка после отображения заголовка страницы | |
|---|---|---|
| Параметры | row | Ссылка на объект контента |
| params | ссылка на объект JParameter контента | |
| page | Номер страницы | |
| Возвращает | XHTML после отображения заголовка страницы | |
onBeforeDisplayContent
| Описание | Создается xhtml строка перед отображением элемента text. Например плагин - рейтинг записи | |
|---|---|---|
| Параметры | row | Ссылка на объект контента |
| params | ссылка на объект JParameter контента | |
| page | Номер страницы | |
| Возвращает | XHTML перед выводом эелемента text | |
onPrepareContent
| Описание | Обрабатывает один элемент контента. Если вы собираетесь внести изменения в текст элемента, то вы должны использовать это событие. | |
|---|---|---|
| Параметры | row | Ссылка на объект контента |
| params | ссылка на объект JParameter контента | |
| page | Номер страницы | |
| Возвращает | Истина, если успешно | |
onPrepareContent
| Описание | Обрабатывает один элемент контента. Если вы собираетесь внести изменения в текст элемента, то вы должны использовать это событие. | |
|---|---|---|
| Параметры | row | Ссылка на объект контента |
| params | ссылка на объект JParameter контента | |
| page | Номер страницы | |
| Возвращает | Истина, если успешно | |
Editors
Пожалуй, самой сложный из всех основных плагинов - это редактор. Одним из основных редакторов является TinyMCE (http://tinymce.moxiecode.com/). TinyMCE это JavaScript редактор, который позволяет пользователю легко изменять данные в текстовом поле без знаний XHTML.
Очень важно здесь отметить что кнопки под редактором создаются отдельными плагинами editors-xtd группы, о которых расскажу чуть позже.
Приведу список самых популярных редактором за бугром:
- ASBRU Web Content Editor
- FCKeditor
- wysiwygPro
- XStandard
- Yahoo Rich Text Editor
Импортирование нового редактора задача не из легких. Зато если редактор уже встроен, то его очень просто подключать.
Yahoo Rich Text Editor подает большие надежды. Это очень навороченный редактор, созданный на основе YUI (Yahoo User Interface). Про интерфейс Yahoo скоро начну вести отдельную серию статей.
События плагинов:
onDisplay
| Описание | Получить XHTML поле элемента формы | |
|---|---|---|
| Параметры | name | Уникальное название редактора |
| content | Инициируемые контент | |
| width | Ширина редактора в пикселях | |
| height | Высота редактора в пикселях | |
| col | Ширина редактора в символах | |
| row | Высота редактора в строках | |
| buttons | Булево значение, служит чтобы скрыть или показать дополнительные кнопки; смотрите событие onCustomEditorButton из раздела editors-xtd этой статьи. | |
| Возвращает | XHTML форма редактора | |
onGetContent
| Описание | Некоторый Javascript код, который возвращает контент | |
|---|---|---|
| Параметры | editor | Уникальное имя редактора |
| Возвращает | Запускает Javascript на стороне клиента, который вернет контент редактора. Код должен заканчиваться точкой с запятой. | |
onGetInsertMethod
| Описание | Запускаем javascript код, который определен функцией jInsertEditorText() | |
|---|---|---|
| Параметры | name | Уникальное имя редактора |
| Возвращает | Вставляем в редактор заданный текст в позицию курсора | |
onInit
| Описание | Инициализация запускается один раз независимо от колличества выхванных редакторов. | |
|---|---|---|
| Возвращает | Необходимые функции по запуску редактора | |
onSave
| Описание | Запускаем javascript код, который необходим для сохранения редактора | |
|---|---|---|
| Параметры | name | Уникальное имя редактора |
| Возвращает | Javascript строку, которая запускается перед сохранением контента. | |
onSetContent
| Описание | Запускаем javascript код, который установит заданный контент в редакторе | |
|---|---|---|
| Параметры | name | Уникальное имя редактора |
| HTML | Новый контент редактора | |
| Возвращает | Новый контент в редакторе | |
Editors-xtd
Эта группа плагинов используется для расширения функциональности редакторов, путем добавления к ним кнопок. К сожалению встроенный редактор xStandart не поддерживает эту группу плагинов. Существует только одно событие, связанные с этой группой, onCustomEditorButton.
Поскольку существует только одно событие, связанное с этой группой, мы, будем использовать функции вместо работы с подклассами JPlugin. Этот пример показывает, каким образом мы можем добавить кнопку смайлика ":)" в редактор.
// no direct access
defined('_JEXEC') or die('Restricted access');
$mainframe->registerEvent('onCustomEditorButton',
'plgSmileyButton');
/**
* Кнопка смайликов
*
* @name string Name of the editor
* @return array Array of three elements: JavaScript action,
Button name, CSS class.
*/
function plgSmileyButton($name)
{
global $mainframe;
// get the image base URI
$doc =& JFactory::getDocument();
$url = $mainframe->isAdmin() ? $mainframe->getSiteURL() : JURI::base();
// get the JavaScript
$js = "
function insertSmiley()
{
jInsertEditorText(' :) ');
}
";
$css = " .button1-left .smiley { background:
url($url/plugins/editors-xtd/smiley1.gif)
100% 0 no-repeat; }";
$css .= "\n .button2-left .smiley { background:
url($url/plugins/editors-xtd/smiley2.gif)
100% 0 no-repeat; }";
$doc->addStyleDeclaration($css);
$doc->addScriptDeclaration($js);
$button = array("insertSmiley()", JText::('Smiley'),
'smiley');
return $button;
}
В этом коде мы делаем два важных действия: мы определяем обработчик функции, и регистрируем ее в глобальном диспетчере событий.
Переходя к функции plgSmileyButton() обратим внимание, что у нее есть параметр $name, который указывает на имя редактора. Он нужен для определения с каким редактором мы работает, так как их может быть несколько на одной странице. В самой функции мы этот параметр никак не используем.
Мы создаем JavaScript и CSS код. Клиент будет выполнять JavaScript при нажатии кнопки. Также мы определяем два стиля CSS, чтобы сделать кнопку в разных местах (в админке и во фронте).
В массиве $button мы возвращаем три элемента. Первый элемент - это JavaScript, который будет выполнен при нажатии кнопки. Вторым элементом является название кнопки. Третьим элементом является название класса CSS для кнопки.
Также в плагине мы используем картинки, которые должны быть добавлены в xml файле:
<files> <filename plugin="smiley">smiley.php</filename> <filename>smiley1.gif</filename> <filename>smiley2.gif</filename> </files>
Перед тем как мы задействуем нашу кнопку в редакторе, обратим внимание на особо полезные методы редактора:
| Метод> | Описание |
|---|---|
| getContent | JavaScript возвращает текст в редакторе |
| save | JavaScript сохраняет содержимое редактора (используется не во всех редакторах) |
| setContent | JavaScript устанавливает определенный контент |
Все эти методы возвращают JavaScript строку. Мы можем использовать эти строки для создания скриптов, которые взаимодействуют с редактором.
Покажу на примере, как пользоваться этими методами:
// получаем редактор $editor =& JFactory::getEditor(); // вызываем javascript, который вызвращает контент $getContent = $editor->getContent($name); // строим javascript, который в окне отображает контент $js = 'var content = '.$getContent."\n" .'alert(content);';
onCustomEditorButton
| Описание | Добавляет кнопку в редактор | |
|---|---|---|
| Параметры | name | Уникальное имя редактора |
| Возвращает | Массив из трех элементов: javascript, имя кнопки и css стиль | |
Search
Плагины группы search использюется для расширения основного компонента поиска и получения результатов поиска. Есть два события, связанные с этой группой, onSearch и onSearchAreas. Цель onSearchAreas трудновато понять.
В рамках поиска, пользователь может выбрать, где он хочет искать. В данном случае можно выбрать "Статьи", "Ссылки", "Контакты", "Категории", "Разделы", и "Ленты'. Когда мы инициируем onSearchAreas, мы выбираем в каком компоненте будет произведен поиск.
Важно: Один плагин может искать в нескольких компонентах.
Событие onSearch более косвенно, и срабатывает только когда происходит поиск. В итоге должен вернуться массив результатов. Реалзиция поиска будет зависеть от того, что вы ищете.
onSearch
| Описание | Выполняет поиск и возвращает результаты | |
|---|---|---|
| Параметры | text | Искомая строка |
| phrase | Тип поиска 'any', 'all', или 'exact'. | |
| ordering | Виды сортировки: 'newest', 'oldest', 'popular', 'alpha' (alphabetical), or 'category'. | |
| areas | Раздел в котором ищем (основано на onSearchArea). | |
| Возвращает | Ассоциативный массив со следующими полями: 'title', 'text', 'created', 'href', 'browsernav' (1 = открывать в новом окне), и 'section' (необязательное). | |
onSearchAreas
| Описание | Получить массив различных областях, которые могли быть обнаружены с помощью этого плагина. Каждый поиск плагин должны обратиться по крайней мере к одной области. | |
| Возвращает | Ассоциативный массив различных областей для поиска. | |
System Есть четыре важных системных события. Вот их порядок:
- onAfterInitialize
- onAfterRoute
- onAfterDispatch
- onAfterRender
onAfterDispatch
| Описание | Происходит после того, как была отправлена заявка |
onAfterRoute
| Описание | Происходит после того как приложение инициализировано |
onAfterDispatch
| Описание | Происходит когда приложение отрендерено, но еще не отправлено пользователю |
onAfterRender
| Описание | Происходит после применения роутера |
User
Плагины группы user позволяют выполнить дополнительную обработку в конкретных событиях связанных с пользователем. Это особенно полезно, когда эти плагины используются в сочетании с компонентом, который связан с таблицей # __users.
Рассмотрим на примере событие onAfterUserStore. Это событие срабатывает после того, пользователь соханяет свои данные (срабатывает для новых и уже существующих пользователей).
Этот пример показывает, как мы можем поддерживать другую таблицу, # __some_table, когда создан новый пользователь:
$mainframe->registerEvent('onAfterStoreUser',
'plgUserMaintainSomeTableStoreUser');
/**
* Add new rcord to #__some_table when a new user is created
*
* @param array User attributes
* @param boolean True if the user is new
* @param boolean True if the user was successfully stored
* @param string Error message
* @return array Array of three elements: JavaScript action, Button
name, CSS class.
*/
function plgUserMaintainSomeTableStoreUser($user, $isnew, $success,
$msg)
{
// if they are a new user and the store was successful
if ($isnew && $success)
{
// add a record to #__some_table
$db = JFactory::getDBO();
$query = 'INSERT INTO '.$db->nameQuote('#__some_table')
.' SET '.$db->nameQuote('userid').' = '.$user['id'];
$db->setQuery($query);
$db->query();
}
}
onBeforeStoreUser
| Описание | Позволяет модифицировать данные пользователя перед сохранением | |
| Параметры | user | Ассоциативный массив с данными пользователя |
| isnew | Истина если пользователь новый | |
onAfterStoreUser
| Описание | Позволяет выполнять код, после того как данныен пользователя были сохранены | |
| Параметры | user | Ассоциативный массив с данными пользователя |
| isnew | Истина если пользователь новый | |
| success | Истина в случае успешного сохранения | |
| msg | Сообщение об ошибке если она имеется | |
onBeforeDeleteUser
| Описание | Позволяет нам выполнять дополнительную обработку до того, как пользователь будет удален. Это полезно для обновления неосновных таблиц, которые имеют отношение к ключевым таблицам #__users | |
| Параметры | user | Ассоциативный массив с данными пользователя |
onAfterDeleteUser
| Описание | Позволяет выполнять код, после того как пользователь удален | |
| Параметры | user | Ассоциативный массив с данными пользователя |
| success | Истина в случае успешного удаления | |
| msg | Сообщение об ошибке если она имеется | |
onLoginFailure
| Описание | Если логин или пароль введен неверно | |
| Параметры | response | JAuthenticationResponse обьект |
onLoginUser
| Описание | Происходит при успешной авторизации пользователя | |
| Параметры | user | JAuthenticationResponse обьект |
| remember | Истина если пользователь хочет быть "запомненным" | |
| Возвращает | Булево знчение ложь, если неуспешно | |
onLogoutUser
| Описание | Пользователь пытается выйти. В это время плагин "joomla" удаляет сессии. | |
| Параметры | user | JAuthenticationResponse обьект |
| Возвращает | Булево знчение ложь, если неуспешно | |
XML-RPC
XML-RPC - стандарт/протокол вызова удалённых процедур, основанный на XML, является прародителем SOAP, отличается исключительной простотой применения. XML-RPC, как и любой другой интерфейс RPC, определяет набор стандартных типов данных и команд, которые программист может использовать для доступа к функциональности другой программы, находящейся на другом компьютере в сети. Joomla! включает в себя XML-RPC сервер, который, как мы можем расширить с помощью плагинов.
Плагины XML-RPC состоят из двух частей: обработчик события onGetWebServices, который возвращает массив поддерживающий веб-сервис вызовов, и статический класс или набор функций, которые управляют удаленным вызовом процедур. Если руки дойдут, расскажу об использовании XML-RPC в Joomla в отдельной статье.
onGetWebServices
| Описание | Получить ассоциативный массив с описанием доступных методов веб-службы. | |
| Возвращает | Ассоциативный массив в ассоциативном массиве, который определяет доступен ли сервис вызова. | |
