Intranet 26.2000.0: журнал соглашений только администраторам, проверка стартовой страницы и «MCP-подключения» в меню коробки
Обновление безопасности
Закрыта уязвимость или ослабленная проверка прав. Ставить в первую очередь.
Intranet 26.2000.0 закрывает журнал принятых соглашений от всех, кроме администраторов, и принимает стартовой страницей левого меню только адреса внутри портала. В левом меню коробки с модулем aiassistant появился пункт «MCP-подключения», а панель чатов сменила arResult, так что переделанные копии её шаблона нужно проверить. Обновлению нужны vibecodeconnector 26.1000.0 (разбор) и humanresources 26.700.0 (разбор), прошлый разбор модуля — Intranet 26.1775.0–26.1900.0.
Новое
«MCP-подключения» в левом меню коробки
Пункт «MCP-подключения» со ссылкой SITE_DIR . 'mcp/' и ID menu_mcp_integrations добавлен в .top.menu_ext.php корня сайта и мастера портала. Коробка без .superleft.menu_ext.php в корне строит левое меню из меню типа top, поэтому пункт оказывается слева. Виден он, если установлен модуль aiassistant, в нём включён MCP-сервер коробки (isBoxMcpServerEnabled()) и текущий пользователь может работать с локальными токенами (McpEntryPointAccess::canUseLocalTokens()). По официальному описанию версии сервер включается в настройках портала. Опция aiassistant/left_menu_mcp в коробке для пункта больше не нужна, в облаке условие прежнее.
Страница /mcp/ в корне тоже новая. Без aiassistant она отвечает «Access denied», с модулем проверяет McpEntryPointAccess::canOpenMcpPage() и при отказе отдаёт 404, иначе показывает список MCP-серверов в слайдере. Меню смотрит на MCP-сервер и токены пользователя, а страница только на canOpenMcpPage(), поэтому видимость пункта и доступ к странице могут не совпасть.
Пункт Вайбкода в левом меню
Левое меню само добавляет пункт Вайбкода (menu_vibecode) на сайте по умолчанию, если установлен vibecodeconnector, модуль открыт на портале и пользователь — сотрудник интранета. Открыт ли модуль, решает опция vibecodeconnector/is_ready (в коробке по умолчанию N), так что пункт появится только там, где Вайбкод уже открыли. Он открывается в новой вкладке, стартовой страницей не становится, удалить его пользователь не может.
В модуле есть обновление, которое вставляет пункт в уже сохранённые порядки меню, общие и личные, перед пунктом Маркета (menu_marketplace_sect). Запуск этого обновления в коде модуля не найден, его может выполнить установщик обновления.
Виджет тарифа: консультация и баланс за флагом
В виджете тарифа появилась кнопка «Заказать консультацию». Она видна, если к порталу не подключён интегратор, и открывает форму партнёра BX.UI.Feedback.PartnerForm. Если данные виджета не загрузились, лоадер закрывается, а программа экранного доступа сообщает об ошибке.
Блок баланса Вайбкода (License\Widget\Content\VibeBalance) в виджет уже добавлен, но по умолчанию выключен флагом из vibecodeconnector, подробности в разборе Vibecodeconnector 26.1000.0.
Форматтер суммы вынесен в расширение intranet.vibe-balance-format. formatVibeBalance(raw, locale) берёт десятичную строку и не переводит её во float: два знака, арифметическое округление, разряды по Intl.NumberFormat, для ru неразрывный пробел и запятая, минус символом U+2212. Пригодится для своих сумм, которые приходят строкой.
import { formatVibeBalance } from 'intranet.vibe-balance-format';
formatVibeBalance('-1234567.005', 'ru'); // '−1 234 567,01'
formatVibeBalance('1234567.004', 'en'); // '1,234,567.00'
Без сборки подключите расширение через \Bitrix\Main\UI\Extension::load('intranet.vibe-balance-format') и вызывайте BX.Intranet.formatVibeBalance().
Берите ID администраторов из публичного провайдера
Bitrix\Intranet\Public\Provider\User\AdminUserProvider::getAdminUserIds() отдаёт ID администраторов портала без повторов и нулей. Метод не статический. Получатели уведомления без автора действия:
<?php
declare(strict_types=1);
namespace Local\Notify;
use Bitrix\Intranet\Public\Provider\User\AdminUserProvider;
final class AdminRecipients
{
public function __construct(
private readonly AdminUserProvider $admins = new AdminUserProvider(),
) {
}
/** @return list<int> */
public function exceptAuthor(int $authorId): array
{
return array_values(array_diff($this->admins->getAdminUserIds(), [$authorId]));
}
}
Вызывайте (new AdminRecipients())->exceptAuthor($userId) после Loader::includeModule('intranet').
Включите «Сотрудничество» сверху без перестройки левого меню
Верхнее меню «Сотрудничество» теперь включает отдельный флаг Internal\Config\Feature\CollaborationTopMenuFlag (через Main\Config\Feature) или, как раньше, CollaborationWebMenuFlag. Пункт слева и модель в настройках инструментов смотрят только на CollaborationWebMenuFlag, поэтому новый флаг левое меню не трогает. Оба флага выключены по умолчанию, экстранет-пользователи меню не получают. «Лента» и «Диск» в нём больше не помечены недоступными, пункт «Сотрудничество» слева открывается без слайдера, балун о новом меню отключён.
Слайдер мессенджера берёт уже выведенное верхнее меню вместо копии. Его ID хранят Templates\Air\CollaborationWebMenu::setRenderedMenuId() и getRenderedMenuId(), а у BX.Intranet.Bitrix24.Template.getChatMenu() появились prepareForSlider() и finishSlider().
Вход с 2FA и ошибка алиаса в приглашении
На подтверждении входа через Push OTP появилась кнопка «Назад» к выбору учётной записи, а если он недоступен, к форме входа. Для классического OTP адрес считается так же, так что кнопка есть и там. Баннер обязательной 2FA раньше обещал вход только с подтверждением в мобильном приложении, теперь перечисляет приложение, СМС, письмо и резервный код.
Ошибка с кодом EMAIL_ALIAS_NOT_ALLOWED теперь доходит до формы приглашения вместе со ссылкой «Подробнее» на статью помощи. Код ищется и среди исключений обработчиков OnBeforeUserAdd, а штатного модуля, который его выбрасывает, я не нашёл. Свой обработчик с $APPLICATION->ThrowException($text, 'EMAIL_ALIAS_NOT_ALLOWED') тоже получит эту ссылку под ошибкой, других кодов форма не пропускает.
Запрос ссылки-приглашения без отделов на портале без корневого отдела раньше падал на вызове метода у null, теперь возвращает «Нет доступного отдела, в который можно пригласить сотрудника».
Что сломается
Журнал соглашений видят только администраторы
Компонент bitrix:intranet.userconsent первым делом вызывает новый Bitrix\Intranet\UserConsent\Access::canView() и до проверки ничего не читает. Метод пропускает администратора, а в облаке ещё пользователя с правом bitrix24_config, остальные получают отказ в доступе. Раньше права не проверялись, и список соглашений вместе с журналом (пользователь, IP-адрес и страница каждого согласия) видел любой сотрудник, открывший configs/userconsent.php или settings/configs/userconsent.php.
Ваши страницы с этим компонентом тоже закрылись для обычных пользователей.
У компонента самого журнала bitrix:main.userconsent.consent.list своей проверки нет. Если он стоит на вашей странице напрямую, добавьте ту же проверку:
<?php
declare(strict_types=1);
use Bitrix\Intranet\UserConsent\Access;
use Bitrix\Main\Application;
use Bitrix\Main\Loader;
require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/header.php';
/** @global CMain $APPLICATION */
if (!Loader::includeModule('intranet') || !Access::canView()) {
ShowError('Журнал согласий доступен только администраторам');
} else {
$request = Application::getInstance()->getContext()->getRequest();
$APPLICATION->IncludeComponent('bitrix:main.userconsent.consent.list', '', [
'AGREEMENT_ID' => (int)$request->getQuery('AGREEMENT_ID'),
]);
}
require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/footer.php';
С журналом из CRM-форм вышло наоборот. Модуль crm 26.1900.0 уже проверял наличие Access::canView() и без него держал журнал закрытым для всех, теперь журнал видят администраторы с правом чтения форм.
Стартовая страница принимает только путь внутри портала
Контроллер левого меню проверяет стартовую страницу при сохранении порядка пунктов, своего пресета, главного инструмента и в setFirstPage. Проходит только относительный путь без схемы и хоста, не начинающийся с // и не совпадающий со служебным AJAX-адресом ядра. setFirstPage с другим адресом возвращает ошибку MAIN_TOOL_INVALID_ARGUMENT, сохранение сортировки такой адрес молча пропускает.
Та же проверка работает при открытии портала. Раньше там отсекались только http и //, теперь любые схемы, а вместо отброшенного адреса открывается /online/ или /stream/. Полный URL не открывался и раньше, а теперь и не сохраняется: ставьте путь от корня сайта.
Заодно пункты левого меню, которые открываются в новой вкладке, получили rel="noopener".
Обновите копию шаблона панели чатов
bitrix:intranet.chat.bar больше не всегда отдаёт $arResult['MENU_ITEMS']. На /online/ и там, где верхнее меню уже выведено, компонент не строит второе меню, а кладёт ID существующего в EXISTING_MENU_ID, и MENU_ITEMS появляется, только когда этот ключ пустой. Ещё добавились MENU_ID (может быть ID меню «Сотрудничество»), IS_COLLABORATION_MENU и MENU_CLIENT_CONFIG. Контейнер #im-chat-menu получил атрибуты data-menu-id и data-menu-mode, их читает JS слайдера, а без атрибута берётся chat-menu. Старая копия шаблона на /online/ обратится к несуществующему ключу и покажет пустое меню. Перенесите в неё блок #im-chat-menu из нового .default целиком.
Мелочи и находки
company/birthdays.phpпередаётDATE_INTERVAL= 30 вместо 14 по умолчанию, аintranet.structure.birthday.nearestдобавил интервал в ключ кеша. Раньше вызовы с разным интервалом делили один кеш.- Атрибут
langу названий языков в переключателе шапки и на странице входа берётся изb_language.CODE. Если у языка код не заполнен, атрибута не будет. - Виджеты настроек, аватара и приглашений больше не закрываются, когда из них открывают слайдер, и возвращают фокус. В английской версии виджета приглашений «Create a collab» стал «Create a project».
- Форма добавления в
intranet.absence.calendarбольше не получает заголовок иABSENCE_ELEMENT_IDформы редактирования.