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

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

Однако на деле это не так просто реализовать, т.к. нужно лезть в компонент, отвечающий за вывод универсальных списков и править ссылку там.
Поэтому мы пойдём иным путём — через Javascript будем открывать страницу в слайдере, используя битриксовую библиотеку SidePanel.
Сделать это можно двумя способами — в init.php и своём модуле. Также необходимо зарегистрировать свою JS-библиотеку.
И хотя второй способ более удобен, я покажу вам именно первый, а в конце статьи дам ссылку на свой модуль.
Итак, поехали. Все действия нужно выполнять в папке local.
Для начала нужно создать отдельную папку, где будет храниться наша библиотека. Назовём её, к примеру, viewer, и будет она иметь следующую структуру:
Здесь немного остановимся. Для php-кода я создал отдельный файл, который потом подключу в init.php, чтобы не засорять последний.
Давайте теперь зарегистрируем нашу библиотеку с помощью метода старого ядра CJSCore::RegisterExt:
Осталось только подключить данную библиотеку на странице универсальных списков методом CJSCore::Init, и, казалось бы, дело в шляпе — можно приступать к написанию самой библиотеки.
Однако не всё так просто, т.к. перед подключением необходимо проверить, что мы находимся на нужной странице. Делать это лучше с помощью регулярных выражений, т.к. id списка в адресе может меняться
Итак, библиотеку подключили, осталось её написать. Для этого создаём файл viewer.js (если ранее не создали) и первым делом объявляем неймспейс с помощью функции BX.namespace:
Теперь все переменные и функции можно объявлять следующим способом:
Чтобы не писать весь код в одной функции, разобьём её для удобства на более мелкие.
Пе��вым делом нам необходимо найти на странице узел, содержащий ссылку на детальную страницу. Для этого воспользуемся функцией BX.findChildren, которая должна вернуть нам список всех объектов, содержащих ссылки на детальную страницу:
Заодно напишем функцию, которая будет извлекать id списка и элемента из ссылки для дальнейшей работы:
Вернёмся к BX.findChildren. Особенность данной функции в том, что она возвращает список всех объектов с указанным css-классом, и не факт, что это будет ссылка. Поэтому нам нужно выполнить проверку, и уже только после этого отменять событие открытия ссылки и открывать слайдер:
Нам осталось написать последнюю функцию, которая будет открывать слайдер. Для этого задействуем библиотеку SidePanel:
Ну что же, библиотека написана, осталось вызвать функцию init после подключения. Для этого вернёмся в include.php, где проверяется адрес страницы:
Остался последний штрих — подключить наш код в init.php:
Если всё сделано правильно, то при нажатии на элемент универсального списка откроется слайдер:


В заключении, как и обещал, ссылка на модуль, реализующий то же самое.
Спасибо за внимание.
Если вы когда-либо работали с универсальными списками в Битрикс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;
}
Если всё сделано правильно, то при нажатии на элемент универсального списка откроется слайдер:


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