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; } }