Custom Page Development on D7 1C-Bitrix

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1175
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    747
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    976

Developing Custom Pages on D7 for 1C-Bitrix

A custom page in the Bitrix admin area is not simply an HTML file dropped into /bitrix/admin/. It is a controller that checks permissions, accepts parameters, executes business logic, and renders the result through a template. D7 provides a \Bitrix\Main\Engine\Controller mechanism with routing, CSRF checking, and session management — no need to build it from scratch.

Two Approaches to Custom Pages

Classic admin file (/bitrix/admin/my_page.php) — the simplest option, suitable for auxiliary tools:

<?php
require_once($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/main/include/prolog_admin_before.php");

use Bitrix\Main\Loader;
use Bitrix\Main\Localization\Loc;

Loader::includeModule('my.module');

// Permission check
if (!$APPLICATION->IsAdminPage() || !\CMain::GetUserRight("main") >= "S") {
    $APPLICATION->AuthForm("Access denied");
}

$APPLICATION->SetTitle("My Tool");

require($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/main/include/prolog_admin_after.php");

// Page content output
?>
<form method="post" action="<?= $APPLICATION->GetCurPage() ?>">
    <?= bitrix_sessid_post() ?>
    <!-- form fields -->
    <input type="submit" value="Execute">
</form>
<?php
require($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/main/include/epilog_admin.php");

MVC via Controller — the correct approach for pages with logic:

namespace MyProject\Controllers;

use Bitrix\Main\Engine\Controller;
use Bitrix\Main\Engine\ActionFilter;

class ReportController extends Controller
{
    public function configureActions(): array
    {
        return [
            'getReport' => [
                'prefilters' => [
                    new ActionFilter\Authentication(),
                    new ActionFilter\HttpMethod(['GET', 'POST']),
                    new ActionFilter\Csrf(),
                ],
            ],
        ];
    }

    public function getReportAction(int $periodDays = 30): array
    {
        // permissions are checked in prefilters
        $data = \MyProject\Services\ReportService::build($periodDays);
        return ['data' => $data, 'total' => count($data)];
    }
}

Routing in Bitrix

Controllers require route registration. In /local/php_interface/init.php or in a module method:

\Bitrix\Main\Routing\Router::getInstance()->add(
    '/local/my-tool/report/',
    ['controller' => '\MyProject\Controllers\ReportController', 'action' => 'getReport'],
    'GET'
);

Alternative — UrlManager in the module config or the /local/routes.php file (Bitrix 22+, must be enabled in core settings).

Custom Public Page

For public pages, the standard component mechanism applies. But sometimes a page is needed outside the component system: a utility, a payment gateway callback, a webhook. In that case, a page file is created with the core bootstrapped:

<?php
define('STOP_STATISTICS', true);   // do not write statistics
define('NO_AGENT_STATISTIC', true); // do not run agents
define('NOT_CHECK_PERMISSIONS', true); // disable Bitrix permission check
define('NO_LANG_FILES', false);

require_once($_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php');
// all Bitrix classes are now available

// All logic via D7
$request = \Bitrix\Main\Application::getInstance()->getContext()->getRequest();
if (!$request->isPost()) {
    http_response_code(405);
    exit;
}

// ... processing

D7 Pagination

For pages with data lists:

use Bitrix\Main\UI\PageNavigation;

$nav = new PageNavigation('page');
$nav->allowAllRecords(false)
    ->setPageSize(20)
    ->initFromUri();

$result = \MyProject\Storage\OrderLogTable::getList([
    'select'     => ['*'],
    'order'      => ['ID' => 'DESC'],
    'count_total' => true,
    'offset'     => $nav->getOffset(),
    'limit'      => $nav->getLimit(),
]);

$nav->setRecordCount($result->getCount());
// Render pagination via $nav->getPageNavString(...)

Access Control on Custom Pages

Permission check based on user groups:

global $USER;

if (!$USER->IsAdmin() && !$USER->IsInGroup([MANAGER_GROUP_ID])) {
    LocalRedirect('/access-denied/');
}

For module pages — via the Bitrix access rights API:

if (\CModule::IncludeModule('my.module')) {
    $rights = \CMyModule::GetCurrentUserPermissions();
    if ($rights < MY_MODULE_PERMISSION_WRITE) {
        ShowError('Insufficient permissions');
        exit;
    }
}

Integration with Bitrix UI: Grids and Filters

For admin pages with lists — ready-made UI: CAdminList, CAdminFilter. This ensures visual consistency with the core:

$adminList = new CAdminList('my_list', $by, $order);
$adminList->AddColumn(['id' => 'ID', 'content' => 'ID', 'sort' => 'ID']);
$adminList->AddColumn(['id' => 'NAME', 'content' => 'Name', 'sort' => 'NAME']);

while ($row = $result->fetch()) {
    $adminRow = $adminList->AddRow($row['ID'], $row);
    $adminRow->AddActions([
        ['text' => 'Edit', 'href' => '/bitrix/admin/my_edit.php?ID=' . $row['ID']],
        ['text' => 'Delete', 'action' => "if(confirm('Delete?')) window.location='...';"],
    ]);
}
$adminList->DisplayList();

Timeline

Task Timeline
Simple admin page (form + data display) 2–3 days
Page with list, pagination, filter, and actions 4–7 days
Full CRUD interface for a custom entity with permissions and logs 1.5–2.5 weeks

A custom D7 page is not a replacement for the component system. It is a tool for tasks that do not fit the standard model: admin tools, import interfaces, dashboards with aggregated data. When written correctly, they do not conflict with the core during updates.