externalSendingActivated]);
$callType = acym_getVar('string', 'type', '');
$this->startQueue = acym_getVar('int', 'startqueue', 0);
$this->externalSendingRepeat = !empty(acym_getVar('int', 'external_sending_repeat', 0));
if ($callType === 'sending' || !empty($this->startQueue) || !empty($this->externalSendingRepeat)) {
$this->skip = array_diff(self::ALL_STEPS, [self::STEP_SEND]);
$this->isSendingCall = true;
}
}
public function addSkipFromString(string $skipVar): void
{
if (empty($skipVar)) {
return;
}
$skipVar = explode(',', $skipVar);
if (empty($skipVar)) {
return;
}
$this->skip = array_unique(array_merge($this->skip, $skipVar));
}
public function cron(): void
{
$this->handleUnlinkLicenseCalls();
$this->triggeredMessage();
if (!$this->checkCronFrequency()) {
return;
}
$this->queueScheduledCampaigns();
$this->cleanQueue();
$this->handleQueue();
$this->handleBounceMessages();
$this->handleAutomations();
$this->handleAutomaticCampaigns();
$this->handleSpecificEmails();
$this->handleFollowups();
$this->handleMailboxActions();
$this->cleanData();
$this->handleABTestCampaigns();
$this->handleScenario();
$this->continueCronCall();
$this->handleCronReport();
}
public function saveReport(array $messages = [], array $detailMessages = []): void
{
$saveReport = $this->config->get('cron_savereport');
$reportPath = $this->config->get('cron_savepath');
if (empty($saveReport) || empty($reportPath)) {
return;
}
if (!(($saveReport == 2 && $this->processed) || $saveReport == 1 || ($saveReport == 3 && $this->errorDetected))) {
return;
}
if (!empty($messages)) {
$this->messages = $messages;
}
if (!empty($detailMessages)) {
$this->detailMessages = $detailMessages;
}
$reportPath = str_replace(['{year}', '{month}'], [date('Y'), date('m')], $reportPath);
$reportPath = acym_cleanPath(ACYM_ROOT.trim(html_entity_decode($reportPath)));
acym_createDir(dirname($reportPath), true, true);
$lr = "\r\n";
ob_start();
file_put_contents(
$reportPath,
$lr.$lr.'******************** '.acym_getDate(time()).' UTC ********************'.$lr.implode($lr, $this->messages),
FILE_APPEND
);
if ($saveReport == 2 && !empty($this->detailMessages)) {
file_put_contents(
$reportPath,
$lr.'---- Details ----'.$lr.implode($lr, $this->detailMessages),
FILE_APPEND
);
}
$potentialWarnings = ob_get_clean();
if (!empty($potentialWarnings)) {
$this->messages[] = $potentialWarnings;
}
}
public function setEmailTypes(array $emailTypes): void
{
$this->emailTypes = $emailTypes;
}
public function addMessage(string $message): void
{
$this->messages[] = $message;
}
public function handleCronReport(): void
{
$sendReport = (int)$this->config->get('cron_sendreport');
if (
($sendReport === self::SEND_REPORT_ONLY_ON_ACTION && $this->processed)
|| $sendReport === self::SEND_REPORT_EACH_TIME
|| ($sendReport === self::SEND_REPORT_ONLY_ON_ERROR && $this->errorDetected)
) {
$mailerHelper = new MailerHelper();
$mailerHelper->report = false;
$mailerHelper->autoAddUser = true;
$mailerHelper->addParam('report', implode('
', $this->messages));
$mailerHelper->addParam('mainreport', $this->mainMessage);
$mailerHelper->addParam('detailreport', implode('
', $this->detailMessages));
$receiverString = $this->config->get('cron_sendto');
$receivers = [];
if (substr_count($receiverString, '@') > 1) {
$receivers = explode(' ', trim(preg_replace('# +#', ' ', str_replace([';', ','], ' ', $receiverString))));
} else {
$receivers[] = trim($receiverString);
}
if (!empty($receivers)) {
$mailClass = new MailClass();
foreach ($receivers as $oneReceiver) {
if (empty($oneReceiver)) {
continue;
}
try {
$reportEmail = $mailClass->getOneByName('acy_report');
if (!empty($reportEmail)) {
$mailerHelper->sendOne($reportEmail->id, $oneReceiver);
}
} catch (\Exception $e) {
acym_logError('Error while sending the cron report to '.$oneReceiver.' : '.$e->getMessage());
}
}
}
}
$this->saveReport();
$newConfig = [
'cron_report' => implode("\n", $this->messages),
];
if (strlen($newConfig['cron_report']) > 800) {
$newConfig['cron_report'] = substr($newConfig['cron_report'], 0, 795).'...';
}
$this->config->saveConfig($newConfig);
}
private function handleUnlinkLicenseCalls(): void
{
if (acym_getVar('int', 'unlink', 0) !== 1) {
return;
}
$callLicenseKey = acym_getVar('string', 'licenseKey');
$configLicenseKey = $this->config->get('license_key');
if (!empty($configLicenseKey) && $configLicenseKey === $callLicenseKey) {
$this->config->saveConfig(['license_key' => '', 'active_cron' => 0]);
}
exit;
}
private function triggeredMessage(): void
{
$firstMessage = acym_translationSprintf('ACYM_CRON_TRIGGERED', acym_date('now', 'd F Y H:i'));
$this->messages[] = $firstMessage;
acym_display($firstMessage, 'info');
}
private function checkCronFrequency(): bool
{
if ($this->isSendingCall) {
return true;
}
$time = time();
$nextCronTime = $this->config->get('cron_next', 0);
$cronFrequency = $this->config->get('cron_frequency', 900);
if ($nextCronTime > $time) {
if ($nextCronTime > ($time + $cronFrequency)) {
$this->config->saveConfig(['cron_next' => $time + $cronFrequency]);
}
$notTimeMessage = acym_translationSprintf('ACYM_CRON_NEXT', acym_date($this->config->get('cron_next'), 'd F Y H:i'));
$this->messages[] = $notTimeMessage;
acym_display($notTimeMessage, 'info');
return false;
}
$newConfig = [
'cron_last' => $time,
'cron_fromip' => acym_getIP(true),
'cron_next' => $nextCronTime + $cronFrequency,
];
if ($newConfig['cron_next'] <= $time || $newConfig['cron_next'] > $time + $cronFrequency) {
$newConfig['cron_next'] = $time + $cronFrequency;
}
$this->config->saveConfig($newConfig);
return true;
}
private function queueScheduledCampaigns(): void
{
if (in_array(self::STEP_SCHEDULE, $this->skip)) {
return;
}
$queueClass = new QueueClass();
$nbScheduled = $queueClass->scheduleReady();
if (!empty($nbScheduled)) {
$this->messages[] = acym_translationSprintf('ACYM_NB_SCHEDULED', $nbScheduled);
$this->detailMessages = array_merge($this->detailMessages, $queueClass->messages);
$this->processed = true;
}
}
private function cleanQueue(): void
{
if (in_array(self::STEP_CLEAN_QUEUE, $this->skip)) {
return;
}
$queueClass = new QueueClass();
$deletedNb = $queueClass->cleanQueue();
if (!empty($deletedNb)) {
$this->messages[] = acym_translationSprintf('ACYM_EMAILS_REMOVED_QUEUE_CLEAN', $deletedNb);
$this->processed = true;
}
}
private function handleQueue(): void
{
if (in_array(self::STEP_SEND, $this->skip) || !$this->canSendQueuedEmails()) {
return;
}
if (!$this->isSendingCall && acym_level(ACYM_ENTERPRISE) && function_exists('curl_multi_exec')) {
$this->handleMultiCron();
}
if ($this->isSendingCall || $this->config->get('dedicated_send_process', 0) != 1) {
$this->sendQueuedEmails();
}
}
private function checkTimeRemaining(): void
{
$time = time();
if (empty($this->cronTimeLimit) || $time < $this->cronTimeLimit) {
return;
}
$this->messages[] = acym_translation('ACYM_CRON_TIME_LIMIT_REACHED');
$this->cronTimeLimitReached = true;
}
private function handleMultiCron(): void
{
$emailsPerBatches = $this->config->get('queue_nbmail_auto', 70);
if (empty($emailsPerBatches)) {
return;
}
$nbBatches = $this->config->get('queue_batch_auto', 1);
$nbBatches = intval($nbBatches);
if ($this->config->get('dedicated_send_process', 0) == 1) {
$startCron = 0;
} else {
if ($nbBatches < 2) {
return;
}
$startCron = 1;
}
$cronParams = '&t='.time();
if (!empty($this->config->get('cron_security', 0)) && !empty($this->config->get('cron_key'))) {
$cronParams .= '&cronKey='.$this->config->get('cron_key');
}
$urls = [];
for ($i = $startCron; $i < $nbBatches; $i++) {
$urls[] = acym_frontendLink('cron&task=cron&type=sending&startqueue='.($emailsPerBatches * $i).$cronParams);
}
acym_asyncUrlCalls($urls);
}
private function handleBounceMessages(): void
{
$this->checkTimeRemaining();
$time = time();
$autoBounceHandlingActive = (int)$this->config->get('auto_bounce', 0) === 1;
$autoBounceHandlingNextTime = (int)$this->config->get('auto_bounce_next', 0);
$autoBounceHandlingFrequency = (int)$this->config->get('auto_bounce_frequency', 0);
$isEnterprise = acym_level(ACYM_ENTERPRISE);
if (in_array(self::STEP_BOUNCE, $this->skip) || $this->cronTimeLimitReached || !$isEnterprise || !$autoBounceHandlingActive || $time < $autoBounceHandlingNextTime) {
return;
}
$this->config->saveConfig(
[
'auto_bounce_last' => $time,
'auto_bounce_next' => $time + $autoBounceHandlingFrequency,
]
);
$bounceHelper = new BounceHelper();
$bounceHelper->report = false;
$bounceHelper->stoptime = $this->cronTimeLimit;
$newConfig = [];
if ($bounceHelper->init() && $bounceHelper->connect()) {
$nbMessages = $bounceHelper->getNBMessages();
$nbMessagesReport = acym_translationSprintf('ACYM_NB_MAIL_MAILBOX', $nbMessages);
$this->messages[] = $nbMessagesReport;
$newConfig['auto_bounce_report'] = $nbMessagesReport;
$this->detailMessages[] = $nbMessagesReport;
if (!empty($nbMessages)) {
$bounceHelper->handleMessages();
$this->processed = true;
}
$this->detailMessages = array_merge($this->detailMessages, $bounceHelper->messages);
} else {
$bounceErrors = $bounceHelper->getErrors();
$newConfig['auto_bounce_report'] = implode('
', $bounceErrors);
if (!empty($bounceErrors[0])) {
$bounceErrors[0] = acym_translation('ACYM_BOUNCE_HANDLING').' : '.$bounceErrors[0];
}
$this->messages = array_merge($this->messages, $bounceErrors);
$this->processed = true;
$this->errorDetected = true;
}
$this->config->saveConfig($newConfig);
}
private function handleAutomations(): void
{
$this->checkTimeRemaining();
if (in_array(self::STEP_AUTOMATION, $this->skip) || $this->cronTimeLimitReached || !acym_level(ACYM_ENTERPRISE)) {
return;
}
$automationClass = new AutomationClass();
$automationClass->trigger('classic');
$userStatusCheckTriggers = [];
acym_trigger('onAcymDefineUserStatusCheckTriggers', [&$userStatusCheckTriggers]);
$automationClass->trigger($userStatusCheckTriggers);
if (!empty($automationClass->report)) {
if ($automationClass->didAnAction) {
$this->processed = true;
}
$this->messages = array_merge($this->messages, $automationClass->report);
}
}
private function handleAutomaticCampaigns(): void
{
$this->checkTimeRemaining();
if (in_array(self::STEP_CAMPAIGN, $this->skip) || $this->cronTimeLimitReached || !acym_level(ACYM_ENTERPRISE)) {
return;
}
$campaignClass = new CampaignClass();
$campaignClass->triggerAutoCampaign();
if (!empty($campaignClass->messages)) {
$this->messages = array_merge($this->messages, $campaignClass->messages);
$this->processed = true;
}
}
private function handleSpecificEmails(): void
{
$this->checkTimeRemaining();
if (in_array(self::STEP_SPECIFIC, $this->skip) || $this->cronTimeLimitReached) {
return;
}
$campaignClass = new CampaignClass();
$specialTypes = [];
acym_trigger('getCampaignTypes', [&$specialTypes]);
$specialMail = $campaignClass->getCampaignsByTypes($specialTypes, true);
$time = time();
acym_trigger('filterSpecificMailsToSend', [&$specialMail, $time]);
foreach ($specialMail as $onespecialMail) {
$campaignClass->send($onespecialMail->id);
}
}
private function handleFollowups(): void
{
$this->checkTimeRemaining();
if (in_array(self::STEP_FOLLOWUP, $this->skip) || $this->cronTimeLimitReached) {
return;
}
$followupClass = new FollowupClass();
$followups = $followupClass->getFollowupDailyBases();
$dailyHour = $this->config->get('daily_hour', '12');
$dailyMinute = $this->config->get('daily_minute', '00');
$dayBasedOnCMSTimezone = acym_date('now', 'Y-m-d');
$dayBasedOnCMSTimezoneAtSpecifiedHour = acym_getTimeFromCMSDate($dayBasedOnCMSTimezone.' '.$dailyHour.':'.$dailyMinute);
$time = time();
foreach ($followups as $followup) {
$lastTrigger = empty($followup->last_trigger) ? '' : acym_date($followup->last_trigger, 'Y-m-d');
if ($time >= $dayBasedOnCMSTimezoneAtSpecifiedHour && $lastTrigger != $dayBasedOnCMSTimezone) {
if (!empty($followup->condition)) {
$followup->condition = json_decode($followup->condition, true);
}
acym_trigger('onAcymFollowupDailyBasesNeedToBeTriggered', [$followup]);
}
}
}
private function handleMailboxActions(): void
{
$this->checkTimeRemaining();
if (in_array(self::STEP_MAILBOX_ACTION, $this->skip) || $this->cronTimeLimitReached || !acym_level(ACYM_ENTERPRISE)) {
return;
}
$mailboxActionClass = new MailboxClass();
$mailboxes = $mailboxActionClass->getAllActiveReadyWithActions();
if (empty($mailboxes)) {
return;
}
$mailboxHelper = new MailboxHelper();
$mailboxHelper->report = false;
foreach ($mailboxes as $oneMailboxAction) {
$this->processed = true;
if (!$mailboxHelper->isConnectionValid($oneMailboxAction, false)) {
$this->messages[] = acym_translationSprintf('ACYM_CONNECTION_FAILED_MAILBOX_X', $oneMailboxAction->id.'-'.$oneMailboxAction->name);
$this->errorDetected = true;
continue;
}
$nbMessages = $mailboxHelper->getNBMessages();
if (!$nbMessages) {
$this->messages[] = acym_translationSprintf('ACYM_NO_MESSAGE_IN_MAILBOX_X', $oneMailboxAction->id.'-'.$oneMailboxAction->name);
$mailboxHelper->close();
continue;
}
$mailboxHelper->handleAction();
$mailboxHelper->close();
$oneMailboxAction->nextdate = time() + $oneMailboxAction->frequency;
unset($oneMailboxAction->conditions);
unset($oneMailboxAction->actions);
$mailboxActionClass->save($oneMailboxAction);
}
}
private function cleanData(): void
{
if (!in_array(self::STEP_DELETE_HISTORY, $this->skip) && $this->isDailyCron()) {
$userStatClass = new UserStatClass();
$userDetailedStatsDeleted = $userStatClass->deleteDetailedStatsPeriod();
if (!empty($userDetailedStatsDeleted['message'])) {
$this->messages[] = $userDetailedStatsDeleted['message'];
$this->processed = true;
}
$userClass = new UserClass();
$userHistoryDeleted = $userClass->deleteHistoryPeriod();
if (!empty($userHistoryDeleted['message'])) {
$this->messages[] = $userHistoryDeleted['message'];
$this->processed = true;
}
$mailArchiveClass = new MailArchiveClass();
$archiveDeleted = $mailArchiveClass->deleteArchivePeriod();
if (!empty($archiveDeleted['message'])) {
$this->messages[] = $archiveDeleted['message'];
$this->processed = true;
}
}
if (!in_array(self::STEP_CLEAN_DATA_EXTERNAL_SENDING_METHOD, $this->skip) && $this->isDailyCron()) {
acym_trigger('onAcymCleanDataExternalSendingMethod');
}
if (!in_array(self::STEP_CLEAN_EXPORT_CHANGES, $this->skip) && $this->isDailyCron()) {
$exportHelper = new ExportHelper();
$exportHelper->cleanExportChangesFile();
}
if ($this->isDailyCron()) {
$time = time();
$this->config->saveConfig(['cron_last_daily' => $time]);
}
}
private function isDailyCron(): bool
{
$dailyHour = $this->config->get('daily_hour', '12');
$dailyMinute = $this->config->get('daily_minute', '00');
$dayBasedOnCMSTimezone = acym_date('now', 'Y-m-d');
$dayBasedOnCMSTimezoneAtSpecifiedHour = acym_getTimeFromCMSDate($dayBasedOnCMSTimezone.' '.$dailyHour.':'.$dailyMinute);
$time = time();
$lastCronDayBasedOnCMSTimezone = acym_date($this->config->get('cron_last_daily', $time - 86400), 'Y-m-d');
return $time >= $dayBasedOnCMSTimezoneAtSpecifiedHour && $lastCronDayBasedOnCMSTimezone != $dayBasedOnCMSTimezone;
}
private function handleABTestCampaigns(): void
{
$this->checkTimeRemaining();
if (in_array(self::STEP_ABTEST, $this->skip) || $this->cronTimeLimitReached) {
return;
}
$campaignClass = new CampaignClass();
$abTestCampaigns = $campaignClass->getAllAbTestCampaignsToFinishSending();
foreach ($abTestCampaigns as $campaign) {
if ($campaignClass->finishAbTestCampaign($campaign)) {
$this->messages[] = acym_translationSprintf('ACYM_ABTEST_CAMPAIGN_X_FINAL_VERSION_SENT', $campaign->id);
$this->processed = true;
}
}
}
private function handleScenario(): void
{
$this->checkTimeRemaining();
if (in_array(self::STEP_SCENARIO, $this->skip) || $this->cronTimeLimitReached) {
return;
}
$scenarioHelper = new ScenarioHelper();
$scenarioHelper->executeAvailableSteps();
$scenarioHelper->triggerTimeScenarios();
}
private function continueCronCall(): void
{
if ($this->externalSendingNotFinished) {
$cronKey = '';
if (!empty($this->config->get('cron_security', 0)) && !empty($this->config->get('cron_key'))) {
$cronKey = '&cronKey='.$this->config->get('cron_key');
}
acym_makeCurlCall(acym_frontendLink('cron&task=cron&external_sending_repeat=1&t='.time().$cronKey), ['verifySsl' => false]);
}
}
private function canSendQueuedEmails(): bool
{
if ($this->config->get('queue_type') === 'manual') {
return false;
}
$dayOfWeek = acym_date('now', 'N');
if ($this->config->get('queue_stop_weekend', 0) && $dayOfWeek >= 6) {
return false;
}
$fromHour = $this->config->get('queue_send_from_hour', '00');
$fromMinute = $this->config->get('queue_send_from_minute', '00');
$toHour = $this->config->get('queue_send_to_hour', '23');
$toMinute = $this->config->get('queue_send_to_minute', '59');
$time = time();
if ($fromHour != '00' || $fromMinute != '00' || $toHour != '23' || $toMinute != '59') {
$dayBasedOnCMSTimezone = acym_date('now', 'Y-m-d');
$fromBasedOnCMSTimezoneAtSpecifiedHour = acym_getTimeFromCMSDate($dayBasedOnCMSTimezone.' '.$fromHour.':'.$fromMinute);
$toBasedOnCMSTimezoneAtSpecifiedHour = acym_getTimeFromCMSDate($dayBasedOnCMSTimezone.' '.$toHour.':'.$toMinute);
if ($fromBasedOnCMSTimezoneAtSpecifiedHour > $toBasedOnCMSTimezoneAtSpecifiedHour) {
if ($time > $fromBasedOnCMSTimezoneAtSpecifiedHour) {
$toBasedOnCMSTimezoneAtSpecifiedHour = acym_getTimeFromCMSDate(acym_date('tomorrow', 'Y-m-d').' '.$toHour.':'.$toMinute);
} elseif ($time < $toBasedOnCMSTimezoneAtSpecifiedHour) {
$fromBasedOnCMSTimezoneAtSpecifiedHour = acym_getTimeFromCMSDate(acym_date('yesterday', 'Y-m-d').' '.$fromHour.':'.$fromMinute);
}
}
if ($time < $fromBasedOnCMSTimezoneAtSpecifiedHour || $time > $toBasedOnCMSTimezoneAtSpecifiedHour) {
return false;
}
}
return true;
}
private function sendQueuedEmails(): void
{
$queueHelper = new QueueHelper();
$queueHelper->send_limit = (int)$this->config->get('queue_nbmail_auto');
$queueHelper->report = false;
$queueHelper->emailTypes = $this->emailTypes;
$queueHelper->startSendingFrom = $this->startQueue;
$queueHelper->process();
if (!empty($queueHelper->messages)) {
$this->detailMessages = array_merge($this->detailMessages, $queueHelper->messages);
}
if (!empty($queueHelper->nbprocess)) {
$this->processed = true;
if (!$queueHelper->finish && $this->externalSendingActivated) {
$this->externalSendingNotFinished = true;
}
}
$this->mainMessage = acym_translationSprintf(
'ACYM_CRON_PROCESS',
$queueHelper->nbprocess,
$queueHelper->successSend,
$queueHelper->errorSend
);
$this->messages[] = $this->mainMessage;
if (!empty($queueHelper->errorSend)) {
$this->errorDetected = true;
}
$this->cronTimeLimit = $queueHelper->stoptime;
}
}