calendar 26.0.0: потолок повторов RRULE, права на удалённые sharing-события и разбор ошибок REST
Релиз про повторяющиеся события и права. Появился жёсткий лимит на количество повторов, у удалённых событий по общей ссылке — отдельная проверка доступа, а REST перестал терять ошибки между вызовами. Публичные сигнатуры не удалялись, но пара изменений в поведении заметна.
Потолок на повторы
CCalendarEvent::$MAX_RRULE_COUNT = 10000, метод limitRRuleCount() обрезает RRULE.COUNT.
Ограничение очевидно откуда взялось: правило «повторять 999999 раз» — законный ICS, который приезжает из внешнего календаря, и дальше портал честно материализует экземпляры. Десять тысяч — компромисс между «ежедневно 27 лет» и «не положить базу одним импортом».
Рядом переписан пересчёт UNTIL для правил, заданных через COUNT: появились getRRuleCalculationTimezone() и calculateUntilForCountRRuleInCurrentTimezone(). Считать «двадцатое повторение» без учёта таймзоны самого события нельзя — на границе перехода на летнее время расчёт уезжает на час, а на длинной серии это сдвиг даты.
Сдвиг экземпляров при переносе серии вынесен в отдельные методы: getRecurrentEventTimestampDelta(), shiftRecurrentDateByDays(), shiftRecurrentExDatesByDays(). Появился CCalendar::normalizeRRuleByDay($byDay): array — нормализация BYDAY к ключам SU…SA. Если вы разбирали BYDAY сами, теперь есть штатный способ.
Доступ к удалённым событиям по общей ссылке
Новый DeletedSharedEventAccessChecker::canView(int $userId, EventLink $eventLink, ?Link $parentLink) разбирает три случая: ссылка на сделку CRM, на группу и на пользователя. CCalendar при просмотре удалённого sharing-события идёт через него и возвращает null при отсутствии доступа.
Сценарий, который это закрывает: событие по публичной ссылке удалили, но ссылка осталась у получателя. Раньше страница удалённого события отдавала данные всем, у кого есть URL. Теперь у неё есть своя проверка прав, привязанная к сущности, ради которой ссылку выдавали.
Под ту же задачу приехали UserPermissionsHandler::canReadDeal(int $userId, int $dealId) и MemberFilterService::filterAccessibleMemberIds(array $memberIds) — последний фильтрует участников через CSocNetUser::CanProfileView, чтобы в списке участников не светились те, чьи профили пользователю недоступны. Подключается через ServiceLocator в фабрике sharing-ссылок.
REST: секция стала необязательной, ошибки перестали копиться
В CCalendarRestService при добавлении события section больше не обязателен, если auto_detect_section !== "Y". Точнее: секция определяется автоматически при пустом section даже когда опция выставлена в "N". Формально это изменение поведения — раньше отсутствие секции при выключенной опции было ошибкой. Практически — на одну причину отказа REST-вызова меньше.
Второе: CCalendar::GetAndClearErrors() возвращает накопленные ошибки и обнуляет массив, и REST теперь пользуется им. Статический массив ошибок, который никто не чистит, — это классическая причина «второй вызов в том же хите вернул ошибку от первого».
Ещё в релизе
Integration\Booking\EventDescription::getDescription()/clearOutFromDescription()— обёртка над\Bitrix\Booking\Service\EventDescription, если модульbookingподключён. Разумный способ не тащить жёсткую зависимость.- Синхронизация:
ExceptionProcessorTraitтеперь бросаетNoLoggableExceptionвместоRecoverableMessageExceptionдляNoLogSynchronizerException, HTTP 5xx иDtoValidationException. То есть такие сообщения больше не уходят в бесконечный ретрай очереди — сообщение, которое не станет валидным от повторной попытки, повторять бессмысленно. - Office365:
EventResponseстроитDateTimeDtoиз$data['start'], а не$item['start']— похоже на обычный багфикс разбора ответа. - Из
CCalendarEvent::Editвычищены закомментированные deprecated-блоки проDAV_XML_IDиConnectEventToSection.
Схемы БД в релизе нет, но появился migration_config.json (defaultTableName = b_calendar_event) — модуль готовят к переезду на UpdateSystem\Migration, самих файлов миграций пока нет.
Что делать
- Импортируете внешние календари — знайте про потолок 10000 повторов, серии длиннее будут обрезаны.
- Разбираете
BYDAYруками — переходите наnormalizeRRuleByDay(). - Если у вас свои страницы просмотра sharing-событий, посмотрите на
DeletedSharedEventAccessChecker: та же дыра могла быть и у вас.