Шаблоны писем в Joomla. Вступление
С недавнего времени (с выходом Joomla 4) в CMS Joomla появился замечательный функционал - шаблоны писем. Эти шаблоны позволяют администратору настроить "под себя" все системные уведомления, которые отсылает CMS как пользователям, так и администраторам.

В предыдущих версиях Joomla, все это было реализовано через языковые переменные, что было крайне неудобно и максимально ограничивало администратора в "творческом полёте", именно администратора или контент менеджера, которые ни чего не понимают в разработке и им было практически невозможно поменять дизайн писем без привлечения разработчика. Слава Богу, это в прошлом!
На данный момент (Joomla 5) функционал шаблонов писем значительно расширен (и это, конечно же, не предел). Теперь администратор из админ. панели может отредактировать в удобном HTML редакторе (TinyMCE) любое системное письмо, которое отправляет Joomla. Может добавить любой текст, изображения, файлы - это круто!
Так же CMS позволяет вставлять необходимые переменные в тело письма (имя пользователя, почту, имя сайта и т.д.) так, как нужно в конкретном случае. Поддерживается редактирование для разных языков, то есть предусмотрена полная мультиязычность, как и в остальном функционале Joomla.

И все это предлагается "под капотом" Joomla и отлично работает!
Но, как это всегда бывает, стандартного функционала может не хватить для определенных задач. Именно об этом и пойдет речь в этой статье.
Не хватает переменных...
В каждом наборе переменных, любого шаблона письма, содержится необходимый минимум, который Joomla предлагает "из коробки".
Естественно, что для каждого конкретного случая этот набор должен быть скорректирован и доработан. СMS Joomla предоставляет нам такую возможность с помощью использования плагинов.
Ко мне обратился заказчик, с просьбой расширить список переменных в шаблонах, а, конкретно, добавить поля пользователей в шаблон письма об успешной активации пользователя. Далее я расскажу, как я это реализовал.
Спасибо, Бро!
Успешной реализации задачи очень помог Сергей Толкачев. В своем посте на Хабре он описал триггеры событий, которые позволяют добавить новые переменные к шаблонам писем.
И в целом, моя статья, это только расширенное описание поста Сергея. За что ему огромное спасибо!
Кстати, не забудем поблагодарить и заказчика расширения, который спонсировал все это производство, а так же дал согласие на публикацию решения в свободном доступе - это веб студия Креативные Бизнес Системы.
Триггеры для плагина
И так... Задача поставлена: добавить в шаблоны писем переменные, которые будут содержать значения полей пользователей.
Для решения нам нужно:
Вывести переменные, в нашем случае это поля пользователей, в шаблоны писем в панели администратора Joomla. Для этого в Joomla предусмотрен триггер
BeforeRenderingMailTemplateEvent.Передать новые переменные в письмо администратору. Для этого предусмотрен триггер
onMailBeforeRendering.
Давайте рассмотрим каждое событие отдельно.
onMailBeforeTagsRendering
Это событие поможет нам добавить переменные (шорт-коды) в шаблон письма в панели администратора.

В моем случае нужно добавить к стандартным переменным поля пользователя. В качестве шорт-кодов будем использовать имя (не заголовок) полей.

Для получения всех полей пользователей используем
use \Joomla\Component\Fields\Administrator\Helper\FieldsHelper; $user_fields = FieldsHelper::getFields('com_users.user', true);
Вся функция будет выглядеть так
public function onMailBeforeTagsRendering(Event $event): void { $template = $event->getArgument(1); //получаем весь объект $tags = $template->params['tags']; //получаем стандартные шорт-коды $user_fields = FieldsHelper::getFields('com_users.user', true); //получаем все поля пользователя $newtags = []; foreach($user_fields as $field) { $newtags[] = $field->name; //формируем массив с полями по имени поля } $tags = array_merge($tags, $newtags); //добавляем поля пользователя к стандартным шорт-кодам $template->params['tags'] = $tags;// добавляем все в объект }
Теперь в шаблонах писем, в переменных, у нас отображаются и шорт-коды полей пользователей.

Но этого еще не достаточно, нужно еще "заставить" Joomla понимать, какие данные отправлять, используя эти шорт-коды.
onMailBeforeRendering
Это событие позволит нам научить Joomla понимать новые шорт-коды и корректно отправлять данные по ним в письма.
Аргументом $event для этой функции является экземпляр класса BeforeRenderingMailTemplateEvent
подключаем
use Joomla\CMS\Event\Mail\BeforeRenderingMailTemplateEvent;
У меня стояла задача, передать поля пользователей в письме администратору о необходимости активации нового пользователя, поэтому следующий код будет немного не стандартный, но очень хорошо подойдет для примера
public function onMailBeforeRendering(BeforeRenderingMailTemplateEvent $event): void { $templateId = $event->getTemplateId(); //id шаблона, как контекст $app = Factory::getApplication(); if($templateId == 'com_users.registration.admin.verification_request') { //нужный мне шаблон письма $activiti = $app->input->get('token', '', 'string');//получение токена (ключа активации), который содержится в ссылке на активацию $db = Factory::getContainer()->get(DatabaseInterface::class); //подключаем базу $db->setQuery("SELECT `id` FROM `#__users` WHERE `activation` = '{$activiti}'"); $user_id = $db->loadResult(); //получаем id пользователя, что бы передать его поля } $user_fields = FieldsHelper::getFields('com_users.user', ['id' => $user_id], true); //получаем все поля пользователя $template = $event->getTemplate();//получаем весь объект письма foreach($user_fields as $field) { $data[$field->name] = $field->value; //получаем поля пользователя в нужном нам виде по имени поля } $template->addTemplateData($data);// передаем в объект новый массив шорт-кодов }
Давайте подробно рассмотрим $data - массив, который передает данные о новых переменных (шорт-кодах).
$template = $event->getTemplate();//получаем весь объект письма $data = [ 'field_name' => 'field_vale', ]; $template->addTemplateData($data);// передаем в объект новый массив шорт-кодов
Теперь в шаблоне письма используем {field_name}, чтобы в самом письме получить field_value.
Всю логику разобрали. Далее соберем все в системный плагин, который позволит реализовать нужный функционал.
Системный плагин ShortMail
Структура плагина
- language -- en-GB --- plg_system_shortmail.ini --- plg_system_shortmail.sys.ini - services -- provider.php - src -- Extension --- Shortmail.php - shortmail.xml
Файл манифеста shortmail.xml
<?xml version="1.0" encoding="utf-8"?> <extension type="plugin" group="system" method="upgrade"> <name>plg_system_shortmail</name> <author>Alexandr Novikov</author> <creationDate>2025-01-20</creationDate> <copyright>(C) 2025 Alexandr Novikov. All rights reserved.</copyright> <license>GNU General Public License version 2 or later</license> <authorEmail>support@joomlab.ru</authorEmail> <authorUrl>https://joomlab.ru</authorUrl> <version>1.0.0</version> <description><![CDATA[Дополнительные поля пользователей в шаблоне письма]]></description> <namespace path="src">joomLab\Plugin\System\Shortmail</namespace> <files> <folder plugin="shortmail">services</folder> <folder>src</folder> <folder>language</folder> </files> <languages folder="language"> <language tag="en-GB">en-GB/plg_system_shortmail.ini</language> <language tag="en-GB">en-GB/plg_system_shortmail.sys.ini</language> </languages> <config> <fields name="params"> <fieldset name="basic"> <!-- Add your plugin parameters here --> </fieldset> </fields> </config> </extension>
provider.php
<?php /** * @package joomLab.Plugin * @subpackage System.Shortmail * * @copyright (C) 2025 Alexandr Novikov. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ \defined('_JEXEC') or die; use Joomla\CMS\Extension\PluginInterface; use Joomla\CMS\Factory; use Joomla\CMS\Plugin\PluginHelper; use Joomla\DI\Container; use Joomla\DI\ServiceProviderInterface; use Joomla\Event\DispatcherInterface; use joomLab\Plugin\System\Shortmail\Extension\Shortmail; return new class () implements ServiceProviderInterface { public function register(Container $container): void { $container->set( PluginInterface::class, function (Container $container) { $plugin = new Shortmail( $container->get(DispatcherInterface::class), (array) PluginHelper::getPlugin('system', 'shortmail') ); $plugin->setApplication(Factory::getApplication()); return $plugin; } ); } }
shortmail.php
<?php /** * @package joomLab.Plugin * @subpackage System.Shortmail * * @copyright (C) 2025 Alexandr Novikov. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace joomLab\Plugin\System\Shortmail\Extension; defined('_JEXEC') or die; use Joomla\CMS\Plugin\CMSPlugin; use Joomla\Event\SubscriberInterface; use Joomla\Event\Event; use Joomla\CMS\Event\Mail\BeforeRenderingMailTemplateEvent; use Joomla\Database\DatabaseInterface; use \Joomla\Component\Fields\Administrator\Helper\FieldsHelper; use Joomla\CMS\Factory; class Shortmail extends CMSPlugin implements SubscriberInterface { protected $autoloadLanguage = true; public static function getSubscribedEvents(): array { return [ 'onMailBeforeRendering' => 'onMailBeforeRendering', 'onMailBeforeTagsRendering' => 'onMailBeforeTagsRendering' ]; } public function onMailBeforeRendering(BeforeRenderingMailTemplateEvent $event): void { $templateId = $event->getTemplateId(); $app = Factory::getApplication(); $activiti = '1'; if($templateId == 'com_users.registration.admin.verification_request') { $activiti = $app->input->get('token', '', 'string'); $db = Factory::getContainer()->get(DatabaseInterface::class); $db->setQuery("SELECT `id` FROM `#__users` WHERE `activation` = '{$activiti}'"); $user_id = $db->loadResult(); } $user_fields = FieldsHelper::getFields('com_users.user', ['id' => $user_id], true); $template = $event->getTemplate(); foreach($user_fields as $field) { $data[$field->name] = $field->value; } $template->addTemplateData($data); } public function onMailBeforeTagsRendering(Event $event): void { $template = $event->getArgument(1); $tags = $template->params['tags']; $user_fields = FieldsHelper::getFields('com_users.user', true); $newtags = []; foreach($user_fields as $field) { $newtags[] = $field->name; } $tags = array_merge($tags, $newtags); $template->params['tags'] = $tags; } }
Контекст
Чуть не забыл про контекст. Структура шаблонов писем отличается от общей структуры CMS, где, помимо триггеров, в основном используется еще и $context для вывода плагина в нужном месте компонента.
В шаблонах писем вместо контекста используется template_id
$templateId = $event->getTemplateId(); //com_users.registration.admin.verification_request

Используя template_id вы сможете добавить в разные шаблоны писем свои наборы полей.
Заключение
С помощью этого плагина вы сможете добавить в шаблоны писем поля пользователя. А на примере этого плагина вы сможете добавить в свои шаблоны писем абсолютно любые переменные (шорт-коды), при чем в разных шаблонах они могут быть разные!
Удачи!
