How to Save 90% of Managers' Time on Bulk Operations in Bitrix
A manager needs to update prices for 500 products — manually that's 4 hours of work. The standard Bitrix list provides only five actions: activate, deactivate, delete, change section, export. No custom operations exist. We implement bulk actions that save up to 90% of time: price updates, markups, property updates, synchronization with 1C and marketplaces.
Our team has been developing administrative interfaces for Bitrix for over 7 years. During this time, we have completed more than 50 projects, including catalogs with 100,000+ products. What scenarios are most common? Mass price changes (markup/discount), copying properties from one product to another, SEO field generation, updating stock via file or API. In this article, we will look at how to add a custom button to the infoblock element list, write a PHP handler, and protect the operation from CSRF and errors. The payback period for such an operation is several months due to a 10x acceleration of managers' work.
How to Add a Custom Action to the Context Menu?
The standard infoblock list is built by the bitrix:iblock.admin.element.list component. To add a new button, you don't need to edit system files — use JavaScript injection or the OnAdminListDisplay event. JavaScript injection is 3 times faster to implement than modifying the core and does not require updates.
Example: adding a "Update Prices" button via JavaScript:
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 in PHP
The file /bitrix/admin/my_bulk_price_update.php is the entry point. It receives the IDs of selected elements via $_REQUEST['ID'][], checks permissions, and performs the update.
<?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=ru');
}
?>
<!-- Confirmation form -->
<form method="post" action="">
<p>Selected products: <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'; ?>
Why Is It Important to Check Access Rights?
Bulk operations can modify thousands of records. Without access control, one careless click can delete half the catalog. In each handler, we always:
- Check the CSRF token via
check_bitrix_sessid(). - Check admin privileges (
isAdmin()) or via the Bitrix role system. - Validate passed IDs: they must belong to the expected infoblock — protection against substitution of foreign elements.
According to the official 1C-Bitrix documentation, CAdminContextMenu allows adding arbitrary menu items without modifying the core.
Comparison of Button Addition Methods
| Method | Complexity | Flexibility | Requires Core Modification |
|---|---|---|---|
| JavaScript injection | Low | High (any button) | No |
OnAdminListDisplay event |
Medium | Medium (via CAdminContextMenu) | No |
| Editing admin files | High | High | Yes (forbidden on updates) |
We recommend JavaScript injection — it's fast, doesn't touch the core, and easily adapts to different scenarios.
Typical Operations and Their Complexity
| Operation Type | Development Time | Lines of Code | Example |
|---|---|---|---|
| Update one property | 3 days | 50-100 | Set size for a group of products |
| Markup/discount based on purchase price | 5 days | 100-200 | Increase price by 20% |
| Complex logic with multiple fields | 10 days | 200-500 | Import from Excel with field mapping |
Progress Bar for Long Operations
When processing 1000+ items without progress, the user might think the site has frozen. We implement an AJAX pattern: the operation is split into steps of 50-100 records each, each step is an AJAX request. The script returns {processed: N, total: M}, JavaScript updates the progress bar and launches the next step. The queue is stored in $_SESSION or b_option by a temporary key. An AJAX pattern with a progress bar is 10 times more user-friendly than waiting without feedback.
Example in JavaScript:
function processBatch(ids, stepSize, total) {
var processed = 0;
function next() {
if (processed >= total) { alert('Done'); return; }
var batch = ids.slice(processed, processed + stepSize);
BX.ajax.post('/bitrix/admin/ajax_bulk.php', { IDs: batch, sessid: BX.bitrix_sessid() }, function(res) {
var data = JSON.parse(res);
processed += data.batchSize;
// Update progress bar
document.getElementById('progress').style.width = (processed / total * 100) + '%';
next();
});
}
next();
}
What Is Included in the Work
- Analysis — study the catalog, identify bottlenecks, record requirements.
- Design — define the list of operations, access rights, data schema.
- Implementation — write code using ORM, JS injections, server handlers.
- Testing — test on a copy of the catalog with 10,000+ items, simulate load.
- Deployment — roll out to production, configure monitoring.
- Warranty — 6 months free support for documented bugs.
Contact us for an estimate of your project — we will send a preliminary calculation within one business day.
Typical Mistakes in Bulk Operation Development
- Ignoring memory limits. When processing 50,000 products without chunking, the script will crash with a 500 error. Always use pagination.
- Skipping ID validation. If the
$_REQUEST['ID']array contains elements from another infoblock, the operation may corrupt data. Check viaCIBlockElement::GetListwith a filter. - Lack of CSRF protection. An attacker could craft a link with parameters and trigger an unwanted action. Always check sessid.
More about CSRF protection
Bitrix uses sessid, which is checked by the function check_bitrix_sessid(). Always include this call in your handlers. For AJAX requests, pass sessid in the parameters.Request a consultation — we will select the optimal solution for your catalog.







