Setting up 1C-Bitrix media file versioning

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

Media File Versioning Setup in 1C-Bitrix

Media file versioning is the ability to save the history of file changes, roll back to a previous version, and track who made edits and when. Bitrix does not include such a mechanism in the fileman module by default. Out of the box, when a file is overwritten, the old version is permanently deleted.

How File Storage Works in Bitrix

All files are registered in the b_file table. When a file is "updated" through the standard Bitrix interface, a new record is created in b_file with a new ID, while the old file is physically deleted from disk via CFile::Delete(). References to the old FILE_ID in other tables are updated in a chain — this is why there is no history.

Versioning Architecture

Versioning requires an additional history table:

CREATE TABLE bl_file_versions (
    id           INT AUTO_INCREMENT PRIMARY KEY,
    medialib_id  INT NOT NULL,          -- ID of b_medialib_item element
    file_id      INT NOT NULL,          -- ID in b_file (old version)
    version      INT NOT NULL DEFAULT 1,
    created_by   INT NOT NULL,          -- b_user.ID
    created_at   DATETIME NOT NULL,
    comment      VARCHAR(500),
    INDEX idx_medialib (medialib_id, version)
);

Logic: when a file in the Media Library is updated, the old record from b_file and the physical file are not deleted — instead, the FILE_ID is written to bl_file_versions. The current version remains in b_medialib_item.FILE_ID, and all previous versions are stored in the history table.

Intercepting the Update Event

The event handler is registered in init.php or in a module:

AddEventHandler('fileman', 'OnMedialibItemUpdate', 'SaveFileVersion');

function SaveFileVersion(int $itemId, array $oldFields): void {
    if (empty($oldFields['FILE_ID'])) return;

    global $USER;
    $DB->Query("INSERT INTO bl_file_versions
        (medialib_id, file_id, version, created_by, created_at)
        SELECT " . intval($itemId) . ", " . intval($oldFields['FILE_ID']) . ",
               COALESCE(MAX(version), 0) + 1, " . (int)$USER->GetID() . ", NOW()
        FROM bl_file_versions WHERE medialib_id = " . intval($itemId));
}

The OnMedialibItemUpdate event fires before the new data is written, making it possible to save the FILE_ID of the old version.

Physical Storage of Version Files

Version files are stored in /upload/fileman/versions/{item_id}/v{N}/. On rollback, a new record is created in b_file and the file path is restored. Physical deletion of old versions only occurs on an explicit "Clear history" action — not automatically.

To save disk space, only the last N versions can be retained. An agent runs daily to check the bl_file_versions table and delete versions older than the threshold:

$maxVersions = COption::GetOptionInt('mymodule', 'max_file_versions', 10);

Version History and Rollback Interface

A "Version History" button is added to the Media Library admin interface, opening a list with date, author, and a "Restore" button. Rollback means creating a new b_file based on the version file and updating b_medialib_item.FILE_ID.

Operation Method
Save version OnMedialibItemUpdate event
Get history SELECT from bl_file_versions
Roll back version CMedialibItem::Update() + CFile::MakeFileArray()
Delete version CFile::Delete() + DELETE from bl_file_versions

What Is Included in the Setup

  • Creating the bl_file_versions table and indexes
  • Writing the OnMedialibItemUpdate event handler
  • Configuring physical storage of version files
  • Admin interface for viewing history and rolling back
  • Agent for cleaning up old versions based on a configurable limit