Итак, задача: Необходимо при поступлении нового письма на соответствующий e-mail, заведенный в корпортале создавать задачу пользователю на обработку данных из письма, а также запускать соответствующий бизнес-процесс.
Решение. Создаем для данного письма ручное правило. Прописываем php-код:
if (CModule::IncludeModule("tasks")) {
$from = CMailUtil::ExtractMailAddress($arMessageFields['FIELD_FROM']);
$USER_GROUP = array(ИД группы пользователей для обработки);
$UFilter = array("ACTIVE" => "Y","GROUPS_ID" => $USER_GROUP);
$sort_by = "ID";
$sort_ord = "ASC";
$dbUsers = CUser::GetList($sort_by, $sort_ord, $UFilter);
while ($arUser = $dbUsers->Fetch()) {
$USERS_for_TASK[] = $arUser["ID"];
}
if(count($USERS_for_TASK)>0){
$arFields = Array(
"TITLE" => $from.': Обработать письмо от '.date('d.m.Y H:i:s') ,
"DESCRIPTION" => $arMessageFields['TITLE'].$arMessageFields['BODY'],
"RESPONSIBLE_ID" => $USERS_for_TASK[0],
"STATUS" => 2,
"CREATED_BY" => $USERS_for_TASK[0]
);
$obTask = new CTasks;
$ID = $obTask->Add($arFields);
$success = ($ID > 0);
if ($success) {
if (count($USERS_for_TASK) > 1) {
$Accomplices = $USERS_for_TASK;
unset($Accomplices[0]);
CTasks::AddAccomplices($ID, $Accomplices);
}
//добавляем к задаче вложения
$dbr_attach = CMailAttachment::GetList(Array("NAME" => "ASC", "ID" => "ASC"), Array("MESSAGE_ID" => $arMessageFields['ID']));
while ($dbr_attach_arr = $dbr_attach->GetNext()) {
if ($dbr_attach_arr["FILE_NAME"]=='1.tmp' ||
preg_match_all('/\\.(?:exe|html|phtml|pl|js|htm|py|php|php4|php3|phtml|shtml)$/i', $dbr_attach_arr["FILE_NAME"], $p_matches, PREG_PATTERN_ORDER))
continue;
$attach_id = $dbr_attach_arr["ID"];
$dbr = CMailAttachment::GetByID($attach_id);
if($dbr_arr = $dbr->Fetch())
{
$fname = $_SERVER['DOCUMENT_ROOT']."/upload/from_mail/".$dbr_attach_arr["FILE_NAME"];
$handle = fopen($fname, 'wb');
fwrite($handle, $dbr_arr["FILE_DATA"]);
fclose($handle);
$arFile = CFile::MakeFileArray($fname);
$arFile["old_file"] = "";
$arFile["del"] = "Y";
$arFile["MODULE_ID"] = "tasks";
$fid[] = CFile::SaveFile($arFile, "tasks");
}
}
CTasks::AddFiles($ID, $fid);
if(CModule::IncludeModule("im")){
foreach ($USERS_for_TASK as $taskUser) {
$TaskLink = $_SERVER['HTTP_HOST'].'/company/personal/user/' . $taskUser . '/tasks/task/view/' . $ID . '/';
$arMessageFields = array(
"TO_USER_ID" => $taskUser, // получатель
"FROM_USER_ID" => 0, // отправитель (может быть >0)
"NOTIFY_TYPE" => IM_NOTIFY_SYSTEM, // тип уведомления
"NOTIFY_MODULE" => "tasks", // модуль запросивший отправку уведомления
"NOTIFY_TAG" => "FAX-TASKS", // символьный тэг для группировки (будет выведено только одно сообщение), если это не требуется - не задаем параметр
// текст уведомления на сайте (доступен html и бб-коды)
"NOTIFY_MESSAGE" => '[b]Внимание:[/b] получен новый заказ с сайта. Для просмотра перейдите по <a href="' . $TaskLink . '">ссылке</a>',
);
CIMNotify::Add($arMessageFields);
}
}
if(
CModule::IncludeModule("bizproc")
&&
CModule::IncludeModule("iblock")
){
$arErrorsTmp = array();
$el = new CIBlockElement;
$PROP = array();
$arLoadProductArray = Array(
"MODIFIED_BY" => $taskUser,
"IBLOCK_SECTION_ID" => false,
"IBLOCK_ID" => ИД_инфоблока_с_бизнес-процессом,
"PROPERTY_VALUES"=> $PROP,
"NAME" => $from.': Обработать письмо '.date('d.m.Y H:i:s'),
"ACTIVE" => "Y",
"PREVIEW_TEXT" => $arMessageFields['TITLE'].$arMessageFields['BODY'],
);
if($PRODUCT_ID = $el->Add($arLoadProductArray)){
$arWorkflowParameters = array('TaskID'=>$ID);
$wfId = CBPDocument::StartWorkflow(
ИД_шаблона_инфоблока,
array("bizproc", "CBPVirtualDocument", $PRODUCT_ID),
array_merge($arWorkflowParameters, array("TargetUser" => "user_".$taskUser)),
$arErrorsTmp
);
} else {
$arErrorsTmp[] = "Error: ".$el->LAST_ERROR;
}
if (count($arErrorsTmp) > 0)
{
foreach ($arErrorsTmp as $e)
$errorMessage .= "[".$e["code"]."] ".$e["message"]."";
$arMessageFields = array(
"TO_USER_ID" => $taskUser, // получатель
"FROM_USER_ID" => 0, // отправитель (может быть >0)
"NOTIFY_TYPE" => IM_NOTIFY_SYSTEM, // тип уведомления
"NOTIFY_MODULE" => "tasks", // модуль запросивший отправку уведомления
"NOTIFY_TAG" => "FAX-TASKS", // символьный тэг для группировки (будет выведено только одно сообщение), если это не требуется - не задаем параметр
// текст уведомления на сайте (доступен html и бб-коды)
"NOTIFY_MESSAGE" => 'ERROR: '.$errorMessage,
);
CIMNotify::Add($arMessageFields);
}
}
}
}
}
После создания новой задачи (комментарии к алгоритму в предыдущем посте) необходимо запустить новый экземпляр бизнес-процесса. Сам бизнес-процесс должен быть уже готов вместе с шаблоном к нему. Создаем элемент инфоблока с бизнес-процессами. Запускаем новый экземпляр бизнес-процесса для созданного элемента инфоблока.
Алексей, подскажите, пожалуйста, как реализовать выставление различного автоматического дедлайна по заявкам из письма и по заявкам, созданным "вручную". Создано пользовательское поле "да/нет", в форме оно "да", в init.php делаю проверку по этому полю и выставление дедлайна по обработчику, но весь тот блок в init.php меняет ВСЕ задачи - и из формы, и ручные.. как будто этого поля при работе init.php и нету вовсе...
Алексей Крячко, так навскидку даже не подскажу... Давно не работал с задачами. Тут нужно проверку делать. + событие стоит у техподдержки уточнить, какое нужно использовать для обработки значений пользовательских полей....
AddEventHandler("tasks", "OnTaskAdd",array("AddAuditors","AudHandler")); class AddAuditors { function AudHandler($ID, $arFields) { CModule::IncludeModule('tasks'); if($arFields["UF_TASKS_USER_TICKET"]=="0") { if (!isset($arTask["DEADLINE"])) { $date1 = date('d.m.Y H:i:s', strtotime('+24 hours')); $TimeToDeadline = Array( "DEADLINE" => $date1); $obTask = new CTasks; $success = $obTask->Update($ID, $TimeToDeadline); } }
"UF_TASKS_USER_TICKET"- искомое пользовательское поле (да/нет). Возвращаемые переменные очень странные - то 0, то 1, то Y. Тип- string, поэтому нолик в условии взят в кавычки(иначе не работает). Разобрались вместе с Евгением Куклиным, огромное ему спасибо, просто мегамозг.
Бизнес-процессы на данный момент имеют только самую базовую поддержку расширенных прав элементов и, что часто самое неприятное, не могут изменять существующие права элемента, а только добавлять свои, если они не совпадают с уже существующими. Это существенный недостаток, так как не позволяет динамически управлять правами на документы в соответствии с заложенной вами логикой бизнес-процесса. Например, дать доступ к документу только тем сотрудникам, которые указаны в бизнес-процессе, а остальным запретить. Или запретить изменять документ автору после какого-то этапа. И тому подобные кейзы. Поддержка такого функционала обещается в дальнейшем, с полным набором объектов новых расширенных полномочий, но когда это будет - доподлинно не известно никому, а работать нужно уже сейчас. Поэтому по просьбе некоторых пользователей собрал мод стандартного активити установки прав на основе своих хаков модулей. Собирал "на коленке", особо не проверяя, так что перед использованием на рабочих проектах настоятельно рекомендую всесторонне протестировать.
Теперь ближе к делу. Установка прав на бизнес-процесс и объект, над которым он происходит, производится методом DocumentService->SetPermissions(), который в свою очередь вызывает обёртку объекта для бизнес-процессов в соответствующем модуле. Для библиотеки документов и инфоблоков это класс CIBlockDocument и его соответствующий метод SetPermissions(), в нём-то и происходит обработка прав, которая нас на текущий момент не устраивает. Поэтому мы выдернем метод из класса и поместим в наше действие, где переписываем его под наши нужды. Но так как DocumentService->SetPermissions() вызывается не непосредственно из активити, а опосредованно, через метод StateService->SetStatePermissions(), то придётся и последний выдёргивать к нам, чтобы не ломать его в модуле. Итак, склеив и модифицировав функционал двух указанных выше методов, мы получим примерно следующее (с пояснениями "по коду дела"):
public function SetStatePermissionsHack($documentId)
{
global $DB;
$arStatePermissions = $this->Permission; // массив полномочий
$workflowId = trim($this->GetWorkflowInstanceId()); // ИД БП
$bRewrite = $this->Rewrite; // наш способ работы с правами
list($moduleId, $entity, $documentType) = $this->GetDocumentType(); // дёргаем тип документа
if (strlen($workflowId) <= 0)
throw new Exception("workflowId");
/* === Блок работы со штатными правами БП из StateService->SetStatePermissions() ===
Идеологически плохой и неправильный кусок, так как база может поменяться, а активити менять за вас никто не будет.
Но, выбора, в общем-то, нет */
if (in_array($bRewrite, array(self::ClearPermissions, self::ClearAllPermissions)))
{
$DB->Query(
"DELETE FR OM b_bp_workflow_permissions ".
"WH ERE WORKFLOW_ID = '".$DB->ForSql($workflowId)."' "
);
}
/* Как видно ниже, написан он тоже не ахти, никакой защиты от дублей и пустышек, просто фигачим всё, что есть, поэтому в правах БП часто много мусора */
foreach ($arStatePermissions as $taskId => $arUsers)
{
foreach ($arUsers as $user)
{
$DB->Query(
"INS ERT INTO b_bp_workflow_permissions (WORKFLOW_ID, OBJECT_ID, PERMISSION) ".
"VAL UES ('".$DB->ForSql($workflowId)."', '".$DB->ForSql($user)."', '".$DB->ForSql($taskId)."')"
);
}
}
/* === Конец блока работы со штатными правами БП === */
$documentId = intval($documentId);
if ($documentId <= 0)
throw new CBPArgumentNullException("documentId");
$iblockId = intval(substr($documentType, strlen("iblock_")));
if ($iblockId <= 0)
throw new CBPArgumentOutOfRangeException("documentType", $documentType);
if (CIBlock::GetArrayByID($iblockId, "RIGHTS_MODE") !== "E") // нас интересуют только расширенные права
return;
$i = 0;
$l = strlen("user_");
$arNew = array(); // Массив новых прав
$arNewGroupCodes = array(); // Группкоды новых прав
foreach ($arStatePermissions as $taskId => $arUsers)
{
foreach ($arUsers as $user)
{
if (!empty($user))
{
$gc = null;
if ($user == 'author')
{
$gc = "CR"; // Родная роль "Автор" инфоблоков, вместо использования конкретного пользователя, как сейчас. Позволяет гибче управлять правами в дальнейшем
}
elseif (substr($user,0,2) != "{=") // тупозащита для отсечения переданных данных не о юзере, а макросов переменных или параметров. игнорим, чтобы не было мусора в правах иб
{
$gc = ((substr($user, 0, $l) == "user_") ? "U".substr($user, $l) : "G".$user); // либо юзер, либо юзергрупп, другое не поддерживаем
}
if ($gc != null)
{
$arNew["n".$i] = array("GROUP_CODE" => $gc, "TASK_ID" => $taskId, "XML_ID" => $workflowId); // забиваем массив новых прав
$arNewGroupCodes[] = $gc; // сохраняем группкод для дальнейшего парсинга старых прав
$i++;
}
}
}
}
$ob = new CIBlockElementRights($iblockId, $documentId); // создаём объект прав и инициализируем нашим элементом
if ($bRewrite == self::ClearAllPermissions) // если нам не нужны старые права, мы их просто не извлекаем, делаем пустой массив
$ar = array();
else // иначе дёргаем текущие права на элемент
$ar = $ob->GetRights();
if ($bRewrite != self::AddPermissions && $bRewrite != self::ClearAllPermissions) // парсим права только если надо
{
foreach ($ar as $i => $arRight)
{
if ($bRewrite == self::ClearPermissions && $arRight["XML_ID"] == $workflowId) // если очищаем старые права данного БП
unset($ar[$i]);
elseif ($bRewrite == self::RewritePermissions && $arRight["XML_ID"] == $workflowId && in_array($arRight["GROUP_CODE"], $arNewGroupCodes)) // если заменяем права данного БП при совпадении группкода
unset($ar[$i]);
elseif ($bRewrite == self::RewriteAllPermissions && in_array($arRight["GROUP_CODE"], $arNewGroupCodes)) // если заменяем любые права при совпадении
unset($ar[$i]);
}
}
$ar = $ar+$arNew; // склеиваем массивы без потери ключей
$ob->SetRights($ar); // устанавливаем права на элемент, радуемся
}
Такой вот немного заковыристый метод получился. Он вводит пять возможных способов работы с правами, вместо существующих двух (которые эквивалентны нашим новым AddPermissions и ClearPermissions). Вот эти константы:
const AddPermissions = 1; // Добавляем права БП, приоритет старых прав элемента и БП
const RewritePermissions = 2; // Перетираем старые права БП при совпадении, приоритет прав элемента
const ClearPermissions = 3; // Удаляем старые полномочия БП, приоритет прав элемента
const RewriteAllPermissions = 4;// Перетираем любые полномочия при совпадении, приоритет новых прав БП
const ClearAllPermissions = 5; // Удаляем все старые полномочия
Новый метод и наши константы мы разместим в кастомном, откопированном с оригинального SetPermissionsActivity действии, которое обзовём SetPermissionsActivityExRights. Помимо этого, нам ещё нужно модифицировать часть, которая вызывается при исполнении действия, чтобы она обращалась к нашему новому методу в нужных нам случаях, и работала как обычно в других.
public function Execute()
{
// модифицируем метод, вебдав и иблоки отправляем по новому пути, всё остальное - по старому
list($moduleId, $entity, $documentId) = CBPHelper::ParseDocumentId($this->GetDocumentId());
if ($moduleId == "iblock" || $moduleId == "webdav")
$this->SetStatePermissionsHack($documentId); // используем нашу функцию
else
{
$bRewriteOld = true; // стандартный тип перезаписи прав
if ($this->Rewrite == self::AddPermissions) // не стираем старые права только в одном случае
$bRewriteOld = false;
$stateService = $this->workflow->GetService("StateService");
$stateService->SetStatePermissions($this->GetWorkflowInstanceId(), $this->Permission, $bRewriteOld);
}
return CBPActivityExecutionStatus::Closed;
}
Ещё немного требуется изменить свойства действия и диалог параметров действия, но это просто, поэтому не буду заострять внимания. Полученное действие позволяет гибко управлять правами элемента: добавлять только новые права, заменять/удалять права, установленные ранее из нашего бизнес-процесса, заменять или вовсе удалять любые права, ранее установленные или отнаследованные элементом. Готовое действие можно взять в приложенном архиве, потом положить его в /bitrix/activities/custom и использовать.
Ещё раз повторюсь о необходимости тестирования, так как действие собирал быстро и у меня времени погонять его на данный момент нет. Использовать на собственный страх и риск.
ЗЫ. Может возникнуть вопрос, почему нет никакой обработки дублирующихся прав для операции AddPermissions - просто потому, что дубли самостоятельно откинет метод $ob->SetRights($ar), так что я не стал дублировать эту работу.
UPD: Отмечу одну, возможно, неочевидную вещь: может возникнуть вопрос, а как быть, если используется управление правами через права статусов бизнес-процесса, необходимо будет помимо установки прав непосредственно в настройках статуса дополнять этим кастомным действием установки прав? Ответ - нет, этого не требуется, потому что текущая реализация схемы установки прав из БП не позволяет изменять права, установленные не из нашего бизнес-процесса, права же, установленные из текущего бизнес-процесса ранее, успешно меняются текущим функционалом. Если подумать, отсюда следует, что нам достаточно единожды за весь бизнес-процесс применить новое кастомное действие, например, при входе в первый статус, и сбросить уже существовавшие права элемента, записать новые права из бизнес-процесса, и в дальнейшем мы сможем уже переопределять их штатными механизмами, в т.ч. и через права на статус БП.
Если поразмышлять ещё немного, то становится очевидным, что и без этого кастомного действия можно обойтись, достаточно только сбросить текущие права элемента, а затем штатными активити установить требуемые новые. Такой сброс можно осуществить, например, действием PHP-код, в котором использовать примерно следующий код из вышеприведённого примера:
list($moduleId, $entity, $documentId) = CBPHelper::ParseDocumentId($this->GetDocumentId()); // получаем ИД документа
list($moduleId, $entity, $documentType) = $this->GetDocumentType(); // дёргаем тип документа
$documentId = intval($documentId);
if ($documentId <= 0)
throw new CBPArgumentNullException("documentId");
$iblockId = intval(substr($documentType, strlen("iblock_")));
if ($iblockId <= 0)
throw new CBPArgumentOutOfRangeException("documentType", $documentType);
$ar = array();
$ob = new CIBlockElementRights($iblockId, $documentId); // создаём объект прав и инициализируем нашим элементом
$ob->SetRights($ar); // сбрасываем все текущие права элемента
Этого достаточно, а дальше уже назначаем и изменяем права элемента штатными механизмами.
Пардон за большое "лирическое отступление" (или вступление? пусть не лирическое): "Бизнес-процессы организации" - так в документации окрещён функционал, штатно располагающийся в разделе "Сервисы - Бизнес-процессы" в КП и реализованный с помощью комплексного компонента bitrix:bizproc.wizards (для краткости - БПО). Я пользуюсь в основном им. Главное преимущество - гибкое управление правами просмотра элементов, как на группы, так и на конкретных пользователей (теперь несколько нивелированное появлением расширенных прав в инфоблоках. Отмечу, что на текущий момент бизнес-процессы не могут переопределять расширенные права элементов, только добавлять свои и только если они не совпадают с правами элемента!). Причём фишка появилась задолго до появления расширенных прав ИБ. Права на доступ к элементу можно выставлять динамически из бизнес-процесса, пользуясь параметрами, переменными, определять кодом, в зависимости от требований на доступ к документу во время маршрута. Кроме этого - возможность конструировать ИБ без админки, через интерфейс дизайнера БП. Также компонент не имеет формы редактирования элемента, поэтому все изменения в нём производятся только из БП, только те поля и свойства, которые мы определим в шаблоне БП. И ещё одна замечательная фишка, которая весной прошла неафишированной и незамеченной - возможность каждому шаблону БПО назначить шаблоны компонентов запроса параметров при запуске БП, списка бизнес-процессов и формы просмотра элемента. Отличная вещь, позволяющая привычным механизмом шаблонов компонентов делать формы или список с требуемым отображением и логикой именно для этого бизнес-процесса (там есть ошибка по подключению шаблона списка, но скоро, думаю, будет исправлена, тикет есть).
Итак, как уже описал, основное из преимуществ бизнес-процессов организации (БПО) - ограничение прав доступа к документу. Особенно актуально для какой-нибудь корпоративной информации с ограниченным доступом (например, согласование документов - ограничение списком утверждающих документ и исполнителем, регистрация корреспонденции - ограничение регистратором, адресатами, исполнителем и руководством). Часто при использовании механизма БПО для ограничения доступа возникает следующая проблема - доступ к документу установлен, документ прошёл и завершил свой маршрут и... навсегда остаётся в системе с такими правами доступа, так как штатного механизма переопределить их в дальнейшем - увы, не предусмотрено. А потребность такая возникает довольно часто - уволился сотрудник, пришёл другой на замену, должен иметь доступ к этой информации. Ну или просто захотелось ознакомить кого-то ещё, кого не предусмотрели раньше. Но всё не безнадёжно - такую фишку можно организовать штатными средствами бизнес-процессов - с помощью зацикленной "команды" и действий "Запрос дополнительной информации" и "Установка прав". Кроме того, факт выдачи кому-то прав таким образом будет зафиксирован в логе бизнес-процесса.
В шаблоне бизнес-процесса добавляем следующую последовательность действий:
Цикл делаем вечным
Настраиваем права доступа к команде
Запрашиваем информацию у пользователя. Обратите внимание на поле "Заполняют сотрудники", в нём вызываем диалог по кнопке, идём в "Дополнительные результаты" и выбираем "Пользователь, отправивший команду" (фишка появилась тоже относительно недавно, раньше это выполнялось через специальный алиас модуля, выбирающий текущего пользователя)
Ну и установка прав, не забываем снять крыжик "Перезаписать", иначе новые права будут не добавлены, а заменят старые
Вот, в общем, и всё. Мы легко сделали нужный функционал и получили вечный бизнес-процесс, в котором в любой момент пользователи с полномочиями выполнения созданной команды смогут добавить права на просмотр элемента какому-нибудь сотруднику и это отразится в логе БП. При необходимости можно добавить уведомления добавленным пользователям или таким же образом можно подцепить и другие команды в этот финальный цикл с помощью распараллеливания обработки.
Немного усложним задачу - допустим, мы хотим, чтобы пользователи, которым мы только что предоставили доступ, также в дальнейшем могли выполнять команду предоставления доступа. Для этого заранее через интерфейс дизайнера БП создадим переменную, например, commandUsers, тип - привязка к пользователю, множественная. После чего в настройках действия "Команда" к списку тех, кто может выполнять команду, добавим эту переменную. А после действия "Установка прав" добавим действие "PHP-код" следующего содержания Всё. Теперь при каждой итерации цикла в действии "Команда" у нас будет переменная с обновлённым списком пользователей, которым доступно выполнение команды. Подход определения пользователей через переменную универсален, его можно использовать и во многих других случаях, например, динамически составлять список получателей уведомлений, пользователей для утверждения, ознакомления с документом, назначать права и т.п.
Для бизнес-процессов со статусами можно действовать по похожей схеме, но зацикливая сам статус. Установка прав будет производиться в этом случае не отдельным действием "Установка прав" (так как это бесполезно при зацикленном статусе - при возврате в статус каждый раз права будут переписаны правами этого статуса):
В финальном статусе в правах статуса устанавливаем переменную, которую будем в дальнейшем заполнять;
Добавляем к статусу команду, переходим к редактированию её подпроцесса;
Добавляем запрос дополнительной информации по пользователям, которым нужно предоставить доступ;
Затем действие PHP-код, аналогичное приведённому выше, в котором склеиваем массив вновь запрошенных пользователей с переменной, которая указана в правах на статус;
Затем действие "Установить статус", в котором выбираем этот же финальный статус, в котором сейчас находимся, чем его зацикливаем.
Всё.
Справедливости ради отмечу, что описанный способ зацикливания команды в конце БП, тем не менее, не лишён недостатков - такой бизнес-процесс никогда не будет завершён, соответственно его рабочие данные не будут удалены из системы, запись со всеми данными БП будет висеть в базе. Также это плохо тем, что если вы запрашивали в бизнес-процессе какие-то файлы в параметрах или переменных - они тоже будут висеть вечно и занимать место на диске, так как сборщик мусора, уничтожающий временные данные по завершению процесса, запущен не будет. PS. На самом деле, есть два метода, которые вызываются при завершении процесса и грохающие все связанные с параметрами и переменными БП файлы соответственно. Но это ковровое бомбометание, не всегда осознанное и безопасное, в дальнейшем обещают добавить методы, позволяющие точечно зачистить параметры и избавится от проблемы временных файлов. PPS. Текущие методы это CBPActivity::ClearProperties() и CBPActivity::ClearVariables(), которые подчищают все файлы, связанные с параметрами и переменными соответственно. Можно использовать в действии PHP-код, как $this->ClearProperties() и $this->ClearVariables(), но используйте с осторожностью, если уверены, что эти файлы вам больше не понадобятся в процессе (уже сохранены в свойства инфоблока). Сами параметры и переменные при этом не пострадают, просто будут хранить айдишники уже несуществующих файлов.
UPD: добавил пример использования переменной для определения списка пользователей и её динамической сборки через действие "PHP-код" UPD2: добавил краткое описание для бизнес-процесса со статусами. UPD3: добавил методы для зачистки временных файлов.
Группы на сайте создаются не только сотрудниками «1С-Битрикс», но и партнерами компании. Поэтому мнения участников групп могут не совпадать с позицией компании «1С-Битрикс».