Landing 26.1300.0–26.1300.50: публикация больше не склеит PHP-тег, сайт по описанию через REST, а note меняет ответы REST v3
Обновление безопасности
Закрыта уязвимость или ослабленная проверка прав. Ставить в первую очередь.
Вышли landing 26.1300.0 и 26.1300.50, вместе с первой пришёл note 26.500.0. Note на БУС нет, его смотрели на коробочном Битрикс24. В landing закрыли дыру в публикации блоков и добавили REST-методы, которые собирают сайт из текстового описания. Note научился забирать к себе Базу знаний 1.0 и поменял ответы REST v3, так что интеграции с note.* проверьте до обновления.
Новое
Соберите сайт из описания через REST (landing 26.1300.0)
В REST v3 появились landing.aisite.generate и landing.aisite.status. Первый принимает description до 4000 символов и запускает генерацию, второй по id возвращает её состояние. Ответ у обоих одинаковый: id, status (running, completed или failed), siteId и pageId готового сайта, draftSiteId и draftPageId, если генерация упала уже после создания черновика, плюс message, error и errorDetails. Чужую генерацию метод показывает как несуществующую.
На портале без модуля AI придёт LANDING_AI_MODULE_UNAVAILABLE, при выключенных AI-сайтах LANDING_AI_SITES_DISABLED.
Две оговорки взяты из описания метода. Генерация занимает тот же свободный черновик, что и AI-чат портала, и чат, открытый у пользователя в этот момент, черновик теряет. Метод помечен #[Idempotent]: повтор с тем же заголовком Idempotency-Key в течение 24 часов вернёт сохранённый ответ, но только когда первый вызов уже ответил. Повтор по таймауту, пока первый ещё работает, может запустить вторую генерацию. Тот же ключ с другим телом REST v3 отклонит.
<?php declare(strict_types=1);
namespace Vendor\Sites\Service;
use Bitrix\Main\Security\Random;
use Bitrix\Main\Web\HttpClient;
use Bitrix\Main\Web\Json;
final class AiSiteStarter
{
// адрес landing.aisite.generate для вашего вебхука или приложения
public function __construct(private readonly string $methodUrl) {}
public function start(string $description, ?string $idempotencyKey = null): array
{
// ключ храните вместе с заданием и при повторе отправляйте тот же
$idempotencyKey ??= Random::getString(32);
$http = new HttpClient(['socketTimeout' => 30, 'streamTimeout' => 120]);
$http->setHeader('Content-Type', 'application/json');
$http->setHeader('Idempotency-Key', $idempotencyKey);
$body = $http->post($this->methodUrl, Json::encode(['description' => $description]));
if ($body === false) {
throw new \RuntimeException(implode('; ', $http->getError()));
}
return ['key' => $idempotencyKey, 'response' => Json::decode($body)];
}
}
Если пришёл failed с ненулевым draftSiteId, удалите черновик сами. По комментарию к DTO, без этих полей клиент не смог бы ни открыть черновик, ни удалить его, и каждая повторная попытка плодила бы новый.
note 26.500.0: импорт Базы знаний 1.0, файлы Диска и перенос через REST
Note импортирует базы из landing (sourceType = 'landing', без адреса и токена). Тексты, заголовки, таблицы, галереи, аккордеоны, FAQ, видео и ещё восемь типов блоков конвертируются в markdown, неизвестные блоки забирает запасной конвертер. Права переносятся по таблице: read даёт просмотр, edit и sett дают управление именованным субъектам (U, G, SG, DR), а глобальным (*, AU, G2) только просмотр. Модератором новой базы становится тот, кто запустил конвертацию, и больше никто. Импорт включается опцией note:landing_import_enabled, по умолчанию там N.
Вложения из Диска в документах тоже спрятаны за опцией note:disk_attachments_enabled со значением N. Права на прикреплённый файл определяет документ: note регистрирует свой коннектор через disk:onBuildAdditionalConnectorList.
В REST добавились note.document.move и note.collection.move. Документ переезжает вместе с поддеревом в другую коллекцию, под другого родителя или на новое место среди соседей. position задаёт вес сортировки, но итоговый вес модуль пересчитывает по соседям, так что сверяйтесь с ответом. Перенос в ветку с более широкой аудиторией отклоняется кодом NOTE_MOVE_ACCESS_ESCALATION. У коллекций в ответе появились accessLevel (none, view, edit, full) и isArchived, а поля, которых нет в select, модуль теперь вообще не вычисляет.
Для «Итогов года» note подключает провайдер через событие intranet:onInitSummaryProviders и считает созданные базы, статьи и прочтения. Если хотите добавить в итоги свои цифры, это готовый образец.
Что закрыли
В landing 26.1300.0 закрыли путь, по которому в контенте блока мог появиться открывающий PHP-тег (mantis 255166). Контент блока при выводе исполняется через eval, поэтому с таким тегом часть текста блока выполнилась бы на сервере как PHP. При сохранении и публикации атрибут редактора contenteditable="true" вырезался простой заменой строки, и склейка соседних фрагментов могла дать <?. Теперь Sanitizer::removeContentEditableAttribute() снимает атрибут только внутри открытого тега, а публикация сравнивает число PHP-тегов до и после своих преобразований (Sanitizer::publishableContent()).
Если тегов стало больше, в публичную часть уходит контент из базы, а в журнал событий пишется запись типа LANDING_PUBLICATION_PHP_TAG с ID блока и ID страницы в описании. Встретите такую запись — проверьте этот блок. Уже сохранённые блоки обновление не перепроверяет: тег, который лежал в базе раньше, публикация переносит как есть. Обновитесь в первую очередь там, где сайты правят не только администраторы.
Кэш операций Site\Scope\Group::getOperationsForSite() раньше хранился по ID сайта, теперь по сайту и пользователю. Консольный скрипт или агент, который меняет пользователя по ходу работы, больше не получит права на групповой сайт от предыдущего.
В note 26.500.0 корзину проверяют раньше обычных прав. По комментарию в DocumentStateResolver, обратный порядок давал утечку: документ в корзине становился виден по праву, выданному на сам документ.
Что сломается
Ссылки на Базу знаний 1.0 ведут в note (landing 26.1300.0)
Публичный адрес /knowledge/<код сайта>/.../<код страницы>/ (и /mobile/knowledge/...) теперь перенаправляет в note, если база уже сконвертирована и читатель видит новую коллекцию. Не нашлась статья по коду страницы — редирект ведёт на коллекцию. Анонимы и пользователи без доступа остаются на старой странице, редактор /kb/ не затронут. Работает только там, где установлен note, на БУС ничего не меняется.
Диалог действий редактора стал MessageBox (landing 26.1300.0)
BX.Landing.UI.Tool.ActionDialog теперь построен на BX.UI.Dialogs.MessageBox. Свойство popup создаётся в show(), до этого в нём null, и это уже не BX.PopupWindow. Классов landing-ui-dialog-action и landing-ui-dialog-action-content больше нет, action_dialog.css расширение не подключает. Если ваш код редактора обращался к popup.popupContainer или стилизовал эти классы, его придётся переписать.
note 26.500.0: проверьте клиентов REST v3
- Даты
createdAt,updatedAtиcontentUpdatedAtприходили строкой UTC вида2026-10-14T12:00:00Z. Теперь REST v3 отдаёт их в форматеDATE_ATOMсо смещением, и сравнение строками или разбор по шаблону сZсломается. note.document.tree.listбольше не возвращаетtruncated. Дерево приходит целиком, а если в базе больше 5000 статей, метод отвечает ошибкойNOTE_TREE_TOO_LARGE. На коллекцию без доступа он теперь отвечает «не найдено» вместо «доступ запрещён».note.document.getотдаёт архивные документы и документы из корзины. Отличайте их по новым полямisArchived,isTrashedиisOrphan(коллекция удалена).title,markdownи имя коллекции больше не принимают явныйnull. Раньшеnullвmarkdownмолча стирал документ.- В PHP удалены
DocumentProvider::getForRead()иDocumentReadDto, вместо нихInfrastructure\Rest\V3\Read\DocumentReader::read()иDocumentReadModelс датами-объектами. В конструктореMaterializeDocumentCommandчетвёртым встал$actorId, вOverwriteDocumentContentCommandпараметр$backlinkNotifierпоявился перед$lockService. Позиционные вызовы с переданными репозиториями сломаются, именованные аргументы переживут обновление.
Мелочи и находки
- Keyset-курсор в служебном сервисе AI-сайтов стартует с
null, а не с условияID > 0, которое по комментарию уводит оптимизатор с составного индекса(SCENARIO, ID)(26.1300.0). В 26.1300.50 порции закэшировали черезsetCacheTtl(86400)без ручной инвалидации: ORM чистит кэш таблицы при любой записи черезDataManager. - Виджет новостей на главной отсекает отложенные посты по времени сервера (раньше к отсечке прибавлялся часовой пояс пользователя), выводит не больше 50 и строит ссылку по числовому ID поста вместо символьного кода.
- У iframe виджета приложения появилось доступное имя (название приложения, обрезанное до 100 символов). Ошибку виджет озвучивает скринридеру один раз, до восстановления, а обновления данных не чаще раза в 10 секунд.
- Исправили опечатки:
checkParam('PAGE_URL_SITE_DESI GN', '')вlanding.sites(параметрPAGE_URL_SITE_DESIGNне получал значения по умолчанию),target="_bllank"вlanding.domainsиcase 'unknow'в плитке сайтов.
Устаревшие и удалённые API в этой версии
| Символ | Статус | Чем заменять |
|---|---|---|
Bitrix\Note\Public\Provider\DocumentProvider::getForRead()
|
Удалено |
Bitrix\Note\Infrastructure\Rest\V3\Read\DocumentReader::read()
|
Bitrix\Note\Public\Provider\Dto\DocumentReadDto
|
Удалено |
Bitrix\Note\Infrastructure\Rest\V3\Read\DocumentReadModel
|