Меняем страницу просмотра элемента универсальных списков в коробочном Битрикс24

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Всем привет.

Если вы когда-либо работали с универсальными списками в Битрикс24, то, наверное, в курсе, что страница детального просмотра элемента полностью идентична странице редактирования. Единственное отличие — если у пользователя права только на чтение, то на странице не будет кнопок «Сохранить» и «Применить». Согласитесь, не самый приятный интерфейс.



И поэтому когда на работе возникла необходимость использования универсальных списков, я решил поменять страницу детального просмотра, благо мы используем коробку, и возможности для кастомизации просто неограниченные.

ВАЖНОЕ ПРЕДУПРЕЖДЕНИЕ

Создатели Битрикс24 крайне не рекомендуют менять интерфейс корпоративного портала, поскольку в коробке публичная часть приравнивается к ядру, и при обновлении системы есть риск, что все ваши модификации будут стёрты.

Если же вы решите скопировать нужные шаблоны в папку local и издеваться над ними как хотите, то при обновлении они затронуты не будут. Но и новые фичи к ним тоже не будут применены.

Поэтому единственно верный способ в данном случае — это модификация DOM-дерева через Javascript.

По сути нам нужно всего-то подменить ссылку на детальную страницу в таблице списка:



Однако на деле это не так просто реализовать, т.к. нужно лезть в компонент, отвечающий за вывод универсальных списков и править ссылку там.

Поэтому мы пойдём иным путём — через Javascript будем открывать страницу в слайдере, используя битриксовую библиотеку SidePanel.

Сделать это можно двумя способами — в init.php и своём модуле. Также необходимо зарегистрировать свою JS-библиотеку.

И хотя второй способ более удобен, я покажу вам именно первый, а в конце статьи дам ссылку на свой модуль.

Итак, поехали. Все действия нужно выполнять в папке local.

Для начала нужно создать отдельную папку, где будет храниться наша библиотека. Назовём её, к примеру, viewer, и будет она иметь следующую структуру:

/viewer 
-/js
--viewer.js // наша js-библиотека
-include.php // файл, который мы будем подключать в init.php

Здесь немного остановимся. Для php-кода я создал отдельный файл, который потом подключу в init.php, чтобы не засорять последний.

Давайте теперь зарегистрируем нашу библиотеку с помощью метода старого ядра CJSCore::RegisterExt:

// include.php

// т.к. в битриксе есть js-библиотека viewer, то нужно использовать другое название
CJSCore::RegisterExt('elementviewer', [ 
    'js' => '/local/viewer/js/viewer.js', // путь к библиотеке
    'rel' => ['SidePanel'] // слайдер
]);

Осталось только подключить данную библиотеку на странице универсальных списков методом CJSCore::Init, и, казалось бы, дело в шляпе — можно приступать к написанию самой библиотеки.

Однако не всё так просто, т.к. перед подключением необходимо проверить, что мы находимся на нужной странице. Делать это лучше с помощью регулярных выражений, т.к. id списка в адресе может меняться

// include.php

$pattern = '/\/lists\/(\d+)\/view\//'; // часть адреса страницы универсального списка, где (\d+) = id списка
$server = Bitrix\Main\Context::getCurrent()->getServer(); // объект Server, потребуется для получения адреса страницы

if(preg_match($pattern, $server->getRequestUri())) {
       CJSCore::Init(['elementviewer']); // подключаем библиотеку
}

Итак, библиотеку подключили, осталось её написать. Для этого создаём файл viewer.js (если ранее не создали) и первым делом объявляем неймспейс с помощью функции BX.namespace:

const ElementViewer = BX.namespace('Viewer');

Теперь все переменные и функции можно объявлять следующим способом:

ElementViewer.init = function() {

}

Чтобы не писать весь код в одной функции, разобьём её для удобства на более мелкие.

Первым делом нам необходимо найти на странице узел, содержащий ссылку на детальную страницу. Для этого воспользуемся функцией BX.findChildren, которая должна вернуть нам список всех объектов, содержащих ссылки на детальную страницу:

ElementViewer.findCell = function () {
    return BX.findChildren(document, {
        class: 'main-grid-cell-content' // css-класс узла с искомой ссылкой
    }, true);
}

Заодно напишем функцию, которая будет извлекать id списка и элемента из ссылки для дальнейшей работы:

ElementViewer.pattern = '/lists/(\\d+)/element/0/(\\d+)'; // часть адреса страницы детального просмотра элемента универсального списка, где первый шаблон = id списка, а второй = id элемента.

ElementViewer.extractListData = function (url) {
    let match = url.match(this.pattern); // проверяем ссылку регуляркой
    if(match) {
        return {
            list_id: Number(match[1]),
            element_id: Number(match[2])
        };
    }
}

Вернёмся к BX.findChildren. Особенность данной функции в том, что она возвращает список всех объектов с указанным css-классом, и не факт, что это будет ссылка. Поэтому нам нужно выполнить проверку, и уже только после этого отменять событие открытия ссылки и открывать слайдер:

ElementViewer.init = function (sliderUrl) {

    const cell = this.findCell();

    cell.forEach(item => {

        let itemChild = item.children;
        let child = itemChild[0];

        if(child && child.tagName === 'A') {
            const listData = this.extractListData(child.toString()); // получаем id списка и элемента из ссылки

            if(listData !== undefined) {
                child.addEventListener('click', (e) => {
                    e.preventDefault(); // отменяем переход по ссылке
                    this.openSlider(sliderUrl, listData.list_id, listData.element_id); // открываем слайдер
                })
            }
        }
    });

}

Нам осталось написать последнюю функцию, которая будет открывать слайдер. Для этого задействуем библиотеку SidePanel:

ElementViewer.openSlider = function (sliderUri, listId, elementId) {

// в слайдер можно передавать данные методом POST, поэтому воспользуемся этой возможностью для передачи id списка и элемента
    let sliderParams = {
        list_id: listId,
        element_id: elementId
    }

    return BX.SidePanel.Instance.open(sliderUri, {
        allowChangeHistory: false,
        cacheable: false,
        requestMethod: 'POST',
        requestParams: sliderParams
    });
}

Ну что же, библиотека написана, осталось вызвать функцию init после подключения. Для этого вернёмся в include.php, где проверяется адрес страницы:

if(preg_match($pattern, $server->getRequestUri())) {
       CJSCore::Init(['elementviewer']); // подключаем библиотеку
       $asset = Bitrix\Main\Page\Asset::getInstance();
       $script = '<script>BX.ready(function() {
  ElementViewer.init();
})</script>';
      $asset->addString($script);
}

Остался последний штрих — подключить наш код в init.php:

// init.php

$file = $_SERVER['DOCUMENT_ROOT'] . '/local/path/to/viewer/include.php';

if(file_exists($file)) {
   require $file;
}

Если всё сделано правильно, то при нажатии на элемент универсального списка откроется слайдер:





В заключении, как и обещал, ссылка на модуль, реализующий то же самое.

Спасибо за внимание.
Источник: https://habr.com/ru/post/518214/

Интересные статьи

Интересные статьи

Предыстория Когда-то у меня возникла необходимость проверять наличие неотправленных сообщений в «1С-Битрикс: Управление сайтом» (далее Битрикс) и получать уведомления об этом. Пробле...
Американский стартап Nano Diamond Battery представил прототип бета-гальванической батареи, которая способна проработать тысячи лет. Это не теория, сейчас разработку переводят на к...
Как быстро определить, что на отдельно взятый сайт забили, и им никто не занимается? Если в подвале главной страницы в копирайте стоит не текущий год, а старый, то именно в этом году опека над са...
Компании растут и меняются. Если для небольшого бизнеса легко прогнозировать последствия любых изменений, то у крупного для такого предвидения — необходимо изучение деталей.
Приступая к животрепещущей теме резервного копирования на «Битрикс», прежде всего хотелось бы поблагодарить разработчиков, реализовавших автоматическое резервное копирование в облачное хранилище в вер...