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 / Cron / Workers / Scheduler.php
mailpoet / lib / Cron / Workers Last commit date
Automations 1 year ago KeyCheck 1 year ago SendingQueue 5 days ago StatsNotifications 2 months ago AuthorizedSendingEmailsCheck.php 3 years ago BackfillEngagementData.php 1 year ago Bounce.php 5 days ago BounceTaskSubscribersCleanup.php 1 month ago BulkConfirmationEmailResend.php 2 months ago ExportFilesCleanup.php 2 months ago InactiveSubscribersMaintenance.php 2 weeks ago LogCleanup.php 10 months ago Mixpanel.php 9 months ago NewsletterTemplateThumbnails.php 1 year ago ReEngagementEmailsScheduler.php 1 year ago Scheduler.php 2 months ago SendingQueueBodyCleanup.php 2 months ago SendingTaskSubscribersCleanup.php 2 months ago SimpleWorker.php 1 year ago StatisticsExport.php 2 months ago SubscriberLimitNotificationWorker.php 2 months ago SubscriberLinkTokens.php 1 year ago SubscribersCountCacheRecalculation.php 2 weeks ago SubscribersEngagementScore.php 4 days ago SubscribersLastEngagement.php 2 months ago SubscribersSegmentsCountSync.php 2 weeks ago SubscribersStatsReport.php 1 year ago Tracks.php 9 months ago UnconfirmedSubscribersCleanup.php 2 months ago UnsubscribeTokens.php 1 month ago WooCommercePastOrders.php 1 year ago WooCommerceSync.php 3 years ago WorkersFactory.php 2 weeks ago index.php 3 years ago
Scheduler.php
510 lines
1 <?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
2
3 namespace MailPoet\Cron\Workers;
4
5 if (!defined('ABSPATH')) exit;
6
7
8 use MailPoet\Cron\CronHelper;
9 use MailPoet\Cron\CronWorkerScheduler;
10 use MailPoet\Entities\NewsletterEntity;
11 use MailPoet\Entities\NewsletterSegmentEntity;
12 use MailPoet\Entities\ScheduledTaskEntity;
13 use MailPoet\Entities\ScheduledTaskSubscriberEntity;
14 use MailPoet\Entities\SegmentEntity;
15 use MailPoet\Entities\SubscriberEntity;
16 use MailPoet\Logging\LoggerFactory;
17 use MailPoet\Newsletter\NewslettersRepository;
18 use MailPoet\Newsletter\Scheduler\PostNotificationScheduler;
19 use MailPoet\Newsletter\Scheduler\Scheduler as NewsletterScheduler;
20 use MailPoet\Newsletter\Scheduler\WelcomeScheduler;
21 use MailPoet\Newsletter\Segment\NewsletterSegmentRepository;
22 use MailPoet\Newsletter\Sending\NewsletterReplayMetadata;
23 use MailPoet\Newsletter\Sending\ScheduledTasksRepository;
24 use MailPoet\Newsletter\Sending\ScheduledTaskSubscribersRepository;
25 use MailPoet\Newsletter\Sending\SendingQueuesRepository;
26 use MailPoet\Newsletter\Sending\TimeZoneCampaignScheduler;
27 use MailPoet\Segments\SegmentsRepository;
28 use MailPoet\Segments\SubscribersFinder;
29 use MailPoet\Subscribers\SubscriberSegmentRepository;
30 use MailPoet\Subscribers\SubscribersRepository;
31 use MailPoet\Util\Security;
32 use MailPoetVendor\Carbon\Carbon;
33 use MailPoetVendor\Doctrine\ORM\EntityNotFoundException;
34
35 class Scheduler {
36 const TASK_BATCH_SIZE = 5;
37
38 /** @var SubscribersFinder */
39 private $subscribersFinder;
40
41 /** @var LoggerFactory */
42 private $loggerFactory;
43
44 /** @var CronHelper */
45 private $cronHelper;
46
47 /** @var CronWorkerScheduler */
48 private $cronWorkerScheduler;
49
50 /** @var ScheduledTasksRepository */
51 private $scheduledTasksRepository;
52
53 /** @var ScheduledTaskSubscribersRepository */
54 private $scheduledTaskSubscribersRepository;
55
56 /** @var SendingQueuesRepository */
57 private $sendingQueuesRepository;
58
59 /** @var NewslettersRepository */
60 private $newslettersRepository;
61
62 /** @var SegmentsRepository */
63 private $segmentsRepository;
64
65 /** @var NewsletterSegmentRepository */
66 private $newsletterSegmentRepository;
67
68 /** @var Security */
69 private $security;
70
71 /** @var NewsletterScheduler */
72 private $scheduler;
73
74 /** @var SubscriberSegmentRepository */
75 private $subscriberSegmentRepository;
76
77 /** @var SubscribersRepository */
78 private $subscribersRepository;
79
80 /** @var TimeZoneCampaignScheduler */
81 private $timeZoneCampaignScheduler;
82
83 public function __construct(
84 SubscribersFinder $subscribersFinder,
85 LoggerFactory $loggerFactory,
86 CronHelper $cronHelper,
87 CronWorkerScheduler $cronWorkerScheduler,
88 ScheduledTasksRepository $scheduledTasksRepository,
89 ScheduledTaskSubscribersRepository $scheduledTaskSubscribersRepository,
90 SendingQueuesRepository $sendingQueuesRepository,
91 NewslettersRepository $newslettersRepository,
92 SegmentsRepository $segmentsRepository,
93 NewsletterSegmentRepository $newsletterSegmentRepository,
94 Security $security,
95 NewsletterScheduler $scheduler,
96 SubscriberSegmentRepository $subscriberSegmentRepository,
97 SubscribersRepository $subscribersRepository,
98 TimeZoneCampaignScheduler $timeZoneCampaignScheduler
99 ) {
100 $this->cronHelper = $cronHelper;
101 $this->subscribersFinder = $subscribersFinder;
102 $this->loggerFactory = $loggerFactory;
103 $this->cronWorkerScheduler = $cronWorkerScheduler;
104 $this->scheduledTasksRepository = $scheduledTasksRepository;
105 $this->scheduledTaskSubscribersRepository = $scheduledTaskSubscribersRepository;
106 $this->sendingQueuesRepository = $sendingQueuesRepository;
107 $this->newslettersRepository = $newslettersRepository;
108 $this->segmentsRepository = $segmentsRepository;
109 $this->newsletterSegmentRepository = $newsletterSegmentRepository;
110 $this->security = $security;
111 $this->scheduler = $scheduler;
112 $this->subscriberSegmentRepository = $subscriberSegmentRepository;
113 $this->subscribersRepository = $subscribersRepository;
114 $this->timeZoneCampaignScheduler = $timeZoneCampaignScheduler;
115 }
116
117 public function process($timer = false) {
118 $timer = $timer ?: microtime(true);
119
120 // abort if execution limit is reached
121 $this->cronHelper->enforceExecutionLimit($timer);
122
123 $scheduledTasks = $this->getScheduledSendingTasks();
124 $this->updateTasks($scheduledTasks);
125 foreach ($scheduledTasks as $task) {
126 $queue = $task->getSendingQueue();
127 if (!$queue) {
128 $this->deleteByTask($task);
129 continue;
130 }
131
132 $newsletter = $queue->getNewsletter();
133 $isLatestNewsletterReplay = NewsletterReplayMetadata::isLatestNewsletterReplayMeta($queue->getMeta());
134 try {
135 if (!$newsletter instanceof NewsletterEntity || $newsletter->getDeletedAt() !== null) {
136 $this->deleteByTask($task);
137 } elseif (
138 $newsletter->getStatus() !== NewsletterEntity::STATUS_ACTIVE
139 && $newsletter->getStatus() !== NewsletterEntity::STATUS_SCHEDULED
140 && !($newsletter->getStatus() === NewsletterEntity::STATUS_SENDING && $this->timeZoneCampaignScheduler->isTimeZoneQueue($queue))
141 && !$this->canProcessLatestNewsletterReplay($newsletter, $isLatestNewsletterReplay)
142 ) {
143 $task->setStatus(ScheduledTaskEntity::STATUS_PAUSED);
144 $this->scheduledTasksRepository->flush();
145 continue;
146 } elseif ($newsletter->getType() === NewsletterEntity::TYPE_WELCOME) {
147 $this->processWelcomeNewsletter($newsletter, $task);
148 } elseif ($newsletter->getType() === NewsletterEntity::TYPE_NOTIFICATION) {
149 $this->processPostNotificationNewsletter($newsletter, $task);
150 } elseif ($this->canProcessLatestNewsletterReplay($newsletter, $isLatestNewsletterReplay)) {
151 $this->processLatestNewsletterReplay($task);
152 } elseif ($newsletter->getType() === NewsletterEntity::TYPE_STANDARD) {
153 $this->processScheduledStandardNewsletter($newsletter, $task);
154 } elseif ($newsletter->getType() === NewsletterEntity::TYPE_AUTOMATIC) {
155 $this->processScheduledAutomaticEmail($newsletter, $task);
156 } elseif ($newsletter->getType() === NewsletterEntity::TYPE_RE_ENGAGEMENT) {
157 $this->processReEngagementEmail($task);
158 } elseif ($newsletter->getType() === NewsletterEntity::TYPE_AUTOMATION) {
159 $this->processScheduledAutomationEmail($task);
160 } elseif ($newsletter->getType() === NewsletterEntity::TYPE_AUTOMATION_TRANSACTIONAL) {
161 $this->processScheduledTransactionalEmail($task);
162 }
163 } catch (EntityNotFoundException $e) {
164 // Doctrine throws this exception when newsletter doesn't exist but is referenced in a scheduled task.
165 // This was added while refactoring this method to use Doctrine instead of Paris. We have to handle this case
166 // for the SchedulerTest::testItDeletesQueueDuringProcessingWhenNewsletterNotFound() test. I'm not sure
167 // if this problem could happen in production or not.
168 $this->deleteByTask($task);
169 }
170 $this->cronHelper->enforceExecutionLimit($timer);
171 }
172 }
173
174 public function processWelcomeNewsletter(NewsletterEntity $newsletter, ScheduledTaskEntity $task) {
175 $subscribers = $task->getSubscribers();
176 if (empty($subscribers[0])) {
177 $this->deleteByTask($task);
178 return false;
179 }
180 $subscriberId = (int)$subscribers[0]->getSubscriberId();
181 if ($newsletter->getOptionValue('event') === 'segment') {
182 if ($this->verifyMailpoetSubscriber($subscriberId, $newsletter, $task) === false) {
183 return false;
184 }
185 } else {
186 if ($newsletter->getOptionValue('event') === 'user') {
187 if ($this->verifyWPSubscriber($subscriberId, $newsletter, $task) === false) {
188 return false;
189 }
190 }
191 }
192 $task->setStatus(null);
193 $this->scheduledTasksRepository->flush();
194 return true;
195 }
196
197 public function processPostNotificationNewsletter(NewsletterEntity $newsletter, ScheduledTaskEntity $task) {
198 $this->loggerFactory->getLogger(LoggerFactory::TOPIC_POST_NOTIFICATIONS)->info(
199 'process post notification in scheduler',
200 ['newsletter_id' => $newsletter->getId(), 'task_id' => $task->getId()]
201 );
202
203 // ensure that segments exist
204 $segments = $newsletter->getSegmentIds();
205 if (empty($segments)) {
206 $this->loggerFactory->getLogger(LoggerFactory::TOPIC_POST_NOTIFICATIONS)->info(
207 'post notification no segments',
208 ['newsletter_id' => $newsletter->getId(), 'task_id' => $task->getId()]
209 );
210 $this->deleteQueueOrUpdateNextRunDate($task, $newsletter);
211 return false;
212 }
213
214 // ensure that subscribers are in segments
215 $this->subscribersFinder->addSubscribersToTaskFromSegments($task, $segments, $newsletter->getFilterSegmentId());
216 $subscribersCount = $task->getSubscribers()->count();
217 if (empty($subscribersCount)) {
218 $this->loggerFactory->getLogger(LoggerFactory::TOPIC_POST_NOTIFICATIONS)->info(
219 'post notification no subscribers',
220 ['newsletter_id' => $newsletter->getId(), 'task_id' => $task->getId(), 'segment_ids' => $segments]
221 );
222 $this->deleteQueueOrUpdateNextRunDate($task, $newsletter);
223 return false;
224 }
225
226 // create a duplicate newsletter that acts as a history record
227 try {
228 $notificationHistory = $this->createPostNotificationHistory($newsletter);
229 } catch (\Exception $exception) {
230 $this->loggerFactory->getLogger(LoggerFactory::TOPIC_POST_NOTIFICATIONS)->error(
231 'creating post notification history failed',
232 ['newsletter_id' => $newsletter->getId(), 'task_id' => $task->getId(), 'error' => $exception->getMessage()]
233 );
234 return false;
235 }
236
237 // queue newsletter for delivery
238 $queue = $task->getSendingQueue();
239 if (!$queue) {
240 $this->loggerFactory->getLogger(LoggerFactory::TOPIC_POST_NOTIFICATIONS)->error(
241 'post notification no queue',
242 ['newsletter_id' => $newsletter->getId(), 'task_id' => $task->getId()]
243 );
244 return false;
245 }
246 $queue->setNewsletter($notificationHistory);
247 $this->sendingQueuesRepository->updateCounts($queue);
248 $task->setStatus(null);
249 $this->scheduledTasksRepository->flush();
250
251 $this->loggerFactory->getLogger(LoggerFactory::TOPIC_POST_NOTIFICATIONS)->info(
252 'post notification set status to sending',
253 ['newsletter_id' => $newsletter->getId(), 'task_id' => $task->getId()]
254 );
255 return true;
256 }
257
258 public function processScheduledAutomaticEmail(NewsletterEntity $newsletter, ScheduledTaskEntity $task) {
259 if ($newsletter->getOptionValue('sendTo') === 'segment') {
260 $segment = $this->segmentsRepository->findOneById($newsletter->getOptionValue('segment'));
261 if ($segment instanceof SegmentEntity) {
262 $this->subscribersFinder->addSubscribersToTaskFromSegments($task, [(int)$segment->getId()]);
263 if (!$task->getSubscribers()->count()) {
264 $this->deleteByTask($task);
265 return false;
266 }
267 }
268 } else {
269 $subscribers = $task->getSubscribers();
270 $subscriber = isset($subscribers[0]) ? $subscribers[0]->getSubscriber() : null;
271 if (!$subscriber) {
272 $this->deleteByTask($task);
273 return false;
274 }
275 if ($this->verifySubscriber($subscriber, $task) === false) {
276 return false;
277 }
278 }
279
280 $task->setStatus(null);
281 $this->scheduledTasksRepository->flush();
282 return true;
283 }
284
285 public function processScheduledAutomationEmail(ScheduledTaskEntity $task): bool {
286 $subscribers = $task->getSubscribers();
287 $subscriber = isset($subscribers[0]) ? $subscribers[0]->getSubscriber() : null;
288 if (!$subscriber) {
289 $this->deleteByTask($task);
290 return false;
291 }
292 if (!$this->verifySubscriber($subscriber, $task)) {
293 return false;
294 }
295
296 $task->setStatus(null);
297 $this->scheduledTasksRepository->flush();
298 return true;
299 }
300
301 public function processScheduledTransactionalEmail(ScheduledTaskEntity $task): bool {
302 $subscribers = $task->getSubscribers();
303 $subscriber = isset($subscribers[0]) ? $subscribers[0]->getSubscriber() : null;
304 if (!$subscriber) {
305 $this->deleteByTask($task);
306 return false;
307 }
308 if (!$this->verifySubscriber($subscriber, $task)) {
309 $this->deleteByTask($task);
310 return false;
311 }
312
313 $task->setStatus(null);
314 $this->scheduledTasksRepository->flush();
315 return true;
316 }
317
318 public function processScheduledStandardNewsletter(NewsletterEntity $newsletter, ScheduledTaskEntity $task) {
319 $queue = $task->getSendingQueue();
320 if (!$queue || !$this->timeZoneCampaignScheduler->isTimeZoneQueue($queue)) {
321 $segments = $newsletter->getSegmentIds();
322 $this->subscribersFinder->addSubscribersToTaskFromSegments($task, $segments, $newsletter->getFilterSegmentId());
323 }
324
325 $task->setStatus(null);
326 if ($queue) {
327 $this->sendingQueuesRepository->updateCounts($queue);
328 }
329 $newsletter->setStatus(NewsletterEntity::STATUS_SENDING);
330 $this->scheduledTasksRepository->flush();
331 return true;
332 }
333
334 private function canProcessLatestNewsletterReplay(NewsletterEntity $newsletter, bool $isLatestNewsletterReplay): bool {
335 return $isLatestNewsletterReplay
336 && $newsletter->getType() === NewsletterEntity::TYPE_STANDARD
337 && $newsletter->getStatus() === NewsletterEntity::STATUS_SENT;
338 }
339
340 private function processLatestNewsletterReplay(ScheduledTaskEntity $task): bool {
341 $subscribers = $task->getSubscribers();
342 if ($subscribers->isEmpty()) {
343 $this->deleteByTask($task);
344 return false;
345 }
346
347 $queue = $task->getSendingQueue();
348 $meta = $queue ? $queue->getMeta() : [];
349 $taskSubscriber = $subscribers->first();
350 $expectedSubscriberId = $meta[NewsletterReplayMetadata::REPLAY_SUBSCRIBER_ID] ?? null;
351 if (
352 $subscribers->count() !== 1
353 || !$taskSubscriber instanceof ScheduledTaskSubscriberEntity
354 || !is_numeric($expectedSubscriberId)
355 || (int)$taskSubscriber->getSubscriberId() !== (int)$expectedSubscriberId
356 ) {
357 $task->setStatus(ScheduledTaskEntity::STATUS_PAUSED);
358 $this->scheduledTasksRepository->flush();
359 return false;
360 }
361
362 $task->setStatus(null);
363 if ($queue) {
364 $this->sendingQueuesRepository->updateCounts($queue);
365 }
366 $this->scheduledTasksRepository->flush();
367 return true;
368 }
369
370 private function processReEngagementEmail(ScheduledTaskEntity $task) {
371 $task->setStatus(null);
372 $this->scheduledTasksRepository->flush();
373 return true;
374 }
375
376 public function verifyMailpoetSubscriber(int $subscriberId, NewsletterEntity $newsletter, ScheduledTaskEntity $task): bool {
377 $subscriber = $this->subscribersRepository->findOneById($subscriberId);
378
379 // check if subscriber is in proper segment
380 $subscriberInSegment = $this->subscriberSegmentRepository->findOneBy(
381 [
382 'subscriber' => $subscriberId,
383 'segment' => $newsletter->getOptionValue('segment'),
384 'status' => SubscriberEntity::STATUS_SUBSCRIBED,
385 ]
386 );
387 if (!$subscriber || !$subscriberInSegment) {
388 $this->deleteByTask($task);
389 return false;
390 }
391 return $this->verifySubscriber($subscriber, $task);
392 }
393
394 public function verifyWPSubscriber(int $subscriberId, NewsletterEntity $newsletter, ScheduledTaskEntity $task): bool {
395 // check if user has the proper role
396 $subscriber = $this->subscribersRepository->findOneById($subscriberId);
397 if (!$subscriber || $subscriber->isWPUser() === false || is_null($subscriber->getWpUserId())) {
398 $this->deleteByTask($task);
399 return false;
400 }
401 $wpUser = get_userdata($subscriber->getWpUserId());
402 if ($wpUser === false) {
403 $this->deleteByTask($task);
404 return false;
405 }
406 if (
407 $newsletter->getOptionValue('role') !== WelcomeScheduler::WORDPRESS_ALL_ROLES
408 && !in_array($newsletter->getOptionValue('role'), ((array)$wpUser)['roles'])
409 ) {
410 $this->deleteByTask($task);
411 return false;
412 }
413 return $this->verifySubscriber($subscriber, $task);
414 }
415
416 public function verifySubscriber(SubscriberEntity $subscriber, ScheduledTaskEntity $task): bool {
417 $queue = $task->getSendingQueue();
418 $newsletter = $queue ? $queue->getNewsletter() : null;
419 if ($newsletter && $newsletter->isTransactional()) {
420 return $subscriber->getStatus() !== SubscriberEntity::STATUS_BOUNCED;
421 }
422 if ($subscriber->getStatus() === SubscriberEntity::STATUS_UNCONFIRMED) {
423 // reschedule delivery
424 $this->cronWorkerScheduler->rescheduleProgressively($task);
425 return false;
426 } else if ($subscriber->getStatus() === SubscriberEntity::STATUS_UNSUBSCRIBED) {
427 $this->deleteByTask($task);
428 return false;
429 }
430 return true;
431 }
432
433 public function deleteQueueOrUpdateNextRunDate(ScheduledTaskEntity $task, NewsletterEntity $newsletter) {
434 if ($newsletter->getOptionValue('intervalType') === PostNotificationScheduler::INTERVAL_IMMEDIATELY) {
435 $this->deleteByTask($task);
436 } else {
437 $nextRunDate = $this->scheduler->getNextRunDateTime($newsletter->getOptionValue('schedule'));
438 if (!$nextRunDate) {
439 $this->deleteByTask($task);
440 return;
441 }
442 $task->setScheduledAt($nextRunDate);
443 $this->scheduledTasksRepository->flush();
444 }
445 }
446
447 public function createPostNotificationHistory(NewsletterEntity $newsletter): NewsletterEntity {
448 // clone newsletter
449 $notificationHistory = clone $newsletter;
450 $notificationHistory->setParent($newsletter);
451 $notificationHistory->setType(NewsletterEntity::TYPE_NOTIFICATION_HISTORY);
452 $notificationHistory->setStatus(NewsletterEntity::STATUS_SENDING);
453 $notificationHistory->setUnsubscribeToken($this->security->generateUnsubscribeTokenByEntity($notificationHistory));
454
455 // reset timestamps
456 $createdAt = Carbon::now()->millisecond(0);
457 $notificationHistory->setCreatedAt($createdAt);
458 $notificationHistory->setUpdatedAt($createdAt);
459 $notificationHistory->setDeletedAt(null);
460
461 // reset hash
462 $notificationHistory->setHash(Security::generateHash());
463
464 $this->newslettersRepository->persist($notificationHistory);
465 $this->newslettersRepository->flush();
466
467 // create relationships between notification history and segments
468 foreach ($newsletter->getNewsletterSegments() as $newsletterSegment) {
469 $segment = $newsletterSegment->getSegment();
470 if (!$segment) {
471 continue;
472 }
473 $duplicateSegment = new NewsletterSegmentEntity($notificationHistory, $segment);
474 $notificationHistory->getNewsletterSegments()->add($duplicateSegment);
475 $this->newsletterSegmentRepository->persist($duplicateSegment);
476 }
477 $this->newslettersRepository->flush();
478
479 return $notificationHistory;
480 }
481
482 /**
483 * @param ScheduledTaskEntity[] $scheduledTasks
484 */
485 private function updateTasks(array $scheduledTasks): void {
486 $ids = array_map(function (ScheduledTaskEntity $scheduledTask): ?int {
487 return $scheduledTask->getId();
488 }, $scheduledTasks);
489 $ids = array_filter($ids);
490 $this->scheduledTasksRepository->touchAllByIds($ids);
491 }
492
493 /**
494 * @return ScheduledTaskEntity[]
495 */
496 public function getScheduledSendingTasks(): array {
497 return $this->scheduledTasksRepository->findScheduledSendingTasks(self::TASK_BATCH_SIZE);
498 }
499
500 private function deleteByTask(ScheduledTaskEntity $task): void {
501 $queue = $task->getSendingQueue();
502 if ($queue) {
503 $this->sendingQueuesRepository->remove($queue);
504 }
505 $this->scheduledTaskSubscribersRepository->deleteByScheduledTask($task);
506 $this->scheduledTasksRepository->remove($task);
507 $this->scheduledTasksRepository->flush();
508 }
509 }
510