![]() Server : Apache System : Linux server2.corals.io 4.18.0-348.2.1.el8_5.x86_64 #1 SMP Mon Nov 15 09:17:08 EST 2021 x86_64 User : corals ( 1002) PHP Version : 7.4.33 Disable Function : exec,passthru,shell_exec,system Directory : /home/corals/mautic.corals.io/app/bundles/CampaignBundle/Executioner/Scheduler/Mode/ |
<?php namespace Mautic\CampaignBundle\Executioner\Scheduler\Mode; use Doctrine\Common\Collections\ArrayCollection; use Mautic\CampaignBundle\Entity\Event; use Mautic\CampaignBundle\Entity\LeadEventLog; use Mautic\CampaignBundle\Executioner\Scheduler\Exception\NotSchedulableException; use Mautic\CampaignBundle\Executioner\Scheduler\Mode\DAO\GroupExecutionDateDAO; use Mautic\CoreBundle\Helper\CoreParametersHelper; use Mautic\CoreBundle\Helper\DateTimeHelper; use Mautic\LeadBundle\Entity\Lead; use Psr\Log\LoggerInterface; class Interval implements ScheduleModeInterface { public const LOG_DATE_FORMAT = 'Y-m-d H:i:s T'; private ?\DateTimeZone $defaultTimezone = null; public function __construct( private LoggerInterface $logger, private CoreParametersHelper $coreParametersHelper ) { } /** * @throws NotSchedulableException */ public function getExecutionDateTime(Event $event, \DateTimeInterface $compareFromDateTime, \DateTimeInterface $comparedToDateTime): \DateTimeInterface { $interval = $event->getTriggerInterval(); $unit = $event->getTriggerIntervalUnit(); try { $this->logger->debug( 'CAMPAIGN: ('.$event->getId().') Adding interval of '.$interval.$unit.' to '.$comparedToDateTime->format(self::LOG_DATE_FORMAT) ); /** @var \DateTime $comparedToDateTime */ $comparedToDateTime->add((new DateTimeHelper())->buildInterval($interval, $unit)); } catch (\Exception $exception) { $this->logger->error('CAMPAIGN: Determining interval scheduled failed with "'.$exception->getMessage().'"'); throw new NotSchedulableException($exception->getMessage()); } if ($comparedToDateTime > $compareFromDateTime) { $this->logger->debug( 'CAMPAIGN: ('.$event->getId().') '.$comparedToDateTime->format(self::LOG_DATE_FORMAT).' is later than ' .$compareFromDateTime->format(self::LOG_DATE_FORMAT).' and thus returning '.$comparedToDateTime->format(self::LOG_DATE_FORMAT) ); // the event is to be scheduled based on the time interval return $comparedToDateTime; } $this->logger->debug( 'CAMPAIGN: ('.$event->getId().') '.$comparedToDateTime->format(self::LOG_DATE_FORMAT).' is earlier than ' .$compareFromDateTime->format(self::LOG_DATE_FORMAT).' and thus returning '.$compareFromDateTime->format(self::LOG_DATE_FORMAT) ); return $compareFromDateTime; } /** * @return \DateTimeInterface * * @throws NotSchedulableException */ public function validateExecutionDateTime(LeadEventLog $log, \DateTimeInterface $compareFromDateTime) { $event = $log->getEvent(); $dateTriggered = clone $log->getDateTriggered(); if (!$this->isContactSpecificExecutionDateRequired($event)) { return $this->getExecutionDateTime($event, $compareFromDateTime, $dateTriggered); } $interval = $event->getTriggerInterval(); $unit = $event->getTriggerIntervalUnit(); if ($interval && $unit) { /** @var \DateTime $dateTriggered */ $dateTriggered->add((new DateTimeHelper())->buildInterval($interval, $unit)); } if ($dateTriggered < $compareFromDateTime) { $this->logger->debug( sprintf('CAMPAIGN: (%s) %s is earlier than %s and thus setting %s', $event->getId(), $dateTriggered->format(self::LOG_DATE_FORMAT), $compareFromDateTime->format(self::LOG_DATE_FORMAT), $compareFromDateTime->format(self::LOG_DATE_FORMAT)) ); $dateTriggered = clone $compareFromDateTime; } $hour = $event->getTriggerHour(); $startTime = $event->getTriggerRestrictedStartHour(); $endTime = $event->getTriggerRestrictedStopHour(); $dow = $event->getTriggerRestrictedDaysOfWeek(); return $this->getGroupExecutionDateTime($event->getId(), $log->getLead(), $dateTriggered, $hour, $startTime, $endTime, $dow); } /** * @return GroupExecutionDateDAO[] */ public function groupContactsByDate(Event $event, ArrayCollection $contacts, \DateTimeInterface $executionDate, \DateTimeInterface $compareFromDateTime = null): array { $groupedExecutionDates = []; $hour = $event->getTriggerHour(); $startTime = $event->getTriggerRestrictedStartHour(); $endTime = $event->getTriggerRestrictedStopHour(); $daysOfWeek = $event->getTriggerRestrictedDaysOfWeek(); /** @var Lead $contact */ foreach ($contacts as $contact) { $groupExecutionDate = $this->getGroupExecutionDateTime( $event->getId(), $contact, $executionDate, $hour, $startTime, $endTime, $daysOfWeek ); if (!isset($groupedExecutionDates[$groupExecutionDate->getTimestamp()])) { $groupedExecutionDates[$groupExecutionDate->getTimestamp()] = new GroupExecutionDateDAO($groupExecutionDate); } $groupedExecutionDates[$groupExecutionDate->getTimestamp()]->addContact($contact); } return $groupedExecutionDates; } /** * Checks if an event has a relative time configured. */ public function isContactSpecificExecutionDateRequired(Event $event): bool { if (!$this->isTriggerModeInterval($event) || $this->isRestrictedToDailyScheduling($event) || $this->hasTimeRelatedRestrictions($event)) { return false; } return true; } private function isTriggerModeInterval(Event $event): bool { return Event::TRIGGER_MODE_INTERVAL === $event->getTriggerMode(); } private function isRestrictedToDailyScheduling(Event $event): bool { return !in_array($event->getTriggerIntervalUnit(), ['i', 'h', 'd', 'm', 'y']) && empty($event->getTriggerRestrictedDaysOfWeek()); } private function hasTimeRelatedRestrictions(Event $event): bool { return null === $event->getTriggerHour() && (null === $event->getTriggerRestrictedStartHour() || null === $event->getTriggerRestrictedStopHour()) && empty($event->getTriggerRestrictedDaysOfWeek()); } /** * @return \DateTimeInterface */ private function getGroupExecutionDateTime( $eventId, Lead $contact, \DateTimeInterface $compareFromDateTime, \DateTimeInterface $hour = null, \DateTimeInterface $startTime = null, \DateTimeInterface $endTime = null, array $daysOfWeek = [] ) { $this->logger->debug( sprintf('CAMPAIGN: Comparing calculated executed time for event ID %s and contact ID %s with %s', $eventId, $contact->getId(), $compareFromDateTime->format('Y-m-d H:i:s e')) ); if ($hour) { $this->logger->debug( sprintf('CAMPAIGN: Scheduling event ID %s for contact ID %s based on hour of %s', $eventId, $contact->getId(), $hour->format('H:i e')) ); $groupDateTime = $this->getExecutionDateTimeFromHour($contact, $hour, $eventId, $compareFromDateTime); } elseif ($startTime && $endTime) { $this->logger->debug( sprintf( 'CAMPAIGN: Scheduling event ID %s for contact ID %s based on hour range of %s to %s', $eventId, $contact->getId(), $startTime->format('H:i e'), $endTime->format('H:i e') ) ); $groupDateTime = $this->getExecutionDateTimeBetweenHours($contact, $startTime, $endTime, $eventId, $compareFromDateTime); } else { $this->logger->debug( sprintf('CAMPAIGN: Scheduling event ID %s for contact ID %s without hour restrictions.', $eventId, $contact->getId()) ); $groupDateTime = clone $compareFromDateTime; } if ($daysOfWeek) { $this->logger->debug( sprintf( 'CAMPAIGN: Scheduling event ID %s for contact ID %s based on DOW restrictions of %s', $eventId, $contact->getId(), implode(',', $daysOfWeek) ) ); // Schedule for the next day of the week if applicable while (!in_array((int) $groupDateTime->format('w'), $daysOfWeek)) { /** @var \DateTime $groupDateTime */ $groupDateTime->modify('+1 day'); } } return $groupDateTime; } /** * @return \DateTimeInterface */ private function getExecutionDateTimeFromHour(Lead $contact, \DateTimeInterface $hour, $eventId, \DateTimeInterface $compareFromDateTime) { /** @var \DateTime $groupHour */ $groupHour = clone $hour; /** @var \DateTime $groupExecutionDate */ $groupExecutionDate = $this->getGroupExecutionDateWithTimeZone($contact, $eventId, $compareFromDateTime); $groupExecutionDate->setTime((int) $groupExecutionDate->format('H'), (int) $groupExecutionDate->format('i')); $testGroupHour = clone $groupExecutionDate; $testGroupHour->setTime($groupHour->format('H'), $groupHour->format('i')); if ($groupExecutionDate <= $testGroupHour) { return $testGroupHour; } else { $groupExecutionDate->modify('+1 day')->setTime($groupHour->format('H'), $groupHour->format('i')); } return $groupExecutionDate; } /** * @return \DateTimeInterface */ private function getExecutionDateTimeBetweenHours( Lead $contact, \DateTimeInterface $startTime, \DateTimeInterface $endTime, $eventId, \DateTimeInterface $compareFromDateTime ) { /* @var \DateTime $startTime */ $startTime = clone $startTime; /* @var \DateTime $endTime */ $endTime = clone $endTime; if ($endTime < $startTime) { // End time is after start time so switch them $tempStartTime = clone $startTime; $startTime = clone $endTime; $endTime = clone $tempStartTime; unset($tempStartTime); } /** @var \DateTime $groupExecutionDate */ $groupExecutionDate = $this->getGroupExecutionDateWithTimeZone($contact, $eventId, $compareFromDateTime); // Is the time between the start and end hours? $testStartDateTime = clone $groupExecutionDate; $testStartDateTime->setTime($startTime->format('H'), $startTime->format('i')); $testStopDateTime = clone $groupExecutionDate; $testStopDateTime->setTime($endTime->format('H'), $endTime->format('i')); if ($groupExecutionDate < $testStartDateTime) { // Too early so set it to the start date return $testStartDateTime; } if ($groupExecutionDate >= $testStopDateTime) { // Too late so try again tomorrow $groupExecutionDate->modify('+1 day')->setTime((int) $startTime->format('H'), (int) $startTime->format('i')); } return $groupExecutionDate; } /** * @return \DateTimeZone */ private function getDefaultTimezone() { if ($this->defaultTimezone) { return $this->defaultTimezone; } $this->defaultTimezone = new \DateTimeZone( $this->coreParametersHelper->get('default_timezone', 'UTC') ); return $this->defaultTimezone; } private function getGroupExecutionDateWithTimeZone(Lead $contact, int $eventId, \DateTimeInterface $compareFromDateTime): \DateTimeInterface { /** @var \DateTime $groupExecutionDate */ $groupExecutionDate = clone $compareFromDateTime; $contactTimezone = $this->getDefaultTimezone(); // Set execution to UTC if ($timezone = $contact->getTimezone()) { try { // Set the group's timezone to the contact's $contactTimezone = new \DateTimeZone($timezone); $this->logger->debug( 'CAMPAIGN: ('.$eventId.') Setting '.$timezone.' for contact '.$contact->getId() ); } catch (\Exception) { // Timezone is not recognized so use the default $this->logger->debug( 'CAMPAIGN: ('.$eventId.') '.$timezone.' for contact '.$contact->getId().' is not recognized' ); } } $groupExecutionDate->setTimezone($contactTimezone); return $groupExecutionDate; } }