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 +68 -2 10.10.2 → 11.1.0 View file →
@@ -60,14 +60,18 @@
60 60 private int $cronTimeLimit = 0;
61 61 private bool $cronTimeLimitReached = false;
62 62 private int $startQueue;
63 63
64 + // Save the main message
64 65 private string $mainMessage = '';
65 66
67 + // Did the cron process anything?
66 68 private bool $processed = false;
67 69
70 + // Is there any error in the process
68 71 private bool $errorDetected = false;
69 72
73 + // If we call the cron just to send a batch of emails
70 74 private bool $externalSendingActivated = false;
71 75 private bool $externalSendingRepeat;
72 76 private bool $externalSendingNotFinished = false;
73 77 private bool $isSendingCall = false;
@@ -148,13 +152,15 @@
148 152 if (!empty($detailMessages)) {
149 153 $this->detailMessages = $detailMessages;
150 154 }
151 155
152 - $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);
153 158 $reportPath = acym_cleanPath(ACYM_ROOT.trim(html_entity_decode($reportPath)));
154 159 acym_createDir(dirname($reportPath), true, true);
155 160
156 161 $lr = "\r\n";
162 + // Catch warnings
157 163 ob_start();
158 164 file_put_contents(
159 165 $reportPath,
160 166 $lr.$lr.'******************** '.acym_getDate(time()).' UTC ********************'.$lr.implode($lr, $this->messages),
@@ -174,8 +180,11 @@
174 180 $this->messages[] = $potentialWarnings;
175 181 }
176 182 }
177 183
184 + /**
185 + * Sets the types of emails that will be sent by the cron
186 + */
178 187 public function setEmailTypes(array $emailTypes): void
179 188 {
180 189 $this->emailTypes = $emailTypes;
181 190 }
@@ -240,8 +249,11 @@
240 249
241 250 $this->config->saveConfig($newConfig);
242 251 }
243 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 + */
244 256 private function handleUnlinkLicenseCalls(): void
245 257 {
246 258 if (acym_getVar('int', 'unlink', 0) !== 1) {
247 259 return;
@@ -256,8 +268,11 @@
256 268
257 269 exit;
258 270 }
259 271
272 + /**
273 + * Displays the message shown on the cron URL page
274 + */
260 275 private function triggeredMessage(): void
261 276 {
262 277 $firstMessage = acym_translationSprintf('ACYM_CRON_TRIGGERED', acym_date('now', 'd F Y H:i'));
263 278 $this->messages[] = $firstMessage;
@@ -263,8 +278,11 @@
263 278 $this->messages[] = $firstMessage;
264 279 acym_display($firstMessage, 'info');
265 280 }
266 281
282 + /**
283 + * Checks the cron frequency in the configuration to make sure the call is wanted
284 + */
267 285 private function checkCronFrequency(): bool
268 286 {
269 287 if ($this->isSendingCall) {
270 288 return true;
@@ -275,21 +293,24 @@
275 293 $cronFrequency = $this->config->get('cron_frequency', 900);
276 294
277 295 if ($nextCronTime > $time) {
278 296 if ($nextCronTime > ($time + $cronFrequency)) {
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
279 298 $this->config->saveConfig(['cron_next' => $time + $cronFrequency]);
280 299 }
281 300
282 301 $notTimeMessage = acym_translationSprintf('ACYM_CRON_NEXT', acym_date($this->config->get('cron_next'), 'd F Y H:i'));
283 302 $this->messages[] = $notTimeMessage;
303 + // We don't need to trigger anything, it's not time yet
284 304 acym_display($notTimeMessage, 'info');
285 305
286 306 return false;
287 307 }
288 308
309 + // We update the next cron and the last cron dates
289 310 $newConfig = [
290 311 'cron_last' => $time,
291 - 'cron_fromip' => acym_getIP(),
312 + 'cron_fromip' => acym_getIP(true),
292 313 'cron_next' => $nextCronTime + $cronFrequency,
293 314 ];
294 315
295 316 if ($newConfig['cron_next'] <= $time || $newConfig['cron_next'] > $time + $cronFrequency) {
@@ -300,8 +321,11 @@
300 321
301 322 return true;
302 323 }
303 324
325 + /**
326 + * Adds the scheduled emails to the queue when the date is reached
327 + */
304 328 private function queueScheduledCampaigns(): void
305 329 {
306 330 if (in_array(self::STEP_SCHEDULE, $this->skip)) {
307 331 return;
@@ -315,8 +339,11 @@
315 339 $this->processed = true;
316 340 }
317 341 }
318 342
343 + /**
344 + * Cleans the emails that are stuck in the queue for too long (disabled/unconfirmed recipients)
345 + */
319 346 private function cleanQueue(): void
320 347 {
321 348 if (in_array(self::STEP_CLEAN_QUEUE, $this->skip)) {
322 349 return;
@@ -345,8 +372,11 @@
345 372 $this->sendQueuedEmails();
346 373 }
347 374 }
348 375
376 + /**
377 + * Makes sure the current process doesn't reach the server time limit
378 + */
349 379 private function checkTimeRemaining(): void
350 380 {
351 381 $time = time();
352 382 if (empty($this->cronTimeLimit) || $time < $this->cronTimeLimit) {
@@ -357,8 +387,11 @@
357 387
358 388 $this->cronTimeLimitReached = true;
359 389 }
360 390
391 + /**
392 + * If the configuration is set to send multiple batches of emails at the same time
393 + */
361 394 private function handleMultiCron(): void
362 395 {
363 396 $emailsPerBatches = $this->config->get('queue_nbmail_auto', 70);
364 397 if (empty($emailsPerBatches)) {
@@ -429,8 +462,9 @@
429 462 $this->detailMessages = array_merge($this->detailMessages, $bounceHelper->messages);
430 463 } else {
431 464 $bounceErrors = $bounceHelper->getErrors();
432 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...
433 467 if (!empty($bounceErrors[0])) {
434 468 $bounceErrors[0] = acym_translation('ACYM_BOUNCE_HANDLING').' : '.$bounceErrors[0];
435 469 }
436 470 $this->messages = array_merge($this->messages, $bounceErrors);
@@ -440,8 +474,11 @@
440 474
441 475 $this->config->saveConfig($newConfig);
442 476 }
443 477
478 + /**
479 + * Triggers the automations based on time frequency
480 + */
444 481 private function handleAutomations(): void
445 482 {
446 483 $this->checkTimeRemaining();
447 484 if (in_array(self::STEP_AUTOMATION, $this->skip) || $this->cronTimeLimitReached || !acym_level(ACYM_ENTERPRISE)) {
@@ -478,8 +515,11 @@
478 515 $this->processed = true;
479 516 }
480 517 }
481 518
519 + /**
520 + * Sends special emails such as birthday or WooCommerce reminders
521 + */
482 522 private function handleSpecificEmails(): void
483 523 {
484 524 $this->checkTimeRemaining();
485 525 if (in_array(self::STEP_SPECIFIC, $this->skip) || $this->cronTimeLimitReached) {
@@ -500,8 +540,11 @@
500 540 $campaignClass->send($onespecialMail->id);
501 541 }
502 542 }
503 543
544 + /**
545 + * Sends follow-ups based on time frequency like birthdays
546 + */
504 547 private function handleFollowups(): void
505 548 {
506 549 $this->checkTimeRemaining();
507 550 if (in_array(self::STEP_FOLLOWUP, $this->skip) || $this->cronTimeLimitReached) {
@@ -510,11 +553,14 @@
510 553
511 554 $followupClass = new FollowupClass();
512 555 $followups = $followupClass->getFollowupDailyBases();
513 556
557 + // Only once a day
514 558 $dailyHour = $this->config->get('daily_hour', '12');
515 559 $dailyMinute = $this->config->get('daily_minute', '00');
560 + // The day it is currently based on the timezone specified in the CMS configuration
516 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
517 563 $dayBasedOnCMSTimezoneAtSpecifiedHour = acym_getTimeFromCMSDate($dayBasedOnCMSTimezone.' '.$dailyHour.':'.$dailyMinute);
518 564 $time = time();
519 565
520 566 foreach ($followups as $followup) {
@@ -528,8 +574,11 @@
528 574 }
529 575 }
530 576 }
531 577
578 + /**
579 + * Checks messages received on mailboxes and triggers actions accordingly
580 + */
532 581 private function handleMailboxActions(): void
533 582 {
534 583 $this->checkTimeRemaining();
535 584 if (in_array(self::STEP_MAILBOX_ACTION, $this->skip) || $this->cronTimeLimitReached || !acym_level(ACYM_ENTERPRISE)) {
@@ -564,8 +613,9 @@
564 613 $mailboxHelper->handleAction();
565 614 $mailboxHelper->close();
566 615
567 616
617 + // Update next trigger
568 618 $oneMailboxAction->nextdate = time() + $oneMailboxAction->frequency;
569 619 unset($oneMailboxAction->conditions);
570 620 unset($oneMailboxAction->actions);
571 621 $mailboxActionClass->save($oneMailboxAction);
@@ -573,8 +623,9 @@
573 623 }
574 624
575 625 private function cleanData(): void
576 626 {
627 + // Clean the history and detailed stats based on the configuration
577 628 if (!in_array(self::STEP_DELETE_HISTORY, $this->skip) && $this->isDailyCron()) {
578 629 $userStatClass = new UserStatClass();
579 630 $userDetailedStatsDeleted = $userStatClass->deleteDetailedStatsPeriod();
580 631 if (!empty($userDetailedStatsDeleted['message'])) {
@@ -596,12 +647,14 @@
596 647 $this->processed = true;
597 648 }
598 649 }
599 650
651 + // Clean data on external sending method
600 652 if (!in_array(self::STEP_CLEAN_DATA_EXTERNAL_SENDING_METHOD, $this->skip) && $this->isDailyCron()) {
601 653 acym_trigger('onAcymCleanDataExternalSendingMethod');
602 654 }
603 655
656 + // Clean export changes
604 657 if (!in_array(self::STEP_CLEAN_EXPORT_CHANGES, $this->skip) && $this->isDailyCron()) {
605 658 $exportHelper = new ExportHelper();
606 659 $exportHelper->cleanExportChangesFile();
607 660 }
@@ -613,15 +666,19 @@
613 666 }
614 667
615 668 private function isDailyCron(): bool
616 669 {
670 + // Only once a day
617 671 $dailyHour = $this->config->get('daily_hour', '12');
618 672 $dailyMinute = $this->config->get('daily_minute', '00');
673 + // The day it is currently based on the timezone specified in the CMS configuration
619 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
620 676 $dayBasedOnCMSTimezoneAtSpecifiedHour = acym_getTimeFromCMSDate($dayBasedOnCMSTimezone.' '.$dailyHour.':'.$dailyMinute);
621 677
622 678 $time = time();
623 679
680 + //If we do not have run the cron we set it to yesterday so it will be trigger
624 681 $lastCronDayBasedOnCMSTimezone = acym_date($this->config->get('cron_last_daily', $time - 86400), 'Y-m-d');
625 682
626 683 return $time >= $dayBasedOnCMSTimezoneAtSpecifiedHour && $lastCronDayBasedOnCMSTimezone != $dayBasedOnCMSTimezone;
627 684 }
@@ -654,8 +711,11 @@
654 711 $scenarioHelper->executeAvailableSteps();
655 712 $scenarioHelper->triggerTimeScenarios();
656 713 }
657 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 + */
658 718 private function continueCronCall(): void
659 719 {
660 720 if ($this->externalSendingNotFinished) {
661 721 $cronKey = '';
@@ -676,8 +736,9 @@
676 736 if ($this->config->get('queue_stop_weekend', 0) && $dayOfWeek >= 6) {
677 737 return false;
678 738 }
679 739
740 + // Make sure we are within the sending hours defined in the configuration
680 741 $fromHour = $this->config->get('queue_send_from_hour', '00');
681 742 $fromMinute = $this->config->get('queue_send_from_minute', '00');
682 743 $toHour = $this->config->get('queue_send_to_hour', '23');
683 744 $toMinute = $this->config->get('queue_send_to_minute', '59');
@@ -683,15 +744,20 @@
683 744 $toMinute = $this->config->get('queue_send_to_minute', '59');
684 745 $time = time();
685 746
686 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
687 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
688 751 $fromBasedOnCMSTimezoneAtSpecifiedHour = acym_getTimeFromCMSDate($dayBasedOnCMSTimezone.' '.$fromHour.':'.$fromMinute);
689 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
690 754 if ($fromBasedOnCMSTimezoneAtSpecifiedHour > $toBasedOnCMSTimezoneAtSpecifiedHour) {
755 + // TO becomes tomorrow as we are not passed midnight
691 756 if ($time > $fromBasedOnCMSTimezoneAtSpecifiedHour) {
692 757 $toBasedOnCMSTimezoneAtSpecifiedHour = acym_getTimeFromCMSDate(acym_date('tomorrow', 'Y-m-d').' '.$toHour.':'.$toMinute);
693 758 } elseif ($time < $toBasedOnCMSTimezoneAtSpecifiedHour) {
759 + // FROM becomes yesterday as we are passed midnight
694 760 $fromBasedOnCMSTimezoneAtSpecifiedHour = acym_getTimeFromCMSDate(acym_date('yesterday', 'Y-m-d').' '.$fromHour.':'.$fromMinute);
695 761 }
696 762 }
697 763