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 / StatsNotifications / Worker.php
mailpoet / lib / Cron / Workers / StatsNotifications Last commit date
AutomatedEmails.php 2 months ago NewsletterLinkRepository.php 2 months ago Scheduler.php 2 months ago StatsNotificationsRepository.php 2 years ago Worker.php 3 months ago index.php 3 years ago
Worker.php
244 lines
1 <?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
2
3 namespace MailPoet\Cron\Workers\StatsNotifications;
4
5 if (!defined('ABSPATH')) exit;
6
7
8 use MailPoet\Config\Renderer;
9 use MailPoet\Config\ServicesChecker;
10 use MailPoet\Cron\CronHelper;
11 use MailPoet\Entities\NewsletterEntity;
12 use MailPoet\Entities\NewsletterLinkEntity;
13 use MailPoet\Entities\ScheduledTaskEntity;
14 use MailPoet\Entities\SendingQueueEntity;
15 use MailPoet\Entities\StatsNotificationEntity;
16 use MailPoet\Mailer\MailerFactory;
17 use MailPoet\Mailer\MetaInfo;
18 use MailPoet\Newsletter\Statistics\NewsletterStatisticsRepository;
19 use MailPoet\Settings\SettingsController;
20 use MailPoet\Subscribers\SubscribersRepository;
21 use MailPoet\Util\License\Features\Subscribers as SubscribersFeature;
22 use MailPoet\WP\Functions as WPFunctions;
23 use MailPoet\WPCOM\DotcomHelperFunctions;
24 use MailPoetVendor\Carbon\Carbon;
25 use MailPoetVendor\Doctrine\ORM\EntityManager;
26
27 class Worker {
28
29 const TASK_TYPE = 'stats_notification';
30 const SETTINGS_KEY = 'stats_notifications';
31 const BATCH_SIZE = 5;
32
33 /** @var Renderer */
34 private $renderer;
35
36 /** @var MailerFactory */
37 private $mailerFactory;
38
39 /** @var SettingsController */
40 private $settings;
41
42 /** @var CronHelper */
43 private $cronHelper;
44
45 /** @var MetaInfo */
46 private $mailerMetaInfo;
47
48 /** @var StatsNotificationsRepository */
49 private $repository;
50
51 /** @var EntityManager */
52 private $entityManager;
53
54 /** @var NewsletterLinkRepository */
55 private $newsletterLinkRepository;
56
57 /** @var NewsletterStatisticsRepository */
58 private $newsletterStatisticsRepository;
59
60 /** @var SubscribersFeature */
61 private $subscribersFeature;
62
63 /** @var SubscribersRepository */
64 private $subscribersRepository;
65
66 /** @var ServicesChecker */
67 private $servicesChecker;
68
69 /** @var DotcomHelperFunctions */
70 private $dotcomHelperFunctions;
71
72 public function __construct(
73 MailerFactory $mailerFactory,
74 Renderer $renderer,
75 SettingsController $settings,
76 CronHelper $cronHelper,
77 MetaInfo $mailerMetaInfo,
78 StatsNotificationsRepository $repository,
79 NewsletterLinkRepository $newsletterLinkRepository,
80 NewsletterStatisticsRepository $newsletterStatisticsRepository,
81 EntityManager $entityManager,
82 SubscribersFeature $subscribersFeature,
83 SubscribersRepository $subscribersRepository,
84 ServicesChecker $servicesChecker,
85 DotcomHelperFunctions $dotcomHelperFunctions
86 ) {
87 $this->renderer = $renderer;
88 $this->mailerFactory = $mailerFactory;
89 $this->settings = $settings;
90 $this->cronHelper = $cronHelper;
91 $this->mailerMetaInfo = $mailerMetaInfo;
92 $this->repository = $repository;
93 $this->entityManager = $entityManager;
94 $this->newsletterLinkRepository = $newsletterLinkRepository;
95 $this->newsletterStatisticsRepository = $newsletterStatisticsRepository;
96 $this->subscribersFeature = $subscribersFeature;
97 $this->subscribersRepository = $subscribersRepository;
98 $this->servicesChecker = $servicesChecker;
99 $this->dotcomHelperFunctions = $dotcomHelperFunctions;
100 }
101
102 /** @throws \Exception */
103 public function process($timer = false) {
104 $timer = $timer ?: microtime(true);
105 $settings = $this->settings->get(self::SETTINGS_KEY);
106 // Cleanup potential orphaned task created due bug MAILPOET-3015
107 $this->repository->deleteOrphanedScheduledTasks();
108 foreach ($this->repository->findScheduled(self::BATCH_SIZE) as $statsNotificationEntity) {
109 try {
110 $extraParams = [
111 'meta' => $this->mailerMetaInfo->getStatsNotificationMetaInfo(),
112 ];
113 $this->mailerFactory->getDefaultMailer()->send($this->constructNewsletter($statsNotificationEntity, $settings), $settings['address'], $extraParams);
114 } catch (\Exception $e) {
115 if (WP_DEBUG) {
116 throw $e;
117 }
118 } finally {
119 $task = $statsNotificationEntity->getTask();
120 if ($task instanceof ScheduledTaskEntity) {
121 $this->markTaskAsFinished($task);
122 }
123 }
124 $this->cronHelper->enforceExecutionLimit($timer);
125 }
126 }
127
128 private function constructNewsletter(StatsNotificationEntity $statsNotificationEntity, array $settings) {
129 $newsletter = $statsNotificationEntity->getNewsletter();
130 if (!$newsletter instanceof NewsletterEntity) {
131 throw new \RuntimeException('Missing newsletter entity for statistic notification.');
132 }
133 $link = $this->newsletterLinkRepository->findTopLinkForNewsletter((int)$newsletter->getId());
134 $sendingQueue = $newsletter->getLatestQueue();
135 if (!$sendingQueue instanceof SendingQueueEntity) {
136 throw new \RuntimeException('Missing sending queue entity for statistic notification.');
137 }
138 $context = $this->prepareContext($newsletter, $sendingQueue, $link, $settings);
139 $subject = $sendingQueue->getNewsletterRenderedSubject();
140 if ($this->dotcomHelperFunctions->isGarden()) {
141 // translators: %s is the name of the email campaign.
142 $emailSubject = sprintf(__('Campaign performance summary: %s', 'mailpoet'), $subject);
143 } else {
144 // translators: %s is the subject of the email.
145 $emailSubject = sprintf(_x('Stats for email %s', 'title of an automatic email containing statistics (newsletter open rate, click rate, etc)', 'mailpoet'), $subject);
146 }
147 return [
148 'subject' => $emailSubject,
149 'body' => [
150 'html' => $this->renderer->render('emails/statsNotification.html', $context),
151 'text' => $this->renderer->render('emails/statsNotification.txt', $context),
152 ],
153 ];
154 }
155
156 private function prepareContext(NewsletterEntity $newsletter, SendingQueueEntity $sendingQueue, ?NewsletterLinkEntity $link = null, array $settings = []) {
157 $statistics = $this->newsletterStatisticsRepository->getStatistics($newsletter);
158 $totalSentCount = $statistics->getTotalSentCount() ?: 1;
159 $clicked = ($statistics->getClickCount() * 100) / $totalSentCount;
160 $opened = ($statistics->getOpenCount() * 100) / $totalSentCount;
161 $machineOpened = ($statistics->getMachineOpenCount() * 100) / $totalSentCount;
162 $unsubscribed = ($statistics->getUnsubscribeCount() * 100) / $totalSentCount;
163 $bounced = ($statistics->getBounceCount() * 100) / $totalSentCount;
164 $subject = $sendingQueue->getNewsletterRenderedSubject();
165 $subscribersCount = $this->subscribersRepository->getTotalSubscribers();
166 $hasValidApiKey = $this->subscribersFeature->hasValidApiKey();
167 $context = [
168 'subject' => $subject,
169 'preheader' => sprintf(
170 // translators: %1$s is the percentage of clicks, %2$s the percentage of opens and %3$s the number of unsubscribes.
171 _x(
172 '%1$s%% clicks, %2$s%% opens, %3$s%% unsubscribes in a nutshell.',
173 'newsletter open rate, click rate and unsubscribe rate',
174 'mailpoet'
175 ),
176 number_format($clicked, 2),
177 number_format($opened, 2),
178 number_format($unsubscribed, 2)
179 ),
180 'topLinkClicks' => 0,
181 'linkSettings' => WPFunctions::get()->applyFilters(
182 'mailpoet_stats_notification_link_settings',
183 WPFunctions::get()->getSiteUrl(null, '/wp-admin/admin.php?page=mailpoet-settings#basics')
184 ),
185 'linkStats' => WPFunctions::get()->applyFilters(
186 'mailpoet_stats_notification_link_stats',
187 WPFunctions::get()->getSiteUrl(null, '/wp-admin/admin.php?page=mailpoet-newsletters&stats=' . $newsletter->getId()),
188 $newsletter->getId()
189 ),
190 'clicked' => $clicked,
191 'opened' => $opened,
192 'machineOpened' => $machineOpened,
193 'unsubscribed' => $unsubscribed,
194 'bounced' => $bounced,
195 'subscribersLimitReached' => $this->subscribersFeature->check(),
196 'hasValidApiKey' => $hasValidApiKey,
197 'subscribersLimit' => $this->subscribersFeature->getSubscribersLimit(),
198 'upgradeNowLink' => $hasValidApiKey
199 ? 'https://account.mailpoet.com/orders/upgrade/' . $this->servicesChecker->generatePartialApiKey()
200 : 'https://account.mailpoet.com/?s=' . ($subscribersCount + 1),
201 ];
202 if ($link) {
203 $context['topLinkClicks'] = $link->getTotalClicksCount();
204 $mappings = self::getShortcodeLinksMapping();
205 $context['topLink'] = isset($mappings[$link->getUrl()]) ? $mappings[$link->getUrl()] : $link->getUrl();
206 }
207 $context['blogName'] = WPFunctions::get()->getBloginfo('name');
208 $context['recipientFirstName'] = $this->getRecipientFirstName($settings['address'] ?? '');
209 return $context;
210 }
211
212 private function getRecipientFirstName(string $email): string {
213 if (empty($email)) {
214 return '';
215 }
216 $user = WPFunctions::get()->getUserBy('email', $email);
217 if (!$user) {
218 return '';
219 }
220 $firstName = $user->first_name; // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
221 if (!empty($firstName)) {
222 return $firstName;
223 }
224 $displayName = $user->display_name; // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
225 return !empty($displayName) ? $displayName : '';
226 }
227
228 private function markTaskAsFinished(ScheduledTaskEntity $task) {
229 $task->setStatus(ScheduledTaskEntity::STATUS_COMPLETED);
230 $task->setProcessedAt(Carbon::now()->millisecond(0));
231 $task->setScheduledAt(null);
232 $this->entityManager->flush();
233 }
234
235 public static function getShortcodeLinksMapping() {
236 return [
237 NewsletterLinkEntity::UNSUBSCRIBE_LINK_SHORT_CODE => __('Unsubscribe link', 'mailpoet'),
238 NewsletterLinkEntity::INSTANT_UNSUBSCRIBE_LINK_SHORT_CODE => __('Unsubscribe link (without confirmation)', 'mailpoet'),
239 '[link:subscription_manage_url]' => __('Manage subscription link', 'mailpoet'),
240 '[link:newsletter_view_in_browser_url]' => __('View in browser link', 'mailpoet'),
241 ];
242 }
243 }
244