Привет, Хабр. Недавно я написал целый цикл статей по работе со смарт‑процессами, помогающий погрузить «непосвященного» человека в азы API коробочной версии, реализующего возможности управления смарт‑процессами и связанными с ними элементами. В рамках последней статьи, разъясняющей применение обработчиков события, от слушателей и интересующихся получил в личку много вопросов, связанных в целом с применением обработчиков событий в Битрикс24. В сегодняшней статье рассмотрим один из популярных практических кейсов — реализовать возможность управлять какими‑либо настройками обработчиков события из стандартной панели администратора Битрикс24.
Я понимаю, что руководств по сборке модулей Битрикс и на Хабре и в официальной документации достаточно много. Но на мой взгляд основная проблема таких руководств — это их объем (автор руководства пытается расписать все возможные файлы в составе модуля), что для начинающего неподготовленного пользователя (вспоминаю тут себя в начале карьеры Битрикс разработчика) может быть трудным к пониманию. Я ни в коем случае не утверждаю, что авторы данных материалов написали их плохо, на определенном этапе развития специалистам нужны и полные материалы целиком, поэтому своих читателей такие материалы конечно же найдут. Мои же статьи в данном цикле рассчитаны на аудиторию, только начинающую делать первые шаги в Битрикс разработке, потому упрощение пойдет им определенно на пользу.
Если что, основная статья начинается отсюда :-)
В сегодняшней статье‑туториале мы соберем модуль для Битрикс24, реализующий добавление и удаление обработчиков события, а также страницу настроек, позволяющую включать и отключать эти обработчики для определенных воронок сделки. Многоязычность пока для простоты понимания реализовывать не будем. Может, разберем данный вопрос в следующих статьях. Пишите в комментариях, если хотите что‑то разобрать в следующих статьях цикла.
Если обратиться к принятым стандартам разработки BitrixFramework, системные модули и модули, загружаемые из 1С‑Битрикс: Маркетплейс, располагаются в папке /bitrix/modules/. У нас же модуль полностью кастомный, его структуру мы будем создавать в папке /local/modules/.
Начинаем создание модуля
Как корабль назовешь — так он и поплывет пойдет, говорят моряки. Перед тем как собирать вместе файлы модуля, нам нужно определиться с его техническим названием, которое будет его однозначно идентифицировать в системе. Раз основная задача — научиться оперировать обработчиками события через модуль — назовем его learnevents.
Соответственно создаем папку для нашего модуля по пути /local/modules/learnevents.
Разбирать полный состав файлов модуля мы не будем, писал выше почему. Далее будем создавать минимальный набор файлов, который нам понадобится для решения нашей задачи.
Папка install
Первой создаем папку /local/modules/learnevents/install. В ней будут находиться файлы, отвечающие за установку и удаление нашего модуля.
Первым в папке создаем файл /local/modules/learnevents/install/version.php следующего содержания:
<?php
$arModuleVersion = [
'VERSION' => '1.0.0',
'VERSION_DATE' => '2025-05-13',
];
Нетрудно догадаться, что в данном файле мы указываем текущую версию модуля и дату его выпуска. Можно указать в принципе любые подходящие по формату данные.
Далее создаем основной файл установщика модуля /local/modules/learnevents/install/index.php. В коде будут поясняющие комментарии:
<?php
use Bitrix\Main\ModuleManager;
//Основной класс установщика модуля, должен наследоваться от системного класса CModule
class learnevents extends CModule
{
//Системный идентификатор модуля, должен совпадать с названием класса
public $MODULE_ID = 'learnevents';
//Версия модуля и дата. Оставляем пустыми так как подгрузим нужные в конструкторе
public $MODULE_VERSION;
public $MODULE_VERSION_DATE;
//Название модуля, выводимое в админ панели
public $MODULE_NAME = 'Управляемые обработчики';
//Описание модуля, выводимое в админ панели
public $MODULE_DESCRIPTION = 'Тестовый модуль, показывающий возможности управления обработчиками события';
//Адрес сайта разработчика модуля
public $PARTNER_URI = 'https://site.ru';
//Не использовать права доступа к модулю на основе групп пользователей
public $MODULE_GROUP_RIGHTS = 'N';
//Основной конструктор класса, в котором мы читаем файл version.php и берем оттуда версию модуля и дату релиза
public function __construct()
{
$arModuleVersion = [];
include __DIR__ . '/version.php';
if (is_array($arModuleVersion) && array_key_exists('VERSION', $arModuleVersion)) {
$this->MODULE_VERSION = $arModuleVersion['VERSION'];
$this->MODULE_VERSION_DATE = $arModuleVersion['VERSION_DATE'];
}
}
//Метод, устанавливающий обработчики события при установке модуля
public function InstallEvents()
{
$eventManager = \Bitrix\Main\EventManager::getInstance();
$eventManager->registerEventHandler("crm",
"OnBeforeCrmDealAdd",//Событие, на которое вешаем обработчик
$this->MODULE_ID,//Идентификатор модуля, берем текущий
"Learn\\Event\\Main",//PHP Класс с неймспейсом, в котором реализован метод обработчика
"RunEvent"//Название метода-обработчика
);
//Далее код других обработчиков
return true;
}
//Метод, удаляющий обработчики события при удалении модуля
public function UnInstallEvents()
{
$eventManager = \Bitrix\Main\EventManager::getInstance();
$eventManager->unRegisterEventHandler("crm",
"OnBeforeCrmDealAdd",//Событие, на которое вешаем обработчик
$this->MODULE_ID,//Идентификатор модуля, берем текущий
"Learn\\Event\\Main",//PHP Класс с неймспейсом, в котором реализован метод обработчика
"RunEvent"//Название метода-обработчика
);
//Далее код других обработчиков
return true;
}
//Метод, запускающийся при установке модуля
public function DoInstall()
{
$this->InstallEvents();//Запускаем установку обработчиков
ModuleManager::registerModule($this->MODULE_ID);//Регистрируем модуль в системе как установленный
}
//Метод, запускающийся при удалении модуля
public function DoUninstall()
{
$this->UnInstallEvents();//Запускаем удаление обработчиков
ModuleManager::unRegisterModule($this->MODULE_ID);//Снимаем регистрацию модуля в системе
}
}
Далее добавляем файл /local/modules/learnevent/install/unstep1.php. Он будет отвечать за страничку, которая появляется перед удалением модуля в панели администратора:
<?
//Если пользователь не авторизован, прерываем метод
if(!check_bitrix_sessid()) return;
echo CAdminMessage::ShowNote('Управляемые обработчики');
?>
<form action="<?=$APPLICATION->GetCurPage(); ?>">
<?=bitrix_sessid_post(); ?>
<input type="hidden" name="step" value="2">
<input type="hidden" name="id" value="learnevent">
<input type="hidden" name="uninstall" value="Y">
<input type="submit" name="nextstep" value="Далее">
<input type="submit" name="cancel" value="Отменить">
</form>
Часто на данную страничку добавляют сообщения для тех, кто собирается удалить модуль, например «Пожалуйста, не удаляйте! Мы еще можем быть полезны».:-)
Папка lib
В данной папке традиционно размещаются классы модуля, которые будут автоматически подключены при его установке. В нашем случае создаем файл /local/modules/learnevent/lib/main.php:
namespace Learn\Event;
use Bitrix\Main\Config\Option;
class Main
{
/**
* @return array|false Массив направлений сделки
* @description Возвращает массим текущих направлений сделок, поднадобится в админке
*/
public static function getDealFunnels()
{
//Проверяем активен ли модуль CRM. Без него никакие Сделки доступны не будут
if (\Bitrix\Main\Loader::includeModule('crm')) {
//Получаем ООП Фабрику работы со сделками
$factory = \Bitrix\Crm\Service\Container::getInstance()->getFactory(
\CCrmOwnerType ::Deal
);
//Получаем массив всех объектов направлений и делаем перебор
$categories = $factory->getCategories();
$arCategories = [];
foreach ($categories as $category) {
//Получаем данные каждого направления и записываем в массив
$arCategories[] = $category->getData();
}
return $arCategories;
} else {
return false;
}
}
/**
* @return array|false Результат обработчика
* @description Обработчик, который мы подключаем через модуля
*/
public static function RunEvent(&$arFields)
{
//Проверяем, установлен ли флажок "Активно" в админке
if (Option::get("learnevent", "ACTIVE") == "Y") {
global $USER;
//Получаем список пользователей
$userExeptions = unserialize(Option::get("learnevent", "USER_EXEPTIONS"));
$pipeline = Option::get("learnevent", "PIPELINE");
if ((is_array($userExeptions) && in_array($USER->GetID(), $userExeptions)) || $userExeptions == $USER->GetID()) {
if (isset($arFields['CATEGORY_ID']) && !empty($arFields['CATEGORY_ID']) && ($arFields['CATEGORY_ID'] == $pipeline)) {
//Какие-то действия, если наше условие сработало. Например прерываем создание Сделки
$arFields['RESULT_MESSAGE'] = "Извините, данному пользователю запрещено создавать Сделку в данной воронке!";
return false;
}
}
}
}
}
В данном файле можно разместить методы для других обработчиков. Также никто не запрещает повесить несколько разных методов на одно событие.
Корневая папка модуля
Теперь перейдем к одному из важнейших файлов, который реализует окно настроек модуля в панели администрирования Битрикс24. Этот файл по стандарту лежит в папке модуля и носит название options.php. В нашем случае создаем его по пути /local/modules/learnevent/options.php и добавляем туда код:
<?php
//Подключаем нужные нам неймспейсы и скрипты
use \Bitrix\Main\Loader,
\Bitrix\Main\Config\Option;
require_once $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_admin_before.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_admin_after.php';
global $APPLICATION;
$moduleName = 'learnevent';
if (!Loader::IncludeModule($moduleName)) {
return;
}
//Проверяем права доступа, если их нет - перекидываем на форму авторизации
$POST_RIGHT = $APPLICATION->GetGroupRight($moduleName);
if ($POST_RIGHT == "D")
$APPLICATION->AuthForm(GetMessage("ACCESS_DENIED"));
//Получаем данные POST Запроса если настройки изменены, проводим фильтрацию данных для безопасности
$request = Bitrix\Main\Application::getInstance()->getContext()->getRequest();
$request->addFilter(new \Bitrix\Main\Web\PostDecodeFilter);
//Сохраняем настройки модуля в системные опции (таблица b_option в БД)
if ($POST_RIGHT == "W" && check_bitrix_sessid() && $request->isPost()) {
COption::SetOptionString($moduleName, 'ACTIVE', $request->get('ACTIVE'));
COption::SetOptionString($moduleName, 'PIPELINE', $request->get('PIPELINE'));
//Так как у нас может быть множественное значение а база можно хранить только единицное - делаем сериализацию массива в строку
COption::SetOptionString($moduleName, 'USER_EXEPTIONS', serialize($request->get('USER_EXEPTIONS')));
}
//Инициализируем табы админки, у нас таб будет один
$tabs = [
[
'DIV' => 'main',
'TAB' => 'Настройки',
'ICON' => '',
]
];
$tabControl = new \CAdminTabControl('tabControl', $tabs);
//Далее добавляем форму с полями настроек, которые будут выводиться в админке
?>
<form method="POST"
action="<? echo $APPLICATION->GetCurPage() ?>?mid=<?= urlencode($mid) ?>&lang=<? echo LANGUAGE_ID ?>"
ENCTYPE="multipart/form-data" name="post_form">
<?= bitrix_sessid_post() ?>
<?php
$tabControl->Begin();
$tabControl->BeginNextTab();
?>
<?//Поле "Обработчик активен"?>
<tr>
<td>Обработчик активен</td>
<td><input type="checkbox" name="ACTIVE"
value="Y" <?php if (Option::get($moduleName, 'ACTIVE') == "Y") echo 'checked'; ?>/></td>
</tr>
<?//Поле "Воронка Сделки". Далее получаем массив воронок и текущую настройку?>
<tr>
<?php $allCategories = \Learn\Event\Main::getDealFunnels(); ?>
<?php $currentCategory = Option::get($moduleName, 'PIPELINE'); ?>
<td>Воронка Сделки</td>
<td>
<select name="PIPELINE">
<?php foreach ($allCategories as $allCategory) { ?>
<option value="<?= $allCategory['ID'] ?>"<?php if ($allCategory['ID'] == $currentCategory) echo ' selected'; ?>><?= $allCategory['NAME'] ?></option>
<?php } ?>
</select>
</td>
</tr>
<?//Поле "Для пользователей". Множественный выбор пользователей для которых будет срабатывать обработчик.?>
<tr>
<?php $userCurId = unserialize(Option::get($moduleName, 'USER_EXEPTIONS')); ?>
<?php $order = array('sort' => 'asc');
$tmp = 'sort'; // параметр проигнорируется методом, но обязан быть
$rsUsers = \CUser::GetList($order, $tmp);
$allUsers = [];
while ($arUser = $rsUsers->Fetch()) {
$allUsers[$arUser["ID"]] = "[" . $arUser["ID"] . "] " . $arUser["NAME"] . " " . $arUser["LAST_NAME"];
} ?>
<td>Для пользователей</td>
<td>
<select name="USER_EXEPTIONS[]" multiple>
<?php foreach ($allUsers as $userID => $userName) { ?>
<option value="<?= $userID ?>"<?php if ((is_array($userCurId) && in_array($userID, $userCurId)) || $userCurId == $userID) echo ' selected'; ?>><?= $userName ?></option>
<?php } ?>
</select>
</td>
</tr>
<? $tabControl->End(); ?>
<input type="submit" value="Сохранить">
</form>
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/epilog_admin.php';
?>
Данный файл реализует стандартную страницу настроек нашего модуля в разделе панели администратора Битрикс (Настройки — Настройки продукта — Настройки модулей — Управляемые обработчики).
Вместо заключения
Выше мы разобрали минимально необходимый набор файлов, реализующих модуль для Битрикс24, устанавливающий обработчики события, и хранящий настройки для них. Модуль содержит минимальный набор системных методов для того, чтобы Битрикс воспринимал его именно как модуль, а не как набор скриптов непонятного назначения. Всем спасибо за внимание и жду нашей встречи в следующих статьях!
Если вы работаете с коробочной версией Битрикс24 и хотите глубже разобраться в механике событий — их типах, регистрации и возможностях для управления сущностями, обратите внимание на предстоящий открытый урок «События в Битрикс24: Путеводитель по изменениям».
Разберёмся в архитектуре событийной модели и обсудим, как эффективно внедрять собственные сценарии обработки изменений. Старт 21 мая в 20:00 — участие бесплатное, нужно только зарегистрироваться.
А в календаре мероприятий можно записаться на открытые уроки по другим ИТ-направлениям.