Шаблоны писем в 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
вы сможете добавить в разные шаблоны писем свои наборы полей.
Заключение
С помощью этого плагина вы сможете добавить в шаблоны писем поля пользователя. А на примере этого плагина вы сможете добавить в свои шаблоны писем абсолютно любые переменные (шорт-коды), при чем в разных шаблонах они могут быть разные!
Удачи!
Комментарии (6)
b2z
23.01.2025 09:49Спасибо за полезный материал!
Маленькая придирка по коду. Если ты в провайдере установил application, то в плагине его надо получать не через Factory, а через $this->getApplication().
codersite
Очень нужная статья, благодарю