static function repeatTask($templateId, array $parameters = [])
{
$forceExecute = false;
if (array_key_exists('FORCE_EXECUTE', $parameters))
{
$forceExecute = $parameters['FORCE_EXECUTE'];
}
$templateId = (int)$templateId;
if (!$templateId)
{
return ''; // delete agent
}
$userId = static::getEffectiveUser();
static::liftLogAgent();
// todo: replace this with itemorm call
$templateDbRes = CTaskTemplates::getList(
[],
['ID' => $templateId],
false,
['USER_IS_ADMIN' => true],
['*', 'UF_*']
);
$template = $templateDbRes->Fetch();
if ($template && $template['REPLICATE'] === 'Y')
{
$agentName = str_replace('#ID#', $templateId, $parameters['AGENT_NAME_TEMPLATE']); // todo: when AGENT_NAME_TEMPLATE is not set?
$replicateParams = $template['REPLICATE_PARAMS'] = unserialize($template['REPLICATE_PARAMS'], ['allowed_classes' => false]);
$executionTime = static::getExecutionTime($agentName, $replicateParams);
if (
$executionTime
&&
(
static::taskByTemplateAlreadyExist($templateId, $executionTime)
|| time() < MakeTimeStamp($executionTime)
)
&& !$forceExecute
)
{
return $agentName;
}
$result = new UtilReplicatorResult();
if (is_array($parameters['RESULT']))
{
$parameters['RESULT']['RESULT'] = $result;
}
$createMessage = '';
$taskId = 0;
$resumeReplication = true;
$replicationCancelReason = '';
if (!$userId)
{
$createMessage = Loc::getMessage('TASKS_REPLICATOR_TASK_WAS_NOT_CREATED');
$result->addError('REPLICATION_FAILED', Loc::getMessage('TASKS_REPLICATOR_CANT_IDENTIFY_USER'));
}
// check if CREATOR is alive
if (!User::isActive($template['CREATED_BY']))
{
$resumeReplication = false; // no need to make another try
$createMessage = Loc::getMessage('TASKS_REPLICATOR_TASK_WAS_NOT_CREATED');
$result->addError('REPLICATION_FAILED', Loc::getMessage('TASKS_REPLICATOR_CREATOR_INACTIVE'));
}
// create task if no error occured
if ($result->isSuccess())
{
// todo: remove this spike
$userChanged = false;
$originalUser = null;
if (intval($template['CREATED_BY']))
{
if (array_key_exists('FORCE_USER', $parameters))
{
$userId = (int) $parameters['FORCE_USER'];
}
else
{
$userId = (int) $template['CREATED_BY'];
}
$userChanged = true;
$originalUser = User::getOccurAsId();
User::setOccurAsId($userId); // not admin in logs, but template creator
}
try
{
/** @var BitrixTasksUtilReplicatorTask $replicator */
$replicator = new static();
$replicator->setConfig('DISABLE_SOURCE_ACCESS_CONTROLLER', true); // do not query rights and do not check it
$produceResult = $replicator->produce($templateId, $userId, array('OVERRIDE_DATA' => array('CREATED_DATE' => $executionTime)));
if ($produceResult->isSuccess())
{
static::incrementReplicationCount($templateId, $template['TPARAM_REPLICATION_COUNT']);
$task = $produceResult->getInstance();
$subInstanceResult = $produceResult->getSubInstanceResult();
$result->setInstance($task);
if (Collection::isA($subInstanceResult))
{
$result->setSubInstanceResult($produceResult->getSubInstanceResult());
}
$taskId = $task->getId();
if ($produceResult->getErrors()->isEmpty())
{
$createMessage = Loc::getMessage('TASKS_REPLICATOR_TASK_CREATED');
}
else
{
$createMessage = Loc::getMessage('TASKS_REPLICATOR_TASK_CREATED_WITH_ERRORS');
}
if ($taskId)
{
$createMessage .= ' (#'.$taskId.')';
}
}
else
{
$createMessage = Loc::getMessage('TASKS_REPLICATOR_TASK_WAS_NOT_CREATED');
}
$result->adoptErrors($produceResult);
}
catch(Exception $e) // catch EACH exception, as we dont want the agent to repeat every 10 minutes in case of smth is wrong
{
$createMessage = Loc::getMessage('TASKS_REPLICATOR_TASK_POSSIBLY_WAS_NOT_CREATED');
if ($taskId)
{
$createMessage = Loc::getMessage('TASKS_REPLICATOR_TASK_CREATED_WITH_ERRORS').' (#'.$taskId.')';
}
$result->addException($e, Loc::getMessage('TASKS_REPLICATOR_INTERNAL_ERROR'));
}
// switch an original hit user back, unless we want some strange things to happen
if ($userChanged)
{
User::setOccurAsId($originalUser);
}
}
if ($createMessage !== '')
{
static::sendToSysLog($templateId, intval($taskId), $createMessage, $result->getErrors());
}
// calculate next execution time
if ($resumeReplication)
{
$currentUserTimezone = User::getTimeZoneOffsetCurrentUser();
$lastTime = $executionTime;
$iterationCount = 0;
do
{
$nextResult = static::getNextTime($template, $lastTime);
$nextData = $nextResult->getData();
$nextTime = $nextData['TIME'];
// next time is legal, but goes before or equals to the current time
if (($nextTime && MakeTimeStamp($lastTime) >= MakeTimeStamp($nextTime)) || ($iterationCount > 10000))
{
if ($iterationCount > 10000)
{
$message = 'insane iteration count reached while calculating next execution time';
}
else
{
$creator = $template['CREATED_BY'];
$creatorTimezone = User::getTimeZoneOffset($creator);
$eDebug = array(
$creator,
time(),
$currentUserTimezone,
$creatorTimezone,
$replicateParams['TIME'],
$replicateParams['TIMEZONE_OFFSET'],
$iterationCount
);
$message = 'getNextTime() loop detected for replication by template '.$templateId.' ('.$nextTime.' => '.$lastTime.') ('.implode(', ', $eDebug).')';
}
Util::log($message); // write to b24 exception log
static::sendToSysLog($templateId, 0, Loc::getMessage('TASKS_REPLICATOR_PROCESS_ERROR'), null, true);
$nextTime = false; // possible endless loop, this agent must be stopped
break;
}
// $nextTime in current user`s time (or server time, if no user)
$lastTime = $nextTime;
// we can compare one user`s time only with another user`s time, we canna just take time() value
$cTime = time() + $currentUserTimezone;
$iterationCount++;
}
while (($nextResult->isSuccess() && $nextTime) && MakeTimeStamp($nextTime) < $cTime);
if ($nextTime)
{
// we can not use CAgent::Update() here, kz the agent will be updated again just after this function ends ...
global $pPERIOD;
// still have $nextTime in current user timezone, we need server time now, so:
$nextTime = MakeTimeStamp($nextTime) - $currentUserTimezone;
$nextTimeFormatted = UI::formatDateTime($nextTime);
$replicateParams['NEXT_EXECUTION_TIME'] = $nextTimeFormatted;
$template = new CTaskTemplates();
$template->Update(
$templateId,
array('REPLICATE_PARAMS' => serialize($replicateParams)),
array('SKIP_AGENT_PROCESSING' => true)
);
// ... but we may set some global var called $pPERIOD
// "why ' - time()'?" you may ask. see CAgent::ExecuteAgents(), in the last sql we got:
// NEXT_EXEC=DATE_ADD(".($arAgent["IS_PERIOD"]=="Y"? "NEXT_EXEC" : "now()").", INTERVAL ".$pPERIOD." SECOND),
$pPERIOD = $nextTime - time();
$timeZoneFromGmtInSeconds = date('Z', time());
static::sendToSysLog(
$templateId,
0,
Loc::getMessage('TASKS_REPLICATOR_NEXT_TIME', array(
'#TIME#' => $nextTimeFormatted.' ('.UI::formatTimezoneOffsetUTC($timeZoneFromGmtInSeconds).')',
'#PERIOD#' => $pPERIOD,
'#SECONDS#' => Loc::getMessagePlural('TASKS_REPLICATOR_SECOND', $pPERIOD),
))
);
return $agentName; // keep agent working
}
else
{
$firstError = $nextResult->getErrors()->first();
if ($firstError)
{
$replicationCancelReason = $firstError->getMessage();
}
}
}
static::sendToSysLog(
$templateId,
0,
Loc::getMessage('TASKS_REPLICATOR_PROCESS_STOPPED').($replicationCancelReason != ''? ': '.$replicationCancelReason : '')
);
}
return ''; // agent will be simply deleted
}