PluginProbe ʕ •ᴥ•ʔ
MailPoet – Newsletters, Email Marketing, and Automation / 5.34.2
MailPoet – Newsletters, Email Marketing, and Automation v5.34.2
5.34.2 5.34.1 5.34.0 5.33.1 5.33.0 5.32.0 5.31.0 5.30.0 5.29.0 5.28.1 5.28.0 5.27.0 5.26.0 5.26.1 5.25.0 5.24.0 4.43.0 4.43.1 4.44.0 4.44.1 4.45.0 4.46.0 4.47.0 4.48.0 4.48.1 4.48.2 4.49.0 4.49.1 4.5.0 4.5.1 4.5.2 4.50.0 4.50.1 4.51.0 4.51.1 4.51.2 4.52.0 4.53.0 4.54.0 4.55.0 4.56.0 4.57.0 4.58.0 4.58.1 4.58.2 4.6.0 4.6.1 4.6.2 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 5.0.0 5.0.1 5.0.2 5.1.0 5.1.1 5.10.0 5.10.1 5.11.0 5.12.0 5.12.1 5.12.10 5.12.11 5.12.12 5.12.13 5.12.2 5.12.3 5.12.4 5.12.5 5.12.6 5.12.7 5.12.8 5.12.9 5.13.0 5.13.1 5.13.2 5.14.0 5.14.1 5.14.2 5.14.3 5.15.0 5.15.1 5.16.0 5.16.1 5.16.2 5.16.3 5.16.4 5.17.0 5.17.1 5.17.2 5.17.3 5.17.4 5.17.5 5.17.6 5.18.0 5.19.0 5.2.0 5.2.1 5.2.2 5.2.3 5.20.0 5.21.0 5.21.1 5.21.2 5.21.3 5.22.0 5.22.1 5.22.2 5.22.3 5.22.4 5.23.0 5.23.1 5.23.2 5.3.0 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 5.4.0 5.4.1 5.4.2 5.5.0 5.5.1 5.5.2 5.6.0 5.6.1 5.6.2 5.6.3 5.6.4 5.7.0 5.7.1 5.8.0 5.8.1 5.9.0 3.0.0-beta.15 3.7.1 3.0.0-beta.16 3.7.2 3.0.0-beta.17 3.7.3 3.0.0-beta.18 3.7.4 3.0.0-beta.19 3.7.5 3.0.0-beta.2 3.7.6 3.0.0-beta.20 3.7.8 3.0.0-beta.21 3.70.0 3.0.0-beta.22 3.71.0 3.0.0-beta.23 3.71.1 3.0.0-beta.23.1 3.71.2 3.0.0-beta.23.2 3.71.3 3.0.0-beta.24 3.72.0 3.0.0-beta.25 3.73.0 3.0.0-beta.26 3.73.1 3.0.0-beta.27 3.73.2 3.0.0-beta.28 3.74.0 3.0.0-beta.29 3.74.1 3.0.0-beta.3 3.74.2 3.0.0-beta.30 3.74.3 3.0.0-beta.31 3.75.0 3.0.0-beta.32 3.75.1 3.0.0-beta.33 3.76.0 3.0.0-beta.33.1 3.77.0 3.0.0-beta.34.0.0 3.77.1 3.0.0-beta.36.0.0 3.78.0 3.0.0-beta.36.0.1 3.79.0 3.0.0-beta.36.2.0 3.8 3.0.0-beta.36.3.0 3.8.1 3.0.0-beta.36.3.1 3.8.2 3.0.0-beta.37.0.0 3.8.3 3.0.0-beta.4 3.8.4 3.0.0-beta.5 3.8.5 3.0.0-beta.6 3.8.6 3.0.0-beta.7 3.80.0 3.0.0-beta.7.1 3.81.0 3.0.0-beta.8 3.82.0 3.0.0-beta.9 3.83.0 3.0.0-rc.1.0.0 3.84.0 3.0.0-rc.1.0.1 3.84.1 3.0.0-rc.1.0.2 3.85.0 3.0.0-rc.1.0.3 3.85.1 3.0.0-rc.1.0.4 3.86.0 3.0.0-rc.2.0.0 3.87.0 3.0.0-rc.2.0.1 3.87.1 3.0.0-rc.2.0.2 3.87.2 3.0.0-rc.2.0.3 3.88.0 3.0.1 3.88.1 3.0.2 3.88.2 3.0.3 3.89.0 3.0.4 3.89.1 3.0.5 3.89.2 3.0.6 3.89.3 3.0.7 3.89.4 3.0.8 3.9.0 3.0.9 3.9.1 3.1.0 3.90.0 3.10 3.90.1 3.10.1 3.90.2 3.100.0 3.91.0 3.100.1 3.91.1 3.100.2 3.92.0 3.101.0 3.92.1 3.101.1 3.93.0 3.102.0 3.93.1 3.102.1 3.94.0 3.103.0 3.95.0 3.103.1 3.95.1 3.11.0 3.96.0 3.11.1 3.96.1 3.11.2 3.97.0 3.11.3 3.98.0 3.11.4 3.98.1 3.11.5 3.99.0 3.12.0 3.99.1 3.12.1 4.0.0 3.13.0 4.0.1 3.14.0 4.1.0 3.14.1 4.1.1 3.15.0 4.10.0 3.16.0 4.11.0 3.16.1 4.11.1 3.16.2 4.12.0 3.16.3 4.12.1 3.17.0 4.12.2 3.17.1 4.13.0 3.17.2 4.14.0 3.18.0 4.15.0 3.18.1 4.16.0 3.18.2 4.17.0 3.19.0 4.17.1 3.19.1 4.18.0 3.19.2 4.18.1 3.19.3 4.19.0 3.2.0 4.2.0 3.2.1 4.20.0 3.2.2 4.20.1 3.2.3 4.20.2 3.2.4 4.21.0 3.2.5 4.22.0 3.20.0 4.22.1 3.21.0 4.22.2 3.21.1 4.23.0 3.22.0 4.24.0 3.23.0 4.25.0 3.23.1 4.26.0 3.23.2 4.26.1 3.24.0 4.27.0 3.25.0 4.28.0 3.25.1 4.29.0 3.26.0 4.3.0 3.26.1 4.3.1 3.27.0 4.30.0 3.28.0 4.31.0 3.29.0 4.31.1 3.3.0 4.32.0 3.3.1 4.33.0 3.3.2 4.34.0 3.3.3 4.35.0 3.3.4 4.35.1 3.3.5 4.36.0 3.3.6 4.37.0 3.30.0 4.38.0 3.31.0 4.39.0 3.31.1 4.4.0 3.32.0 4.40.0 3.32.1 4.41.0 3.32.2 4.41.1 3.33.0 4.41.2 3.34.0 4.41.3 3.34.1 4.42.0 3.34.2 4.42.1 3.34.3 3.34.4 3.35.0 3.35.1 3.35.3 3.35.4 3.36.0 3.37.0 3.37.1 3.37.2 3.37.3 3.38.0 3.38.1 3.39.0 3.39.1 3.39.2 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.40.0 3.40.1 3.41.0 3.41.1 3.41.2 3.42.0 3.42.1 3.42.2 3.42.3 3.43.0 3.43.1 3.44.0 3.45.0 3.45.1 3.46.0 3.46.1 3.46.10 3.46.11 3.46.12 3.46.13 3.46.14 3.46.2 3.46.3 3.46.4 3.46.5 3.46.6 3.46.7 3.46.8 3.46.9 3.47.0 3.47.1 3.47.10 3.47.11 3.47.2 3.47.3 3.47.5 3.47.6 3.47.7 3.47.9 3.48.0 3.48.1 3.49.0 3.49.1 3.5.0 3.5.1 3.50.0 3.51.0 3.51.1 3.51.2 3.52.0 3.53.0 3.54.0 3.54.1 3.54.2 3.54.3 3.55.0 3.55.1 3.56.0 3.56.1 3.56.2 3.57.0 3.57.1 3.58.0 3.59.0 3.59.1 3.59.2 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.60.0 3.60.1 3.60.10 3.60.11 3.60.12 3.60.2 3.60.3 3.60.4 3.60.6 3.60.7 3.60.8 3.60.9 3.61.0 3.62.0 3.62.1 3.63.0 3.64.0 3.64.1 3.64.2 3.64.3 3.65.0 trunk 3.65.1 3.0.0 3.66.0 3.0.0-beta.1 3.67.0 3.0.0-beta.10 3.67.1 3.0.0-beta.11 3.68.0 3.0.0-beta.12 3.69.0 3.0.0-beta.13 3.69.1 3.0.0-beta.14 3.7.0
mailpoet / lib / Newsletter / Scheduler / ReEngagementScheduler.php
mailpoet / lib / Newsletter / Scheduler Last commit date
AutomaticEmailScheduler.php 2 years ago AutomationEmailScheduler.php 1 year ago LatestNewsletterScheduler.php 2 months ago PostNotificationScheduler.php 2 months ago ReEngagementScheduler.php 5 days ago Scheduler.php 1 year ago WelcomeScheduler.php 2 years ago index.php 3 years ago
ReEngagementScheduler.php
182 lines
1 <?php declare(strict_types = 1);
2
3 namespace MailPoet\Newsletter\Scheduler;
4
5 if (!defined('ABSPATH')) exit;
6
7
8 use MailPoet\Cron\Workers\SendingQueue\SendingQueue;
9 use MailPoet\Entities\NewsletterEntity;
10 use MailPoet\Entities\NewsletterOptionFieldEntity;
11 use MailPoet\Entities\ScheduledTaskEntity;
12 use MailPoet\Entities\ScheduledTaskSubscriberEntity;
13 use MailPoet\Entities\SendingQueueEntity;
14 use MailPoet\Entities\StatisticsNewsletterEntity;
15 use MailPoet\Entities\SubscriberEntity;
16 use MailPoet\Entities\SubscriberSegmentEntity;
17 use MailPoet\Newsletter\NewslettersRepository;
18 use MailPoet\Newsletter\Sending\ScheduledTasksRepository;
19 use MailPoet\Subscribers\TrackingConsentController;
20 use MailPoetVendor\Carbon\Carbon;
21 use MailPoetVendor\Doctrine\DBAL\ParameterType;
22 use MailPoetVendor\Doctrine\ORM\EntityManager;
23
24 class ReEngagementScheduler {
25
26 /** @var NewslettersRepository */
27 private $newslettersRepository;
28
29 /** @var ScheduledTasksRepository */
30 private $scheduledTasksRepository;
31
32 /** @var EntityManager */
33 private $entityManager;
34
35 /** @var TrackingConsentController */
36 private $trackingConsentController;
37
38 public function __construct(
39 NewslettersRepository $newslettersRepository,
40 ScheduledTasksRepository $scheduledTasksRepository,
41 EntityManager $entityManager,
42 TrackingConsentController $trackingConsentController
43 ) {
44 $this->newslettersRepository = $newslettersRepository;
45 $this->scheduledTasksRepository = $scheduledTasksRepository;
46 $this->entityManager = $entityManager;
47 $this->trackingConsentController = $trackingConsentController;
48 }
49
50 /**
51 * Schedules sending tasks for re-engagement emails
52 * @return ScheduledTaskEntity[]
53 */
54 public function scheduleAll(): array {
55 $scheduled = [];
56 $emails = $this->newslettersRepository->findActiveByTypes([NewsletterEntity::TYPE_RE_ENGAGEMENT]);
57 if (!$emails) {
58 return $scheduled;
59 }
60 foreach ($emails as $email) {
61 $scheduled[] = $this->scheduleForEmail($email);
62 }
63 return array_filter($scheduled);
64 }
65
66 private function scheduleForEmail(NewsletterEntity $newsletter): ?ScheduledTaskEntity {
67 $scheduledOrRunning = $this->scheduledTasksRepository->findByScheduledAndRunningForNewsletter($newsletter);
68 if ($scheduledOrRunning) {
69 return null;
70 }
71 $intervalUnit = $newsletter->getOptionValue(NewsletterOptionFieldEntity::NAME_AFTER_TIME_TYPE);
72 $intervalValue = (int)$newsletter->getOptionValue(NewsletterOptionFieldEntity::NAME_AFTER_TIME_NUMBER);
73 if (!$intervalValue || !in_array($intervalUnit, ['weeks', 'months'], true)) {
74 return null;
75 }
76 if (!$newsletter->getNewsletterSegments()->count()) {
77 return null;
78 }
79
80 $scheduledTask = $this->scheduleTask();
81 $enqueuedCount = 0;
82 foreach ($newsletter->getSegmentIds() as $segmentId) {
83 $enqueuedCount += $this->enqueueSubscribersForSegment((int)$newsletter->getId(), $segmentId, $scheduledTask, $intervalUnit, $intervalValue);
84 }
85
86 if ($enqueuedCount) {
87 $this->createSendingQueue($newsletter, $scheduledTask, $enqueuedCount);
88 return $scheduledTask;
89 } else {
90 // Nothing to send
91 $this->scheduledTasksRepository->remove($scheduledTask);
92 $this->scheduledTasksRepository->flush();
93 return null;
94 }
95 }
96
97 private function scheduleTask(): ScheduledTaskEntity {
98 // Scheduled task
99 $scheduledTask = new ScheduledTaskEntity();
100 $scheduledTask->setStatus(ScheduledTaskEntity::STATUS_SCHEDULED);
101 $scheduledTask->setScheduledAt(Carbon::now()->millisecond(0));
102 $scheduledTask->setType(SendingQueue::TASK_TYPE);
103 $scheduledTask->setPriority(SendingQueueEntity::PRIORITY_MEDIUM);
104 $this->scheduledTasksRepository->persist($scheduledTask);
105 $this->scheduledTasksRepository->flush();
106 return $scheduledTask;
107 }
108
109 private function createSendingQueue(NewsletterEntity $newsletter, ScheduledTaskEntity $scheduledTask, int $countToProcess): SendingQueueEntity {
110 // Sending queue
111 $sendingQueue = new SendingQueueEntity();
112 $sendingQueue->setTask($scheduledTask);
113 $sendingQueue->setNewsletter($newsletter);
114 $sendingQueue->setCountToProcess($countToProcess);
115 $sendingQueue->setCountTotal($countToProcess);
116 $this->entityManager->persist($sendingQueue);
117 $this->entityManager->flush();
118 return $sendingQueue;
119 }
120
121 /**
122 * Finds subscribers that should receive re-engagement email and saves scheduled tasks subscribers
123 * @return int Count of enqueued subscribers
124 */
125 private function enqueueSubscribersForSegment(int $newsletterId, int $segmentId, ScheduledTaskEntity $scheduledTask, string $intervalUnit, int $intervalValue): int {
126 // Parameters for scheduled task subscribers query
127 $thresholdDate = Carbon::now()->millisecond(0);
128 if ($intervalUnit === 'months') {
129 $thresholdDate->subMonths($intervalValue);
130 } else {
131 $thresholdDate->subWeeks($intervalValue);
132 }
133 $thresholdDateSql = $thresholdDate->toDateTimeString();
134 // When checking engagement, we ignore emails that subscribers received in the last 24 hours so that we leave them some time to engage.
135 // This is prevention for sending re-engagement emails to subscribers who have received a single email very recently.
136 $upperThresholdDate = Carbon::now()->millisecond(0);
137 $upperThresholdDate->subDay();
138 $upperThresholdDate = $upperThresholdDate->toDateTimeString();
139 $taskId = $scheduledTask->getId();
140 $subscribedStatus = SubscriberEntity::STATUS_SUBSCRIBED;
141 $newsletterStatsTable = $this->entityManager->getClassMetadata(StatisticsNewsletterEntity::class)->getTableName();
142 $scheduledTaskSubscribersTable = $this->entityManager->getClassMetadata(ScheduledTaskSubscriberEntity::class)->getTableName();
143 $subscriberSegmentTable = $this->entityManager->getClassMetadata(SubscriberSegmentEntity::class)->getTableName();
144 $subscribersTable = $this->entityManager->getClassMetadata(SubscriberEntity::class)->getTableName();
145 $nowSql = Carbon::now()->millisecond(0)->toDateTimeString();
146
147 $query = "INSERT IGNORE INTO $scheduledTaskSubscribersTable
148 (subscriber_id, task_id, processed, created_at)
149 SELECT DISTINCT ns.subscriber_id as subscriber_id, :taskId as task_id, 0 as processed, :now as created_at
150 FROM $newsletterStatsTable as ns
151 JOIN $subscribersTable s ON
152 ns.subscriber_id = s.id
153 AND s.deleted_at is NULL
154 AND s.status = :subscribed
155 AND s.tracking_consent != 'denied'
156 AND (:trackUnknown = 1 OR s.tracking_consent != 'unknown')
157 AND GREATEST(COALESCE(s.created_at, '0'), COALESCE(s.last_subscribed_at, '0'), COALESCE(s.last_engagement_at, '0')) < :thresholdDate
158 JOIN $subscriberSegmentTable as ss ON ns.subscriber_id = ss.subscriber_id
159 AND ss.segment_id = :segmentId
160 AND ss.status = :subscribed
161 WHERE ns.sent_at > :thresholdDate
162 AND ns.sent_at < :upperThresholdDate
163 AND ns.subscriber_id NOT IN (
164 SELECT DISTINCT subscriber_id as id FROM $newsletterStatsTable WHERE newsletter_id = :newsletterId AND sent_at > :thresholdDate
165 );
166 ";
167
168 $statement = $this->entityManager->getConnection()->prepare($query);
169 $statement->bindValue('now', $nowSql, ParameterType::STRING);
170 $statement->bindValue('taskId', $taskId, ParameterType::INTEGER);
171 $statement->bindValue('subscribed', $subscribedStatus, ParameterType::STRING);
172 $statement->bindValue('thresholdDate', $thresholdDateSql, ParameterType::STRING);
173 $statement->bindValue('upperThresholdDate', $upperThresholdDate, ParameterType::STRING);
174 $statement->bindValue('newsletterId', $newsletterId, ParameterType::INTEGER);
175 $statement->bindValue('segmentId', $segmentId, ParameterType::INTEGER);
176 $statement->bindValue('trackUnknown', $this->trackingConsentController->shouldTrackUnknownConsent() ? 1 : 0, ParameterType::INTEGER);
177
178 $result = $statement->executeQuery();
179 return $result->rowCount();
180 }
181 }
182