PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 11.1.0
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v11.1.0
11.1.1 11.1.0 11.0.5 11.0.4 11.0.3 11.0.2 11.0.1 11.0.0 10.11.1 10.11.0 10.10.2 10.10.1 10.10.0 10.9.1 trunk 10.0.0 10.0.1 10.1.0 10.1.1 10.1.2 10.1.3 10.1.4 10.2.0 10.2.1 10.2.2 All 62 releases
← All changes | back/Helpers/CronHelper.php +219 -105 10.2.2 → 11.1.0 View file →
@@ -46,8 +46,13 @@
46 46 self::STEP_ABTEST,
47 47 self::STEP_SCENARIO,
48 48 ];
49 49
50 + const SEND_REPORT_NO = 0;
51 + const SEND_REPORT_EACH_TIME = 1;
52 + const SEND_REPORT_ONLY_ON_ACTION = 2;
53 + const SEND_REPORT_ONLY_ON_ERROR = 3;
54 +
50 55 private array $messages = [];
51 56 private array $detailMessages = [];
52 57 private array $emailTypes = [];
53 58 private array $skip = [];
@@ -55,28 +60,36 @@
55 60 private int $cronTimeLimit = 0;
56 61 private bool $cronTimeLimitReached = false;
57 62 private int $startQueue;
58 63
64 + // Save the main message
59 65 private string $mainMessage = '';
60 66
67 + // Did the cron process anything?
61 68 private bool $processed = false;
62 69
70 + // Is there any error in the process
63 71 private bool $errorDetected = false;
64 72
73 + // If we call the cron just to send a batch of emails
65 74 private bool $externalSendingActivated = false;
66 75 private bool $externalSendingRepeat;
67 76 private bool $externalSendingNotFinished = false;
77 + private bool $isSendingCall = false;
68 78
69 79 public function __construct()
70 80 {
71 81 parent::__construct();
72 - $this->startQueue = acym_getVar('int', 'startqueue', 0);
73 82
74 83 acym_trigger('onAcymProcessQueueExternalSendingCampaign', [&$this->externalSendingActivated]);
75 84
85 + $callType = acym_getVar('string', 'type', '');
86 + $this->startQueue = acym_getVar('int', 'startqueue', 0);
76 87 $this->externalSendingRepeat = !empty(acym_getVar('int', 'external_sending_repeat', 0));
77 - if (!empty($this->startQueue) || !empty($this->externalSendingRepeat)) {
88 +
89 + if ($callType === 'sending' || !empty($this->startQueue) || !empty($this->externalSendingRepeat)) {
78 90 $this->skip = array_diff(self::ALL_STEPS, [self::STEP_SEND]);
91 + $this->isSendingCall = true;
79 92 }
80 93 }
81 94
82 95 public function addSkipFromString(string $skipVar): void
@@ -104,22 +117,15 @@
104 117 }
105 118
106 119 $this->queueScheduledCampaigns();
107 120 $this->cleanQueue();
108 - $this->sendQueuedEmails();
109 - $this->checkTimeRemaining();
121 + $this->handleQueue();
110 122 $this->handleBounceMessages();
111 - $this->checkTimeRemaining();
112 123 $this->handleAutomations();
113 - $this->checkTimeRemaining();
114 124 $this->handleAutomaticCampaigns();
115 - $this->checkTimeRemaining();
116 125 $this->handleSpecificEmails();
117 - $this->checkTimeRemaining();
118 126 $this->handleFollowups();
119 - $this->checkTimeRemaining();
120 127 $this->handleMailboxActions();
121 - $this->checkTimeRemaining();
122 128 $this->cleanData();
123 129 $this->handleABTestCampaigns();
124 130 $this->handleScenario();
125 131 $this->continueCronCall();
@@ -146,13 +152,15 @@
146 152 if (!empty($detailMessages)) {
147 153 $this->detailMessages = $detailMessages;
148 154 }
149 155
150 - $reportPath = str_replace(['{year}', '{month}'], [date('Y'), date('m')], $reportPath);
156 + // Prepare the cron file path
157 + $reportPath = str_replace(['{year}', '{month}'], [gmdate('Y'), gmdate('m')], $reportPath);
151 158 $reportPath = acym_cleanPath(ACYM_ROOT.trim(html_entity_decode($reportPath)));
152 159 acym_createDir(dirname($reportPath), true, true);
153 160
154 161 $lr = "\r\n";
162 + // Catch warnings
155 163 ob_start();
156 164 file_put_contents(
157 165 $reportPath,
158 166 $lr.$lr.'******************** '.acym_getDate(time()).' UTC ********************'.$lr.implode($lr, $this->messages),
@@ -172,8 +180,11 @@
172 180 $this->messages[] = $potentialWarnings;
173 181 }
174 182 }
175 183
184 + /**
185 + * Sets the types of emails that will be sent by the cron
186 + */
176 187 public function setEmailTypes(array $emailTypes): void
177 188 {
178 189 $this->emailTypes = $emailTypes;
179 190 }
@@ -184,11 +195,15 @@
184 195 }
185 196
186 197 public function handleCronReport(): void
187 198 {
188 - $sendReport = $this->config->get('cron_sendreport');
199 + $sendReport = (int)$this->config->get('cron_sendreport');
189 200
190 - if (($sendReport == 2 && $this->processed) || $sendReport == 1 || ($sendReport == 3 && $this->errorDetected)) {
201 + if (
202 + ($sendReport === self::SEND_REPORT_ONLY_ON_ACTION && $this->processed)
203 + || $sendReport === self::SEND_REPORT_EACH_TIME
204 + || ($sendReport === self::SEND_REPORT_ONLY_ON_ERROR && $this->errorDetected)
205 + ) {
191 206 $mailerHelper = new MailerHelper();
192 207 $mailerHelper->report = false;
193 208 $mailerHelper->autoAddUser = true;
194 209 $mailerHelper->addParam('report', implode('<br />', $this->messages));
@@ -223,18 +238,22 @@
223 238 }
224 239
225 240 $this->saveReport();
226 241
227 - $newConfig = new \stdClass();
228 - $newConfig->cron_report = implode("\n", $this->messages);
242 + $newConfig = [
243 + 'cron_report' => implode("\n", $this->messages),
244 + ];
229 245
230 - if (strlen($newConfig->cron_report) > 800) {
231 - $newConfig->cron_report = substr($newConfig->cron_report, 0, 795).'...';
246 + if (strlen($newConfig['cron_report']) > 800) {
247 + $newConfig['cron_report'] = substr($newConfig['cron_report'], 0, 795).'...';
232 248 }
233 249
234 - $this->config->save($newConfig);
250 + $this->config->saveConfig($newConfig);
235 251 }
236 252
253 + /**
254 + * Call made by the API to remove the API key when the user unlinks the website from their account page on our website
255 + */
237 256 private function handleUnlinkLicenseCalls(): void
238 257 {
239 258 if (acym_getVar('int', 'unlink', 0) !== 1) {
240 259 return;
@@ -243,14 +262,17 @@
243 262 $callLicenseKey = acym_getVar('string', 'licenseKey');
244 263 $configLicenseKey = $this->config->get('license_key');
245 264
246 265 if (!empty($configLicenseKey) && $configLicenseKey === $callLicenseKey) {
247 - $this->config->save(['license_key' => '', 'active_cron' => 0]);
266 + $this->config->saveConfig(['license_key' => '', 'active_cron' => 0]);
248 267 }
249 268
250 269 exit;
251 270 }
252 271
272 + /**
273 + * Displays the message shown on the cron URL page
274 + */
253 275 private function triggeredMessage(): void
254 276 {
255 277 $firstMessage = acym_translationSprintf('ACYM_CRON_TRIGGERED', acym_date('now', 'd F Y H:i'));
256 278 $this->messages[] = $firstMessage;
@@ -256,11 +278,14 @@
256 278 $this->messages[] = $firstMessage;
257 279 acym_display($firstMessage, 'info');
258 280 }
259 281
282 + /**
283 + * Checks the cron frequency in the configuration to make sure the call is wanted
284 + */
260 285 private function checkCronFrequency(): bool
261 286 {
262 - if (!empty($this->startQueue)) {
287 + if ($this->isSendingCall) {
263 288 return true;
264 289 }
265 290
266 291 $time = time();
@@ -268,34 +293,39 @@
268 293 $cronFrequency = $this->config->get('cron_frequency', 900);
269 294
270 295 if ($nextCronTime > $time) {
271 296 if ($nextCronTime > ($time + $cronFrequency)) {
272 - $newConfig = new \stdClass();
273 - $newConfig->cron_next = $time + $cronFrequency;
274 - $this->config->save($newConfig);
297 + // The next cron time is too far in the future, should not happen but we'll handle the case, so we reset the next cron time
298 + $this->config->saveConfig(['cron_next' => $time + $cronFrequency]);
275 299 }
276 300
277 301 $notTimeMessage = acym_translationSprintf('ACYM_CRON_NEXT', acym_date($this->config->get('cron_next'), 'd F Y H:i'));
278 302 $this->messages[] = $notTimeMessage;
303 + // We don't need to trigger anything, it's not time yet
279 304 acym_display($notTimeMessage, 'info');
280 305
281 306 return false;
282 307 }
283 308
284 - $newConfig = new \stdClass();
285 - $newConfig->cron_last = $time;
286 - $newConfig->cron_fromip = acym_getIP();
287 - $newConfig->cron_next = $nextCronTime + $cronFrequency;
309 + // We update the next cron and the last cron dates
310 + $newConfig = [
311 + 'cron_last' => $time,
312 + 'cron_fromip' => acym_getIP(true),
313 + 'cron_next' => $nextCronTime + $cronFrequency,
314 + ];
288 315
289 - if ($newConfig->cron_next <= $time || $newConfig->cron_next > $time + $cronFrequency) {
290 - $newConfig->cron_next = $time + $cronFrequency;
316 + if ($newConfig['cron_next'] <= $time || $newConfig['cron_next'] > $time + $cronFrequency) {
317 + $newConfig['cron_next'] = $time + $cronFrequency;
291 318 }
292 319
293 - $this->config->save($newConfig);
320 + $this->config->saveConfig($newConfig);
294 321
295 322 return true;
296 323 }
297 324
325 + /**
326 + * Adds the scheduled emails to the queue when the date is reached
327 + */
298 328 private function queueScheduledCampaigns(): void
299 329 {
300 330 if (in_array(self::STEP_SCHEDULE, $this->skip)) {
301 331 return;
@@ -302,9 +332,9 @@
302 332 }
303 333
304 334 $queueClass = new QueueClass();
305 335 $nbScheduled = $queueClass->scheduleReady();
306 - if ($nbScheduled) {
336 + if (!empty($nbScheduled)) {
307 337 $this->messages[] = acym_translationSprintf('ACYM_NB_SCHEDULED', $nbScheduled);
308 338 $this->detailMessages = array_merge($this->detailMessages, $queueClass->messages);
309 339 $this->processed = true;
310 340 }
@@ -309,8 +339,11 @@
309 339 $this->processed = true;
310 340 }
311 341 }
312 342
343 + /**
344 + * Cleans the emails that are stuck in the queue for too long (disabled/unconfirmed recipients)
345 + */
313 346 private function cleanQueue(): void
314 347 {
315 348 if (in_array(self::STEP_CLEAN_QUEUE, $this->skip)) {
316 349 return;
@@ -324,71 +357,26 @@
324 357 $this->processed = true;
325 358 }
326 359 }
327 360
328 - private function sendQueuedEmails(): void
361 + private function handleQueue(): void
329 362 {
330 - if (in_array(self::STEP_SEND, $this->skip) || $this->config->get('queue_type') === 'manual') {
363 + if (in_array(self::STEP_SEND, $this->skip) || !$this->canSendQueuedEmails()) {
331 364 return;
332 365 }
333 366
334 - $fromHour = $this->config->get('queue_send_from_hour', '00');
335 - $fromMinute = $this->config->get('queue_send_from_minute', '00');
336 - $toHour = $this->config->get('queue_send_to_hour', '23');
337 - $toMinute = $this->config->get('queue_send_to_minute', '59');
338 - $time = time();
339 -
340 - if ($fromHour != '00' || $fromMinute != '00' || $toHour != '23' || $toMinute != '59') {
341 - $dayBasedOnCMSTimezone = acym_date('now', 'Y-m-d');
342 - $fromBasedOnCMSTimezoneAtSpecifiedHour = acym_getTimeFromCMSDate($dayBasedOnCMSTimezone.' '.$fromHour.':'.$fromMinute);
343 - $toBasedOnCMSTimezoneAtSpecifiedHour = acym_getTimeFromCMSDate($dayBasedOnCMSTimezone.' '.$toHour.':'.$toMinute);
344 - if ($fromBasedOnCMSTimezoneAtSpecifiedHour > $toBasedOnCMSTimezoneAtSpecifiedHour) {
345 - if ($time > $fromBasedOnCMSTimezoneAtSpecifiedHour) {
346 - $toBasedOnCMSTimezoneAtSpecifiedHour = acym_getTimeFromCMSDate(acym_date('tomorrow', 'Y-m-d').' '.$toHour.':'.$toMinute);
347 - } elseif ($time < $toBasedOnCMSTimezoneAtSpecifiedHour) {
348 - $fromBasedOnCMSTimezoneAtSpecifiedHour = acym_getTimeFromCMSDate(acym_date('yesterday', 'Y-m-d').' '.$fromHour.':'.$fromMinute);
349 - }
350 - }
351 -
352 - if ($time < $fromBasedOnCMSTimezoneAtSpecifiedHour || $time > $toBasedOnCMSTimezoneAtSpecifiedHour) {
353 - return;
354 - }
367 + if (!$this->isSendingCall && acym_level(ACYM_ENTERPRISE) && function_exists('curl_multi_exec')) {
368 + $this->handleMultiCron();
355 369 }
356 370
357 - $dayOfWeek = acym_date('now', 'N');
358 - if ($this->config->get('queue_stop_weekend', 0) && $dayOfWeek >= 6) {
359 - return;
371 + if ($this->isSendingCall || $this->config->get('dedicated_send_process', 0) != 1) {
372 + $this->sendQueuedEmails();
360 373 }
361 -
362 - $this->handleMultiCron();
363 - $queueHelper = new QueueHelper();
364 - $queueHelper->send_limit = (int)$this->config->get('queue_nbmail_auto');
365 - $queueHelper->report = false;
366 - $queueHelper->emailTypes = $this->emailTypes;
367 - $queueHelper->process();
368 -
369 - if (!empty($queueHelper->messages)) {
370 - $this->detailMessages = array_merge($this->detailMessages, $queueHelper->messages);
371 - }
372 -
373 - if (!empty($queueHelper->nbprocess)) {
374 - $this->processed = true;
375 -
376 - if (!$queueHelper->finish && $this->externalSendingActivated) {
377 - $this->externalSendingNotFinished = true;
378 - }
379 - }
380 -
381 - $this->mainMessage = acym_translationSprintf('ACYM_CRON_PROCESS', $queueHelper->nbprocess, $queueHelper->successSend, $queueHelper->errorSend);
382 - $this->messages[] = $this->mainMessage;
383 -
384 - if (!empty($queueHelper->errorSend)) {
385 - $this->errorDetected = true;
386 - }
387 -
388 - $this->cronTimeLimit = $queueHelper->stoptime;
389 374 }
390 375
376 + /**
377 + * Makes sure the current process doesn't reach the server time limit
378 + */
391 379 private function checkTimeRemaining(): void
392 380 {
393 381 $time = time();
394 382 if (empty($this->cronTimeLimit) || $time < $this->cronTimeLimit) {
@@ -399,36 +387,47 @@
399 387
400 388 $this->cronTimeLimitReached = true;
401 389 }
402 390
391 + /**
392 + * If the configuration is set to send multiple batches of emails at the same time
393 + */
403 394 private function handleMultiCron(): void
404 395 {
405 - if (!empty($this->startQueue) || !acym_level(ACYM_ENTERPRISE)) {
396 + $emailsPerBatches = $this->config->get('queue_nbmail_auto', 70);
397 + if (empty($emailsPerBatches)) {
406 398 return;
407 399 }
408 400
409 - $emailsBatches = $this->config->get('queue_batch_auto', 1);
410 - $emailsBatches = intval($emailsBatches);
411 - $emailsPerBatches = $this->config->get('queue_nbmail_auto', 70);
412 - if ($emailsBatches < 2 || empty($emailsPerBatches)) {
413 - return;
401 + $nbBatches = $this->config->get('queue_batch_auto', 1);
402 + $nbBatches = intval($nbBatches);
403 +
404 + if ($this->config->get('dedicated_send_process', 0) == 1) {
405 + $startCron = 0;
406 + } else {
407 + if ($nbBatches < 2) {
408 + return;
409 + }
410 +
411 + $startCron = 1;
414 412 }
415 413
416 - $cronKey = '';
414 + $cronParams = '&t='.time();
417 415 if (!empty($this->config->get('cron_security', 0)) && !empty($this->config->get('cron_key'))) {
418 - $cronKey = '&cronKey='.$this->config->get('cron_key');
416 + $cronParams .= '&cronKey='.$this->config->get('cron_key');
419 417 }
420 418
421 419 $urls = [];
422 - for ($i = 1 ; $i <= $emailsBatches - 1 ; $i++) {
423 - $urls[] = acym_frontendLink('cron&task=cron&startqueue='.($emailsPerBatches * $i).'&t='.time().$cronKey);
420 + for ($i = $startCron; $i < $nbBatches; $i++) {
421 + $urls[] = acym_frontendLink('cron&task=cron&type=sending&startqueue='.($emailsPerBatches * $i).$cronParams);
424 422 }
425 423
426 - acym_asyncCurlCall($urls);
424 + acym_asyncUrlCalls($urls);
427 425 }
428 426
429 427 private function handleBounceMessages(): void
430 428 {
429 + $this->checkTimeRemaining();
431 430 $time = time();
432 431 $autoBounceHandlingActive = (int)$this->config->get('auto_bounce', 0) === 1;
433 432 $autoBounceHandlingNextTime = (int)$this->config->get('auto_bounce_next', 0);
434 433 $autoBounceHandlingFrequency = (int)$this->config->get('auto_bounce_frequency', 0);
@@ -437,23 +436,25 @@
437 436 if (in_array(self::STEP_BOUNCE, $this->skip) || $this->cronTimeLimitReached || !$isEnterprise || !$autoBounceHandlingActive || $time < $autoBounceHandlingNextTime) {
438 437 return;
439 438 }
440 439
441 - $newConfig = new \stdClass();
442 - $newConfig->auto_bounce_last = $time;
443 - $newConfig->auto_bounce_next = $time + $autoBounceHandlingFrequency;
444 - $this->config->save($newConfig);
440 + $this->config->saveConfig(
441 + [
442 + 'auto_bounce_last' => $time,
443 + 'auto_bounce_next' => $time + $autoBounceHandlingFrequency,
444 + ]
445 + );
445 446
446 447 $bounceHelper = new BounceHelper();
447 448 $bounceHelper->report = false;
448 449 $bounceHelper->stoptime = $this->cronTimeLimit;
449 450
450 - $newConfig = new \stdClass();
451 + $newConfig = [];
451 452 if ($bounceHelper->init() && $bounceHelper->connect()) {
452 453 $nbMessages = $bounceHelper->getNBMessages();
453 454 $nbMessagesReport = acym_translationSprintf('ACYM_NB_MAIL_MAILBOX', $nbMessages);
454 455 $this->messages[] = $nbMessagesReport;
455 - $newConfig->auto_bounce_report = $nbMessagesReport;
456 + $newConfig['auto_bounce_report'] = $nbMessagesReport;
456 457 $this->detailMessages[] = $nbMessagesReport;
457 458 if (!empty($nbMessages)) {
458 459 $bounceHelper->handleMessages();
459 460 $this->processed = true;
@@ -460,9 +461,10 @@
460 461 }
461 462 $this->detailMessages = array_merge($this->detailMessages, $bounceHelper->messages);
462 463 } else {
463 464 $bounceErrors = $bounceHelper->getErrors();
464 - $newConfig->auto_bounce_report = implode('<br />', $bounceErrors);
465 + $newConfig['auto_bounce_report'] = implode('<br />', $bounceErrors);
466 + //We add "bounce handling" just before the error so the user knows where it comes from...
465 467 if (!empty($bounceErrors[0])) {
466 468 $bounceErrors[0] = acym_translation('ACYM_BOUNCE_HANDLING').' : '.$bounceErrors[0];
467 469 }
468 470 $this->messages = array_merge($this->messages, $bounceErrors);
@@ -469,13 +471,17 @@
469 471 $this->processed = true;
470 472 $this->errorDetected = true;
471 473 }
472 474
473 - $this->config->save($newConfig);
475 + $this->config->saveConfig($newConfig);
474 476 }
475 477
478 + /**
479 + * Triggers the automations based on time frequency
480 + */
476 481 private function handleAutomations(): void
477 482 {
483 + $this->checkTimeRemaining();
478 484 if (in_array(self::STEP_AUTOMATION, $this->skip) || $this->cronTimeLimitReached || !acym_level(ACYM_ENTERPRISE)) {
479 485 return;
480 486 }
481 487
@@ -495,8 +501,9 @@
495 501 }
496 502
497 503 private function handleAutomaticCampaigns(): void
498 504 {
505 + $this->checkTimeRemaining();
499 506 if (in_array(self::STEP_CAMPAIGN, $this->skip) || $this->cronTimeLimitReached || !acym_level(ACYM_ENTERPRISE)) {
500 507 return;
501 508 }
502 509
@@ -508,10 +515,14 @@
508 515 $this->processed = true;
509 516 }
510 517 }
511 518
519 + /**
520 + * Sends special emails such as birthday or WooCommerce reminders
521 + */
512 522 private function handleSpecificEmails(): void
513 523 {
524 + $this->checkTimeRemaining();
514 525 if (in_array(self::STEP_SPECIFIC, $this->skip) || $this->cronTimeLimitReached) {
515 526 return;
516 527 }
517 528
@@ -529,10 +540,14 @@
529 540 $campaignClass->send($onespecialMail->id);
530 541 }
531 542 }
532 543
544 + /**
545 + * Sends follow-ups based on time frequency like birthdays
546 + */
533 547 private function handleFollowups(): void
534 548 {
549 + $this->checkTimeRemaining();
535 550 if (in_array(self::STEP_FOLLOWUP, $this->skip) || $this->cronTimeLimitReached) {
536 551 return;
537 552 }
538 553
@@ -538,11 +553,14 @@
538 553
539 554 $followupClass = new FollowupClass();
540 555 $followups = $followupClass->getFollowupDailyBases();
541 556
557 + // Only once a day
542 558 $dailyHour = $this->config->get('daily_hour', '12');
543 559 $dailyMinute = $this->config->get('daily_minute', '00');
560 + // The day it is currently based on the timezone specified in the CMS configuration
544 561 $dayBasedOnCMSTimezone = acym_date('now', 'Y-m-d');
562 + // The UTC timestamp of the current day based on the CMS timezone, at the specified hour
545 563 $dayBasedOnCMSTimezoneAtSpecifiedHour = acym_getTimeFromCMSDate($dayBasedOnCMSTimezone.' '.$dailyHour.':'.$dailyMinute);
546 564 $time = time();
547 565
548 566 foreach ($followups as $followup) {
@@ -556,10 +574,14 @@
556 574 }
557 575 }
558 576 }
559 577
578 + /**
579 + * Checks messages received on mailboxes and triggers actions accordingly
580 + */
560 581 private function handleMailboxActions(): void
561 582 {
583 + $this->checkTimeRemaining();
562 584 if (in_array(self::STEP_MAILBOX_ACTION, $this->skip) || $this->cronTimeLimitReached || !acym_level(ACYM_ENTERPRISE)) {
563 585 return;
564 586 }
565 587
@@ -591,8 +613,9 @@
591 613 $mailboxHelper->handleAction();
592 614 $mailboxHelper->close();
593 615
594 616
617 + // Update next trigger
595 618 $oneMailboxAction->nextdate = time() + $oneMailboxAction->frequency;
596 619 unset($oneMailboxAction->conditions);
597 620 unset($oneMailboxAction->actions);
598 621 $mailboxActionClass->save($oneMailboxAction);
@@ -600,8 +623,9 @@
600 623 }
601 624
602 625 private function cleanData(): void
603 626 {
627 + // Clean the history and detailed stats based on the configuration
604 628 if (!in_array(self::STEP_DELETE_HISTORY, $this->skip) && $this->isDailyCron()) {
605 629 $userStatClass = new UserStatClass();
606 630 $userDetailedStatsDeleted = $userStatClass->deleteDetailedStatsPeriod();
607 631 if (!empty($userDetailedStatsDeleted['message'])) {
@@ -623,12 +647,14 @@
623 647 $this->processed = true;
624 648 }
625 649 }
626 650
651 + // Clean data on external sending method
627 652 if (!in_array(self::STEP_CLEAN_DATA_EXTERNAL_SENDING_METHOD, $this->skip) && $this->isDailyCron()) {
628 653 acym_trigger('onAcymCleanDataExternalSendingMethod');
629 654 }
630 655
656 + // Clean export changes
631 657 if (!in_array(self::STEP_CLEAN_EXPORT_CHANGES, $this->skip) && $this->isDailyCron()) {
632 658 $exportHelper = new ExportHelper();
633 659 $exportHelper->cleanExportChangesFile();
634 660 }
@@ -634,21 +660,25 @@
634 660 }
635 661
636 662 if ($this->isDailyCron()) {
637 663 $time = time();
638 - $this->config->save(['cron_last_daily' => $time]);
664 + $this->config->saveConfig(['cron_last_daily' => $time]);
639 665 }
640 666 }
641 667
642 668 private function isDailyCron(): bool
643 669 {
670 + // Only once a day
644 671 $dailyHour = $this->config->get('daily_hour', '12');
645 672 $dailyMinute = $this->config->get('daily_minute', '00');
673 + // The day it is currently based on the timezone specified in the CMS configuration
646 674 $dayBasedOnCMSTimezone = acym_date('now', 'Y-m-d');
675 + // The UTC timestamp of the current day based on the CMS timezone, at the specified hour
647 676 $dayBasedOnCMSTimezoneAtSpecifiedHour = acym_getTimeFromCMSDate($dayBasedOnCMSTimezone.' '.$dailyHour.':'.$dailyMinute);
648 677
649 678 $time = time();
650 679
680 + //If we do not have run the cron we set it to yesterday so it will be trigger
651 681 $lastCronDayBasedOnCMSTimezone = acym_date($this->config->get('cron_last_daily', $time - 86400), 'Y-m-d');
652 682
653 683 return $time >= $dayBasedOnCMSTimezoneAtSpecifiedHour && $lastCronDayBasedOnCMSTimezone != $dayBasedOnCMSTimezone;
654 684 }
@@ -654,8 +684,9 @@
654 684 }
655 685
656 686 private function handleABTestCampaigns(): void
657 687 {
688 + $this->checkTimeRemaining();
658 689 if (in_array(self::STEP_ABTEST, $this->skip) || $this->cronTimeLimitReached) {
659 690 return;
660 691 }
661 692
@@ -670,8 +701,9 @@
670 701 }
671 702
672 703 private function handleScenario(): void
673 704 {
705 + $this->checkTimeRemaining();
674 706 if (in_array(self::STEP_SCENARIO, $this->skip) || $this->cronTimeLimitReached) {
675 707 return;
676 708 }
677 709
@@ -679,8 +711,11 @@
679 711 $scenarioHelper->executeAvailableSteps();
680 712 $scenarioHelper->triggerTimeScenarios();
681 713 }
682 714
715 + /**
716 + * We reached the maximum time allowed so the tasks are not finished, we begin a new process to finish it up
717 + */
683 718 private function continueCronCall(): void
684 719 {
685 720 if ($this->externalSendingNotFinished) {
686 721 $cronKey = '';
@@ -688,6 +723,85 @@
688 723 $cronKey = '&cronKey='.$this->config->get('cron_key');
689 724 }
690 725 acym_makeCurlCall(acym_frontendLink('cron&task=cron&external_sending_repeat=1&t='.time().$cronKey), ['verifySsl' => false]);
691 726 }
727 + }
728 +
729 + private function canSendQueuedEmails(): bool
730 + {
731 + if ($this->config->get('queue_type') === 'manual') {
732 + return false;
733 + }
734 +
735 + $dayOfWeek = acym_date('now', 'N');
736 + if ($this->config->get('queue_stop_weekend', 0) && $dayOfWeek >= 6) {
737 + return false;
738 + }
739 +
740 + // Make sure we are within the sending hours defined in the configuration
741 + $fromHour = $this->config->get('queue_send_from_hour', '00');
742 + $fromMinute = $this->config->get('queue_send_from_minute', '00');
743 + $toHour = $this->config->get('queue_send_to_hour', '23');
744 + $toMinute = $this->config->get('queue_send_to_minute', '59');
745 + $time = time();
746 +
747 + if ($fromHour != '00' || $fromMinute != '00' || $toHour != '23' || $toMinute != '59') {
748 + // The day it is currently based on the timezone specified in the CMS configuration
749 + $dayBasedOnCMSTimezone = acym_date('now', 'Y-m-d');
750 + // The UTC timestamp of the current day based on the CMS timezone, at the specified hour
751 + $fromBasedOnCMSTimezoneAtSpecifiedHour = acym_getTimeFromCMSDate($dayBasedOnCMSTimezone.' '.$fromHour.':'.$fromMinute);
752 + $toBasedOnCMSTimezoneAtSpecifiedHour = acym_getTimeFromCMSDate($dayBasedOnCMSTimezone.' '.$toHour.':'.$toMinute);
753 + // In case we want to send during the night and the FROM is superior to the TO (from 8pm to 4am), we should change the day of ones of the limits
754 + if ($fromBasedOnCMSTimezoneAtSpecifiedHour > $toBasedOnCMSTimezoneAtSpecifiedHour) {
755 + // TO becomes tomorrow as we are not passed midnight
756 + if ($time > $fromBasedOnCMSTimezoneAtSpecifiedHour) {
757 + $toBasedOnCMSTimezoneAtSpecifiedHour = acym_getTimeFromCMSDate(acym_date('tomorrow', 'Y-m-d').' '.$toHour.':'.$toMinute);
758 + } elseif ($time < $toBasedOnCMSTimezoneAtSpecifiedHour) {
759 + // FROM becomes yesterday as we are passed midnight
760 + $fromBasedOnCMSTimezoneAtSpecifiedHour = acym_getTimeFromCMSDate(acym_date('yesterday', 'Y-m-d').' '.$fromHour.':'.$fromMinute);
761 + }
762 + }
763 +
764 + if ($time < $fromBasedOnCMSTimezoneAtSpecifiedHour || $time > $toBasedOnCMSTimezoneAtSpecifiedHour) {
765 + return false;
766 + }
767 + }
768 +
769 + return true;
770 + }
771 +
772 + private function sendQueuedEmails(): void
773 + {
774 + $queueHelper = new QueueHelper();
775 + $queueHelper->send_limit = (int)$this->config->get('queue_nbmail_auto');
776 + $queueHelper->report = false;
777 + $queueHelper->emailTypes = $this->emailTypes;
778 + $queueHelper->startSendingFrom = $this->startQueue;
779 + $queueHelper->process();
780 +
781 + if (!empty($queueHelper->messages)) {
782 + $this->detailMessages = array_merge($this->detailMessages, $queueHelper->messages);
783 + }
784 +
785 + if (!empty($queueHelper->nbprocess)) {
786 + $this->processed = true;
787 +
788 + if (!$queueHelper->finish && $this->externalSendingActivated) {
789 + $this->externalSendingNotFinished = true;
790 + }
791 + }
792 +
793 + $this->mainMessage = acym_translationSprintf(
794 + 'ACYM_CRON_PROCESS',
795 + $queueHelper->nbprocess,
796 + $queueHelper->successSend,
797 + $queueHelper->errorSend
798 + );
799 + $this->messages[] = $this->mainMessage;
800 +
801 + if (!empty($queueHelper->errorSend)) {
802 + $this->errorDetected = true;
803 + }
804 +
805 + $this->cronTimeLimit = $queueHelper->stoptime;
692 806 }
693 807 }