function GetListItemChangesSinceToken($listName, $viewFields = '', $query = '', $rowLimit = 0, $changeToken = '') { global $APPLICATION, $USER; if (!$this->__Init()) { return $this->error; } if (!($listName_original = CIntranetUtils::checkGUID($listName))) { return new CSoapFault('Data error', 'Wrong GUID - ' . $listName); } $listName = ToUpper(CIntranetUtils::makeGUID($listName_original)); $arFilter = array(); if ($changeToken) { $bitrixTimestamp = $changeToken + CTasksTools::getTimeZoneOffset(); $arFilter['>CHANGED_DATE'] = ConvertTimeStamp($bitrixTimestamp, 'FULL'); if (!$arFilter['>CHANGED_DATE']) { return new CSoapFault('Params error', 'Wrong changeToken: ' . $changeToken); } } $last_change = 0; $data = new CXMLCreator('listitems'); $data->setAttribute('MinTimeBetweenSyncs', 0); $data->setAttribute('RecommendedTimeBetweenSyncs', 180); $data->setAttribute('TimeStamp', $this->__makeDateTime()); $data->setAttribute('EffectivePermMask', 'FullMask'); $data->setAttribute('IncludeAttachmentUrls', 'False'); $data->addChild($obChanges = new CXMLCreator('Changes')); if (!$changeToken) { $obChanges->addChild($this->__getFieldsDefinition()); } $data->addChild($obData = new CXMLCreator('rs:data')); $counter = 0; $arFilter['MEMBER'] = $USER->GetID(); $dbRes = CTasks::GetList(array("ID" => "ASC"), $arFilter, array(), array('bGetZombie' => true)); while ($arRes = $dbRes->Fetch()) { if ($arRes['ZOMBIE'] === 'Y') { $obId = new CXMLCreator('Id'); $obId->setAttribute('ChangeType', 'Delete'); $obId->setData($arRes['ID']); $obChanges->addChild($obId); } else { $rsFiles = CTaskFiles::GetList(array(), array("TASK_ID" => $arRes["ID"])); $arRes["FILES"] = array(); while ($arFiles = $rsFiles->fetch()) { $arRes["FILES"][] = $arFiles["FILE_ID"]; } $obData->addChild($this->__getRow($arRes, $listName, $last_change)); $counter++; } } $obData->setAttribute('ItemCount', $counter); $data->setAttribute('xmlns:rs', 'urn:schemas-microsoft-com:rowset'); $data->setAttribute('xmlns:z', '#RowsetSchema'); if ($last_change > 0) { $obChanges->setAttribute('LastChangeToken', $last_change); } return array('GetListItemChangesSinceTokenResult' => $data); }
private static function getUserTimeZoneOffset($userId = 'current') { if (!isset(self::$cache['TIMEZONE'][$userId]) || !self::$cacheData) { self::$cache['TIMEZONE'][$userId] = CTasksTools::getTimeZoneOffset($userId == 'current' ? false : $userId); } return self::$cache['TIMEZONE'][$userId]; }
private function getCounts($arGroupsIds) { $arCounters = array(); if (empty($arGroupsIds)) { return; } foreach ($arGroupsIds as $groupId) { $arCounters[$groupId] = array('ALL' => 0, 'IN_WORK' => 0, 'COMPLETE' => 0); } $oFilter = CTaskFilterCtrl::getInstance($this->arParams['USER_ID'], true); $arFilterAll = $oFilter->getFilterPresetConditionById(CTaskFilterCtrl::STD_PRESET_ALL_MY_TASKS); $arFilterInWork = $oFilter->getFilterPresetConditionById(CTaskFilterCtrl::STD_PRESET_ACTIVE_MY_TASKS); $arFilterComplete = $oFilter->getFilterPresetConditionById(CTaskFilterCtrl::STD_PRESET_COMPLETED_MY_TASKS); $arFilterInWorkExpired = $arFilterInWork; $arFilterInWorkExpired['<DEADLINE'] = ConvertTimeStamp(time() + CTasksTools::getTimeZoneOffset(), 'FULL'); $arFilterAll['GROUP_ID'] = $arGroupsIds; $arFilterInWork['GROUP_ID'] = $arGroupsIds; $arFilterComplete['GROUP_ID'] = $arGroupsIds; $arFilterInWorkExpired['GROUP_ID'] = $arGroupsIds; $arMap = array('ALL' => &$arFilterAll, 'IN_WORK' => &$arFilterInWork, 'COMPLETE' => &$arFilterComplete, 'EXPIRED' => &$arFilterInWorkExpired); foreach ($arMap as $key => &$arFilter) { $rs = CTasks::GetCount($arFilter, array('bSkipUserFields' => true, 'bSkipExtraTables' => true, 'bSkipJoinTblViewed' => false), array('GROUP_ID')); while ($ar = $rs->fetch()) { $groupId = (int) $ar['GROUP_ID']; if ($groupId) { $arCounters[$groupId][$key] = (int) $ar['CNT']; } } } unset($arFilter); return $arCounters; }
protected static function getTimemanCloseDayData($arParams) { if (CModule::IncludeModule('timeman')) { global $USER; $arTasks = array(); $userId = $USER->getId(); $runningTaskId = null; $taskRunTime = null; // key features of that info: // [REPORT_REQ] => 'A' means that day will be closed right now. other variants - just form show. // [INFO][DATE_START] => 1385459336 - unix timestamp of day start // [INFO][TIME_START] => 46136 - short timestamp of day start // [DURATION] // [TIME_LEAKS] $arTimemanInfo = CTimeMan::GetRunTimeInfo(true); if (!($userId > 0)) { foreach ($arTimemanInfo['PLANNER']['DATA']['TASKS'] as $arTask) { $arTask['TIME'] = 0; $arTasks[] = $arTask; } return array('TASKS' => $arTasks); } $unixTsDateStart = (int) $arTimemanInfo['INFO']['DATE_START']; $oTimer = CTaskTimerManager::getInstance($userId); $arTimer = $oTimer->getLastTimer(); if ($arTimer && $arTimer['TIMER_STARTED_AT'] > 0) { $runningTaskId = $arTimer['TASK_ID']; if ($arTimer['TIMER_STARTED_AT'] >= $unixTsDateStart) { $taskRunTime = max(0, time() - (int) $arTimer['TIMER_STARTED_AT']); } else { $taskRunTime = max(0, time() - $unixTsDateStart); } } $bitrixTimestampDateStart = $unixTsDateStart + CTasksTools::getTimeZoneOffset(); $dateStartAsString = ConvertTimeStamp($bitrixTimestampDateStart, 'FULL'); foreach ($arTimemanInfo['PLANNER']['DATA']['TASKS'] as $arTask) { $rsElapsedTime = CTaskElapsedTime::getList(array('ID' => 'ASC'), array('TASK_ID' => $arTask['ID'], 'USER_ID' => $userId, '>=CREATED_DATE' => $dateStartAsString), array('skipJoinUsers' => true)); $arTask['TIME'] = 0; while ($arElapsedTime = $rsElapsedTime->fetch()) { $arTask['TIME'] += max(0, $arElapsedTime['SECONDS']); } if ($runningTaskId && $arTask['ID'] == $runningTaskId) { $arTask['TIME'] += $taskRunTime; } $arTasks[] = $arTask; } return array('TASKS' => $arTasks); } }
/** * This method is deprecated. Use CTaskItem::delete() instead. * @deprecated */ public static function Delete($ID, $arParams = array()) { global $DB, $CACHE_MANAGER, $USER; if (isset($USER) && is_object($USER)) { $actorUserId = (int) $USER->getId(); } else { $actorUserId = 1; } if (isset($arParams['META::EVENT_GUID'])) { $eventGUID = $arParams['META::EVENT_GUID']; unset($arParams['META::EVENT_GUID']); } else { $eventGUID = sha1(uniqid('AUTOGUID', true)); } $paramSkipExchangeSync = false; if (is_array($arParams)) { if (isset($arParams['skipExchangeSync']) && ($arParams['skipExchangeSync'] === 'Y' || $arParams['skipExchangeSync'] === true)) { $paramSkipExchangeSync = true; } } $ID = intval($ID); if ($ID < 1) { return false; } $rsTask = CTasks::GetByID($ID, false); if ($arTask = $rsTask->Fetch()) { foreach (GetModuleEvents('tasks', 'OnBeforeTaskDelete', true) as $arEvent) { if (ExecuteModuleEventEx($arEvent, array($ID, $arTask)) === false) { return false; } } CTaskCountersProcessor::onBeforeTaskDelete($ID, $arTask); CTaskMembers::DeleteByTaskID($ID); CTaskFiles::DeleteByTaskID($ID); CTaskDependence::DeleteByTaskID($ID); CTaskDependence::DeleteByDependsOnID($ID); CTaskTags::DeleteByTaskID($ID); $strSql = "DELETE FROM b_tasks_viewed WHERE TASK_ID = " . $ID; $DB->Query($strSql, false, "File: " . __FILE__ . "<br>Line: " . __LINE__); $strSql = "DELETE FROM b_tasks_reminder WHERE TASK_ID = " . $ID; $DB->Query($strSql, false, "File: " . __FILE__ . "<br>Line: " . __LINE__); // clear cache if ($arTask["GROUP_ID"]) { $CACHE_MANAGER->ClearByTag("tasks_group_" . $arTask["GROUP_ID"]); } $arParticipants = array_unique(array_merge(array($arTask["CREATED_BY"], $arTask["RESPONSIBLE_ID"]), $arTask["ACCOMPLICES"], $arTask["AUDITORS"])); foreach ($arParticipants as $userId) { $CACHE_MANAGER->ClearByTag("tasks_user_" . $userId); } $strSql = "UPDATE b_tasks_template SET TASK_ID = NULL WHERE TASK_ID = " . $ID; $DB->Query($strSql, false, "File: " . __FILE__ . "<br>Line: " . __LINE__); $strSql = "UPDATE b_tasks_template SET PARENT_ID = " . ($arTask["PARENT_ID"] ? $arTask["PARENT_ID"] : "NULL") . " WHERE PARENT_ID = " . $ID; $DB->Query($strSql, false, "File: " . __FILE__ . "<br>Line: " . __LINE__); $strSql = "UPDATE b_tasks SET PARENT_ID = " . ($arTask["PARENT_ID"] ? $arTask["PARENT_ID"] : "NULL") . " WHERE PARENT_ID = " . $ID; $DB->Query($strSql, false, "File: " . __FILE__ . "<br>Line: " . __LINE__); $strUpdate = $DB->PrepareUpdate("b_tasks", array('ZOMBIE' => 'Y', 'CHANGED_BY' => $actorUserId, 'CHANGED_DATE' => date($DB->DateFormatToPHP(CSite::GetDateFormat('FULL')), time() + CTasksTools::getTimeZoneOffset())), "tasks"); $strSql = "UPDATE b_tasks SET " . $strUpdate . " WHERE ID = " . (int) $ID; if ($DB->Query($strSql, false, "File: " . __FILE__ . "<br>Line: " . __LINE__)) { CTaskNotifications::SendDeleteMessage($arTask); if ($arTask["FORUM_TOPIC_ID"] && CModule::IncludeModule("forum")) { CForumTopic::Delete($arTask["FORUM_TOPIC_ID"]); } if (!$paramSkipExchangeSync) { CTaskSync::DeleteItem($arTask); } // MS Exchange // Emit pull event try { $arPullRecipients = array(); foreach ($arParticipants as $userId) { $arPullRecipients[] = (int) $userId; } $taskGroupId = 0; // no group if (isset($arTask['GROUP_ID']) && $arTask['GROUP_ID'] > 0) { $taskGroupId = (int) $arTask['GROUP_ID']; } $arPullData = array('TASK_ID' => (int) $ID, 'BEFORE' => array('GROUP_ID' => $taskGroupId), 'TS' => time(), 'event_GUID' => $eventGUID); self::EmitPullWithTagPrefix($arPullRecipients, 'TASKS_GENERAL_', 'task_remove', $arPullData); self::EmitPullWithTag($arPullRecipients, 'TASKS_TASK_' . (int) $ID, 'task_remove', $arPullData); } catch (Exception $e) { } foreach (GetModuleEvents('tasks', 'OnTaskDelete', true) as $arEvent) { ExecuteModuleEventEx($arEvent, array($ID)); } if (CModule::IncludeModule("search")) { CSearch::DeleteIndex("tasks", $ID); } } return true; } return false; }
/** * This function resets all counters for all users and recounts them. * * This function do work which IS NOT multi-thread safe. */ public static function setup($step = self::STEP_BEGIN, $extraParam = null) { $nextStep = $nextStepDelay = $nextStepExtraParam = null; //$s = 'CTaskCountersProcessorInstaller::setup()'; //soundex($s); $timeLimit = microtime(true) + 5; // give at least 5 seconds for work while (microtime(true) <= $timeLimit) { /** @noinspection PhpUnusedLocalVariableInspection */ $nextStep = $nextStepDelay = $nextStepExtraParam = null; //soundex($step); if ($step === self::STEP_BEGIN) { self::setStage(self::STAGE_INSTALL_IN_PROGRESS); /** @noinspection PhpDynamicAsStaticMethodCallInspection */ CAgent::RemoveAgent('CTaskCountersProcessor::agent();', 'tasks'); /** @noinspection PhpDynamicAsStaticMethodCallInspection */ CAgent::RemoveAgent('CTaskCountersProcessorInstaller::setup();', 'tasks'); /** @noinspection PhpDynamicAsStaticMethodCallInspection */ CAgent::RemoveAgent('CTaskCountersProcessorInstaller::setup("' . self::STEP_DROP_COUNTERS . '");', 'tasks'); /** @noinspection PhpDynamicAsStaticMethodCallInspection */ CAgent::RemoveAgent('CTaskCountersProcessorInstaller::setup("' . self::STEP_COUNT_NEW_FOR_RESPONSIBLES . '");', 'tasks'); /** @noinspection PhpDynamicAsStaticMethodCallInspection */ CAgent::RemoveAgent('CTaskCountersProcessorInstaller::setup("' . self::STEP_COUNT_NEW_FOR_ACCOMPLICES . '");', 'tasks'); //CAgent::RemoveAgent('CTaskCountersProcessorInstaller::setup("' . self::STEP_COUNT_NEW_FOR_AUDITORS . '");', 'tasks'); /** @noinspection PhpDynamicAsStaticMethodCallInspection */ CAgent::RemoveAgent('CTaskCountersProcessorInstaller::setup("' . self::STEP_COUNT_WAIT_CTRL_FOR_ORIGINATORS . '");', 'tasks'); /** @noinspection PhpDynamicAsStaticMethodCallInspection */ CAgent::RemoveAgent('CTaskCountersProcessorInstaller::setup("' . self::STEP_COUNT_EXPIRED . '");', 'tasks'); /** @noinspection PhpDynamicAsStaticMethodCallInspection */ CAgent::RemoveAgent('CTaskCountersProcessorInstaller::setup("' . self::STEP_COUNT_WITHOUT_DEADLINES_MY . '");', 'tasks'); /** @noinspection PhpDynamicAsStaticMethodCallInspection */ CAgent::RemoveAgent('CTaskCountersProcessorInstaller::setup("' . self::STEP_COUNT_WITHOUT_DEADLINES_FOR_ORIGINATORS . '");', 'tasks'); // defer for at min 130 seconds the next step, // because we must give time for CTaskCountersProcessor::agent() // to complete its work, if it was already started $nextStep = self::STEP_DROP_COUNTERS; $nextStepDelay = 130; } else { switch ($step) { case self::STEP_DROP_COUNTERS: // reset DEADLINE_COUNTED flags and all tasks counters for all users self::reset(); $nextStep = self::STEP_COUNT_NEW_FOR_RESPONSIBLES; break; case self::STEP_COUNT_NEW_FOR_RESPONSIBLES: self::recountCounters_MY_NEW($userId = '*'); // recount for all users $nextStep = self::STEP_COUNT_NEW_FOR_ACCOMPLICES; break; case self::STEP_COUNT_NEW_FOR_ACCOMPLICES: self::recountCounters_ACCOMPLICE_NEW($userId = '*'); // recount for all users //$nextStep = self::STEP_COUNT_NEW_FOR_AUDITORS; $nextStep = self::STEP_COUNT_WAIT_CTRL_FOR_ORIGINATORS; break; case self::STEP_COUNT_WAIT_CTRL_FOR_ORIGINATORS: self::recountCounters_ORIGINATORS_WAIT_CTRL($userId = '*'); // recount for all users $nextStep = self::STEP_COUNT_WITHOUT_DEADLINES_MY; break; case self::STEP_COUNT_WITHOUT_DEADLINES_MY: self::recountCounters_MY_WITHOUT_DEADLINES($userId = '*'); // recount for all users $nextStep = self::STEP_COUNT_WITHOUT_DEADLINES_FOR_ORIGINATORS; break; case self::STEP_COUNT_WITHOUT_DEADLINES_FOR_ORIGINATORS: self::recountCounters_ORIGINATORS_WITHOUT_DEADLINES($userId = '*'); // recount for all users $nextStep = self::STEP_COUNT_EXPIRED; break; case self::STEP_COUNT_EXPIRED: $executionTimeLimit = mt_rand(1, 6); // time limit in seconds $itemsProcessed = CTaskCountersProcessor::countExpiredAndExpiredSoonTasks($executionTimeLimit); // Some items processed? if ($itemsProcessed > 0) { // try again $nextStep = self::STEP_COUNT_EXPIRED; $nextStepDelay = 5; } else { /** @noinspection PhpDynamicAsStaticMethodCallInspection */ CAgent::AddAgent('CTaskCountersProcessor::agent();', 'tasks', 'N', 900); // every 15 minutes self::setStage(self::STAGE_INSTALL_COMPLETE); CTaskCountersProcessorHomeostasis::onCalculationComplete(); $nextStep = null; // the end } break; default: CTaskAssert::logError('[0xd7b90d6d] '); $nextStep = null; // the end break; } } if ($nextStep === null) { break; } if ($nextStepDelay > 0) { break; } $step = $nextStep; } if ($nextStep !== null) { /** @noinspection PhpDeprecationInspection */ $nextExecTimestamp = time() + CTasksTools::getTimeZoneOffset(); if ($nextStepDelay !== null) { $nextExecTimestamp += $nextStepDelay; } else { $nextExecTimestamp += 5; } // delay for ~5 seconds due to probably DB and PHP server time desynchronization $delayedTime = ConvertTimeStamp($nextExecTimestamp, 'FULL'); /** @noinspection PhpDynamicAsStaticMethodCallInspection */ CAgent::AddAgent('CTaskCountersProcessorInstaller::setup("' . $nextStep . '");', 'tasks', 'N', 86400000, $delayedTime, "Y", $delayedTime, 13); } return ""; // remove self from agents table }
public static function countExpiredAndExpiredSoonTasks($executionTimeLimit = 0.5) { $unixTs = time(); /** @noinspection PhpDeprecationInspection */ $bts = $unixTs + CTasksTools::getTimeZoneOffset(); $expiredEdgeDateTime = ConvertTimeStamp($bts, 'FULL'); // Calc current time + 24hours (86400 seconds) $expiredSoonEdgeDateTime = ConvertTimeStamp($bts + 86400, 'FULL'); // Two different filters for expired tasks and to be expired tasks // This is cause to DB->CurrentTimeFunction() != current time in general case // TODO: this should be optimized to one getList() request $arFilterExpiredSoon = array('::LOGIC' => 'AND', '!REAL_STATUS' => array(CTasks::STATE_SUPPOSEDLY_COMPLETED, CTasks::STATE_COMPLETED, CTasks::STATE_DECLINED), '>=DEADLINE' => $expiredEdgeDateTime, '<DEADLINE' => $expiredSoonEdgeDateTime, '!=DEADLINE_COUNTED' => self::DEADLINE_COUNTED_AS_EXPIRED_CANDIDATE); $arFilterExpired = array('::LOGIC' => 'AND', '!REAL_STATUS' => array(CTasks::STATE_SUPPOSEDLY_COMPLETED, CTasks::STATE_COMPLETED, CTasks::STATE_DECLINED), '<DEADLINE' => $expiredEdgeDateTime, '!=DEADLINE_COUNTED' => self::DEADLINE_COUNTED_AS_EXPIRED); $arSelect = array('ID', 'CREATED_BY', 'RESPONSIBLE_ID', 'REAL_STATUS', 'DEADLINE', 'DEADLINE_COUNTED'); $arParams = array('nPageTop' => 50, 'bIgnoreErrors' => true, 'USER_ID' => self::getAdminId()); $itemsTotalProcessed = $itemsLastProcessed = 0; try { $timeLimit = microtime(true) + $executionTimeLimit; $minIterations = 2; // do not less than 2 iterations (in case if there was processed items) $iterationsDone = 0; do { $itemsLastProcessed = self::iterateAgentWork($arFilterExpiredSoon, $arFilterExpired, $arSelect, $arParams); $itemsTotalProcessed += $itemsLastProcessed; $iterationsDone++; if ($itemsLastProcessed == 0) { break; } } while ($iterationsDone < $minIterations || microtime(true) <= $timeLimit); } catch (Exception $e) { CTaskAssert::logError('[0x87d58c6c] '); } // flush counters if ($itemsTotalProcessed != 0) { CTaskCountersQueue::execute(); } return $itemsTotalProcessed; }
public static function SendUpdateMessage($arFields, $arTask, $bSpawnedByAgent = false) { global $USER; $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]; } } $arChanges = CTaskLog::GetChanges($arTask, $arFields); $arMerged = array('ADDITIONAL_RECIPIENTS' => array()); 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); $arRecipientsIDs = array_unique(CTaskNotifications::GetRecipientsIDs($arFields)); if (!empty($arRecipientsIDs) && (is_object($USER) && $USER->GetID() || $arFields["CREATED_BY"])) { $curUserTzOffset = (int) CTasksTools::getTimeZoneOffset(); $arInvariantChangesStrs = array(); $arVolatileDescriptions = array(); $arRecipientsIDsByTimezone = array(); $i = 0; foreach ($arChanges as $key => $value) { ++$i; $actionMessage = GetMessage("TASKS_MESSAGE_" . $key); if (strlen($actionMessage)) { $tmpStr = $actionMessage . ": [COLOR=#000]"; 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"]) . ' -> ' . CTaskNotifications::__Users2String($value["TO_VALUE"], $arUsers, $arFields["NAME_TEMPLATE"]); break; case "ACCOMPLICES": case "AUDITORS": $tmpStr .= CTaskNotifications::__Users2String(explode(",", $value["FROM_VALUE"]), $arUsers, $arFields["NAME_TEMPLATE"]) . ' -> ' . CTaskNotifications::__Users2String(explode(",", $value["TO_VALUE"]), $arUsers, $arFields["NAME_TEMPLATE"]); break; case "DEADLINE": case "START_DATE_PLAN": case "END_DATE_PLAN": // CTasks::Log() returns bitrix timestamps for dates, so adjust them to correct unix timestamps. $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) CTasksTools::getTimeZoneOffset($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) { $fromValueAsString = FormatDate('^' . CDatabase::DateFormatToPHP(FORMAT_DATETIME), $bitrixTsFromValue); $description .= $fromValueAsString; } $description .= ' --> '; if ($utsToValue > 360000) { $toValueAsString = FormatDate('^' . CDatabase::DateFormatToPHP(FORMAT_DATETIME), $bitrixTsToValue); $description .= $toValueAsString; } $arVolatileDescriptions[$tzOffset][$placeholder] = $description; } $arRecipientsIDsByTimezone[$tzOffset][] = $userId; } break; case "DESCRIPTION": $tmpStr .= HTMLToTxt($arFields["DESCRIPTION"]); 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"] && CSocNetGroup::CanUserViewGroup($USER->GetID(), $value["FROM_VALUE"])) { $arGroupFrom = CSocNetGroup::GetByID($value["FROM_VALUE"]); if ($arGroupFrom) { $tmpStr .= $arGroupFrom["NAME"] . " -> "; } } if ($value["TO_VALUE"] && CSocNetGroup::CanUserViewGroup($USER->GetID(), $value["TO_VALUE"])) { $arGroupTo = CSocNetGroup::GetByID($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 .= $arTaskFrom["TITLE"] . " -> "; } } if ($value["TO_VALUE"]) { $rsTaskTo = CTasks::GetList(array(), array("ID" => $value["TO_VALUE"]), array('ID', 'TITLE')); if ($arTaskTo = $rsTaskTo->GetNext()) { $tmpStr .= $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[] = $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[] = $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; } $tmpStr .= "[/COLOR]"; $arInvariantChangesStrs[] = $tmpStr; } } $occurAsUserId = CTasksTools::getOccurAsUserId(); if (!$occurAsUserId) { $occurAsUserId = is_object($USER) && $USER->GetID() ? $USER->GetID() : $arFields["CREATED_BY"]; } $invariantDescription = null; if (!empty($arInvariantChangesStrs)) { $invariantDescription = implode("\r\n", $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; } foreach ($arVolatileDescriptions as $tzOffset => $arVolatileDescriptionsData) { $strDescription = $invariantDescription; foreach ($arVolatileDescriptionsData as $placeholder => $strReplaceTo) { $strDescription = str_replace($placeholder, $strReplaceTo, $strDescription); } $message = str_replace(array("#TASK_TITLE#", "#TASK_EXTRA#"), array(self::formatTaskName($arTask['ID'], $arTask['TITLE'], $arTask['GROUP_ID'], true), $strDescription), GetMessage("TASKS_TASK_CHANGED_MESSAGE")); if ($isBbCodeDescription) { $parser = new CTextParser(); $htmlDescription = str_replace("\t", ' ', $parser->convertText($strDescription)); } else { $htmlDescription = $strDescription; } $message_email = str_replace(array("#TASK_TITLE#", "#TASK_EXTRA#"), array(self::formatTaskName($arTask['ID'], $arTask['TITLE'], $arTask['GROUP_ID']), $htmlDescription . "\r\n" . GetMessage('TASKS_MESSAGE_LINK') . ': #PATH_TO_TASK#'), GetMessage("TASKS_TASK_CHANGED_MESSAGE")); CTaskNotifications::SendMessage($occurAsUserId, $arRecipientsIDsByTimezone[$tzOffset], $message, $arTask["ID"], $message_email, array('ACTION' => 'TASK_UPDATE', 'arFields' => $arFields, 'arChanges' => $arChanges), $taskReassignedTo); } } } // sonet log self::SendMessageToSocNet($arFields, $bSpawnedByAgent, $arChanges, $arTask); }