Просьба, которую я слышал от контент-менеджеров на каждом втором проекте: «сделай кнопку, чтобы из карточки товара сразу открыть его на сайте». Звучит как мелочь, но при работе с каталогом на несколько тысяч позиций копирование символьного кода и сборка URL руками съедают ощутимую часть рабочего дня.
Битрикс закрывает это одним событием — OnAdminContextMenuShow. Ниже минимальный рабочий вариант и версия, которую не стыдно оставить в проекте.
Задача
При редактировании элемента инфоблока в админке добавить кнопку для быстрого перехода на страницу элемента на сайте.
Решение
Событие OnAdminContextMenuShow вызывается при отрисовке контекстного меню любой админской страницы и получает массив пунктов по ссылке. Отсюда два следствия, которые определяют весь код ниже: обработчик срабатывает на всех страницах админки (поэтому первым делом фильтруем по текущему URL) и модифицировать нужно именно $items по ссылке, ничего не возвращая.
<?php
// /local/php_interface/init.php
use Bitrix\Main\EventManager;
use Bitrix\Main\Loader;
EventManager::getInstance()->addEventHandler(
'main',
'OnAdminContextMenuShow',
'addElementPublicLink'
);
function addElementPublicLink(&$items): void
{
global $APPLICATION;
// Только на странице редактирования элемента
if ($APPLICATION->GetCurPage() !== '/bitrix/admin/iblock_element_edit.php') {
return;
}
// Только при редактировании существующего элемента
$elementId = (int) ($_REQUEST['ID'] ?? 0);
$iblockId = (int) ($_REQUEST['IBLOCK_ID'] ?? 0);
if ($elementId <= 0 || $iblockId <= 0) {
return;
}
Loader::includeModule('iblock');
// Получаем URL страницы элемента
$element = \CIBlockElement::GetList(
[],
[
'IBLOCK_ID' => $iblockId,
'ID' => $elementId,
],
false,
['nTopCount' => 1],
['ID', 'NAME', 'DETAIL_PAGE_URL']
)->GetNext(true, false);
if (!$element || empty($element['DETAIL_PAGE_URL'])) {
return;
}
// Добавляем кнопку в меню
$items[] = [
'TEXT' => 'Открыть на сайте',
'TITLE' => 'Открыть страницу элемента в новой вкладке',
'LINK' => $element['DETAIL_PAGE_URL'],
'LINK_PARAM' => 'target="_blank"',
'ICON' => 'btn_view',
];
}Не забудьте про фильтр по странице. Без проверки GetCurPage() пункт меню полезет во все списки и формы админки, включая настройки модулей, — а $_REQUEST['ID'] там означает совсем другую сущность. Ссылка при этом соберётся «успешно» и будет вести в никуда.
GetList здесь вызывается с nTopCount => 1 и минимальным набором полей — при отрисовке меню лишние выборки бьют по времени отклика админки, а DETAIL_PAGE_URL Битрикс подставляет сам, разворачивая шаблон URL инфоблока. Флаги GetNext(true, false) отключают HTML-экранирование: URL идёт в атрибут ссылки, экранированные амперсанды в нём только мешают.
Расширенная версия
<?php
namespace Local\Admin;
use Bitrix\Main\EventManager;
use Bitrix\Main\Loader;
class ElementLinkExtension
{
/**
* Регистрация расширения
*/
public static function register(): void
{
EventManager::getInstance()->addEventHandler(
'main',
'OnAdminContextMenuShow',
[self::class, 'addLinks']
);
}
/**
* Добавление ссылок
*/
public static function addLinks(&$items): void
{
global $APPLICATION;
$page = $APPLICATION->GetCurPage();
// Страница элемента
if ($page === '/bitrix/admin/iblock_element_edit.php') {
self::addElementLink($items);
}
// Страница раздела
if ($page === '/bitrix/admin/iblock_section_edit.php') {
self::addSectionLink($items);
}
}
/**
* Ссылка на элемент
*/
private static function addElementLink(array &$items): void
{
$elementId = (int) ($_REQUEST['ID'] ?? 0);
$iblockId = (int) ($_REQUEST['IBLOCK_ID'] ?? 0);
if ($elementId <= 0) {
return;
}
Loader::includeModule('iblock');
$element = \CIBlockElement::GetList(
[],
['IBLOCK_ID' => $iblockId, 'ID' => $elementId],
false,
['nTopCount' => 1],
['ID', 'NAME', 'CODE', 'DETAIL_PAGE_URL', 'IBLOCK_SECTION_ID']
)->GetNext(true, false);
if (!$element) {
return;
}
// Основная ссылка
if (!empty($element['DETAIL_PAGE_URL'])) {
$items[] = [
'TEXT' => 'Открыть на сайте',
'TITLE' => $element['NAME'],
'LINK' => $element['DETAIL_PAGE_URL'],
'LINK_PARAM' => 'target="_blank"',
'ICON' => 'btn_view',
];
}
// Ссылка на раздел элемента
if ($element['IBLOCK_SECTION_ID'] > 0) {
$section = \CIBlockSection::GetByID($element['IBLOCK_SECTION_ID'])->GetNext();
if ($section && !empty($section['SECTION_PAGE_URL'])) {
$items[] = [
'TEXT' => 'Раздел: ' . $section['NAME'],
'LINK' => $section['SECTION_PAGE_URL'],
'LINK_PARAM' => 'target="_blank"',
'ICON' => 'btn_list',
];
}
}
}
/**
* Ссылка на раздел
*/
private static function addSectionLink(array &$items): void
{
$sectionId = (int) ($_REQUEST['ID'] ?? 0);
$iblockId = (int) ($_REQUEST['IBLOCK_ID'] ?? 0);
if ($sectionId <= 0) {
return;
}
Loader::includeModule('iblock');
$section = \CIBlockSection::GetList(
[],
['IBLOCK_ID' => $iblockId, 'ID' => $sectionId],
false,
['ID', 'NAME', 'SECTION_PAGE_URL']
)->GetNext(true, false);
if ($section && !empty($section['SECTION_PAGE_URL'])) {
$items[] = [
'TEXT' => 'Открыть раздел на сайте',
'TITLE' => $section['NAME'],
'LINK' => $section['SECTION_PAGE_URL'],
'LINK_PARAM' => 'target="_blank"',
'ICON' => 'btn_view',
];
}
}
}Регистрация
<?php
// /local/php_interface/init.php
\Local\Admin\ElementLinkExtension::register();Версия в классе решает две практические задачи: обработчик один на все админские страницы (легче отлаживать и отключать), а ссылка на родительский раздел избавляет от второй частой просьбы — «а теперь покажи, как этот товар выглядит в категории».
Доступные иконки
| Значение ICON | Описание |
|---|---|
btn_view | Глаз (просмотр) |
btn_list | Список |
btn_copy | Копирование |
btn_delete | Удаление |
btn_edit | Редактирование |
btn_new | Добавление |
btn_lock | Блокировка |
Добавление в список элементов
Для добавления ссылки в контекстное меню списка элементов:
<?php
// Событие для списка элементов
EventManager::getInstance()->addEventHandler(
'iblock',
'OnAfterIBlockElementAdd',
function ($arFields) {
// Событие вызывается после добавления
// Здесь можно добавить логику
}
);
// Или переопределение result_modifier.php компонента adminQR-код на страницу
Бонус — добавляем QR-код для мобильного просмотра:
<?php
private static function addQrCode(array &$items, string $url): void
{
$qrUrl = 'https://api.qrserver.com/v1/create-qr-code/?size=200x200&data='
. urlencode($url);
$items[] = [
'TEXT' => 'QR-код страницы',
'TITLE' => 'Отсканируйте для просмотра на телефоне',
'LINK' => $qrUrl,
'LINK_PARAM' => 'target="_blank"',
'ICON' => 'btn_view',
];
}Итоги
Приём стоит десяти минут работы и заметно экономит время контент-менеджеров, поэтому я добавляю такую кнопку почти на каждом проекте с большим каталогом. Что важно помнить:
- обработчик глобальный — фильтрация по
GetCurPage()обязательна; - выборка должна быть минимальной, код выполняется на каждой отрисовке меню;
DETAIL_PAGE_URLвернёт корректный адрес только если у инфоблока заполнен шаблон URL детальной страницы — на инфоблоках без публичной части кнопки просто не будет, и это правильное поведение.
Совет: Если публичная часть живёт на другом домене (headless-фронтенд, поддомен), собирайте абсолютный URL с нужным хостом — относительная ссылка из админки уведёт менеджера на несуществующую страницу админского домена.
Комментарии
Система комментариев скоро будет подключена. А пока вы можете написать мне в Telegram или на email.