Developing Admin Interface for Bulk Operations in 1C-Bitrix
A manager selects 200 products in Bitrix's standard list, clicks "Actions" — and sees only five options: activate, deactivate, delete, change section, export. No custom operations. Add them through admin interface extension.
Bitrix Admin Page Architecture
The admin product list is built by the bitrix:iblock.admin.element.list component in /bitrix/modules/iblock/admin/. To add custom actions on selections, no need to edit system files — there's an extension mechanism via admin menu-file and custom handler.
Custom admin page is placed in /bitrix/admin/my_bulk_action.php. It receives ID list via $_REQUEST['ID'][] — Bitrix standard for passing selected elements from lists.
Adding to Actions Menu
Standard iblock list supports adding buttons via module's admin_action.php or JavaScript injection. Cleaner way — register custom action via CAdminContextMenu:
// In /bitrix/admin/my_module_actions.php
// Included via /bitrix/php_interface/init.php
AddEventHandler('main', 'OnAdminListDisplay', function() {
global $APPLICATION;
$page = $APPLICATION->GetCurPage();
// Add button only on element list page for needed iblock
if (strpos($page, 'iblock_list_admin.php') !== false
&& (int)$_GET['IBLOCK_ID'] === MY_CATALOG_IBLOCK_ID)
{
// Registration not done via event — use JS
}
});
Practically, add button via JavaScript injected at page bottom via $APPLICATION->AddHeadScript() or file in /bitrix/admin/.
JavaScript to add button:
document.addEventListener('DOMContentLoaded', function () {
var toolbar = document.querySelector('.adm-toolbar-panel');
if (!toolbar) return;
var btn = document.createElement('input');
btn.type = 'button';
btn.value = 'Update Prices';
btn.className = 'adm-btn';
btn.addEventListener('click', function () {
var form = document.getElementById('list_form');
var action = document.createElement('input');
action.type = 'hidden';
action.name = 'action';
action.value = 'bulk_price_update';
var target = document.createElement('input');
target.type = 'hidden';
target.name = 'target_url';
target.value = '/bitrix/admin/my_bulk_price_update.php';
form.appendChild(action);
form.appendChild(target);
form.submit();
});
toolbar.appendChild(btn);
});
Bulk Operation Handler
File /bitrix/admin/my_bulk_price_update.php — processing page:
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_admin_before.php';
$APPLICATION->SetTitle('Bulk Price Update');
// Check permissions
if (!\Bitrix\Main\Engine\CurrentUser::get()->isAdmin()) {
die('Access denied');
}
$ids = array_map('intval', (array)$_REQUEST['ID']);
$ids = array_filter($ids);
require_once $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_admin_after.php';
if (!empty($ids) && $_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['confirm'])) {
// Execute operation
$markup = (float)$_POST['markup'];
foreach (array_chunk($ids, 50) as $chunk) {
foreach ($chunk as $id) {
$purchase = getPurchasePrice($id);
if ($purchase > 0) {
updateRetailPrice($id, $purchase * (1 + $markup / 100));
}
}
}
LocalRedirect('/bitrix/admin/iblock_list_admin.php?IBLOCK_ID=' . MY_CATALOG_IBLOCK_ID . '&lang=en');
}
?>
<!-- Confirmation form -->
<form method="post" action="">
<p>Selected items: <b><?= count($ids) ?></b></p>
<label>Markup (%):
<input type="number" name="markup" value="40" min="0" max="500">
</label>
<?php foreach ($ids as $id): ?>
<input type="hidden" name="ID[]" value="<?= $id ?>">
<?php endforeach; ?>
<input type="hidden" name="confirm" value="Y">
<?= bitrix_sessid_post() ?>
<input type="submit" value="Update" class="adm-btn-green">
<a href="javascript:history.back()" class="adm-btn">Cancel</a>
</form>
<?php require_once $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/epilog_admin.php'; ?>
Progress Bar for Long Operations
For 1000+ element updates, need progress indicator. Bitrix implements via CAdminProgress or AJAX polling.
AJAX pattern: operation splits into steps, each step — AJAX request, processes batch, returns {processed: N, total: M}. JavaScript updates progress bar and runs next step. Queue saving between steps — via $_SESSION or b_option by temp key.
Access Control
Each bulk operation handler checks:
- CSRF token via
check_bitrix_sessid(). - User permissions via
\Bitrix\Main\Engine\CurrentUser::get()->isAdmin()or\Bitrix\Main\Access. - Passed ID ownership to expected iblock — protects against foreign ID substitution.







