Notifly в Joomla
В Joomla всё расширяется плагинами. Небольшой системный плагин подписывается
на события (onUserAfterSave, onContentAfterSave, onContactAfterSubmit) и
шлёт push в Notifly. Никаких сторонних библиотек не нужно — используется
встроенный HTTP-клиент фреймворка Joomla.
Функцию-helper notifly_send() берём общую — ту же, что и для остальных CMS.
Внутри плагина её проще завернуть в приватный метод, а токен и URL хранить в
параметрах плагина.
Структура плагина
Заголовок раздела «Структура плагина»plugins/system/notifly/├── notifly.xml└── src/Extension/Notifly.phpnotifly.xml — манифест с параметрами url и token:
<?xml version="1.0" encoding="utf-8"?><extension type="plugin" group="system" method="upgrade"> <name>System - Notifly</name> <author>Notifly</author> <version>1.0.0</version> <namespace path="src">Notifly\Plugin\System\Notifly</namespace> <files> <folder plugin="notifly">src</folder> </files> <config> <fields name="params"> <fieldset name="basic"> <field name="url" type="url" label="Notifly URL" default="https://your-notifly.example.com" /> <field name="token" type="text" label="App token" default="AGdjfk_L.dKe8q" /> </fieldset> </fields> </config></extension>Класс плагина
Заголовок раздела «Класс плагина»src/Extension/Notifly.php — здесь и живёт наш notifly_send() (как приватный
метод) и все подписки на события:
<?php
namespace Notifly\Plugin\System\Notifly\Extension;
\defined('_JEXEC') or die;
use Joomla\CMS\Plugin\CMSPlugin;use Joomla\CMS\Http\HttpFactory;use Joomla\CMS\Uri\Uri;
final class Notifly extends CMSPlugin{ /** Универсальный helper (та же логика, что в /cases/cms/). */ private function send(string $title, string $message, int $priority = 5): void { $url = $this->params->get('url'); $token = $this->params->get('token'); if (!$url || !$token) { return; }
try { HttpFactory::getHttp()->post( "$url/message?token=$token", json_encode([ 'title' => mb_substr($title, 0, 200), 'message' => mb_substr($message, 0, 1500), 'priority' => $priority, ], JSON_UNESCAPED_UNICODE), ['Content-Type' => 'application/json'], 5 ); } catch (\Throwable $e) { // Никогда не роняем запрос сайта из-за нотификации. } }
/** Новая регистрация или изменение пользователя. */ public function onUserAfterSave($user, $isNew, $success, $msg): void { if (!$success || !$isNew) { return; } $this->send( "👤 Новый пользователь: {$user['username']}", "Email: {$user['email']}\n" . "ID: {$user['id']}\n" . "Профиль: " . Uri::root() . "administrator/index.php?option=com_users&task=user.edit&id={$user['id']}", 4 ); }
/** Новая или изменённая статья / материал. */ public function onContentAfterSave($context, $article, $isNew): void { if ($context !== 'com_content.article') { return; } $action = $isNew ? 'создан' : 'обновлён'; $this->send( "📄 Материал $action: {$article->title}", "ID: {$article->id}\n" . "Категория: {$article->catid}\n" . "Открыть: " . Uri::root() . "administrator/index.php?option=com_content&task=article.edit&id={$article->id}", $isNew ? 5 : 4 ); }
/** Отправка формы через com_contact. */ public function onContactAfterSubmit($contact, $data): void { $this->send( "✉️ Обращение через контакт: {$contact->name}", "Тема: " . ($data['contact_subject'] ?? '—') . "\n" . "Сообщение:\n" . mb_substr($data['contact_message'] ?? '', 0, 1000), 7 ); }}Ошибки PHP
Заголовок раздела «Ошибки PHP»Системный плагин загружается очень рано, поэтому в нём же удобно перехватывать
фатальные ошибки через register_shutdown_function — например, в
onAfterInitialise:
public function onAfterInitialise(): void{ register_shutdown_function(function () { $err = error_get_last(); if ($err && \in_array($err['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) { $this->send( "🔥 Fatal: {$err['message']}", "Файл: {$err['file']}:{$err['line']}\n" . "URL: " . ($_SERVER['REQUEST_URI'] ?? '-'), 10 ); } });}Где включить плагин
Заголовок раздела «Где включить плагин»- Заархивируйте папку
notiflyв ZIP и установите через Система → Установка → Расширения. - Откройте Система → Плагины, найдите «System - Notifly», задайте URL и токен, включите плагин.
Системные плагины срабатывают на каждом запросе фронтенда и админки — этого достаточно, чтобы поймать все перечисленные события.
Тротлинг
Заголовок раздела «Тротлинг»Чтобы форма контактов или бот-регистрации не завалили канал, кэшируйте отправку
через встроенный \Joomla\CMS\Cache\Cache:
$cache = \Joomla\CMS\Factory::getCache('notifly', '');$key = 'throttle_' . md5($title);if ($cache->get($key)) { return;}$cache->store(1, $key);- Мгновенная реакция на заявки. Обращение через com_contact — push раньше, чем письмо дойдёт до почты.
- Контроль контента. Каждая правка материала видна редактору сразу.
- Дешёвый мониторинг ошибок без Sentry для небольшого Joomla-сайта.
Что улучшить дальше
Заголовок раздела «Что улучшить дальше»- Разные каналы Notifly для контента, пользователей и ошибок.
- Приоритеты по категориям материалов через параметры плагина.
- Передача
idстатьи/пользователя в extras для deep-link.