Tasks 26.600.0: наблюдатель по запросу доступа, эпики в новой карточке и аватарки чатов на всех порталах
Ломающее обновление
Удалены или изменены публичные API — прикладной код может перестать работать.
В задачах 26.600.0 для коробочного Битрикс24 сотрудника, который запросил доступ, можно добавить в наблюдатели прямо из уведомления, эпики создаются из новой карточки, а задачи завершаются из «Моего плана». Заодно обновление само включает статусные аватарки чатов задач, начинает отклонять пустые результаты и закрывает дыру в правах скрама: эпик мог создать участник группы с доступом только на чтение. Если ваш код пишет результаты задач, повторно завершает закрытые задачи или трогает JS тулбара, проверьте его до установки.
Новое
Добавляйте в наблюдатели из уведомления
Уведомление о запросе доступа стало кликабельным и заканчивается ссылкой «Добавить в наблюдатели». За ней стоит AJAX tasks.V2.Task.Access.addObserver с параметрами taskId и userId. Кандидат попадёт в наблюдатели, если нажавший может смотреть задачу (или он суперпользователь), правило addAuditors разрешает добавить этого кандидата, кандидат активен и от него есть запрос доступа. После добавления запрос удаляется. Ответ выглядит как {outcome} со значением success, already_observer, user_inactive, not_allowed_by_policy, no_permission или no_request. Из PHP то же делает команда Bitrix\Tasks\V2\Public\Command\Task\Access\AddObserverCommand. Как это выглядит в мобильном приложении, мы разобрали в tasksmobile 26.900.0.
Создавайте эпики из новой карточки
В карточке V2 эпик создаётся прямо из селектора. У черновика задачи ещё нет id, поэтому право проверяет отдельный AJAX tasks.V2.Scrum.getEpicCreationRight(int $groupId), а у сохранённой задачи getTaskInfo сразу отдаёт canCreateEpic и canUpdateScrumFields. Если эпик лежит в группе, где у пользователя нет права чтения, карточка получит {id, isRestricted: true} без названия и цвета. Раньше на таком эпике запрос падал с «Access denied».
REST tasks.api.scrum.task.get теперь возвращает и groupId.
Завершайте задачи из «Моего плана»
Из «Моего плана» задачу теперь можно принять, а при завершении сразу закрыть невыполненные пункты чек-листа. У компонента tasks.kanban для этого есть действия prepareMyPlanTaskCompletion (отдаёт снимок чек-листа и ничего не меняет), approveMyPlanTask и параметр completeChecklist у completeTask. Работают они только при PERSONAL = Y, вне таймлайна и вне группового режима.
Берите статус и срок из триггера «Просрочена задача»
Триггер БП отдаёт два новых поля: TASK_STATUS (список) и TASK_DEADLINE. Список статусов вынесли в публичный Bitrix\Tasks\Integration\Bizproc\Document\Task::getStatusOptions(), его можно взять для своих активити. TASK_CREATED_DATE разбирается через CBPHelper::makeTimestamp(), раньше значение приводилось к (int).
Включите автоснятие mute, если нужно
Бывает, что наблюдатель заглушил чат или задачу, а потом сам пишет в чат задачи из интерфейса. Модуль умеет снимать для него mute с задачи, но по умолчанию это выключено:
\Bitrix\Main\Config\Option::set('tasks', 'task_chat_auto_unmute_available', 'Y');
Что сломается
Решите, нужны ли вам статусные аватарки
Раньше аватарки чатов по статусу задачи включала опция chat_avatar_feature модуля tasks, по умолчанию N. Теперь это флаг Bitrix\Tasks\Feature\ChatAvatarFeature поверх Bitrix\Main\Config\Feature. По комментарию в коде, при обновлении регистрируется разовый агент ChatAvatarFeatureInitializer::activateForReleaseAgent() (сам апдейтер в файлы модуля не попадает). Он записывает в опцию Y, если её нет, и запускает степпер по всем задачам с чатом. Пропускаются только порталы, где опция явно не Y или флаг явно выключен. Чат, которому поставили свою, не системную аватарку, модуль не трогает. Состояние «скоро просрочена» аватаркой больше не показывается, картинку expired_soon.png удалили. При включённом флаге чат задачи не создаётся, если файл аватарки недоступен: Chat::addChat() вернёт ошибку Task chat avatar file is not available.
Если аватарки не нужны, запишите опцию явно до обновления: Option::set('tasks', 'chat_avatar_feature', 'N'). После обновления выключить флаг можно через Feature::disable(ChatAvatarFeature::class), но уже выставленные аватарки останутся.
Не отправляйте пустой результат
Результат без файлов теперь обязан содержать текст. Пробелы и одни пустые [p], [quote], [code], [spoiler] или [list] текстом не считаются. Это касается V2\Public\Command\Task\Result\AddResultHandler, UpdateResultHandler и всего, что их вызывает: обработчик бросает InvalidArgumentException «Добавьте текст или файл в результат», а команда через run() вернёт ту же ошибку в Result. Результат из сообщения чата (createFromMessage()) по-прежнему получает заголовок по умолчанию, если текст пуст.
Если ваша интеграция пишет результат сама и текст бывает пустым, решите, что с ним делать, до вызова. Например, подставьте свой текст:
<?php declare(strict_types=1);
namespace Vendor\Reports\Application\Service;
use Bitrix\Main\Error;
use Bitrix\Main\Loader;
use Bitrix\Main\Result;
use Bitrix\Tasks\V2\Internal\Entity;
use Bitrix\Tasks\V2\Public\Command\Task\Result\AddResultCommand;
final class TaskResultWriter
{
// Пустые [p], [quote], [code], [spoiler] и [list] tasks считает пустым текстом
private const BLOCK_TAGS = '/\[\/?(?:p|quote|code|spoiler|list)(?:=[^\]]*)?]|\[\*]/iu';
public function __construct(
private readonly string $fallbackText = 'Выполнено, без комментария',
) {}
public function add(int $taskId, int $userId, string $text): Result
{
if (!Loader::includeModule('tasks')) {
return (new Result())->addError(new Error('Модуль tasks не установлен'));
}
if ($taskId <= 0 || $userId <= 0) {
return (new Result())->addError(new Error('Нужны положительные taskId и userId'));
}
if ($this->isBlank($text)) {
$text = $this->fallbackText;
}
// Исключение из ResultService команда превращает в ошибку Result
return (new AddResultCommand(
result: new Entity\Result(taskId: $taskId, text: $text),
userId: $userId,
))->run();
}
private function isBlank(string $text): bool
{
$plain = preg_replace(self::BLOCK_TAGS, '', $text) ?? '';
return preg_match('/[^\s\x{FEFF}]/u', $plain) !== 1;
}
}
Проверьте права на повторное завершение
В 26.500.0 tasks.V2.Task.Status.complete для задачи в финальном статусе проверял только чтение и отвечал успехом (прошлый разбор). Теперь метод проверяет то же правило, что и для открытой задачи: право завершить или принять задачу и правило обязательного результата. У задачи в статусе «Завершена» все три проверки отказывают, поэтому повторный вызов вернёт ошибку доступа любому, даже постановщику и администратору. Задачу в статусе «Ждёт контроля» метод пропустит, если пользователь может её завершить или принять: закроет чек-лист при completeWithChecklist и вызовет завершение ещё раз. Прежний успешный ответ для закрытой задачи остался только у завершения из «Моего плана» в tasks.kanban.
Эпик создаёт тот, кто может создавать задачи
bitrix:tasks.scrum.epic.createEpic теперь требует Group::ACTION_CREATE_TASKS в группе и без него отвечает ошибкой TASKS_EC_02 («У вас нет доступа.»). Раньше хватало права читать задачи группы, так что эпик мог создать участник с доступом только на чтение.
Уберите свой JS вокруг тулбара
Шаблон .default компонента tasks.interface.header лишился script.js, а вместе с ним и класса BX.Tasks.InterfaceToolbar. У BX.Tasks.Component.TasksToolbar из tasks.interface.toolbar остались getInstance, addInstance и construct. Методы render, rerender, onUserCounter, prepareCounters и подписку на pull user_counter удалили, tasks.interface.topmenu больше не вызывает toolbar.rerender(). Из JS_DATA в result_modifier.php убраны counters, foreign_counters, templates, buttons, classes и messages. Видимого эффекта эти методы и раньше не давали: render() начинался с return, а rerender() был пустым. Теперь их вызов из своего JS упадёт с TypeError, а удалённые ключи JS_DATA придут undefined, так что вызовы удалите.
Мелочи и находки
Item\Task\Template::find()отдаёт вRESPONSIBLES,ACCOMPLICESиAUDITORSмассивы id. Раньше туда попадалаserialize()-строка, которую поле принимало за одного участника, аAUDITORSиз-за копипасты не заполнялся вовсе. СоседнийDEPENDS_ONв том же методе по-прежнему берётся не из того массива.- Если вы наследуете
CheckListFacadeи переопределяетеgetObjectStructuredRoots(), добавьте параметрbool $shouldRecountSortIndexes = true, иначе PHP упадёт с fatal о несовместимой декларации.merge()при этом больше не пересчитываетSORT_INDEX: уникальные индексы из запроса сохраняются как есть, даже с пропусками. - Форма шаблона задачи (
tasks.task.template, шаблоны.defaultиflow) больше не стирает при сохранении участников, для которых не загрузилась запись пользователя. Кастомные копии этих шаблонов исправления не получат.
Устаревшие и удалённые API в этой версии
| Символ | Статус | Чем заменять |
|---|---|---|
BX.Tasks.InterfaceToolbar
|
Удалено | — |
BX.Tasks.Component.TasksToolbar.prototype.render()
|
Удалено | — |
BX.Tasks.Component.TasksToolbar.prototype.rerender()
|
Удалено | — |
BX.Tasks.Component.TasksToolbar.prototype.onUserCounter()
|
Удалено | — |
BX.Tasks.Component.TasksToolbar.prototype.prepareCounters()
|
Удалено | — |