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 / AutomatedEmails.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
AutomatedEmails.php
250 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\Automation\Engine\Data\Automation;
9 use MailPoet\Automation\Engine\Storage\AutomationStorage;
10 use MailPoet\Automation\Integrations\MailPoet\Actions\SendEmailAction;
11 use MailPoet\Config\Renderer;
12 use MailPoet\Cron\Workers\SimpleWorker;
13 use MailPoet\Entities\NewsletterEntity;
14 use MailPoet\Entities\ScheduledTaskEntity;
15 use MailPoet\Mailer\MailerFactory;
16 use MailPoet\Mailer\MetaInfo;
17 use MailPoet\Newsletter\NewslettersRepository;
18 use MailPoet\Newsletter\Statistics\NewsletterStatistics;
19 use MailPoet\Newsletter\Statistics\NewsletterStatisticsRepository;
20 use MailPoet\Settings\SettingsController;
21 use MailPoet\Settings\TrackingConfig;
22 use MailPoet\WP\DateTime as WpDateTime;
23 use MailPoet\WP\Functions as WPFunctions;
24 use MailPoet\WPCOM\DotcomHelperFunctions;
25 use MailPoetVendor\Carbon\Carbon;
26
27 class AutomatedEmails extends SimpleWorker {
28 const TASK_TYPE = 'stats_notification_automated_emails';
29
30 /** @var MailerFactory */
31 private $mailerFactory;
32
33 /** @var SettingsController */
34 private $settings;
35
36 /** @var Renderer */
37 private $renderer;
38
39 /** @var MetaInfo */
40 private $mailerMetaInfo;
41
42 /** @var NewslettersRepository */
43 private $repository;
44
45 /** @var NewsletterStatisticsRepository */
46 private $newsletterStatisticsRepository;
47
48 /** @var TrackingConfig */
49 private $trackingConfig;
50
51 /** @var WpDateTime */
52 private $wpDateTime;
53
54 /** @var DotcomHelperFunctions */
55 private $dotcomHelperFunctions;
56
57 /** @var AutomationStorage */
58 private $automationStorage;
59
60 public function __construct(
61 MailerFactory $mailerFactory,
62 Renderer $renderer,
63 SettingsController $settings,
64 NewslettersRepository $repository,
65 NewsletterStatisticsRepository $newsletterStatisticsRepository,
66 MetaInfo $mailerMetaInfo,
67 TrackingConfig $trackingConfig,
68 DotcomHelperFunctions $dotcomHelperFunctions,
69 AutomationStorage $automationStorage
70 ) {
71 parent::__construct();
72 $this->mailerFactory = $mailerFactory;
73 $this->settings = $settings;
74 $this->renderer = $renderer;
75 $this->mailerMetaInfo = $mailerMetaInfo;
76 $this->repository = $repository;
77 $this->newsletterStatisticsRepository = $newsletterStatisticsRepository;
78 $this->trackingConfig = $trackingConfig;
79 $this->dotcomHelperFunctions = $dotcomHelperFunctions;
80 $this->automationStorage = $automationStorage;
81 $this->wpDateTime = new WpDateTime();
82 }
83
84 public function checkProcessingRequirements() {
85 $settings = $this->settings->get(Worker::SETTINGS_KEY);
86 if (!is_array($settings)) {
87 return false;
88 }
89 if (!isset($settings['automated'])) {
90 return false;
91 }
92 if (!is_string($settings['address'] ?? null) || trim($settings['address']) === '') {
93 return false;
94 }
95 if (!$this->trackingConfig->isEmailTrackingEnabled()) {
96 return false;
97 }
98 return (bool)$settings['automated'];
99 }
100
101 public function processTaskStrategy(ScheduledTaskEntity $task, $timer) {
102 try {
103 $settings = $this->settings->get(Worker::SETTINGS_KEY);
104 $newsletters = $this->getNewsletters();
105 if ($newsletters) {
106 $extraParams = [
107 'meta' => $this->mailerMetaInfo->getStatsNotificationMetaInfo(),
108 ];
109 $this->mailerFactory->getDefaultMailer()->send($this->constructNewsletter($newsletters, $settings), $settings['address'], $extraParams);
110 }
111 } catch (\Exception $e) {
112 if (WP_DEBUG) {
113 throw $e;
114 }
115 }
116 return true;
117 }
118
119 /**
120 * @param array<int, array{newsletter: NewsletterEntity, statistics: NewsletterStatistics}> $newsletters
121 */
122 private function constructNewsletter(array $newsletters, array $settings): array {
123 $context = $this->prepareContext($newsletters, $settings);
124 if ($this->dotcomHelperFunctions->isGarden()) {
125 $subject = __('Your monthly automation stats are in!', 'mailpoet');
126 } else {
127 $subject = __('Your monthly stats are in!', 'mailpoet');
128 }
129 return [
130 'subject' => $subject,
131 'body' => [
132 'html' => $this->renderer->render('emails/statsNotificationAutomatedEmails.html', $context),
133 'text' => $this->renderer->render('emails/statsNotificationAutomatedEmails.txt', $context),
134 ],
135 ];
136 }
137
138 /**
139 * @return array<int, array{newsletter: NewsletterEntity, statistics: NewsletterStatistics}>
140 */
141 protected function getNewsletters(): array {
142 $result = [];
143 $newsletters = $this->repository->findActiveByTypes(
144 [NewsletterEntity::TYPE_AUTOMATIC, NewsletterEntity::TYPE_WELCOME, NewsletterEntity::TYPE_AUTOMATION]
145 );
146 $activeAutomationNewsletterIds = $this->getActiveAutomationNewsletterIds();
147 foreach ($newsletters as $newsletter) {
148 if (
149 $newsletter->getType() === NewsletterEntity::TYPE_AUTOMATION
150 && !in_array($newsletter->getId(), $activeAutomationNewsletterIds, true)
151 ) {
152 continue;
153 }
154 $statistics = $this->newsletterStatisticsRepository->getStatistics($newsletter);
155 if ($statistics->getTotalSentCount()) {
156 $result[] = [
157 'statistics' => $statistics,
158 'newsletter' => $newsletter,
159 ];
160 }
161 }
162 return $result;
163 }
164
165 /** @return int[] */
166 private function getActiveAutomationNewsletterIds(): array {
167 $ids = [];
168 $automations = $this->automationStorage->getAutomations([Automation::STATUS_ACTIVE]);
169 foreach ($automations as $automation) {
170 foreach ($automation->getSteps() as $step) {
171 if ($step->getKey() === SendEmailAction::KEY) {
172 $args = $step->getArgs();
173 if (isset($args['email_id'])) {
174 $ids[] = (int)$args['email_id'];
175 }
176 }
177 }
178 }
179 return $ids;
180 }
181
182 /**
183 * @param array<int, array{newsletter: NewsletterEntity, statistics: NewsletterStatistics}> $newsletters
184 * @return array
185 */
186 private function prepareContext(array $newsletters, array $settings = []): array {
187 $context = [
188 'linkSettings' => WPFunctions::get()->applyFilters(
189 'mailpoet_stats_notification_link_settings',
190 WPFunctions::get()->getSiteUrl(null, '/wp-admin/admin.php?page=mailpoet-settings#basics')
191 ),
192 'blogName' => WPFunctions::get()->getBloginfo('name'),
193 'recipientFirstName' => $this->getRecipientFirstName($settings['address'] ?? ''),
194 'newsletters' => [],
195 ];
196 foreach ($newsletters as $row) {
197 $statistics = $row['statistics'];
198 $newsletter = $row['newsletter'];
199 $totalSentCount = $statistics->getTotalSentCount() ?: 1;
200 $clicked = ($statistics->getClickCount() * 100) / $totalSentCount;
201 $opened = ($statistics->getOpenCount() * 100) / $totalSentCount;
202 $machineOpened = ($statistics->getMachineOpenCount() * 100) / $totalSentCount;
203 $unsubscribed = ($statistics->getUnsubscribeCount() * 100) / $totalSentCount;
204 $bounced = ($statistics->getBounceCount() * 100) / $totalSentCount;
205 $context['newsletters'][] = [
206 'linkStats' => WPFunctions::get()->applyFilters(
207 'mailpoet_stats_notification_link_stats',
208 WPFunctions::get()->getSiteUrl(null, '/wp-admin/admin.php?page=mailpoet-newsletters#/stats/' . $newsletter->getId()),
209 $newsletter->getId()
210 ),
211 'clicked' => $clicked,
212 'opened' => $opened,
213 'machineOpened' => $machineOpened,
214 'unsubscribed' => $unsubscribed,
215 'bounced' => $bounced,
216 'subject' => $newsletter->getSubject(),
217 ];
218 }
219 return $context;
220 }
221
222 private function getRecipientFirstName(string $email): string {
223 if (empty($email)) {
224 return '';
225 }
226 $user = WPFunctions::get()->getUserBy('email', $email);
227 if (!$user) {
228 return '';
229 }
230 $firstName = $user->first_name; // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
231 if (!empty($firstName)) {
232 return $firstName;
233 }
234 $displayName = $user->display_name; // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
235 return !empty($displayName) ? $displayName : '';
236 }
237
238 public function getNextRunDate() {
239 $currentDateTime = $this->wpDateTime->getCurrentDateTime();
240 $date = Carbon::instance($currentDateTime)->millisecond(0);
241 // Get first Monday of next month at midnight
242 $nextMonday = $date->endOfMonth()->next(Carbon::MONDAY)->startOfDay();
243 // Add random time across the entire day (0-23 hours, 0-59 minutes, 0-59 seconds)
244 return $nextMonday
245 ->addHours(rand(0, 23))
246 ->addMinutes(rand(0, 59))
247 ->addSeconds(rand(0, 59));
248 }
249 }
250