disk 26.1175.0 Ломающее

Disk 26.1175.0: runPreviewGeneration отдаёт другой pull-тег, а TIFF, WAV и MKV спрятаны за фича-флагом

5 мин чтения Устаревших API: 1

Ломающее обновление

Удалены или изменены публичные API — прикладной код может перестать работать.

Диск обновился с 26.1150.0 (разбор) до 26.1175.0 и пришёл в коробку вместе с main 26.800.0, который мы разобрали отдельно. Вендор пишет про два исправления общих дисков, но больше всего кода ушло на предпросмотр TIFF и поддержку TIFF, WAV и MKV во встроенном просмотрщике. Новые форматы включает фича-флаг FileViewerFormatsFlag, и по умолчанию он выключен.

Проверить свой код стоит тем, кто сам вызывает экшен runPreviewGeneration, наследует компонент disk.external.link или показывает пользователям MKV. Экшен теперь передаёт файл в transformer при каждом вызове, даже при выключенной опции disk_transform_files_on_open, а MKV без флага после обновления не конвертируется и не открывается в просмотрщике.

Что сломается

Перепишите ожидание после runPreviewGeneration

Controller\File::runPreviewGenerationAction() и Controller\AttachedObject::runPreviewGenerationAction() раньше возвращали результат View\Base::transformOnOpen(). Теперь экшены сами вызывают TransformerManager::transformToView($file) и подписывают текущего пользователя на pull-тег. В ответе previewGeneration осталось два статуса, success и not allowed, а no module и was transformed экшен больше не возвращает. data.pullTag при success приходил и раньше, но тег в нём теперь дисковый, DISKTRANSFORMATION<ID файла Диска>, а был mainTransform<ID из b_file>.

Вместе с transformOnOpen() с этого пути ушла isTransformationAllowedOnOpen(). Она не пускала на конвертацию файл, у которого уже есть view, файл больше лимита из настроек просмотрщика (для видео по умолчанию 300 МБ) и любой файл, если выключена опция disk_transform_files_on_open, а по умолчанию она выключена. Ушли и TransformerManager::checkTransformationAttempts() (отправлялся ли файл на конвертацию раньше), и сброс кеша BlogPostConnector::clearCacheByObjectId(). Теперь экшен передаёт файл в transformer при каждом вызове, независимо от опции. Если конвертация этого файла ещё идёт, transformer присоединит вызов к текущей команде. Из прежних ограничений остались только проверки самого transformer: файл больше его MAX_FILESIZE (для видео 3 ГБ) и пять неудачных конвертаций этого файла. Если ваш код полагался на опцию или лимит из настроек, проверяйте их сами.

Событие о готовности по-прежнему шлёт main, это команда transformationComplete на тег mainTransform<ID из b_file>. В новый дисковый тег Диск ничего не пишет, и фронтенд, который ждал событие по data.pullTag, его больше не получит. Штатный disk.file.transform.video подписывает пользователя на тег main ещё при рендере и сам опрашивает готовность, не дольше 90 секунд. В своём коде подписку на тег main делайте на сервере ((new \Bitrix\Main\UI\Viewer\Transformation\TransformerManager())->subscribeCurrentUserForTransformation($bFileId)), а на клиенте ждите событие main:

        async function runPreviewGeneration(fileId, bFileId)
{
	const response = await BX.ajax.runAction('disk.file.runPreviewGeneration', {
		data: { fileId },
	});

	if (response.data.previewGeneration.status !== 'success')
	{
		// остался только 'not allowed'
		showDownloadLink();

		return;
	}

	// data.pullTag теперь DISKTRANSFORMATION<id>, событий на него нет.
	// Готовность шлёт main на тег mainTransform<bFileId>, подписку на него делает сервер.
	BX.addCustomEvent('onPullEvent-main', (command, params) => {
		if (command === 'transformationComplete' && Number(params.fileId) === bFileId)
		{
			showPreview();
		}
	});
	showPreparingState();
}

    

Для вложений то же самое делает disk.attachedObject.runPreviewGeneration с параметром attachedObjectId.

Уберите переопределение setDocumentSessionSource()

Из CDiskExternalLinkComponent (install/components/bitrix/disk.external.link/class.php) удалён protected-метод setDocumentSessionSource(). Его место занял private setDocumentSource(SessionManager, File, ?Version), который всегда вызывает и setFile(), и setVersion(). Версию компонент теперь берёт из $this->externalLink->getVersion(), а сверка $version->getObjectId() !== $file->getRealObjectId() из старого метода пропала.

Если вы наследовали компонент и переопределяли этот метод, ваш метод останется в классе, но компонент его больше не вызовет, и логика в нём молча перестанет работать. Вызов parent::setDocumentSessionSource() упадёт с Error. Новый метод private, переопределить его не получится. Найти такие места можно так:

        grep -rn "setDocumentSessionSource" local/ bitrix/components/ bitrix/templates/ --include=*.php

    

Проверьте, как открываются MKV после обновления

Для .mkv при выключенном FileViewerFormatsFlag TransformerManager::transformToView() возвращает false, и конвертации нет. View\Video::render() отдаёт для такого файла пустую строку, а isHtmlAvailable() и переопределённый isTransformationAllowed() возвращают false. До 26.1175.0 особых условий для mkv в этих методах не было. Флаг объявлен с enabledByDefault(): false, поэтому MKV перестанет конвертироваться и открываться в просмотрщике сразу после обновления.

Остальные новые форматы без флага тоже не откроются. FileAttributes::refineType() отдаёт для .tif, .tiff, .wav и .mkv заглушку Renderer\Stub. С включённым флагом TIFF уходит в новый рендерер Tiff, WAV в Renderer\Audio.

Сверьте свой код с новым DiskProvider

QuickAccess\FileInfo\DiskProvider теперь работает с File|Version. Для AttachedObject с фиксированной версией (isSpecificVersion()) провайдер берёт саму Version. Раньше он брал getVersion()?->getObject(), то есть файл целиком. getSourceId() для версии возвращает DiskVersion:<id>, а конструктор на неподдерживаемом источнике бросает RuntimeException. Если вы наследуете провайдер или разбираете source ID, учтите новый префикс и исключение.

Новое

Включите FileViewerFormatsFlag на стенде

Bitrix\Disk\Config\Feature\FileViewerFormatsFlag сделан на фича-флагах ядра (Bitrix\Main\Config\Feature\AbstractFlag, механизм разобран в main 26.700.0). Состояние флага возвращает новый Configuration::isEnabledFileViewerFormats().

Флаг один на все четыре расширения, включить TIFF отдельно от MKV нельзя.

Превью TIFF собирает Imagick, поэтому перед включением проверьте расширение:

        <?php declare(strict_types=1);

use Bitrix\Disk\Config\Feature\FileViewerFormatsFlag;
use Bitrix\Disk\Configuration;
use Bitrix\Main\Config\Feature;
use Bitrix\Main\Loader;

Loader::requireModule('disk');

$tiffReady = extension_loaded('imagick') && \Imagick::queryFormats('TIFF*') !== [];
if (!$tiffReady) {
    exit("Imagick без поддержки TIFF: превью TIFF не соберутся\n");
}

if (!Configuration::isEnabledFileViewerFormats()) {
    Feature::enable(FileViewerFormatsFlag::class);
}

echo Configuration::isEnabledFileViewerFormats() ? "флаг включён\n" : "флаг не включился\n";

    

Запускайте его после подключения пролога, например из своей консольной команды на bitrix.php. Без пролога Loader недоступен.

WAV заработает только вместе с main 26.800.0. В нём Bitrix\Main\UI\Viewer\Renderer\Audio научился принимать audio/wav и audio/x-wav, а Диск направляет .wav именно в этот рендерер.

Где брать превью TIFF

Internal\Service\TiffPreviewService (1099 строк) готовит PNG первой страницы TIFF. Точки входа getByFile(), getByVersion(), getByAttachedObject() и getByExternalLink() возвращают Result со статусом из TiffPreview\PreviewStatus (ready, preparing, error) и кодом ошибки из TiffPreview\ErrorCode. Снаружи превью доступно через экшены showTiffPreview в Controller\File, Controller\Version и Controller\AttachedObject с параметром ?string $previewToken = null. Фильтры скопированы с showMarkdown: Authentication(true) и CloseSession без Csrf, плюс HttpMethod только на GET. Без токена экшен отдаёт JSON со статусом, с валидным previewToken отдаёт сам PNG через BFile. Адреса строит UrlManager: getUrlForShowTiffPreview(), getUrlForShowTiffPreviewByFileId(), getUrlForShowTiffPreviewVersion() и getUrlForShowTiffPreviewAttached().

В просмотрщике TIFF рисует рендерер UI\Viewer\Renderer\Tiff (JS-тип tiff, image/tiff) через расширение disk.viewer.tiff-item с классом BX.Disk.Viewer.TiffItem. Пока сервер отвечает preparing, класс переспрашивает его каждые 4 секунды, но не больше 15 раз, это около минуты ожидания. Если выводите атрибуты файла сами, адрес превью в data-tiff-preview-url можно подменить через FileAttributes::setTiffPreviewUrlOverride() или дополнить параметрами через setTiffPreviewUrlParams().

Ожидание видео ограничено 90 секундами

View\Video переопределил isTransformationAllowed($size = 0) (для MKV без флага он возвращает false) и получил константы TRANSFORMATION_RETRY_INTERVAL = 5 и TRANSFORMATION_WAIT_TIMEOUT = 90. Компонент disk.file.transform.video принимает параметры RETRY_INTERVAL и WAIT_TIMEOUT и больше 90 секунд не ждёт, опрос готовности в script.js переписан. В JS есть отладочное событие mkv_wait_expired, по нему видно, что ожидание переделывали под MKV.

В TransformerManager метод getTransformerByFormat() стал protected, а transformToView() вызывает его через static::. Выбор трансформера по формату теперь можно переопределить в наследнике. Сам Диск (контроллеры и модель AttachedObject) вызывает TransformerManager по имени базового класса, поэтому наследник сработает только там, где вы вызываете его сами.

Мастер общих дисков убирает за собой хранилище

Исправление, о котором пишет вендор, сделано в шаблоне страницы disk_common (install/public/templates/.default/page_templates/disk_common/template.php). Путь из мастера сначала проходит через CommonStoragePathNormalizer::normalizeCommonStorageMountPoint($rawPath, $siteDir, $allowLegacyDuplicateCollapse). Нормализатор убирает query и fragment, меняет \ на /, склеивает повторные слеши и отрезает хвостовой index.php. С третьим параметром он ещё схлопывает повтор последнего сегмента под docs.

Хранилище создаёт новый Driver::addCommonStorageWithCreationStatus(), который возвращает ['storage' => Storage, 'created' => bool]. Если changeBaseUrl() не удался, шаблон удаляет хранилище от имени SystemUser, но только то, которое создал сам. Если потом не сохранится сам раздел, хранилище откатит main. В bitrix/modules/main/public/file_new.php из main 26.800.0 появилась функция BXRollbackDiskCommonStorageCreation(), она через CommonStorageCreationContext::consume() удаляет хранилище, созданное в этом запросе.

При этом file_new.php импортирует Bitrix\Disk\Integration\Fileman\* напрямую, а эти классы впервые появились в disk 26.1175.0. Вызов защищён только Loader::includeModule('disk'). Судя по коду, main 26.800.0 с Диском версии ниже 26.1175.0 упал бы при создании раздела с шаблоном disk_common. При штатном обновлении такой пары не получится, потому что в метаданных обновления main 26.800.0 требует disk 26.1175.0, а disk 26.1175.0 требует main 26.800.0 и ui 26.650.0.

Флаг created пригодится и в своих установщиках. Уже существующее хранилище метод возвращает с created = false. Даже если его успел создать параллельный запрос, внутренний addStorageIfNotExistWithCreationStatus() поймает DuplicateEntryException и перечитает запись, так что откат чужое хранилище не заденет:

        <?php declare(strict_types=1);

namespace Vendor\Docs\Application\Service;

use Bitrix\Disk\Driver;
use Bitrix\Disk\Integration\Fileman\CommonStoragePathNormalizer;
use Bitrix\Disk\Storage;
use Bitrix\Disk\SystemUser;
use Bitrix\Main\Error;
use Bitrix\Main\Result;

final class CommonStorageInstaller
{
    public function install(string $entityId, string $name, string $siteId, string $rawPath): Result
    {
        $result = new Result();
        $mountPoint = CommonStoragePathNormalizer::normalizeCommonStorageMountPoint($rawPath);

        $creation = Driver::getInstance()->addCommonStorageWithCreationStatus([
            'NAME' => $name,
            'ENTITY_ID' => $entityId,
            'SITE_ID' => $siteId,
        ], []);

        $storage = $creation['storage'] ?? null;
        if (!$storage instanceof Storage) {
            return $result->addError(new Error('Хранилище не создано', 'STORAGE_NOT_CREATED'));
        }

        if (!$storage->changeBaseUrl($mountPoint)) {
            if ($creation['created']) {
                $storage->delete(SystemUser::SYSTEM_USER_ID);
            }

            return $result->addError(new Error("Не удалось привязать хранилище к {$mountPoint}", 'BASE_URL_FAILED'));
        }

        return $result->setData([
            'storageId' => (int)$storage->getId(),
            'created' => $creation['created'],
        ]);
    }
}

    

Второе исправление, для вложенных папок /docs/название/, пока не подключено. Появилась служебная точка входа bitrix/modules/disk/services/docs_section.php с копиями в bitrix/services/disk/ и install/services/disk/. По докблоку она обслуживает терминальный маршрут /docs/{any}, когда подпуть физически не существует, и поднимается по дереву до ближайшего index.php внутри /docs. Но в этой версии на файл не ссылается ни один маршрут. В bitrix/routes/intranet.php docs/{any} по-прежнему ведёт на docs/index.php, а в urlrewrite.php правила для него нет.

БД

Новых таблиц и полей нет. Превью TIFF хранится в существующей b_file_preview через PreviewManager::setPreviewImageId() и FilePreviewTable. Сервис обновляет TOUCHED_AT не чаще раза в сутки и обнуляет PREVIEW_IMAGE_ID, если файл превью удалён. Ошибки конвертации кешируются на час в каталоге /disk/tiff_preview/errors. Ключ кеша собирается из метаданных b_file, поэтому после замены файла ключ меняется, и старая ошибка из кеша не вернётся (TiffPreviewService::buildConversionErrorCacheId()).

Мелочи и находки

  • Первая конвертация TIFF идёт синхронно, прямо в веб-запросе, без модуля transformer. Комментарий в коде объясняет это рамками «ADR/SDD». Для неблокирующей схемы нужна отдельная постоянная очередь, а CAgent как очередь на каждый файл «не выбран». Перед Imagick сервис сверяет magic bytes TIFF и вызывает pingImage() до readImage(). Картинка ограничена 50 Мпикс и ужимается до 4096 px по большей стороне, исходник принимается до 100 МБ, на конвертацию даётся 15 секунд. Imagick получает лимиты на память 128 МБ, map 256 МБ, диск 512 МБ и один поток, после конвертации прежние значения возвращаются.
  • Одновременно конвертируют не больше двух процессов (CONVERSION_SLOT_COUNT = 2). Семафор собран на именованных блокировках БД. acquireConversionSlot() перебирает $connection->lock('disk_tiff_preview_slot_N', 0), и ещё одна блокировка ставится на конкретный файл. Кому слот не достался, получает preparing и приходит позже. Такой семафор можно перенести к себе, очереди и Redis ему не нужны.
  • Готовый PNG отдаётся по подписанному токену. Signer с солью disk.tiff.preview подписывает строку "<sourceFileId>:<previewImageId>", сверка идёт через hash_equals. Если для файла готова быстрая отдача (QuickAccess), вместо токена используется зашифрованный параметр из FileDataParameterService, для превью там появился getEncryptedPreviewFileData().
  • Публичная ссылка, созданная на версию, теперь показывает TIFF и документы из этой версии. Имя, время и FileAttributes для неё берутся из Version.
  • Из докблока docs_section.php видно, как ядро запускает такие точки входа роутинга. Они выполняются до пролога, модули в них не подключить (нет ни LANGUAGE_ID, ни $APPLICATION), поэтому резолв сделан целиком на файловой системе. Код обёрнут в замыкание, чтобы переменные не утекли в глобальную область подключаемой страницы, а сегменты с %2f и %5c отправляются на запасную страницу.

Устаревшие и удалённые API в этой версии

Символ Статус Чем заменять
CDiskExternalLinkComponent::setDocumentSessionSource() Удалено —

Читайте дальше

disk 26.1150.0 Безопасность Свежее

Disk 26.1100.100 и 26.1150.0: SDK досок больше не шлёт JWT на любой origin, а секрет по умолчанию убран

Диск приехал в коробку двумя версиями одним днём, 21 сентября 2026 года (по официальному каналу «Битрикс24 changelog»). В 26.1100.100 поменялась одна строка, и заметно это только на порталах с сессиями в режиме `separated`. 26.1150.0 закрывает канал `postMessage` в SDK досок, через который JWT и ссы...

2 мин
disk 26.1100.0 Безопасность Свежее

Disk 26.1100.0: закрыты дыры в правах на публичные ссылки и версии, единые ссылки больше не выключаются

Disk 26.1100.0 вышел в коробку 2 сентября 2026 года (по официальному каналу «Битрикс24 changelog») и закрывает три дыры в правах. Любой, кто видел файл, мог поставить или снять пароль и срок действия его публичной ссылки, подпись файла `_uls` открывала всю историю его версий, а вложения к комментари...

4 мин
voximplant 26.800.0 Ломающее Свежее

Voximplant 26.800.0: attachRecord и finish отказывают локальным адресам, searchCrmEntities отдаёт один лид

В voximplant 26.800.0 у четырёх REST-методов внешней телефонии поменялось поведение, хотя сигнатуры остались прежними. `telephony.externalCall.attachRecord` и `telephony.externalCall.finish` с `RECORD_URL` больше не скачивают записи с приватных адресов и не ходят через прокси. `searchCrmEntities` от...

4 мин
timeman 26.100.0 Ломающее Свежее

Timeman 26.100.0: время считается по IANA-зоне сотрудника, у CTimeManEntry и REST другие значения

С timeman 26.100.0 учёт рабочего времени берёт зону сотрудника из IANA-идентификатора в `b_user.TIME_ZONE` и считает смещение UTC на момент каждого события, с переходами на летнее и зимнее время. Форматы полей и ответов остались прежними, поменялись значения: `TIME_START`, `TIME_FINISH` и `DURATION`...

3 мин
Мы используем файлы cookie для улучшения работы сайта. Продолжая использовать сайт, вы соглашаетесь с нашей политикой конфиденциальности.
AI Домовой

AI Домовой История

на связи

пишет…
Нет истории чатов
AI Домовой

Нужна авторизация

Войдите, чтобы задавать вопросы AI Домовому.

Войти