Плагины позволяют нам редактировать функциональность системы, без непосредственного редактирования существующего кода. Например, плагин можно использовать для обработки контента, перед выводом его на экран, расширить возможности поиска, или создать свой механизм авторизации. В этом уроке мы рассмотрим на примере, как заменить определенную стоку в статье с картинкой.
Введение
Когда мы начинаем создавать новый плагин, мы разбиваем создание на несколько частей. Так нам будет проще вникать в суть и удобнее тестировать плагин. В идеале, мы должны иметь более одного сервера, чтобы тестировать плагин (в частности его установку).
Сначала мы создадим простейщий установщик. 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     =&amp; JDispatcher::getInstance();
JPluginHelper::importPlugin('Foobar');
$arguments = array(&amp;$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(&amp;$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 Страна пользователя
email 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(&amp;$row, &amp;$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 Описание Получить ассоциативный массив с описанием доступных методов веб-службы.
Возвращает Ассоциативный массив в ассоциативном массиве, который определяет доступен ли сервис вызова.