static function sendUpdateMessage($arFields, $arTask, $bSpawnedByAgent = false, array $parameters = array())
{
// previous fields = $arTask
// updated fields = $arFields
if (self::useNewNotifications())
{
$task = BitrixTasksInternalsRegistryTaskRegistry::getInstance()
->drop((int)$arTask['ID'])
->getObject((int)$arTask['ID'], true);
if (!$task)
{
return;
}
$controller = new BitrixTasksInternalsNotificationController();
$controller->onTaskUpdated($task, $arFields, $arTask, ['spawned_by_agent' => $bSpawnedByAgent]);
$controller->push();
return;
}
$occurAsUserId = self::getOccurAsUserId($arFields, $arTask, $bSpawnedByAgent, $parameters);
$effectiveUserId = self::getEffectiveUserId($arFields, $arTask, $bSpawnedByAgent, $parameters);
// generally, $occurAsUserId === $effectiveUserId, but sometimes dont
/*
$bSpawnedByAgent === true means that this task was "createdupdated" on angent, and
in case of that, message author is defined as $arTask['CRAETED_BY'] (below)
*/
if (!$bSpawnedByAgent && ($parameters['THROTTLE_MESSAGES'] ?? null))
{
AgentManager::checkAgentIsAlive("notificationThrottleRelease", 300);
ThrottleTable::submitUpdateMessage($arTask['ID'], $occurAsUserId, $arTask, $arFields);
return;
}
$isBbCodeDescription = true;
if (isset($arFields['DESCRIPTION_IN_BBCODE']))
{
if ($arFields['DESCRIPTION_IN_BBCODE'] === 'N')
$isBbCodeDescription = false;
}
elseif (isset($arTask['DESCRIPTION_IN_BBCODE']))
{
if ($arTask['DESCRIPTION_IN_BBCODE'] === 'N')
$isBbCodeDescription = false;
}
$taskReassignedTo = null;
if (
isset($arFields['RESPONSIBLE_ID'])
&& ($arFields['RESPONSIBLE_ID'] > 0)
&& ($arFields['RESPONSIBLE_ID'] != $arTask['RESPONSIBLE_ID'])
)
{
$taskReassignedTo = $arFields['RESPONSIBLE_ID'];
}
foreach (array('CREATED_BY', 'RESPONSIBLE_ID', 'ACCOMPLICES', 'AUDITORS', 'TITLE') as $field)
{
if ( ! isset($arFields[$field])
&& isset($arTask[$field])
)
{
$arFields[$field] = $arTask[$field];
}
}
$cacheWasEnabled = self::enableStaticCache();
// $arChanges contains datetimes IN SERVER TIME, NOT CLIENT
$arChanges = CTaskLog::GetChanges($arTask, $arFields);
$trackedFields = CTaskLog::getTrackedFields();
$arMerged = array(
'ADDITIONAL_RECIPIENTS' => array()
);
// Pack prev users ids to ADDITIONAL_RECIPIENTS, to ensure,
// that they all will receive message
{
if (isset($arTask['CREATED_BY']))
$arMerged['ADDITIONAL_RECIPIENTS'][] = $arTask['CREATED_BY'];
if (isset($arTask['RESPONSIBLE_ID']))
$arMerged['ADDITIONAL_RECIPIENTS'][] = $arTask['RESPONSIBLE_ID'];
if (isset($arTask['ACCOMPLICES']) && is_array($arTask['ACCOMPLICES']))
foreach ($arTask['ACCOMPLICES'] as $userId)
$arMerged['ADDITIONAL_RECIPIENTS'][] = $userId;
if (isset($arTask['AUDITORS']) && is_array($arTask['AUDITORS']))
foreach ($arTask['AUDITORS'] as $userId)
$arMerged['ADDITIONAL_RECIPIENTS'][] = $userId;
}
if (isset($arFields['ADDITIONAL_RECIPIENTS']))
{
$arFields['ADDITIONAL_RECIPIENTS'] = array_merge (
$arFields['ADDITIONAL_RECIPIENTS'],
$arMerged['ADDITIONAL_RECIPIENTS']
);
}
else
{
$arFields['ADDITIONAL_RECIPIENTS'] = $arMerged['ADDITIONAL_RECIPIENTS'];
}
$arUsers = CTaskNotifications::__GetUsers($arFields);
$ignoreAuthor = isset($parameters['IGNORE_AUTHOR']) ? !!$parameters['IGNORE_AUTHOR'] : true;
$arRecipientsIDs = array_unique(CTaskNotifications::GetRecipientsIDs($arFields, $ignoreAuthor, false, $occurAsUserId));
if (
!empty($arRecipientsIDs)
&& (User::getId() || $arFields["CREATED_BY"])
)
{
$arInvariantChangesStrs = [];
$arVolatileDescriptions = [];
$arRecipientsIDsByTimezone = [];
$i = 0;
foreach ($arChanges as $key => $value)
{
if ($key === 'DESCRIPTION')
{
$arInvariantChangesStrs[] = GetMessage('TASKS_MESSAGE_DESCRIPTION_UPDATED');
continue;
}
if ($key === 'ACCOMPLICES' || $key === 'AUDITORS')
{
$fromUsers = explode(",", $value["FROM_VALUE"]);
$toUsers = explode(",", $value["TO_VALUE"]);
$addedUsers = array_unique(array_diff($toUsers, $fromUsers));
$addedUsers = array_filter(
$addedUsers,
static function ($id) {
return (int)$id > 0;
}
);
$removedUsers = array_unique(array_diff($fromUsers, $toUsers));
$removedUsers = array_filter(
$removedUsers,
static function ($id) {
return (int)$id > 0;
}
);
if (count($addedUsers) > 0)
{
$arInvariantChangesStrs[] =
GetMessage("TASKS_MESSAGE_{$key}_ADDED")
. CTaskNotifications::__Users2String($addedUsers, $arUsers, ($arFields['NAME_TEMPLATE'] ?? null))
;
}
if (count($removedUsers) > 0)
{
$arInvariantChangesStrs[] =
GetMessage("TASKS_MESSAGE_{$key}_REMOVED")
. CTaskNotifications::__Users2String($removedUsers, $arUsers, ($arFields['NAME_TEMPLATE'] ?? null))
;
}
continue;
}
++$i;
$actionMessage = GetMessage("TASKS_MESSAGE_".$key);
if($actionMessage == '' && isset($trackedFields[$key]) && ($trackedFields[$key]['TITLE'] ?? null) != '')
{
$actionMessage = $trackedFields[$key]['TITLE'];
}
if($actionMessage <> '')
{
// here we can display value changed for some fields
$changeMessage = $actionMessage;
$tmpStr = '';
switch($key)
{
case 'TIME_ESTIMATE':
$tmpStr .= self::formatTimeHHMM($value["FROM_VALUE"], true)
." -> "
.self::formatTimeHHMM($value["TO_VALUE"], true);
break;
case "TITLE":
$tmpStr .= $value["FROM_VALUE"]." -> ".$value["TO_VALUE"];
break;
case "RESPONSIBLE_ID":
$tmpStr .=
CTaskNotifications::__Users2String($value["FROM_VALUE"], $arUsers, ($arFields["NAME_TEMPLATE"] ?? null))
. ' -> '
. CTaskNotifications::__Users2String($value["TO_VALUE"], $arUsers, ($arFields["NAME_TEMPLATE"] ?? null))
;
break;
case "DEADLINE":
case "START_DATE_PLAN":
case "END_DATE_PLAN":
// $arChanges ALREADY contains server time, no need to substract user timezone again
$utsFromValue = $value['FROM_VALUE'];// - $curUserTzOffset;
$utsToValue = $value['TO_VALUE'];// - $curUserTzOffset;
// It will be replaced below to formatted string with correct dates for different timezones
$placeholder = '###PLACEHOLDER###'.$i.'###';
$tmpStr .= $placeholder;
// Collect recipients' timezones
foreach($arRecipientsIDs as $userId)
{
$tzOffset = (int)self::getUserTimeZoneOffset($userId);
if(!isset($arVolatileDescriptions[$tzOffset]))
{
$arVolatileDescriptions[$tzOffset] = array();
}
if(!isset($arVolatileDescriptions[$tzOffset][$placeholder]))
{
// Make bitrix timestamps for given user
$bitrixTsFromValue = $utsFromValue + $tzOffset;
$bitrixTsToValue = $utsToValue + $tzOffset;
$description = '';
if($utsFromValue > 360000) // is correct timestamp?
{
$fromValueAsString = BitrixTasksUI::formatDateTime($bitrixTsFromValue, '^'.BitrixTasksUI::getDateTimeFormat());
$description .= $fromValueAsString;
}
$description .= ' --> ';
if($utsToValue > 360000) // is correct timestamp?
{
$toValueAsString = BitrixTasksUI::formatDateTime($bitrixTsToValue, '^'.BitrixTasksUI::getDateTimeFormat());
$description .= $toValueAsString;
}
$arVolatileDescriptions[$tzOffset][$placeholder] = trim($description);
}
$arRecipientsIDsByTimezone[$tzOffset][] = $userId;
}
break;
case "TAGS":
$tmpStr .= ($value["FROM_VALUE"]? str_replace(",", ", ", $value["FROM_VALUE"])." -> " : "").($value["TO_VALUE"]? str_replace(",", ", ", $value["TO_VALUE"]) : GetMessage("TASKS_MESSAGE_NO_VALUE"));
break;
case "PRIORITY":
$tmpStr .= GetMessage("TASKS_PRIORITY_".$value["FROM_VALUE"])." -> ".GetMessage("TASKS_PRIORITY_".$value["TO_VALUE"]);
break;
case "GROUP_ID":
if($value["FROM_VALUE"] && self::checkUserCanViewGroup($effectiveUserId, $value["FROM_VALUE"]))
{
$arGroupFrom = self::getSocNetGroup($value["FROM_VALUE"]);
{
if($arGroupFrom)
{
$tmpStr .= $arGroupFrom["NAME"]." -> ";
}
}
}
if($value["TO_VALUE"] && self::checkUserCanViewGroup($effectiveUserId, $value["TO_VALUE"]))
{
$arGroupTo = self::getSocNetGroup($value["TO_VALUE"]);
{
if($arGroupTo)
{
$tmpStr .= $arGroupTo["NAME"];
}
}
}
else
{
$tmpStr .= GetMessage("TASKS_MESSAGE_NO_VALUE");
}
break;
case "PARENT_ID":
if($value["FROM_VALUE"])
{
$rsTaskFrom = CTasks::GetList(array(), array("ID" => $value["FROM_VALUE"]), array('ID', 'TITLE'));
{
if($arTaskFrom = $rsTaskFrom->GetNext())
{
$tmpStr .= BitrixMainTextEmoji::decode($arTaskFrom["TITLE"])." -> ";
}
}
}
if($value["TO_VALUE"])
{
$rsTaskTo = CTasks::GetList(array(), array("ID" => $value["TO_VALUE"]), array('ID', 'TITLE'));
{
if($arTaskTo = $rsTaskTo->GetNext())
{
$tmpStr .= BitrixMainTextEmoji::decode($arTaskTo["TITLE"]);
}
}
}
else
{
$tmpStr .= GetMessage("TASKS_MESSAGE_NO_VALUE");
}
break;
case "DEPENDS_ON":
$arTasksFromStr = array();
if($value["FROM_VALUE"])
{
$rsTasksFrom = CTasks::GetList(array(), array("ID" => explode(",", $value["FROM_VALUE"])), array('ID', 'TITLE'));
while($arTaskFrom = $rsTasksFrom->GetNext())
{
$arTasksFromStr[] = BitrixMainTextEmoji::decode($arTaskFrom["TITLE"]);
}
}
$arTasksToStr = array();
if($value["TO_VALUE"])
{
$rsTasksTo = CTasks::GetList(array(), array("ID" => explode(",", $value["TO_VALUE"])), array('ID', 'TITLE'));
while($arTaskTo = $rsTasksTo->GetNext())
{
$arTasksToStr[] = BitrixMainTextEmoji::decode($arTaskTo["TITLE"]);
}
}
$tmpStr .= ($arTasksFromStr? implode(", ", $arTasksFromStr)." -> " : "").($arTasksToStr? implode(", ", $arTasksToStr) : GetMessage("TASKS_MESSAGE_NO_VALUE"));
break;
case "MARK":
$tmpStr .= (!$value["FROM_VALUE"]? GetMessage("TASKS_MARK_NONE") : GetMessage("TASKS_MARK_".$value["FROM_VALUE"]))." -> ".(!$value["TO_VALUE"]? GetMessage("TASKS_MARK_NONE") : GetMessage("TASKS_MARK_".$value["TO_VALUE"]));
break;
case "ADD_IN_REPORT":
$tmpStr .= ($value["FROM_VALUE"] == "Y"? GetMessage("TASKS_MESSAGE_IN_REPORT_YES") : GetMessage("TASKS_MESSAGE_IN_REPORT_NO"))." -> ".($value["TO_VALUE"] == "Y"? GetMessage("TASKS_MESSAGE_IN_REPORT_YES") : GetMessage("TASKS_MESSAGE_IN_REPORT_NO"));
break;
case "DELETED_FILES":
$tmpStr .= $value["FROM_VALUE"];
$tmpStr .= $value["TO_VALUE"];
break;
case "NEW_FILES":
$tmpStr .= $value["TO_VALUE"];
break;
}
if ($tmpStr !== '')
{
$changeMessage .= ": ".trim($tmpStr);
}
$arInvariantChangesStrs[] = $changeMessage;
}
}
$recp2tz = array();
foreach($arRecipientsIDsByTimezone as $tz => $rcp)
{
foreach($rcp as $uid)
{
$recp2tz[$uid] = $tz;
}
}
$invariantDescription = null;
if ( ! empty($arInvariantChangesStrs) )
$invariantDescription = implode("rn", $arInvariantChangesStrs);
if (
($invariantDescription !== null)
&& ( ! empty($arRecipientsIDs) )
)
{
// If there is no volatile part of descriptions, send to all recipients at once
if (empty($arVolatileDescriptions))
{
$arVolatileDescriptions['some_timezone'] = array();
$arRecipientsIDsByTimezone['some_timezone'] = $arRecipientsIDs;
}
$updateMessage = self::getGenderMessage($occurAsUserId, 'TASKS_TASK_CHANGED_MESSAGE');
$taskName = self::formatTaskName($arTask['ID'], $arTask['TITLE'], $arTask['GROUP_ID'], false);
$instant = str_replace(
array(
"#TASK_TITLE#"
),
array(
$taskName
),
$updateMessage
);
$email = str_replace(
array(
"#TASK_TITLE#"
),
array(
strip_tags($taskName)
),
$updateMessage
);
CTaskNotifications::sendMessageEx($arTask["ID"], $occurAsUserId, $arRecipientsIDs, array(
'INSTANT' => $instant,
'EMAIL' => $email,
'PUSH' => self::makePushMessage('TASKS_TASK_CHANGED_MESSAGE', $occurAsUserId, $arTask)
), array(
'ENTITY_CODE' => 'TASK',
'ENTITY_OPERATION' => 'UPDATE',
'EVENT_DATA' => array(
'ACTION' => 'TASK_UPDATE',
'arFields' => $arFields,
'arChanges' => $arChanges
),
'TASK_DATA' => $arTask,
'TASK_ASSIGNED_TO' => $taskReassignedTo,
'CALLBACK' => array(
'BEFORE_SEND' => function($message) use($isBbCodeDescription, $invariantDescription, $arVolatileDescriptions, $recp2tz)
{
$rcp = $message['TO_USER_IDS'][0];
$volatile = (
isset($recp2tz[$rcp], $arVolatileDescriptions[$recp2tz[$rcp]])
? $arVolatileDescriptions[$recp2tz[$rcp]]
: null
);
if(is_array($volatile))
{
$description = str_replace(array_keys($volatile), $volatile, $invariantDescription);
}
else
{
$description = $invariantDescription;
}
$message['MESSAGE']['INSTANT'] = str_replace(
array(
"#TASK_EXTRA#"
),
array(
$description
),
$message['MESSAGE']['INSTANT']
);
if ($isBbCodeDescription)
{
$parser = new CTextParser();
$description = str_replace(
"t",
' ',
$parser->convertText($description)
);
}
$message['MESSAGE']['EMAIL'] = str_replace(
array(
"#TASK_EXTRA#"
),
array(
$description
),
$message['MESSAGE']['EMAIL']
);
return $message;
}
)
));
}
}
// sonet log
self::SendMessageToSocNet($arFields, $bSpawnedByAgent, $arChanges, $arTask, $parameters);
if($cacheWasEnabled)
{
self::disableStaticCache();
}
}