PluginProbe ʕ •ᴥ•ʔ
MailPoet – Newsletters, Email Marketing, and Automation / 5.34.2
MailPoet – Newsletters, Email Marketing, and Automation v5.34.2
5.34.3 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 / WooCommerce / Subscription.php
mailpoet / lib / WooCommerce Last commit date
Emails 2 months ago Integrations 2 months ago MultichannelMarketing 2 years ago TransactionalEmails 10 months ago WooCommerceBookings 1 month ago WooCommerceSubscriptions 2 months ago CouponPreProcessor.php 2 months ago Emails.php 8 months ago Helper.php 1 month ago MailPoetTask.php 2 years ago NonPersistablePreviewData.php 1 month ago OrderAttributionFields.php 1 month ago OrderAttributionRevenueReader.php 3 weeks ago OrderAttributionWriter.php 1 month ago RandomCouponCodeGenerator.php 2 months ago Settings.php 1 year ago SubscriberEngagement.php 3 years ago Subscription.php 2 months ago Tracker.php 2 years ago TransactionalEmailHooks.php 1 year ago TransactionalEmails.php 1 year ago WooSystemInfo.php 1 month ago WooSystemInfoController.php 1 year ago index.php 3 years ago
Subscription.php
267 lines
1 <?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
2
3 namespace MailPoet\WooCommerce;
4
5 if (!defined('ABSPATH')) exit;
6
7
8 use MailPoet\Entities\SubscriberEntity;
9 use MailPoet\Segments\SegmentsRepository;
10 use MailPoet\Settings\SettingsController;
11 use MailPoet\Subscribers\ConfirmationEmailMailer;
12 use MailPoet\Subscribers\Source;
13 use MailPoet\Subscribers\SubscriberSegmentRepository;
14 use MailPoet\Subscribers\SubscribersRepository;
15 use MailPoet\Util\Helpers;
16 use MailPoet\WP\Functions as WPFunctions;
17 use MailPoetVendor\Carbon\Carbon;
18
19 class Subscription {
20 const CHECKOUT_OPTIN_INPUT_NAME = 'mailpoet_woocommerce_checkout_optin';
21 const CHECKOUT_OPTIN_PRESENCE_CHECK_INPUT_NAME = 'mailpoet_woocommerce_checkout_optin_present';
22 const OPTIN_ENABLED_SETTING_NAME = 'woocommerce.optin_on_checkout.enabled';
23 const OPTIN_SEGMENTS_SETTING_NAME = 'woocommerce.optin_on_checkout.segments';
24 const OPTIN_MESSAGE_SETTING_NAME = 'woocommerce.optin_on_checkout.message';
25 const OPTIN_POSITION_SETTING_NAME = 'woocommerce.optin_on_checkout.position';
26
27 private $allowedHtml = [
28 'input' => [
29 'type' => true,
30 'name' => true,
31 'id' => true,
32 'class' => true,
33 'value' => true,
34 'checked' => true,
35 ],
36 'span' => [
37 'class' => true,
38 ],
39 'label' => [
40 'class' => true,
41 'data-automation-id' => true,
42 'for' => true,
43 ],
44 'p' => [
45 'class' => true,
46 'id' => true,
47 'data-priority' => true,
48 ],
49 ];
50
51 /** @var SettingsController */
52 private $settings;
53
54 /** @var WPFunctions */
55 private $wp;
56
57 /** @var Helper */
58 private $wcHelper;
59
60 /** @var ConfirmationEmailMailer */
61 private $confirmationEmailMailer;
62
63 /** @var SubscribersRepository */
64 private $subscribersRepository;
65
66 /** @var SegmentsRepository */
67 private $segmentsRepository;
68
69 /** @var SubscriberSegmentRepository */
70 private $subscriberSegmentRepository;
71
72 public function __construct(
73 SettingsController $settings,
74 ConfirmationEmailMailer $confirmationEmailMailer,
75 WPFunctions $wp,
76 Helper $wcHelper,
77 SubscribersRepository $subscribersRepository,
78 SegmentsRepository $segmentsRepository,
79 SubscriberSegmentRepository $subscriberSegmentRepository
80 ) {
81 $this->settings = $settings;
82 $this->wp = $wp;
83 $this->wcHelper = $wcHelper;
84 $this->confirmationEmailMailer = $confirmationEmailMailer;
85 $this->subscribersRepository = $subscribersRepository;
86 $this->segmentsRepository = $segmentsRepository;
87 $this->subscriberSegmentRepository = $subscriberSegmentRepository;
88 }
89
90 public function extendWooCommerceCheckoutForm() {
91 $inputName = self::CHECKOUT_OPTIN_INPUT_NAME;
92 $checked = false;
93 if (!empty($_POST[self::CHECKOUT_OPTIN_INPUT_NAME])) {
94 $checked = true;
95 }
96 $labelString = $this->settings->get(self::OPTIN_MESSAGE_SETTING_NAME);
97 $defaultTemplate = wp_kses(
98 $this->getSubscriptionField($inputName, $checked, $labelString),
99 $this->allowedHtml
100 );
101 $filtered = $this->wp->applyFilters(
102 'mailpoet_woocommerce_checkout_optin_template',
103 $defaultTemplate,
104 $inputName,
105 $checked,
106 $labelString
107 );
108 $template = is_string($filtered) ? $filtered : $defaultTemplate;
109 // The template has been sanitized above and can be considered safe.
110 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
111 echo $template;
112 if ($template) {
113 $field = $this->getSubscriptionPresenceCheckField();
114 echo wp_kses($field, $this->allowedHtml);
115 }
116 }
117
118 private function getSubscriptionField($inputName, $checked, $labelString) {
119 $checked = checked($checked, true, false);
120
121 return '<label class="woocommerce-form__label woocommerce-form__label-for-checkbox checkbox" data-automation-id="woo-commerce-subscription-opt-in">
122 <input id="mailpoet_woocommerce_checkout_optin" class="woocommerce-form__input woocommerce-form__input-checkbox input-checkbox" ' . $checked . ' type="checkbox" name="' . $this->wp->escAttr($inputName) . '" value="1" />
123 <span>' . $this->wp->escHtml($labelString) . '</span>
124 </label>';
125 }
126
127 private function getSubscriptionPresenceCheckField() {
128 $field = $this->wcHelper->woocommerceFormField(
129 self::CHECKOUT_OPTIN_PRESENCE_CHECK_INPUT_NAME,
130 [
131 'type' => 'hidden',
132 'return' => true,
133 ],
134 1
135 );
136 if ($field) {
137 return $field;
138 }
139 // Workaround for older WooCommerce versions (below 4.6.0) that don't support hidden fields
140 // We can remove it after we drop support of older WooCommerce
141 $field = $this->wcHelper->woocommerceFormField(
142 self::CHECKOUT_OPTIN_PRESENCE_CHECK_INPUT_NAME,
143 [
144 'type' => 'text',
145 'return' => true,
146 ],
147 1
148 );
149 return str_replace('type="text"', 'type="hidden"', $field);
150 }
151
152 public function subscribeOnOrderPay($orderId) {
153 $wcOrder = $this->wcHelper->wcGetOrder($orderId);
154 if (!$wcOrder instanceof \WC_Order) {
155 return null;
156 }
157
158 $data['billing_email'] = $wcOrder->get_billing_email();
159 $this->subscribeOnCheckout($orderId, $data);
160 }
161
162 public function subscribeOnCheckout($orderId, $data) {
163 $this->triggerAutomateWooOptin();
164 if (empty($data['billing_email'])) {
165 // no email in posted order data
166 return null;
167 }
168
169 $subscriber = $this->subscribersRepository->findOneBy(
170 ['email' => $data['billing_email'], 'isWoocommerceUser' => 1]
171 );
172
173 if (!$subscriber) {
174 // no subscriber: WooCommerce sync didn't work
175 return null;
176 }
177
178 $checkoutOptin = !empty($_POST[self::CHECKOUT_OPTIN_INPUT_NAME]);
179
180 return $this->handleSubscriberOptin($subscriber, $checkoutOptin);
181 }
182
183 /**
184 * Subscribe a subscriber.
185 *
186 * @param SubscriberEntity $subscriber Subscriber object
187 * @param bool $shouldSubscribe Whether the subscriber should be subscribed
188 */
189 public function handleSubscriberOptin(SubscriberEntity $subscriber, bool $shouldSubscribe): bool {
190 $wcSegment = $this->segmentsRepository->getWooCommerceSegment();
191
192 $segmentIds = (array)$this->settings->get(self::OPTIN_SEGMENTS_SETTING_NAME, []);
193 $moreSegmentsToSubscribe = [];
194 if (!empty($segmentIds)) {
195 $moreSegmentsToSubscribe = $this->segmentsRepository->findByIds($segmentIds);
196 }
197 $signupConfirmation = $this->settings->get('signup_confirmation');
198
199 if ($shouldSubscribe) {
200 $subscriber->setSource(Source::WOOCOMMERCE_CHECKOUT);
201
202 if (
203 ($subscriber->getStatus() === SubscriberEntity::STATUS_SUBSCRIBED)
204 || ((bool)$signupConfirmation['enabled'] === false)
205 ) {
206 $this->subscribe($subscriber);
207 } else {
208 $this->requireSubscriptionConfirmation($subscriber);
209 }
210
211 $this->subscriberSegmentRepository->subscribeToSegments($subscriber, array_merge([$wcSegment], $moreSegmentsToSubscribe));
212
213 return true;
214 } else {
215 return false;
216 }
217 }
218
219 public function hideAutomateWooOptinCheckbox(): void {
220 if (!$this->wp->isPluginActive('automatewoo/automatewoo.php')) {
221 return;
222 }
223 // Hide AutomateWoo checkout opt-in so we won't end up with two opt-ins
224 $this->wp->removeAction(
225 'woocommerce_checkout_after_terms_and_conditions',
226 ['AutomateWoo\Frontend', 'output_checkout_optin_checkbox']
227 );
228 }
229
230 private function triggerAutomateWooOptin(): void {
231 if (
232 !$this->wp->isPluginActive('automatewoo/automatewoo.php')
233 || empty($_POST[self::CHECKOUT_OPTIN_INPUT_NAME])
234 ) {
235 return;
236 }
237 // Emulate checkout opt-in triggering for AutomateWoo
238 $_POST['automatewoo_optin'] = 'On';
239 }
240
241 private function subscribe(SubscriberEntity $subscriber) {
242 $subscriber->setStatus(SubscriberEntity::STATUS_SUBSCRIBED);
243 if (empty($subscriber->getConfirmedIp()) && empty($subscriber->getConfirmedAt())) {
244 $subscriber->setConfirmedIp(Helpers::getIP());
245 $subscriber->setConfirmedAt(new Carbon());
246 }
247
248 $this->subscribersRepository->persist($subscriber);
249 $this->subscribersRepository->flush();
250 }
251
252 private function requireSubscriptionConfirmation(SubscriberEntity $subscriber) {
253 $subscriber->setStatus(SubscriberEntity::STATUS_UNCONFIRMED);
254 $this->subscribersRepository->persist($subscriber);
255 $this->subscribersRepository->flush();
256
257 try {
258 // Per-list confirmation settings are not resolved here because this path
259 // subscribes to the WooCommerce Customers segment (TYPE_WC_USERS),
260 // which does not support custom confirmation overrides.
261 $this->confirmationEmailMailer->sendConfirmationEmailOnce($subscriber, null, null, true);
262 } catch (\Exception $e) {
263 // ignore errors
264 }
265 }
266 }
267