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 / Subscribers / BulkConfirmationEmailResender.php
mailpoet / lib / Subscribers Last commit date
ConfirmationEmailTemplate 1 month ago ImportExport 2 weeks ago RestApi 3 days ago Statistics 1 month ago BulkActionController.php 3 days ago BulkActionException.php 1 month ago BulkConfirmationEmailResender.php 2 months ago ConfirmationEmailCustomizer.php 2 months ago ConfirmationEmailMailer.php 2 months ago ConfirmationEmailResolver.php 2 months ago EngagementDataBackfiller.php 2 months ago InactiveSubscribersController.php 4 days ago LinkTokens.php 2 months ago NewSubscriberNotificationMailer.php 2 months ago RequiredCustomFieldValidator.php 2 months ago SegmentsCountRecalculator.php 2 weeks ago Source.php 2 months ago SubscriberActions.php 2 months ago SubscriberCustomFieldRepository.php 3 years ago SubscriberIPsRepository.php 2 years ago SubscriberLimitNotificationEvaluator.php 2 months ago SubscriberLimitNotificationMailer.php 2 months ago SubscriberLimitNotificationScheduler.php 2 months ago SubscriberListingRepository.php 2 weeks ago SubscriberPersonalDataEraser.php 2 months ago SubscriberSaveController.php 3 days ago SubscriberSegmentRepository.php 2 weeks ago SubscriberSubscribeController.php 2 weeks ago SubscriberTagRepository.php 4 years ago SubscribersCountsController.php 2 weeks ago SubscribersEmailCountsController.php 2 weeks ago SubscribersRepository.php 3 days ago TrackingConsentController.php 4 days ago index.php 3 years ago
BulkConfirmationEmailResender.php
210 lines
1 <?php declare(strict_types = 1);
2
3 namespace MailPoet\Subscribers;
4
5 if (!defined('ABSPATH')) exit;
6
7
8 use MailPoet\Cron\Workers\BulkConfirmationEmailResend;
9 use MailPoet\Entities\LogEntity;
10 use MailPoet\Entities\ScheduledTaskEntity;
11 use MailPoet\Entities\ScheduledTaskSubscriberEntity;
12 use MailPoet\Entities\SubscriberEntity;
13 use MailPoet\Listing\ListingDefinition;
14 use MailPoet\Logging\LoggerFactory;
15 use MailPoet\Logging\LogRepository;
16 use MailPoet\Settings\SettingsController;
17 use MailPoet\Util\Helpers;
18 use MailPoet\WP\Functions as WPFunctions;
19 use MailPoetVendor\Carbon\Carbon;
20 use MailPoetVendor\Doctrine\ORM\EntityManager;
21
22 class BulkConfirmationEmailResender {
23 public const BULK_CONFIRMATION_RESEND_LIMIT = 20;
24 public const RECENT_CONFIRMATION_RESEND_INTERVAL_DAYS = 7;
25 public const BULK_CONFIRMATION_MAX_SUBSCRIBER_AGE_DAYS = 90;
26
27 private const AUDIT_LOG_MESSAGE_QUEUED = 'Bulk confirmation email resend queued.';
28 private const LOG_LEVEL_INFO = 200;
29
30 /** @var WPFunctions */
31 private $wp;
32
33 /** @var SettingsController */
34 private $settings;
35
36 /** @var SubscriberListingRepository */
37 private $subscriberListingRepository;
38
39 /** @var LogRepository */
40 private $logRepository;
41
42 /** @var EntityManager */
43 private $entityManager;
44
45 public function __construct(
46 WPFunctions $wp,
47 SettingsController $settings,
48 SubscriberListingRepository $subscriberListingRepository,
49 LogRepository $logRepository,
50 EntityManager $entityManager
51 ) {
52 $this->wp = $wp;
53 $this->settings = $settings;
54 $this->subscriberListingRepository = $subscriberListingRepository;
55 $this->logRepository = $logRepository;
56 $this->entityManager = $entityManager;
57 }
58
59 public function canCurrentUserResend(): bool {
60 return $this->wp->currentUserCan('manage_options');
61 }
62
63 public function isSignupConfirmationEnabled(): bool {
64 return (bool)$this->settings->get('signup_confirmation.enabled', true);
65 }
66
67 public function getConfirmationDisabledMessage(): string {
68 $errorMessage = __('Sign-up confirmation is disabled in your [link]MailPoet settings[/link]. Please enable it to resend confirmation emails or update your subscriber’s status manually.', 'mailpoet');
69 return Helpers::replaceLinkTags($errorMessage, 'admin.php?page=mailpoet-settings#/signup');
70 }
71
72 /**
73 * @param array<string, mixed> $requestData
74 * @return array{selected_count: int, eligible_count: int, queued_count: int, skipped_count: int, skipped_by_reason: array<string, int>, task_id: int|null, message: string}
75 */
76 public function queue(ListingDefinition $definition, array $requestData): array {
77 $now = Carbon::now()->millisecond(0);
78 $queueData = $this->subscriberListingRepository->getConfirmationEmailResendQueueData(
79 $definition,
80 (clone $now)->subDays(self::RECENT_CONFIRMATION_RESEND_INTERVAL_DAYS),
81 (clone $now)->subDays(self::BULK_CONFIRMATION_MAX_SUBSCRIBER_AGE_DAYS),
82 ConfirmationEmailMailer::MAX_CONFIRMATION_EMAILS,
83 self::BULK_CONFIRMATION_RESEND_LIMIT,
84 $this->hasExplicitSelection($requestData)
85 );
86
87 $queuedIds = $queueData['queued_ids'];
88 $task = null;
89 if ($queuedIds) {
90 $task = $this->createTask($queuedIds, $queueData, $requestData, $now);
91 } else {
92 $this->saveQueueAuditLog(null, $queueData, $requestData);
93 }
94
95 $queuedCount = count($queuedIds);
96 return [
97 'selected_count' => $queueData['selected_count'],
98 'eligible_count' => $queueData['eligible_count'],
99 'queued_count' => $queuedCount,
100 'skipped_count' => max(0, $queueData['selected_count'] - $queuedCount),
101 'skipped_by_reason' => $queueData['skipped_by_reason'],
102 'task_id' => $task instanceof ScheduledTaskEntity ? $task->getId() : null,
103 'message' => $queuedCount > 0
104 ? __('Confirmation emails are being resent.', 'mailpoet')
105 : __(
106 'No confirmation emails were resent. The selected subscribers could not receive another confirmation email right now.',
107 'mailpoet'
108 ),
109 ];
110 }
111
112 /**
113 * @param int[] $queuedIds
114 * @param array{selected_count: int, eligible_count: int, queued_ids: int[], skipped_by_reason: array<string, int>} $queueData
115 * @param array<string, mixed> $requestData
116 */
117 private function createTask(array $queuedIds, array $queueData, array $requestData, Carbon $now): ScheduledTaskEntity {
118 $task = new ScheduledTaskEntity();
119 $this->entityManager->transactional(function (EntityManager $entityManager) use ($task, $queuedIds, $queueData, $requestData, $now) {
120 $task->setType(BulkConfirmationEmailResend::TASK_TYPE);
121 $task->setStatus(ScheduledTaskEntity::STATUS_SCHEDULED);
122 $task->setScheduledAt($now);
123 $task->setPriority(ScheduledTaskEntity::PRIORITY_HIGH);
124 $task->setMeta([
125 'requested_by' => (int)$this->wp->getCurrentUserId(),
126 'selected_count' => $queueData['selected_count'],
127 'eligible_count' => $queueData['eligible_count'],
128 'queued_count' => count($queuedIds),
129 'skipped_by_reason' => $queueData['skipped_by_reason'],
130 'constants' => $this->getAuditConstants(),
131 'request' => $this->getRequestSummary($requestData),
132 ]);
133 $entityManager->persist($task);
134
135 foreach ($queuedIds as $subscriberId) {
136 /** @var SubscriberEntity $subscriberReference */
137 $subscriberReference = $entityManager->getReference(SubscriberEntity::class, $subscriberId);
138 $entityManager->persist(new ScheduledTaskSubscriberEntity(
139 $task,
140 $subscriberReference
141 ));
142 }
143
144 $entityManager->flush();
145 $this->saveQueueAuditLog($task, $queueData, $requestData);
146 });
147
148 return $task;
149 }
150
151 /**
152 * @param array{selected_count: int, eligible_count: int, queued_ids: int[], skipped_by_reason: array<string, int>} $queueData
153 * @param array<string, mixed> $requestData
154 */
155 private function saveQueueAuditLog(?ScheduledTaskEntity $task, array $queueData, array $requestData): void {
156 $log = new LogEntity();
157 $log->setName(LoggerFactory::TOPIC_API);
158 $log->setLevel(self::LOG_LEVEL_INFO);
159 $log->setMessage(self::AUDIT_LOG_MESSAGE_QUEUED);
160 $log->setRawMessage(self::AUDIT_LOG_MESSAGE_QUEUED);
161 $log->setContext([
162 'action' => 'bulk_confirmation_email_resend_queued',
163 'requested_by' => (int)$this->wp->getCurrentUserId(),
164 'task_id' => $task instanceof ScheduledTaskEntity ? $task->getId() : null,
165 'selected_count' => $queueData['selected_count'],
166 'eligible_count' => $queueData['eligible_count'],
167 'queued_count' => count($queueData['queued_ids']),
168 'skipped_by_reason' => $queueData['skipped_by_reason'],
169 'constants' => $this->getAuditConstants(),
170 'request' => $this->getRequestSummary($requestData),
171 ]);
172 $this->logRepository->saveLog($log);
173 }
174
175 /**
176 * @return array<string, int>
177 */
178 private function getAuditConstants(): array {
179 return [
180 'bulk_confirmation_resend_limit' => self::BULK_CONFIRMATION_RESEND_LIMIT,
181 'recent_confirmation_resend_interval_days' => self::RECENT_CONFIRMATION_RESEND_INTERVAL_DAYS,
182 'bulk_confirmation_max_subscriber_age_days' => self::BULK_CONFIRMATION_MAX_SUBSCRIBER_AGE_DAYS,
183 'max_confirmation_emails' => ConfirmationEmailMailer::MAX_CONFIRMATION_EMAILS,
184 ];
185 }
186
187 /**
188 * @param array<string, mixed> $requestData
189 * @return array<string, mixed>
190 */
191 private function getRequestSummary(array $requestData): array {
192 $listing = isset($requestData['listing']) && is_array($requestData['listing']) ? $requestData['listing'] : [];
193 return [
194 'group' => $listing['group'] ?? null,
195 'has_selection' => !empty($listing['selection']),
196 'selection_count' => isset($listing['selection']) && is_array($listing['selection']) ? count($listing['selection']) : 0,
197 'has_search' => !empty($listing['search']),
198 'filter_keys' => isset($listing['filter']) && is_array($listing['filter']) ? array_keys($listing['filter']) : [],
199 ];
200 }
201
202 /**
203 * @param array<string, mixed> $requestData
204 */
205 private function hasExplicitSelection(array $requestData): bool {
206 $listing = isset($requestData['listing']) && is_array($requestData['listing']) ? $requestData['listing'] : [];
207 return array_key_exists('selection', $listing);
208 }
209 }
210