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

Ниже разберу, какой способ подходит для каждой задачи и как его подключить. JavaScript нужен для поведения элементов в браузере, admin_header.php — для общего PHP-фрагмента, OnEndBufferContent — для изменения готовой разметки. Замену формы и отдельную административную страницу оставлю напоследок: к ним стоит переходить, если расширения штатного интерфейса недостаточно.

Каждый пример решает отдельную задачу и подключается самостоятельно. PHP-обработчики добавляются в /local/php_interface/init.php, если рядом с кодом не указан другой файл. В существующем PHP-файле повторный <?php не нужен. Значение 7 в условиях — ID инфоблока, который нужно заменить своим. Примеры интерфейсных дополнений ограничены администраторами; для других ролей проверку следует заменить проверкой разрешений на конкретный объект.

Добавить пункт в левое меню: OnBuildGlobalMenu

Пункт меню даёт постоянный вход в нужный раздел. Например, можно вынести разделы конкретного инфоблока в меню «Настройки». Событие OnBuildGlobalMenu позволяет дополнить массив модульных пунктов, сохранив существующее меню.

<?php
\Bitrix\Main\EventManager::getInstance()->addEventHandlerCompatible(
    'main', 'OnBuildGlobalMenu',
    static function (&$globalMenu, &$moduleMenu): void {
        if (!(isset($GLOBALS['USER']) && $GLOBALS['USER']->IsAdmin())) { return; }
        $moduleMenu[] = [
            'parent_menu' => 'global_menu_settings',
            'section' => 'alexeslm_extension',
            'sort' => 900,
            'text' => 'Разделы каталога',
            'title' => 'Открыть разделы каталога',
            'url' => '/bitrix/admin/iblock_section_admin.php'
                . '?IBLOCK_ID=7&type=content&lang='
                . rawurlencode(LANGUAGE_ID),
            'items_id' => 'menu_alexeslm_extension',
            'items' => [],
        ];
    }
);

В ссылке замените IBLOCK_ID=7 и type=content на ID и тип своего инфоблока. После подключения пункт появится в разделе «Настройки» у администратора. Создавать собственную страницу для такого перехода не требуется.

Добавить кнопку над формой: OnAdminContextMenuShow

Когда действие относится к открытому объекту, его удобнее разместить рядом с формой. Этот пример добавляет в карточку элемента кнопку перехода к разделам его инфоблока.

<?php
\Bitrix\Main\EventManager::getInstance()->addEventHandlerCompatible(
    'main', 'OnAdminContextMenuShow',
    static function (&$items, &$additionalItems): void {
        if (!isset($GLOBALS['USER']) || !$GLOBALS['USER']->IsAdmin()
            || !defined('ADMIN_SECTION') || ADMIN_SECTION !== true
            || $GLOBALS['APPLICATION']->GetCurPage() !== '/bitrix/admin/iblock_element_edit.php'
            || (int)($_GET['IBLOCK_ID'] ?? 0) !== 7) {
            return;
        }
        $items[] = [
            'TEXT' => 'Разделы инфоблока',
            'LINK' => '/bitrix/admin/iblock_section_admin.php?IBLOCK_ID=7&type=content&lang='
                . rawurlencode(LANGUAGE_ID),
        ];
    }
);

Условие ограничивает кнопку одной формой и одним инфоблоком. Параметр type в ссылке также нужно заменить на тип своего инфоблока.

Добавить действие в меню строки: OnAdminListDisplay

Если действие выбирают для конкретной строки списка, используйте OnAdminListDisplay. Здесь к действиям элемента добавляется переход к его редактированию.

<?php
\Bitrix\Main\EventManager::getInstance()->addEventHandlerCompatible(
    'main', 'OnAdminListDisplay',
    static function (&$list): void {
        if (!isset($GLOBALS['USER']) || !$GLOBALS['USER']->IsAdmin()
            || !defined('ADMIN_SECTION') || ADMIN_SECTION !== true
            || $GLOBALS['APPLICATION']->GetCurPage() !== '/bitrix/admin/iblock_element_admin.php'
            || (int)($_GET['IBLOCK_ID'] ?? 0) !== 7) {
            return;
        }
        foreach ($list->aRows as $row) {
            $id = (int)($row->arRes['ID'] ?? 0);
            if ($id <= 0) { continue; }
            $url = '/bitrix/admin/iblock_element_edit.php?IBLOCK_ID=7&type=content&ID='
                . $id . '&lang=' . rawurlencode(LANGUAGE_ID);
            $actions = is_array($row->aActions) ? $row->aActions : [];
            $actions[] = ['TEXT' => 'Открыть карточку', 'ACTION' => $list->ActionRedirect($url)];
            $row->AddActions($actions);
        }
    }
);

AddActions() заменяет набор действий строки, поэтому сначала берём существующий aActions. В проекте таким же способом добавляется переход к связанному объекту или отчёту с ID строки. Пример относится к классическому административному списку.

Добавить кнопку в верхнюю панель сайта: OnPanelCreate

Верхняя панель в публичной части сайта — отдельный интерфейс. Если редактору нужен быстрый переход с сайта к списку контента, кнопку добавляют через OnPanelCreate, а не через событие контекстного меню административной формы.

<?php
\Bitrix\Main\EventManager::getInstance()->addEventHandlerCompatible(
    'main', 'OnPanelCreate',
    static function (): void {
        if (!isset($GLOBALS['USER']) || !$GLOBALS['USER']->IsAdmin()) { return; }
        $GLOBALS['APPLICATION']->AddPanelButton([
            'ID' => 'catalog_sections', 'TEXT' => 'Разделы каталога',
            'HREF' => '/bitrix/admin/iblock_section_admin.php?IBLOCK_ID=7&type=content&lang='
                . rawurlencode(LANGUAGE_ID),
            'SORT' => 900,
        ]);
    }
);

Добавить вкладку элемента или раздела инфоблока

Для связанных сведений, которым тесно рядом со штатными полями, подходят OnAdminIBlockElementEdit и OnAdminIBlockSectionEdit. Они подключают набор вкладок через CAdminTabEngine. Пример добавляет вкладку с ID открытого объекта.

<?php

final class ProjectIblockTab
{
    public static function init(array $args)
    {
        if (!(isset($GLOBALS['USER']) && $GLOBALS['USER']->IsAdmin())) { return null; }
        if (isset($args['IBLOCK'])
            && (int)$args['IBLOCK']['ID'] !== 7) {
            return null;
        }
        return [
            'TABSET' => 'alexeslm_info',
            'GetTabs' => [self::class, 'tabs'],
            'ShowTab' => [self::class, 'show'],
        ];
    }

    public static function tabs(array $args): array
    {
        return [[
            'DIV' => 'info', 'TAB' => 'Сведения',
            'TITLE' => 'Контекст открытого объекта',
        ]];
    }

    public static function show($div, array $args, $fromForm): void
    {
        if ($div !== 'info' || !(isset($GLOBALS['USER']) && $GLOBALS['USER']->IsAdmin())) { return; }
        $id = (int)($args['ID'] ?? 0);
        echo '<tr><td>ID:</td><td>'
            . ($id > 0 ? $id : 'Новый объект: ID ещё нет')
            . '</td></tr>';
    }
}

foreach (['OnAdminIBlockElementEdit', 'OnAdminIBlockSectionEdit'] as $event) {
    \Bitrix\Main\EventManager::getInstance()->addEventHandlerCompatible(
        'main', $event, [ProjectIblockTab::class, 'init']
    );
}

Эти события регистрируются в модуле main. GetTabs возвращает описание вкладки, ShowTab печатает её содержимое. При создании объекта ID ещё нет. Для редактируемых данных одного HTML-поля недостаточно: нужны обработчики проверки и сохранения Check/Action либо штатное свойство инфоблока.

Дополнить существующий CAdminTabControl: OnAdminTabControlBegin

Если нужная форма построена на CAdminTabControl, её набор вкладок можно расширить через OnAdminTabControlBegin. Обработчик должен узнавать конкретную форму: имя контроллера и адрес страницы возьмите из её PHP-кода.

<?php
\Bitrix\Main\EventManager::getInstance()->addEventHandlerCompatible(
    'main', 'OnAdminTabControlBegin',
    static function (&$control): void {
        if (!isset($GLOBALS['USER']) || !$GLOBALS['USER']->IsAdmin()
            || !defined('ADMIN_SECTION') || ADMIN_SECTION !== true
            || $GLOBALS['APPLICATION']->GetCurPage() !== '/bitrix/admin/project_settings.php'
            || $control->name !== 'project_settings') {
            return;
        }
        $control->tabs[] = [
            'DIV' => 'project_help', 'TAB' => 'Памятка', 'TITLE' => 'Порядок изменения настроек',
            'CONTENT' => '<tr><td colspan="2">Перед изменением сохраните текущие настройки.</td></tr>',
        ];
    }
);

Здесь project_settings.php и project_settings обозначают страницу и контроллер, которые вы расширяете: замените оба значения. CONTENT выводится внутри таблицы, поэтому содержит <tr> и <td>. Для стандартной формы элемента инфоблока удобнее специальные события из предыдущего раздела.

Создать тип свойства инфоблока: OnIBlockPropertyBuildList

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

<?php

final class ProjectCodeProperty
{
    public static function description(): array
    {
        return [
            'PROPERTY_TYPE' => 'S', 'USER_TYPE' => 'alexeslm_code',
            'DESCRIPTION' => 'Служебный код',
            'GetPropertyFieldHtml' => [self::class, 'input'],
            'GetAdminListViewHTML' => [self::class, 'view'],
            'CheckFields' => [self::class, 'check'],
            'ConvertToDB' => [self::class, 'toDb'],
            'GetLength' => [self::class, 'length'],
        ];
    }

    public static function input($property, $value, $control): string
    {
        return self::renderInput($control['VALUE'], $value['VALUE'] ?? '');
    }

    public static function view($property, $value, $control): string
    {
        return self::escape($value['VALUE'] ?? '');
    }

    public static function check($property, $value): array
    {
        return self::valid($value['VALUE'] ?? null) ? []
            : ['Код: до 32 латинских букв, цифр, дефисов или подчёркиваний.'];
    }

    public static function toDb($property, $value): array
    {
        $value['VALUE'] = self::normalize($value['VALUE'] ?? '');
        return $value;
    }

    public static function length($property, $value): int
    {
        return strlen(self::normalize($value['VALUE'] ?? ''));
    }
    public static function normalize($value): string
    {
        return is_scalar($value) ? strtoupper(trim((string)$value)) : '';
    }

    public static function valid($value): bool
    {
        if (!is_scalar($value) && $value !== null) { return false; }
        $value = self::normalize($value);
        return $value === ''
            || preg_match('/\A[A-Z0-9_-]{1,32}\z/', $value) === 1;
    }

    private static function renderInput($name, $value): string
    {
        return '<input type="text" size="32" maxlength="32" name="'
            . self::escape($name) . '" value="'
            . self::escape($value) . '" placeholder="SKU-001">';
    }

    private static function escape($value): string
    {
        return htmlspecialchars((string)$value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
    }
}

\Bitrix\Main\EventManager::getInstance()->addEventHandlerCompatible(
    'iblock', 'OnIBlockPropertyBuildList', [ProjectCodeProperty::class, 'description']
);

После подключения выберите тип «Служебный код» при создании свойства инфоблока. Ввод sku-001 сохраняется как SKU-001; кириллица и строка длиннее 32 символов отклоняются. Пустое значение допускает сам тип, а обязательность задаётся в настройках поля. HTML-экранирование защищает вывод значения и имени поля.

Создать тип пользовательского поля: OnUserTypeBuildList

Если данные принадлежат сущности с полями UF_*, нужен пользовательский тип поля, а не свойство инфоблока. Например, для служебного кода пользователя можно создать поле UF_SERVICE_CODE. Этот пример сам содержит вывод, проверку и нормализацию значения.

<?php

final class ProjectCodeUserField
{
    public static function description(): array
    {
        return [
            'USER_TYPE_ID' => 'alexeslm_code',
            'CLASS_NAME' => self::class,
            'DESCRIPTION' => 'Служебный код', 'BASE_TYPE' => 'string',
        ];
    }

    public static function GetDBColumnType($field): string
    {
        return 'varchar(32)'; // MySQL, одиночное значение.
    }

    public static function GetEditFormHTML($field, $control): string
    {
        return self::renderInput($control['NAME'], $control['VALUE'] ?? '');
    }

    public static function GetAdminListViewHTML($field, $control): string
    {
        return self::escape($control['VALUE'] ?? '');
    }

    public static function CheckFields($field, $value): array
    {
        return self::valid($value) ? [] : [[
            'id' => $field['FIELD_NAME'],
            'text' => 'Код: до 32 латинских букв, цифр, дефисов или подчёркиваний.',
        ]];
    }

    public static function OnBeforeSave($field, $value): string
    {
        return self::normalize($value);
    }
    public static function normalize($value): string
    {
        return is_scalar($value) ? strtoupper(trim((string)$value)) : '';
    }

    public static function valid($value): bool
    {
        if (!is_scalar($value) && $value !== null) { return false; }
        $value = self::normalize($value);
        return $value === ''
            || preg_match('/\A[A-Z0-9_-]{1,32}\z/', $value) === 1;
    }

    private static function renderInput($name, $value): string
    {
        return '<input type="text" size="32" maxlength="32" name="'
            . self::escape($name) . '" value="'
            . self::escape($value) . '" placeholder="SKU-001">';
    }

    private static function escape($value): string
    {
        return htmlspecialchars((string)$value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
    }
}

\Bitrix\Main\EventManager::getInstance()->addEventHandlerCompatible(
    'main', 'OnUserTypeBuildList', [ProjectCodeUserField::class, 'description']
);

После подключения выберите тип «Служебный код» при создании пользовательского поля. Реализация рассчитана на одиночное строковое поле в MySQL. Ввод sku-001 сохраняется как SKU-001; кириллица и строка длиннее 32 символов отклоняются. Пустое значение допускает сам тип, а обязательность задаётся в настройках поля. HTML-экранирование защищает вывод значения и имени поля.

Добавить настройку использования свойства: OnPropertyFeatureBuildList

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

<?php
\Bitrix\Main\EventManager::getInstance()->addEventHandler(
    'iblock', 'Bitrix\Iblock\Model\PropertyFeature::OnPropertyFeatureBuildList',
    static function (\Bitrix\Main\Event $event): \Bitrix\Main\EventResult {
        $property = $event->getParameter('property');
        $features = (int)($property['IBLOCK_ID'] ?? 0) === 7
            ? [['MODULE_ID' => 'main', 'FEATURE_ID' => 'PROJECT_EXPORT',
                'FEATURE_NAME' => 'Участвует в выгрузке проекта']]
            : [];
        return new \Bitrix\Main\EventResult(\Bitrix\Main\EventResult::SUCCESS, $features);
    }
);

При включённом механизме расширенных свойств появится дополнительный признак. Код выгрузки должен отдельно читать и учитывать его: одна регистрация признака выгрузку не реализует. Здесь используется D7-событие с полным именем класса и параметром property в нижнем регистре.

Показать строку в информационном блоке заказа

Для короткого значения — внутреннего номера или статуса внешней системы — подходит sale:onSaleAdminOrderInfoBlockShow. Обработчик возвращает пары TITLE/VALUE; отдельная вкладка для такой строки не нужна.

<?php
use Bitrix\Main\{Event, EventResult};

\Bitrix\Main\EventManager::getInstance()->addEventHandler(
    'sale', 'onSaleAdminOrderInfoBlockShow',
    static function (Event $event): EventResult {
        if (!(isset($GLOBALS['USER']) && $GLOBALS['USER']->IsAdmin())) {
            return new EventResult(EventResult::SUCCESS, []);
        }
        $order = $event->getParameter('ORDER');
        $id = $order ? (int)$order->getId() : 0;
        return new EventResult(EventResult::SUCCESS, [[
            'TITLE' => 'Внутренний ID',
            'VALUE' => $id > 0 ? (string)$id : 'Ещё не присвоен',
        ]]);
    }
);

Добавить перемещаемый блок в карточку заказа

Для нескольких связанных сведений или памятки оператору удобен отдельный блок. События OnAdminSaleOrderViewDraggable, OnAdminSaleOrderEditDraggable и OnAdminSaleOrderCreateDraggable подключают его соответственно при просмотре, редактировании и создании заказа.

<?php
final class ProjectOrderBlock
{
    public static function init(array $args = [])
    {
        if (!(isset($GLOBALS['USER']) && $GLOBALS['USER']->IsAdmin())) { return null; }
        return [
            'BLOCKSET' => 'alexeslm_order',
            'getBlocksBrief' => [self::class, 'brief'],
            'getBlockContent' => [self::class, 'content'],
        ];
    }

    public static function brief(array $args): array
    {
        return ['alexeslm_order_note' => ['TITLE' => 'Перед передачей заказа']];
    }

    public static function content($code, $selectedTab, array $args): string
    {
        if ($code !== 'alexeslm_order_note' || !(isset($GLOBALS['USER']) && $GLOBALS['USER']->IsAdmin())) { return ''; }
        $order = $args['ORDER'] ?? null;
        $id = $order ? (int)$order->getId() : 0;
        return '<div class="adm-info-message">'
            . ($id > 0 ? 'Заказ №' . $id : 'Заказ пока не сохранён')
            . '. Проверьте контактные данные и способ доставки.</div>';
    }
}

foreach (['OnAdminSaleOrderViewDraggable', 'OnAdminSaleOrderEditDraggable',
          'OnAdminSaleOrderCreateDraggable'] as $event) {
    \Bitrix\Main\EventManager::getInstance()->addEventHandlerCompatible(
        'main', $event, [ProjectOrderBlock::class, 'init']
    );
}

getBlocksBrief описывает блок, getBlockContent возвращает HTML. У нового заказа может ещё не быть ID. События движка блоков регистрируются в main, хотя сама карточка относится к модулю продаж.

Добавить отдельную вкладку заказа

Вкладка подходит для сведений, которые нужно вынести за пределы основного экрана заказа: например, регламента обработки или истории обмена. Для этого служат OnAdminSaleOrderView, OnAdminSaleOrderEdit и OnAdminSaleOrderCreate. Ниже — самостоятельная вкладка с памяткой.

<?php
final class ProjectOrderTab
{
    public static function init(array $args)
    {
        if (!isset($GLOBALS['USER']) || !$GLOBALS['USER']->IsAdmin()) { return null; }
        return ['TABSET' => 'project_order_help',
            'GetTabs' => [self::class, 'tabs'], 'ShowTab' => [self::class, 'show']];
    }
    public static function tabs(array $args): array
    {
        return [['DIV' => 'help', 'TAB' => 'Памятка', 'TITLE' => 'Перед передачей заказа']];
    }
    public static function show($div, array $args, $fromForm): void
    {
        if ($div !== 'help' || !isset($GLOBALS['USER']) || !$GLOBALS['USER']->IsAdmin()) { return; }
        echo '<tr><td colspan="2">Проверьте контактные данные и способ доставки.</td></tr>';
    }
}
foreach (['OnAdminSaleOrderView', 'OnAdminSaleOrderEdit', 'OnAdminSaleOrderCreate'] as $event) {
    \Bitrix\Main\EventManager::getInstance()->addEventHandlerCompatible(
        'main', $event, [ProjectOrderTab::class, 'init']
    );
}

Вкладка использует CAdminTabEngine; её события также относятся к main. Пример только выводит информацию. Если разместить здесь поля, обработку и сохранение их значений нужно реализовать отдельно.

Подключить JavaScript для поведения элементов формы

JavaScript подходит для раскрытия пояснений, переключения видимости полей и реакции на действия пользователя. PHP по-прежнему проверяет и сохраняет данные. Подключим скрипт только в форме элемента нужного инфоблока через OnEpilog и Asset::addJs().

<?php
\Bitrix\Main\EventManager::getInstance()->addEventHandlerCompatible(
    'main', 'OnEpilog',
    static function (): void {
        if (isset($GLOBALS['USER']) && $GLOBALS['USER']->IsAdmin()
            && defined('ADMIN_SECTION') && ADMIN_SECTION === true
            && $GLOBALS['APPLICATION']->GetCurPage() === '/bitrix/admin/iblock_element_edit.php'
            && (int)($_GET['IBLOCK_ID'] ?? 0) === 7) {
            \Bitrix\Main\Page\Asset::getInstance()->addJs('/local/js/admin-help.js');
        }
    }
);

В HTML своего поля или вкладки разместите кнопку и пояснение. Если строка выводится внутри таблицы административной формы, её содержимое нужно поместить в <tr><td>.

<button type="button" data-admin-extension-toggle aria-expanded="false"
        aria-controls="admin-extension-help">Показать пояснение</button>
<p id="admin-extension-help" hidden>Служебный код используется при обмене с внешней системой.</p>

Содержимое /local/js/admin-help.js:

(() => {
    if (window.alexeslmAdminExtensionBound) return;
    window.alexeslmAdminExtensionBound = true;
    document.addEventListener('click', (event) => {
        const button = event.target instanceof Element
            ? event.target.closest('[data-admin-extension-toggle]') : null;
        if (!button) return;
        const help = document.getElementById(button.getAttribute('aria-controls'));
        if (!help) return;
        const expanded = button.getAttribute('aria-expanded') === 'true';
        button.setAttribute('aria-expanded', String(!expanded));
        button.textContent = expanded ? 'Показать пояснение' : 'Скрыть пояснение';
        help.hidden = expanded;
    });
})();

Скрипт меняет hidden и aria-expanded. Делегирование клика позволяет обработать и элементы, добавленные после загрузки страницы. OnEpilog здесь регистрирует ресурс, а не печатает <script> после готового документа.

Вывести общий PHP-фрагмент через admin_header.php

Если сообщение должно появляться перед содержимым административных страниц, можно использовать /local/php_interface/admin_header.php. В примере уведомление ограничено формой редактирования элементов одного инфоблока. Добавьте фрагмент в существующий файл, сохранив его содержимое.

<?php
if (isset($GLOBALS['USER']) && $GLOBALS['USER']->IsAdmin()
    && $GLOBALS['APPLICATION']->GetCurPage() === '/bitrix/admin/iblock_element_edit.php'
    && (int)($_GET['IBLOCK_ID'] ?? 0) === 7) {
    echo '<div class="adm-info-message">Изменения каталога попадут в следующую выгрузку.</div>';
}

Это подключаемый PHP-файл, а не событие. Он подходит для общего сообщения, но не заменяет API вкладок, кнопок и типов полей.

Изменить готовый HTML через OnEndBufferContent

Если подходящего события для элемента нет, остаётся обработка выходного буфера. Например, код ниже заменяет HTML-маркер, который уже выводится вашим шаблоном формы. Этот способ зависит от разметки, поэтому сначала стоит проверить штатные точки расширения.

<?php
\Bitrix\Main\EventManager::getInstance()->addEventHandlerCompatible(
    'main', 'OnEndBufferContent',
    static function (&$content): void {
        if (!isset($GLOBALS['USER']) || !$GLOBALS['USER']->IsAdmin()
            || !defined('ADMIN_SECTION') || ADMIN_SECTION !== true
            || $GLOBALS['APPLICATION']->GetCurPage() !== '/bitrix/admin/iblock_element_edit.php'
            || (int)($_GET['IBLOCK_ID'] ?? 0) !== 7
            || ($_SERVER['REQUEST_METHOD'] ?? '') !== 'GET'
            || stripos($content, '</html>') === false) {
            return;
        }
        $content = str_replace('<!--PROJECT_CATALOG_NOTE-->',
            '<p class="adm-info-message">Проверьте данные перед выгрузкой.</p>', $content);
    }
);

Маркер <!--PROJECT_CATALOG_NOTE--> должен присутствовать в исходном HTML; без него замены не будет. Буфер передаётся по ссылке. Условия ограничивают обработчик полной HTML-страницей выбранной формы и не предназначены для AJAX-ответов.

Заменить форму элемента через EDIT_FILE_AFTER

Если настройки формы, вкладок и типов свойств недостаточно, для инфоблока можно указать собственную форму редактирования. Это уже замена интерфейса: поддержку его поведения и совместимости придётся брать на себя.

В настройках инфоблока укажите путь в параметре EDIT_FILE_AFTER, например /local/admin/catalog_element_form.php. За основу возьмите действующую форму из установленного ядра, разберите её зависимости и сохраните обработку полей, проверку прав, сессии и ошибок. Файл подключается штатной страницей редактирования после обработки данных: он не является самостоятельным обработчиком сохранения. Ветка пользовательской формы отличается от штатной в том числе проверкой сессии, поэтому нельзя ограничиться копированием HTML: проверку запроса нужно сохранить в обработке до записи данных.

Универсального короткого кода для полной замены нет: содержимое зависит от полей конкретного инфоблока. Такой способ оправдан при существенной перестройке экрана; ради дополнительной кнопки или информационного блока менять всю форму не нужно.

Создать отдельную административную страницу

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

Основной код разместите в /local/admin/project_tool.php. Минимальная страница подключает административное окружение, проверяет доступ и выводит содержимое между прологом и эпилогом.

<?php
require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_admin_before.php';
if (!$USER->IsAdmin()) {
    $APPLICATION->AuthForm('Доступ только администратору.');
}
$APPLICATION->SetTitle('Проверка интеграции');
require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_admin_after.php';
echo '<div class="adm-info-message">Здесь размещается результат проверки интеграции.</div>';
require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/epilog_admin.php';

Точка входа /bitrix/admin/project_tool.php подключает этот файл:

<?php
require $_SERVER['DOCUMENT_ROOT'] . '/local/admin/project_tool.php';

Откройте /bitrix/admin/project_tool.php под администратором: появятся заголовок и информационный блок. Если инструмент изменяет данные, добавьте POST-обработчик с проверкой сессии, прав и входных значений до вывода страницы. Сам административный пролог не реализует бизнес-логику инструмента.

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