Атипичная встройка функционала

Кастомизация существующего продукта — одна из основных забот разработчика, которая зачастую может принести немало сложностей и проблем. Рассмотрим способ, который позволяет мне выполнять такие задачи не затрагивая существующие компоненты системы.

В Bitrix24 существует множество способов модифицировать или расширить существующий функционал. Самый распространенный способ — это перенос/дублирование компонента или его шаблона с последующим внесением изменений.

Подход допустим и вполне «легален», но обязывает вас следить за корректностью и актуальностью компонентов после обновления системы. Например:

  • мы вынесли компонент crm.deal.detail;
  • Bitrix создали новую фичу и добавили её в этот компонент;
  • Обновляем Bitrix;
  • В лучшем случае фичи нет.

Почему? Да потому что при обновлении вынесенные компоненты не были изменены и теперь нам нужно вспомнить, по какой причине мы выносили компонент и перенести эту «причину» в обновленный компонент. Думаю, уже стало понятно, что этот процесс одним разом не ограничивается и придется повторять его всякий раз после подобного обновления системы.

Если нас сильно не устраивает существующий функционал, то можно прибегнуть к описанному выше способу (по сути создать свой собственный компонент и обновлять его функционал из соображений необходимости). Но что если нужно всего лишь добавить какое-то содержимое, изменить поведение описанное в js скриптах, css стили или что-то скрыть/показать на странице портала по какому-нибудь дополнительному условию?

Задача

С помощью css изменим положение блоков в crm сделках и визуально скроем timeline компонент.

Шаг 1. Концепция

После загрузки нужной нам страницы выполняем действия, требуемые для достижения результата. Внедрение кода осуществляем по событию OnEpilog модуля main. Это событие вызывается в конце визуальной части эпилога, что позволит нам удобно внедрить код.

Шаг 2. Обработчик события

Создадим класс Hadler.php в пространстве имён Aclips\CustomCrm и добавим в него метод loadCustomExtension. Этот метод будет отвечать за проверку страницы и загрузку расширений.

<?php

namespace Aclips\CustomCrm;

/**
 * Класс обработчиков событий
 */
class Handler
{
    /**
     * Загрузка js компонентов
     */
    public static function loadCustomExtension()
    {
		// Код обработчика
    }
}

В файле событий (в нашем случае event.php) подпишемся на OnEpilog

 <?php

$eventManager = \Bitrix\Main\EventManager::getInstance();

$eventManager->addEventHandler(
    "main",
    "OnEpilog", [
        "\\Aclips\\CustomCrm\\Handler",
        "loadCustomExtension"
    ]
);

Теперь при загрузке каждой страницы будет вызываться метод loadCustomExtension.

Шаг 3. Проверка страниц

Так как по условиям задачи изменения вносятся только на страницу просмотра сделок, наш обработчик сперва должен определить, находимся ли мы на нужной странце. Проверим вхождение подстроки /crm/deal/details/ в адресной строке и отобразим слово «работает», для проверки работоспособности.

В некоторых случаях этого будет недостаточно. Могут потребоваться другие проверки, например: является ли запрос к странице Ajax’овым.

    /**
     * Загрузка js компонентов
     */
    public static function loadCustomExtension()
    {
        global $APPLICATION;

        $currentDirectory = $APPLICATION->getCurDir();

        if (mb_strpos($currentDirectory, '/crm/deal/details/') !== false) {
            print "работает";
        }

    }

На странице детального просмотра сделки в самом низу должна появится наша надпись

Шаг 4. Создание расширения

Механизм встройки готов и работает. Остаётся описать те изменения, которые мы хотим добавить на страницу. Способ создания собственного расширения (js extension) изложен в статье Select в стиле bitrix24.

Файл config.php

<?php
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) {
    die();
}

return array(
    "js" => [
        "./script.js",
    ],
    'css' => [
        "./style.css"
    ]
);

Файл стилей style.css

.crm-entity-card-container {
    width: 100%;
}

.crm-entity-card-container-content .ui-entity-editor-column-content {
    display: grid;
    grid-template-columns: 41.6% 54.4%;
    align-content: space-around;
    justify-content: space-between;
}

Файл скрипта script.js

Для «демонстрации смысла» немного усложним логику удаления timeline компонента. Будем дожидаться загрузки определенного контента на странице, после чего найдём и удалим контейнер с компонентом.

(function () {
    BX.ready(function () {
        let target = document.querySelector('body');
        let observer = new MutationObserver(function () {
            let entityDetailContainer = BX.findChildByClassName(document, 'ui-entity-editor-content-block', true)

            if (entityDetailContainer) {
                let streamContainer = document.querySelector('.crm-entity-stream-container')
                if(streamContainer){
                    streamContainer.remove()
                }
                observer.disconnect()
            }
        });

        observer.observe(target, {attributes: true, characterData: true, childList: true, subtree: true});
    })
})()

Добавляем код для загрузки расширения в обработчике событий и проверяем работоспособность

    /**
     * Загрузка js компонентов
     */
    public static function loadCustomExtension()
    {
        global $APPLICATION;

        $currentDirectory = $APPLICATION->getCurDir();

        if (mb_strpos($currentDirectory, '/crm/deal/details/') !== false) {
            \Bitrix\Main\UI\Extension::load('aclips.custom_crm');
        }
    }

Итог

Таким образом можно изменять внешний вид шаблонов, существующее поведение на страницу, добавлять новый функционал (добавить какой-либо элемент управления в область не предназначенную для отложенных функций) без особых «потрясений» для системы. Вы всегда будете знать где находится ваш код, а Bitrix всегда сможет спокойно обновиться.

2 комментария

  1. добрый день!

    отличный контент, который сильно помогает разобраться особенно на начальной стадии осваивания б24. спасибо!

    общее пожелание/предложение к оформлению — в блоки кода первой строкой добавлять полный path файла, к которому относится тот или иной код, чтоб отслеживать текущий контекст.

Добавить комментарий