init
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\Newsletter\Scheduler;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Cron\Workers\SendingQueue\SendingQueue;
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Entities\NewsletterOptionFieldEntity;
|
||||
use MailPoet\Entities\ScheduledTaskEntity;
|
||||
use MailPoet\Entities\ScheduledTaskSubscriberEntity;
|
||||
use MailPoet\Entities\SendingQueueEntity;
|
||||
use MailPoet\Entities\SubscriberEntity;
|
||||
use MailPoet\Newsletter\Sending\ScheduledTasksRepository;
|
||||
use MailPoet\Newsletter\Sending\ScheduledTaskSubscribersRepository;
|
||||
use MailPoet\Newsletter\Sending\SendingQueuesRepository;
|
||||
|
||||
class AutomaticEmailScheduler {
|
||||
|
||||
/** @var Scheduler */
|
||||
private $scheduler;
|
||||
|
||||
/** @var ScheduledTasksRepository */
|
||||
private $scheduledTasksRepository;
|
||||
|
||||
/** @var SendingQueuesRepository */
|
||||
private $sendingQueuesRepository;
|
||||
|
||||
/** @var ScheduledTaskSubscribersRepository */
|
||||
private $scheduledTaskSubscribersRepository;
|
||||
|
||||
public function __construct(
|
||||
Scheduler $scheduler,
|
||||
ScheduledTasksRepository $scheduledTasksRepository,
|
||||
ScheduledTaskSubscribersRepository $scheduledTaskSubscribersRepository,
|
||||
SendingQueuesRepository $sendingQueuesRepository
|
||||
) {
|
||||
$this->scheduler = $scheduler;
|
||||
$this->scheduledTasksRepository = $scheduledTasksRepository;
|
||||
$this->scheduledTaskSubscribersRepository = $scheduledTaskSubscribersRepository;
|
||||
$this->sendingQueuesRepository = $sendingQueuesRepository;
|
||||
}
|
||||
|
||||
public function scheduleAutomaticEmail(
|
||||
string $group,
|
||||
string $event,
|
||||
?callable $schedulingCondition = null,
|
||||
?SubscriberEntity $subscriber = null,
|
||||
?array $meta = null,
|
||||
?callable $metaModifier = null
|
||||
) {
|
||||
$newsletters = $this->scheduler->getNewsletters(NewsletterEntity::TYPE_AUTOMATIC, $group);
|
||||
if (empty($newsletters)) return false;
|
||||
foreach ($newsletters as $newsletter) {
|
||||
if ($newsletter->getOptionValue(NewsletterOptionFieldEntity::NAME_EVENT) !== $event) continue;
|
||||
if (is_callable($schedulingCondition) && !$schedulingCondition($newsletter)) continue;
|
||||
|
||||
/**
|
||||
* $meta will be the same for all newsletters by default. If we need to store newsletter-specific meta, the
|
||||
* $metaModifier callback can be used.
|
||||
*
|
||||
* This was introduced because of WooCommerce product purchase automatic emails. We only want to store the
|
||||
* product IDs that specifically triggered a newsletter, but $meta includes ALL the product IDs
|
||||
* or category IDs from an order.
|
||||
*/
|
||||
if (is_callable($metaModifier)) {
|
||||
$meta = $metaModifier($newsletter, $meta);
|
||||
}
|
||||
$this->createAutomaticEmailScheduledTask($newsletter, $subscriber, $meta);
|
||||
}
|
||||
}
|
||||
|
||||
public function scheduleOrRescheduleAutomaticEmail(string $group, string $event, SubscriberEntity $subscriber, array $meta): void {
|
||||
$newsletters = $this->scheduler->getNewsletters(NewsletterEntity::TYPE_AUTOMATIC, $group);
|
||||
if (empty($newsletters)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($newsletters as $newsletter) {
|
||||
if ($newsletter->getOptionValue(NewsletterOptionFieldEntity::NAME_EVENT) !== $event) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// try to find existing scheduled task for given subscriber
|
||||
$task = $this->scheduledTasksRepository->findOneScheduledByNewsletterAndSubscriber($newsletter, $subscriber);
|
||||
if ($task) {
|
||||
$this->rescheduleAutomaticEmailSendingTask($newsletter, $task, $meta);
|
||||
} else {
|
||||
$this->createAutomaticEmailScheduledTask($newsletter, $subscriber, $meta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function rescheduleAutomaticEmail(string $group, string $event, SubscriberEntity $subscriber): void {
|
||||
$newsletters = $this->scheduler->getNewsletters(NewsletterEntity::TYPE_AUTOMATIC, $group);
|
||||
if (empty($newsletters)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($newsletters as $newsletter) {
|
||||
if ($newsletter->getOptionValue(NewsletterOptionFieldEntity::NAME_EVENT) !== $event) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// try to find existing scheduled task for given subscriber
|
||||
$task = $this->scheduledTasksRepository->findOneScheduledByNewsletterAndSubscriber($newsletter, $subscriber);
|
||||
if ($task) {
|
||||
$this->rescheduleAutomaticEmailSendingTask($newsletter, $task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function cancelAutomaticEmail(string $group, string $event, SubscriberEntity $subscriber): void {
|
||||
$newsletters = $this->scheduler->getNewsletters(NewsletterEntity::TYPE_AUTOMATIC, $group);
|
||||
if (empty($newsletters)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($newsletters as $newsletter) {
|
||||
if ($newsletter->getOptionValue(NewsletterOptionFieldEntity::NAME_EVENT) !== $event) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// try to find existing scheduled task for given subscriber
|
||||
$task = $this->scheduledTasksRepository->findOneScheduledByNewsletterAndSubscriber($newsletter, $subscriber);
|
||||
if ($task) {
|
||||
$queue = $task->getSendingQueue();
|
||||
if ($queue instanceof SendingQueueEntity) {
|
||||
$this->sendingQueuesRepository->remove($queue);
|
||||
}
|
||||
$this->scheduledTaskSubscribersRepository->deleteByScheduledTask($task);
|
||||
$this->scheduledTasksRepository->remove($task);
|
||||
$this->scheduledTasksRepository->flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function createAutomaticEmailScheduledTask(NewsletterEntity $newsletter, ?SubscriberEntity $subscriber, ?array $meta = null): ScheduledTaskEntity {
|
||||
$scheduledTask = new ScheduledTaskEntity();
|
||||
$scheduledTask->setType(SendingQueue::TASK_TYPE);
|
||||
$scheduledTask->setStatus(SendingQueueEntity::STATUS_SCHEDULED);
|
||||
$scheduledTask->setPriority(ScheduledTaskEntity::PRIORITY_MEDIUM);
|
||||
|
||||
$scheduledTask->setScheduledAt($this->scheduler->getScheduledTimeWithDelay(
|
||||
$newsletter->getOptionValue(NewsletterOptionFieldEntity::NAME_AFTER_TIME_TYPE),
|
||||
$newsletter->getOptionValue(NewsletterOptionFieldEntity::NAME_AFTER_TIME_NUMBER)
|
||||
));
|
||||
$this->scheduledTasksRepository->persist($scheduledTask);
|
||||
$this->scheduledTasksRepository->flush();
|
||||
|
||||
$sendingQueue = new SendingQueueEntity();
|
||||
$sendingQueue->setNewsletter($newsletter);
|
||||
$sendingQueue->setTask($scheduledTask);
|
||||
// Because we changed the way how to updateCounts after sending we need to set initial counts
|
||||
$sendingQueue->setCountTotal($subscriber ? 1 : 0);
|
||||
$sendingQueue->setCountToProcess($subscriber ? 1 : 0);
|
||||
$scheduledTask->setSendingQueue($sendingQueue);
|
||||
|
||||
if ($meta) {
|
||||
$scheduledTask->setMeta($meta);
|
||||
$sendingQueue->setMeta($meta);
|
||||
}
|
||||
|
||||
$this->sendingQueuesRepository->persist($sendingQueue);
|
||||
$this->sendingQueuesRepository->flush();
|
||||
|
||||
if ($newsletter->getOptionValue(NewsletterOptionFieldEntity::NAME_SEND_TO) === 'user' && $subscriber) {
|
||||
$scheduledTaskSubscriber = new ScheduledTaskSubscriberEntity($scheduledTask, $subscriber);
|
||||
$this->scheduledTaskSubscribersRepository->persist($scheduledTaskSubscriber);
|
||||
$this->scheduledTaskSubscribersRepository->flush();
|
||||
$scheduledTask->getSubscribers()->add($scheduledTaskSubscriber);
|
||||
}
|
||||
|
||||
return $scheduledTask;
|
||||
}
|
||||
|
||||
private function rescheduleAutomaticEmailSendingTask(NewsletterEntity $newsletter, ScheduledTaskEntity $scheduledTask, ?array $meta = null): void {
|
||||
$sendingQueue = $this->sendingQueuesRepository->findOneBy(['task' => $scheduledTask]);
|
||||
if (!$sendingQueue) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($meta) {
|
||||
$sendingQueue->setMeta($meta);
|
||||
$scheduledTask->setMeta($meta);
|
||||
}
|
||||
// compute new 'scheduled_at' from now
|
||||
$scheduledTask->setScheduledAt($this->scheduler->getScheduledTimeWithDelay(
|
||||
$newsletter->getOptionValue(NewsletterOptionFieldEntity::NAME_AFTER_TIME_TYPE),
|
||||
$newsletter->getOptionValue(NewsletterOptionFieldEntity::NAME_AFTER_TIME_NUMBER)
|
||||
));
|
||||
$this->sendingQueuesRepository->flush();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace MailPoet\Newsletter\Scheduler;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Automation\Engine\Data\AutomationRun;
|
||||
use MailPoet\Cron\Workers\SendingQueue\SendingQueue;
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Entities\ScheduledTaskEntity;
|
||||
use MailPoet\Entities\ScheduledTaskSubscriberEntity;
|
||||
use MailPoet\Entities\SendingQueueEntity;
|
||||
use MailPoet\Entities\SubscriberEntity;
|
||||
use MailPoet\InvalidStateException;
|
||||
use MailPoet\Newsletter\Sending\ScheduledTaskSubscribersRepository;
|
||||
use MailPoetVendor\Carbon\Carbon;
|
||||
use MailPoetVendor\Doctrine\ORM\EntityManager;
|
||||
|
||||
class AutomationEmailScheduler {
|
||||
/** @var EntityManager */
|
||||
private $entityManager;
|
||||
|
||||
private ScheduledTaskSubscribersRepository $scheduledTaskSubscribersRepository;
|
||||
|
||||
public function __construct(
|
||||
EntityManager $entityManager,
|
||||
ScheduledTaskSubscribersRepository $scheduledTaskSubscribersRepository
|
||||
) {
|
||||
$this->entityManager = $entityManager;
|
||||
$this->scheduledTaskSubscribersRepository = $scheduledTaskSubscribersRepository;
|
||||
}
|
||||
|
||||
public function createSendingTask(NewsletterEntity $email, SubscriberEntity $subscriber, array $meta): ScheduledTaskEntity {
|
||||
if (!in_array($email->getType(), [NewsletterEntity::TYPE_AUTOMATION, NewsletterEntity::TYPE_AUTOMATION_TRANSACTIONAL], true)) {
|
||||
throw InvalidStateException::create()->withMessage(
|
||||
// translators: %s is the type which was given.
|
||||
sprintf(__("Email with type 'automation' or 'automation_transactional' expected, '%s' given.", 'mailpoet'), $email->getType())
|
||||
);
|
||||
}
|
||||
|
||||
$task = new ScheduledTaskEntity();
|
||||
$task->setType(SendingQueue::TASK_TYPE);
|
||||
$task->setStatus(ScheduledTaskEntity::STATUS_SCHEDULED);
|
||||
$task->setScheduledAt(Carbon::now()->millisecond(0));
|
||||
$task->setPriority(ScheduledTaskEntity::PRIORITY_MEDIUM);
|
||||
$task->setMeta($meta);
|
||||
$this->entityManager->persist($task);
|
||||
|
||||
$taskSubscriber = new ScheduledTaskSubscriberEntity($task, $subscriber);
|
||||
$this->entityManager->persist($taskSubscriber);
|
||||
|
||||
$queue = new SendingQueueEntity();
|
||||
$queue->setTask($task);
|
||||
$queue->setMeta($meta);
|
||||
$queue->setNewsletter($email);
|
||||
$queue->setCountToProcess(1);
|
||||
$queue->setCountTotal(1);
|
||||
$this->entityManager->persist($queue);
|
||||
|
||||
$this->entityManager->flush();
|
||||
return $task;
|
||||
}
|
||||
|
||||
public function getScheduledTaskSubscriber(NewsletterEntity $email, SubscriberEntity $subscriber, AutomationRun $run): ?ScheduledTaskSubscriberEntity {
|
||||
$results = $this->entityManager->createQueryBuilder()
|
||||
->select('sts')
|
||||
->from(ScheduledTaskSubscriberEntity::class, 'sts')
|
||||
->join('sts.task', 'st')
|
||||
->join('st.sendingQueue', 'sq')
|
||||
->where('sq.newsletter = :newsletter')
|
||||
->andWhere('sts.subscriber = :subscriber')
|
||||
->andWhere('st.createdAt >= :runCreatedAt')
|
||||
->setParameter('newsletter', $email)
|
||||
->setParameter('subscriber', $subscriber)
|
||||
->setParameter('runCreatedAt', $run->getCreatedAt())
|
||||
->getQuery()
|
||||
->getResult();
|
||||
$result = null;
|
||||
foreach ($results as $scheduledTaskSubscriber) {
|
||||
$task = $scheduledTaskSubscriber->getTask();
|
||||
if (!$task instanceof ScheduledTaskEntity) {
|
||||
continue;
|
||||
}
|
||||
$meta = $task->getMeta();
|
||||
if (($meta['automation']['run_id'] ?? null) === $run->getId()) {
|
||||
$result = $scheduledTaskSubscriber;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $result instanceof ScheduledTaskSubscriberEntity ? $result : null;
|
||||
}
|
||||
|
||||
public function saveError(ScheduledTaskSubscriberEntity $scheduledTaskSubscriber, string $error): void {
|
||||
$task = $scheduledTaskSubscriber->getTask();
|
||||
$subscriber = $scheduledTaskSubscriber->getSubscriber();
|
||||
if (!$task || !$subscriber || !$subscriber->getId()) {
|
||||
return;
|
||||
}
|
||||
$this->scheduledTaskSubscribersRepository->saveError($task, $subscriber->getId(), $error);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\Newsletter\Scheduler;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Cron\Workers\SendingQueue\SendingQueue;
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Entities\NewsletterOptionEntity;
|
||||
use MailPoet\Entities\NewsletterOptionFieldEntity;
|
||||
use MailPoet\Entities\ScheduledTaskEntity;
|
||||
use MailPoet\Entities\SendingQueueEntity;
|
||||
use MailPoet\Logging\LoggerFactory;
|
||||
use MailPoet\Newsletter\NewsletterPostsRepository;
|
||||
use MailPoet\Newsletter\NewslettersRepository;
|
||||
use MailPoet\Newsletter\Options\NewsletterOptionFieldsRepository;
|
||||
use MailPoet\Newsletter\Options\NewsletterOptionsRepository;
|
||||
use MailPoet\Newsletter\Sending\ScheduledTasksRepository;
|
||||
use MailPoet\Newsletter\Sending\SendingQueuesRepository;
|
||||
use MailPoet\WP\DateTime;
|
||||
use MailPoet\WP\Posts;
|
||||
|
||||
class PostNotificationScheduler {
|
||||
|
||||
const SECONDS_IN_MINUTE = 60;
|
||||
const SECONDS_IN_HOUR = 3600;
|
||||
const LAST_WEEKDAY_FORMAT = 'L';
|
||||
const INTERVAL_DAILY = 'daily';
|
||||
const INTERVAL_IMMEDIATELY = 'immediately';
|
||||
const INTERVAL_NTHWEEKDAY = 'nthWeekDay';
|
||||
const INTERVAL_WEEKLY = 'weekly';
|
||||
const INTERVAL_IMMEDIATE = 'immediate';
|
||||
const INTERVAL_MONTHLY = 'monthly';
|
||||
|
||||
/** @var LoggerFactory */
|
||||
private $loggerFactory;
|
||||
|
||||
/** @var NewslettersRepository */
|
||||
private $newslettersRepository;
|
||||
|
||||
/** @var NewsletterOptionsRepository */
|
||||
private $newsletterOptionsRepository;
|
||||
|
||||
/** @var NewsletterOptionFieldsRepository */
|
||||
private $newsletterOptionFieldsRepository;
|
||||
|
||||
/** @var NewsletterPostsRepository */
|
||||
private $newsletterPostsRepository;
|
||||
|
||||
/** @var Scheduler */
|
||||
private $scheduler;
|
||||
|
||||
/*** @var ScheduledTasksRepository */
|
||||
private $scheduledTasksRepository;
|
||||
|
||||
/*** @var SendingQueuesRepository */
|
||||
private $sendingQueuesRepository;
|
||||
|
||||
public function __construct(
|
||||
NewslettersRepository $newslettersRepository,
|
||||
NewsletterOptionsRepository $newsletterOptionsRepository,
|
||||
NewsletterOptionFieldsRepository $newsletterOptionFieldsRepository,
|
||||
NewsletterPostsRepository $newsletterPostsRepository,
|
||||
Scheduler $scheduler,
|
||||
ScheduledTasksRepository $scheduledTasksRepository,
|
||||
SendingQueuesRepository $sendingQueuesRepository
|
||||
) {
|
||||
$this->loggerFactory = LoggerFactory::getInstance();
|
||||
$this->newslettersRepository = $newslettersRepository;
|
||||
$this->newsletterOptionsRepository = $newsletterOptionsRepository;
|
||||
$this->newsletterOptionFieldsRepository = $newsletterOptionFieldsRepository;
|
||||
$this->newsletterPostsRepository = $newsletterPostsRepository;
|
||||
$this->scheduler = $scheduler;
|
||||
$this->scheduledTasksRepository = $scheduledTasksRepository;
|
||||
$this->sendingQueuesRepository = $sendingQueuesRepository;
|
||||
}
|
||||
|
||||
public function transitionHook($newStatus, $oldStatus, $post) {
|
||||
$this->loggerFactory->getLogger(LoggerFactory::TOPIC_POST_NOTIFICATIONS)->info(
|
||||
'transition post notification hook initiated',
|
||||
[
|
||||
'post_id' => $post->ID,
|
||||
'new_status' => $newStatus,
|
||||
'old_status' => $oldStatus,
|
||||
]
|
||||
);
|
||||
$types = Posts::getTypes();
|
||||
if (($newStatus !== 'publish') || $oldStatus === 'publish' || !isset($types[$post->post_type])) { // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
|
||||
return;
|
||||
}
|
||||
$this->schedulePostNotification($post->ID);
|
||||
}
|
||||
|
||||
public function schedulePostNotification($postId) {
|
||||
$this->loggerFactory->getLogger(LoggerFactory::TOPIC_POST_NOTIFICATIONS)->info(
|
||||
'schedule post notification hook',
|
||||
['post_id' => $postId]
|
||||
);
|
||||
$newsletters = $this->newslettersRepository->findActiveByTypes([NewsletterEntity::TYPE_NOTIFICATION]);
|
||||
$this->newslettersRepository->prefetchOptions($newsletters);
|
||||
if (!count($newsletters)) {
|
||||
return false;
|
||||
}
|
||||
foreach ($newsletters as $newsletter) {
|
||||
$post = $this->newsletterPostsRepository->findOneBy([
|
||||
'newsletter' => $newsletter,
|
||||
'postId' => $postId,
|
||||
]);
|
||||
if ($post === null) {
|
||||
$this->createPostNotificationSendingTask($newsletter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function createPostNotificationSendingTask(NewsletterEntity $newsletter): ?ScheduledTaskEntity {
|
||||
$notificationHistory = $this->newslettersRepository->findSendingNotificationHistoryWithoutPausedOrInvalidTask($newsletter);
|
||||
if (count($notificationHistory) > 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$scheduleOption = $newsletter->getOption(NewsletterOptionFieldEntity::NAME_SCHEDULE);
|
||||
if (!$scheduleOption) {
|
||||
return null;
|
||||
}
|
||||
$nextRunDate = $this->scheduler->getNextRunDateTime($scheduleOption->getValue());
|
||||
if (!$nextRunDate) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// do not schedule duplicate queues for the same time
|
||||
$lastQueue = $newsletter->getLatestQueue();
|
||||
$task = $lastQueue !== null ? $lastQueue->getTask() : null;
|
||||
$scheduledAt = $task !== null ? $task->getScheduledAt() : null;
|
||||
if ($scheduledAt && $scheduledAt->format('Y-m-d H:i:s') === $nextRunDate->format('Y-m-d H:i:s')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$scheduledTask = new ScheduledTaskEntity();
|
||||
$scheduledTask->setType(SendingQueue::TASK_TYPE);
|
||||
$scheduledTask->setStatus(ScheduledTaskEntity::STATUS_SCHEDULED);
|
||||
$scheduledTask->setScheduledAt($nextRunDate);
|
||||
$scheduledTask->setPriority(ScheduledTaskEntity::PRIORITY_MEDIUM);
|
||||
$this->scheduledTasksRepository->persist($scheduledTask);
|
||||
$this->scheduledTasksRepository->flush();
|
||||
|
||||
$sendingQueue = new SendingQueueEntity();
|
||||
$sendingQueue->setNewsletter($newsletter);
|
||||
$sendingQueue->setTask($scheduledTask);
|
||||
$this->sendingQueuesRepository->persist($sendingQueue);
|
||||
$this->sendingQueuesRepository->flush();
|
||||
$scheduledTask->setSendingQueue($sendingQueue);
|
||||
|
||||
$this->loggerFactory->getLogger(LoggerFactory::TOPIC_POST_NOTIFICATIONS)->info(
|
||||
'schedule post notification',
|
||||
[
|
||||
'sending_task' => $scheduledTask->getId(),
|
||||
'scheduled_at' => $nextRunDate->format(DateTime::DEFAULT_DATE_TIME_FORMAT),
|
||||
]
|
||||
);
|
||||
return $scheduledTask;
|
||||
}
|
||||
|
||||
public function processPostNotificationSchedule(NewsletterEntity $newsletter) {
|
||||
$intervalTypeOption = $newsletter->getOption(NewsletterOptionFieldEntity::NAME_INTERVAL_TYPE);
|
||||
$intervalType = $intervalTypeOption ? $intervalTypeOption->getValue() : null;
|
||||
|
||||
$timeOfDayOption = $newsletter->getOption(NewsletterOptionFieldEntity::NAME_TIME_OF_DAY);
|
||||
$hour = $timeOfDayOption ? (int)floor((int)$timeOfDayOption->getValue() / self::SECONDS_IN_HOUR) : null;
|
||||
$minute = $timeOfDayOption ? ((int)$timeOfDayOption->getValue() - (int)($hour * self::SECONDS_IN_HOUR)) / self::SECONDS_IN_MINUTE : null;
|
||||
|
||||
$weekDayOption = $newsletter->getOption(NewsletterOptionFieldEntity::NAME_WEEK_DAY);
|
||||
$weekDay = $weekDayOption ? $weekDayOption->getValue() : null;
|
||||
|
||||
$monthDayOption = $newsletter->getOption(NewsletterOptionFieldEntity::NAME_MONTH_DAY);
|
||||
$monthDay = $monthDayOption ? $monthDayOption->getValue() : null;
|
||||
|
||||
$nthWeekDayOption = $newsletter->getOption(NewsletterOptionFieldEntity::NAME_NTH_WEEK_DAY);
|
||||
$nthWeekDay = $nthWeekDayOption ? $nthWeekDayOption->getValue() : null;
|
||||
$nthWeekDay = ($nthWeekDay === self::LAST_WEEKDAY_FORMAT) ? $nthWeekDay : '#' . $nthWeekDay;
|
||||
switch ($intervalType) {
|
||||
case self::INTERVAL_IMMEDIATE:
|
||||
case self::INTERVAL_DAILY:
|
||||
$schedule = sprintf('%s %s * * *', $minute, $hour);
|
||||
break;
|
||||
case self::INTERVAL_WEEKLY:
|
||||
$schedule = sprintf('%s %s * * %s', $minute, $hour, $weekDay);
|
||||
break;
|
||||
case self::INTERVAL_NTHWEEKDAY:
|
||||
$schedule = sprintf('%s %s ? * %s%s', $minute, $hour, $weekDay, $nthWeekDay);
|
||||
break;
|
||||
case self::INTERVAL_MONTHLY:
|
||||
$schedule = sprintf('%s %s %s * *', $minute, $hour, $monthDay);
|
||||
break;
|
||||
case self::INTERVAL_IMMEDIATELY:
|
||||
default:
|
||||
$schedule = '* * * * *';
|
||||
break;
|
||||
}
|
||||
$optionField = $this->newsletterOptionFieldsRepository->findOneBy([
|
||||
'name' => NewsletterOptionFieldEntity::NAME_SCHEDULE,
|
||||
]);
|
||||
if (!$optionField instanceof NewsletterOptionFieldEntity) {
|
||||
throw new \Exception('NewsletterOptionField for schedule doesn’t exist.');
|
||||
}
|
||||
$scheduleOption = $newsletter->getOption(NewsletterOptionFieldEntity::NAME_SCHEDULE);
|
||||
if ($scheduleOption === null) {
|
||||
$scheduleOption = new NewsletterOptionEntity($newsletter, $optionField);
|
||||
$newsletter->getOptions()->add($scheduleOption);
|
||||
}
|
||||
$scheduleOption->setValue($schedule);
|
||||
$this->newsletterOptionsRepository->persist($scheduleOption);
|
||||
$this->newsletterOptionsRepository->flush();
|
||||
return $scheduleOption->getValue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace MailPoet\Newsletter\Scheduler;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Cron\Workers\SendingQueue\SendingQueue;
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Entities\NewsletterOptionFieldEntity;
|
||||
use MailPoet\Entities\ScheduledTaskEntity;
|
||||
use MailPoet\Entities\ScheduledTaskSubscriberEntity;
|
||||
use MailPoet\Entities\SendingQueueEntity;
|
||||
use MailPoet\Entities\StatisticsNewsletterEntity;
|
||||
use MailPoet\Entities\SubscriberEntity;
|
||||
use MailPoet\Entities\SubscriberSegmentEntity;
|
||||
use MailPoet\Newsletter\NewslettersRepository;
|
||||
use MailPoet\Newsletter\Sending\ScheduledTasksRepository;
|
||||
use MailPoetVendor\Carbon\Carbon;
|
||||
use MailPoetVendor\Doctrine\DBAL\ParameterType;
|
||||
use MailPoetVendor\Doctrine\ORM\EntityManager;
|
||||
|
||||
class ReEngagementScheduler {
|
||||
|
||||
/** @var NewslettersRepository */
|
||||
private $newslettersRepository;
|
||||
|
||||
/** @var ScheduledTasksRepository */
|
||||
private $scheduledTasksRepository;
|
||||
|
||||
/** @var EntityManager */
|
||||
private $entityManager;
|
||||
|
||||
public function __construct(
|
||||
NewslettersRepository $newslettersRepository,
|
||||
ScheduledTasksRepository $scheduledTasksRepository,
|
||||
EntityManager $entityManager
|
||||
) {
|
||||
$this->newslettersRepository = $newslettersRepository;
|
||||
$this->scheduledTasksRepository = $scheduledTasksRepository;
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules sending tasks for re-engagement emails
|
||||
* @return ScheduledTaskEntity[]
|
||||
*/
|
||||
public function scheduleAll(): array {
|
||||
$scheduled = [];
|
||||
$emails = $this->newslettersRepository->findActiveByTypes([NewsletterEntity::TYPE_RE_ENGAGEMENT]);
|
||||
if (!$emails) {
|
||||
return $scheduled;
|
||||
}
|
||||
foreach ($emails as $email) {
|
||||
$scheduled[] = $this->scheduleForEmail($email);
|
||||
}
|
||||
return array_filter($scheduled);
|
||||
}
|
||||
|
||||
private function scheduleForEmail(NewsletterEntity $newsletter): ?ScheduledTaskEntity {
|
||||
$scheduledOrRunning = $this->scheduledTasksRepository->findByScheduledAndRunningForNewsletter($newsletter);
|
||||
if ($scheduledOrRunning) {
|
||||
return null;
|
||||
}
|
||||
$intervalUnit = $newsletter->getOptionValue(NewsletterOptionFieldEntity::NAME_AFTER_TIME_TYPE);
|
||||
$intervalValue = (int)$newsletter->getOptionValue(NewsletterOptionFieldEntity::NAME_AFTER_TIME_NUMBER);
|
||||
if (!$intervalValue || !in_array($intervalUnit, ['weeks', 'months'], true)) {
|
||||
return null;
|
||||
}
|
||||
if (!$newsletter->getNewsletterSegments()->count()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$scheduledTask = $this->scheduleTask();
|
||||
$enqueuedCount = 0;
|
||||
foreach ($newsletter->getSegmentIds() as $segmentId) {
|
||||
$enqueuedCount += $this->enqueueSubscribersForSegment((int)$newsletter->getId(), $segmentId, $scheduledTask, $intervalUnit, $intervalValue);
|
||||
}
|
||||
|
||||
if ($enqueuedCount) {
|
||||
$this->createSendingQueue($newsletter, $scheduledTask, $enqueuedCount);
|
||||
return $scheduledTask;
|
||||
} else {
|
||||
// Nothing to send
|
||||
$this->scheduledTasksRepository->remove($scheduledTask);
|
||||
$this->scheduledTasksRepository->flush();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private function scheduleTask(): ScheduledTaskEntity {
|
||||
// Scheduled task
|
||||
$scheduledTask = new ScheduledTaskEntity();
|
||||
$scheduledTask->setStatus(ScheduledTaskEntity::STATUS_SCHEDULED);
|
||||
$scheduledTask->setScheduledAt(Carbon::now()->millisecond(0));
|
||||
$scheduledTask->setType(SendingQueue::TASK_TYPE);
|
||||
$scheduledTask->setPriority(SendingQueueEntity::PRIORITY_MEDIUM);
|
||||
$this->scheduledTasksRepository->persist($scheduledTask);
|
||||
$this->scheduledTasksRepository->flush();
|
||||
return $scheduledTask;
|
||||
}
|
||||
|
||||
private function createSendingQueue(NewsletterEntity $newsletter, ScheduledTaskEntity $scheduledTask, int $countToProcess): SendingQueueEntity {
|
||||
// Sending queue
|
||||
$sendingQueue = new SendingQueueEntity();
|
||||
$sendingQueue->setTask($scheduledTask);
|
||||
$sendingQueue->setNewsletter($newsletter);
|
||||
$sendingQueue->setCountToProcess($countToProcess);
|
||||
$sendingQueue->setCountTotal($countToProcess);
|
||||
$this->entityManager->persist($sendingQueue);
|
||||
$this->entityManager->flush();
|
||||
return $sendingQueue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds subscribers that should receive re-engagement email and saves scheduled tasks subscribers
|
||||
* @return int Count of enqueued subscribers
|
||||
*/
|
||||
private function enqueueSubscribersForSegment(int $newsletterId, int $segmentId, ScheduledTaskEntity $scheduledTask, string $intervalUnit, int $intervalValue): int {
|
||||
// Parameters for scheduled task subscribers query
|
||||
$thresholdDate = Carbon::now()->millisecond(0);
|
||||
if ($intervalUnit === 'months') {
|
||||
$thresholdDate->subMonths($intervalValue);
|
||||
} else {
|
||||
$thresholdDate->subWeeks($intervalValue);
|
||||
}
|
||||
$thresholdDateSql = $thresholdDate->toDateTimeString();
|
||||
// When checking engagement, we ignore emails that subscribers received in the last 24 hours so that we leave them some time to engage.
|
||||
// This is prevention for sending re-engagement emails to subscribers who have received a single email very recently.
|
||||
$upperThresholdDate = Carbon::now()->millisecond(0);
|
||||
$upperThresholdDate->subDay();
|
||||
$upperThresholdDate = $upperThresholdDate->toDateTimeString();
|
||||
$taskId = $scheduledTask->getId();
|
||||
$subscribedStatus = SubscriberEntity::STATUS_SUBSCRIBED;
|
||||
$newsletterStatsTable = $this->entityManager->getClassMetadata(StatisticsNewsletterEntity::class)->getTableName();
|
||||
$scheduledTaskSubscribersTable = $this->entityManager->getClassMetadata(ScheduledTaskSubscriberEntity::class)->getTableName();
|
||||
$subscriberSegmentTable = $this->entityManager->getClassMetadata(SubscriberSegmentEntity::class)->getTableName();
|
||||
$subscribersTable = $this->entityManager->getClassMetadata(SubscriberEntity::class)->getTableName();
|
||||
$nowSql = Carbon::now()->millisecond(0)->toDateTimeString();
|
||||
|
||||
$query = "INSERT IGNORE INTO $scheduledTaskSubscribersTable
|
||||
(subscriber_id, task_id, processed, created_at)
|
||||
SELECT DISTINCT ns.subscriber_id as subscriber_id, :taskId as task_id, 0 as processed, :now as created_at
|
||||
FROM $newsletterStatsTable as ns
|
||||
JOIN $subscribersTable s ON
|
||||
ns.subscriber_id = s.id
|
||||
AND s.deleted_at is NULL
|
||||
AND s.status = :subscribed
|
||||
AND GREATEST(COALESCE(s.created_at, '0'), COALESCE(s.last_subscribed_at, '0'), COALESCE(s.last_engagement_at, '0')) < :thresholdDate
|
||||
JOIN $subscriberSegmentTable as ss ON ns.subscriber_id = ss.subscriber_id
|
||||
AND ss.segment_id = :segmentId
|
||||
AND ss.status = :subscribed
|
||||
WHERE ns.sent_at > :thresholdDate
|
||||
AND ns.sent_at < :upperThresholdDate
|
||||
AND ns.subscriber_id NOT IN (
|
||||
SELECT DISTINCT subscriber_id as id FROM $newsletterStatsTable WHERE newsletter_id = :newsletterId AND sent_at > :thresholdDate
|
||||
);
|
||||
";
|
||||
|
||||
$statement = $this->entityManager->getConnection()->prepare($query);
|
||||
$statement->bindValue('now', $nowSql, ParameterType::STRING);
|
||||
$statement->bindValue('taskId', $taskId, ParameterType::INTEGER);
|
||||
$statement->bindValue('subscribed', $subscribedStatus, ParameterType::STRING);
|
||||
$statement->bindValue('thresholdDate', $thresholdDateSql, ParameterType::STRING);
|
||||
$statement->bindValue('upperThresholdDate', $upperThresholdDate, ParameterType::STRING);
|
||||
$statement->bindValue('newsletterId', $newsletterId, ParameterType::INTEGER);
|
||||
$statement->bindValue('segmentId', $segmentId, ParameterType::INTEGER);
|
||||
|
||||
$result = $statement->executeQuery();
|
||||
return $result->rowCount();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\Newsletter\Scheduler;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Newsletter\NewslettersRepository;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
use MailPoetVendor\Carbon\Carbon;
|
||||
|
||||
class Scheduler {
|
||||
const MYSQL_TIMESTAMP_MAX = '2038-01-19 03:14:07';
|
||||
|
||||
/** @var WPFunctions */
|
||||
private $wp;
|
||||
|
||||
/** @var NewslettersRepository */
|
||||
private $newslettersRepository;
|
||||
|
||||
public function __construct(
|
||||
WPFunctions $wp,
|
||||
NewslettersRepository $newslettersRepository
|
||||
) {
|
||||
$this->wp = $wp;
|
||||
$this->newslettersRepository = $newslettersRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|false
|
||||
*/
|
||||
public function getNextRunDate($schedule) {
|
||||
$nextRunDateTime = $this->getNextRunDateTime($schedule);
|
||||
return $nextRunDateTime ? $nextRunDateTime->format('Y-m-d H:i:s') : $nextRunDateTime;
|
||||
}
|
||||
|
||||
public function getPreviousRunDate($schedule) {
|
||||
// User enters time in WordPress site timezone, but we need to calculate it in UTC before we save it to DB
|
||||
// 1) As the initial time we use time in site timezone via current_datetime
|
||||
// 2) We use CronExpression to calculate previous run (still in site's timezone)
|
||||
// 3) We convert the calculated time to UTC
|
||||
$from = $this->wp->currentDatetime();
|
||||
try {
|
||||
$schedule = new \Cron\CronExpression((string)$schedule);
|
||||
$previousRunDate = $schedule->getPreviousRunDate(Carbon::instance($from));
|
||||
$previousRunDate->setTimezone(new \DateTimeZone('UTC'));
|
||||
$previousRunDate = $previousRunDate->format('Y-m-d H:i:s');
|
||||
} catch (\Exception $e) {
|
||||
$previousRunDate = false;
|
||||
}
|
||||
return $previousRunDate;
|
||||
}
|
||||
|
||||
public function getScheduledTimeWithDelay($afterTimeType, $afterTimeNumber): Carbon {
|
||||
$currentTime = Carbon::now()->millisecond(0);
|
||||
switch ($afterTimeType) {
|
||||
case 'minutes':
|
||||
$currentTime->addMinutes($afterTimeNumber);
|
||||
break;
|
||||
case 'hours':
|
||||
$currentTime->addHours($afterTimeNumber);
|
||||
break;
|
||||
case 'days':
|
||||
$currentTime->addDays($afterTimeNumber);
|
||||
break;
|
||||
case 'weeks':
|
||||
$currentTime->addWeeks($afterTimeNumber);
|
||||
break;
|
||||
}
|
||||
$maxScheduledTime = Carbon::createFromFormat('Y-m-d H:i:s', self::MYSQL_TIMESTAMP_MAX);
|
||||
if ($maxScheduledTime && $currentTime > $maxScheduledTime) {
|
||||
return $maxScheduledTime;
|
||||
}
|
||||
return $currentTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return NewsletterEntity[]
|
||||
*/
|
||||
public function getNewsletters(string $type, ?string $group = null): array {
|
||||
return $this->newslettersRepository->findActiveByTypeAndGroup($type, $group);
|
||||
}
|
||||
|
||||
public function formatDatetimeString($datetimeString) {
|
||||
return Carbon::parse($datetimeString)->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTime|false
|
||||
*/
|
||||
public function getNextRunDateTime($schedule) {
|
||||
// User enters time in WordPress site timezone, but we need to calculate it in UTC before we save it to DB
|
||||
// 1) As the initial time we use time in site timezone via current_datetime
|
||||
// 2) We use CronExpression to calculate next run (still in site's timezone)
|
||||
// 3) We convert the calculated time to UTC
|
||||
//$fromTimestamp = $this->wp->currentTime('timestamp', false);
|
||||
$from = $this->wp->currentDatetime();
|
||||
try {
|
||||
$schedule = new \Cron\CronExpression((string)$schedule);
|
||||
$nextRunDate = $schedule->getNextRunDate(Carbon::instance($from));
|
||||
$nextRunDate->setTimezone(new \DateTimeZone('UTC'));
|
||||
// Work around CronExpression transforming Carbon into DateTime
|
||||
if (!$nextRunDate instanceof Carbon) {
|
||||
$nextRunDate = new Carbon($nextRunDate);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$nextRunDate = false;
|
||||
}
|
||||
return $nextRunDate;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\Newsletter\Scheduler;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Cron\Workers\SendingQueue\SendingQueue;
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Entities\NewsletterOptionFieldEntity;
|
||||
use MailPoet\Entities\ScheduledTaskEntity;
|
||||
use MailPoet\Entities\ScheduledTaskSubscriberEntity;
|
||||
use MailPoet\Entities\SegmentEntity;
|
||||
use MailPoet\Entities\SendingQueueEntity;
|
||||
use MailPoet\Entities\SubscriberEntity;
|
||||
use MailPoet\Newsletter\NewslettersRepository;
|
||||
use MailPoet\Newsletter\Sending\ScheduledTasksRepository;
|
||||
use MailPoet\Segments\SegmentsRepository;
|
||||
use MailPoet\Subscribers\SubscribersRepository;
|
||||
use MailPoetVendor\Doctrine\ORM\EntityManager;
|
||||
|
||||
class WelcomeScheduler {
|
||||
|
||||
const WORDPRESS_ALL_ROLES = 'mailpoet_all';
|
||||
|
||||
/** @var EntityManager */
|
||||
private $entityManager;
|
||||
|
||||
/** @var SubscribersRepository */
|
||||
private $subscribersRepository;
|
||||
|
||||
/** @var SegmentsRepository */
|
||||
private $segmentsRepository;
|
||||
|
||||
/** @var NewslettersRepository */
|
||||
private $newslettersRepository;
|
||||
|
||||
/** @var ScheduledTasksRepository */
|
||||
private $scheduledTasksRepository;
|
||||
|
||||
/** @var Scheduler */
|
||||
private $scheduler;
|
||||
|
||||
public function __construct(
|
||||
EntityManager $entityManager,
|
||||
SubscribersRepository $subscribersRepository,
|
||||
SegmentsRepository $segmentsRepository,
|
||||
NewslettersRepository $newslettersRepository,
|
||||
ScheduledTasksRepository $scheduledTasksRepository,
|
||||
Scheduler $scheduler
|
||||
) {
|
||||
$this->entityManager = $entityManager;
|
||||
$this->subscribersRepository = $subscribersRepository;
|
||||
$this->segmentsRepository = $segmentsRepository;
|
||||
$this->newslettersRepository = $newslettersRepository;
|
||||
$this->scheduledTasksRepository = $scheduledTasksRepository;
|
||||
$this->scheduler = $scheduler;
|
||||
}
|
||||
|
||||
public function scheduleSubscriberWelcomeNotification($subscriberId, $segments): void {
|
||||
$newsletters = $this->newslettersRepository->findActiveByTypes([NewsletterEntity::TYPE_WELCOME]);
|
||||
foreach ($newsletters as $newsletter) {
|
||||
if (
|
||||
$newsletter->getOptionValue(NewsletterOptionFieldEntity::NAME_EVENT) === 'segment' &&
|
||||
in_array($newsletter->getOptionValue(NewsletterOptionFieldEntity::NAME_SEGMENT), $segments)
|
||||
) {
|
||||
$this->createWelcomeNotificationSendingTask($newsletter, $subscriberId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function scheduleWPUserWelcomeNotification(
|
||||
$subscriberId,
|
||||
$wpUser,
|
||||
$oldUserData = false
|
||||
) {
|
||||
$newsletters = $this->newslettersRepository->findActiveByTypes([NewsletterEntity::TYPE_WELCOME]);
|
||||
if (empty($newsletters)) return false;
|
||||
foreach ($newsletters as $newsletter) {
|
||||
if ($newsletter->getOptionValue(NewsletterOptionFieldEntity::NAME_EVENT) !== 'user') {
|
||||
continue;
|
||||
}
|
||||
$newsletterRole = $newsletter->getOptionValue(NewsletterOptionFieldEntity::NAME_ROLE);
|
||||
if (!empty($oldUserData['roles'])) {
|
||||
// do not schedule welcome newsletter if roles have not changed
|
||||
$oldRole = $oldUserData['roles'];
|
||||
$newRole = $wpUser['roles'];
|
||||
if (
|
||||
$newsletterRole === self::WORDPRESS_ALL_ROLES ||
|
||||
!array_diff($newRole, $oldRole)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (
|
||||
$newsletterRole === self::WORDPRESS_ALL_ROLES ||
|
||||
in_array($newsletterRole, $wpUser['roles'])
|
||||
) {
|
||||
$this->createWelcomeNotificationSendingTask($newsletter, $subscriberId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function createWelcomeNotificationSendingTask(NewsletterEntity $newsletter, $subscriberId): void {
|
||||
$subscriber = $this->subscribersRepository->findOneById($subscriberId);
|
||||
if (!($subscriber instanceof SubscriberEntity) || $subscriber->getDeletedAt() !== null) {
|
||||
return;
|
||||
}
|
||||
if ($newsletter->getOptionValue(NewsletterOptionFieldEntity::NAME_EVENT) === 'segment') {
|
||||
$segment = $this->segmentsRepository->findOneById((int)$newsletter->getOptionValue(NewsletterOptionFieldEntity::NAME_SEGMENT));
|
||||
if ((!$segment instanceof SegmentEntity) || $segment->getDeletedAt() !== null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ($newsletter->getOptionValue(NewsletterOptionFieldEntity::NAME_EVENT) === 'user') {
|
||||
$segment = $this->segmentsRepository->getWPUsersSegment();
|
||||
if ((!$segment instanceof SegmentEntity) || $segment->getDeletedAt() !== null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
$previouslyScheduledNotification = $this->scheduledTasksRepository->findByNewsletterAndSubscriberId($newsletter, $subscriberId);
|
||||
if (!empty($previouslyScheduledNotification)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// task
|
||||
$task = new ScheduledTaskEntity();
|
||||
$task->setType(SendingQueue::TASK_TYPE);
|
||||
$task->setStatus(ScheduledTaskEntity::STATUS_SCHEDULED);
|
||||
$task->setPriority(ScheduledTaskEntity::PRIORITY_HIGH);
|
||||
$task->setScheduledAt($this->scheduler->getScheduledTimeWithDelay(
|
||||
$newsletter->getOptionValue(NewsletterOptionFieldEntity::NAME_AFTER_TIME_TYPE),
|
||||
$newsletter->getOptionValue(NewsletterOptionFieldEntity::NAME_AFTER_TIME_NUMBER)
|
||||
));
|
||||
$this->entityManager->persist($task);
|
||||
|
||||
// queue
|
||||
$queue = new SendingQueueEntity();
|
||||
$queue->setTask($task);
|
||||
$queue->setNewsletter($newsletter);
|
||||
// Because we changed the way how to updateCounts after sending we need to set initial counts
|
||||
$queue->setCountTotal(1);
|
||||
$queue->setCountToProcess(1);
|
||||
|
||||
$task->setSendingQueue($queue);
|
||||
$this->entityManager->persist($queue);
|
||||
|
||||
// task subscriber
|
||||
$taskSubscriber = new ScheduledTaskSubscriberEntity($task, $subscriber);
|
||||
$task->getSubscribers()->add($taskSubscriber);
|
||||
$this->entityManager->persist($taskSubscriber);
|
||||
|
||||
$this->entityManager->flush();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<?php
|
||||
Reference in New Issue
Block a user