PluginProbe ʕ •ᴥ•ʔ
MailPoet – Newsletters, Email Marketing, and Automation / 5.37.0
MailPoet – Newsletters, Email Marketing, and Automation v5.37.0
5.37.0 5.36.1 5.36.0 5.35.1 5.35.0 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 / Subscription / Comment.php
mailpoet / lib / Subscription Last commit date
AdminUserSubscription.php 3 months ago Blacklist.php 1 year ago Comment.php 1 day ago Form.php 1 month ago Manage.php 1 month ago ManageSubscriptionFormRenderer.php 3 weeks ago Pages.php 1 week ago Registration.php 1 day ago SubscriptionUrlFactory.php 1 month ago Throttling.php 3 months ago index.php 3 years ago
Comment.php
237 lines
1 <?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
2
3 namespace MailPoet\Subscription;
4
5 if (!defined('ABSPATH')) exit;
6
7
8 use MailPoet\Entities\SubscriberEntity;
9 use MailPoet\Settings\SettingsController;
10 use MailPoet\Subscribers\SubscriberActions;
11 use MailPoet\Subscribers\SubscribersRepository;
12 use MailPoet\Subscribers\TrackingConsentCapture;
13 use MailPoet\WP\Functions as WPFunctions;
14
15 class Comment {
16 const SPAM = 'spam';
17 const APPROVED = 1;
18 const PENDING_APPROVAL = 0;
19
20 /**
21 * Comment meta remembering the tracking-consent choice while a comment waits
22 * for moderation. The subscribe is deferred to approval, which happens in a
23 * later request with no POST data, so the choice has to be stored with the
24 * comment rather than re-read.
25 */
26 const TRACKING_CONSENT_META = 'mailpoet_tracking_consent';
27
28 /** @var SettingsController */
29 private $settings;
30
31 /** @var SubscriberActions */
32 private $subscriberActions;
33
34 /** @var TrackingConsentCapture */
35 private $trackingConsentCapture;
36
37 /** @var SubscribersRepository */
38 private $subscribersRepository;
39
40 public function __construct(
41 SettingsController $settings,
42 SubscriberActions $subscriberActions,
43 TrackingConsentCapture $trackingConsentCapture,
44 SubscribersRepository $subscribersRepository
45 ) {
46 $this->settings = $settings;
47 $this->subscriberActions = $subscriberActions;
48 $this->trackingConsentCapture = $trackingConsentCapture;
49 $this->subscribersRepository = $subscribersRepository;
50 }
51
52 public function extendLoggedInForm($field) {
53 $field .= $this->getSubscriptionField();
54 return $field;
55 }
56
57 public function extendLoggedOutForm() {
58 // The method returns escaped content
59 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
60 echo $this->getSubscriptionField();
61 }
62
63 /**
64 * Returns escaped HTML for the subscription field.
65 *
66 * @return string
67 */
68 private function getSubscriptionField(): string {
69 $label = $this->settings->get(
70 'subscribe.on_comment.label',
71 __('Yes, please add me to your mailing list.', 'mailpoet')
72 );
73
74 return '<p class="comment-form-mailpoet">
75 <label for="mailpoet_subscribe_on_comment">
76 <input
77 type="checkbox"
78 id="mailpoet_subscribe_on_comment"
79 value="1"
80 name="mailpoet[subscribe_on_comment]"
81 />&nbsp;' . esc_html($label) . '
82 </label>
83 </p>' . $this->getTrackingConsentField();
84 }
85
86 /**
87 * A second, independent checkbox. Consent to open and click tracking is never
88 * inferred from the subscribe box above it, and it is only shown on sites
89 * that chose to ask. Never pre-ticked: a pre-ticked consent box is not valid
90 * consent (CJEU Planet49).
91 */
92 private function getTrackingConsentField(): string {
93 if (!$this->trackingConsentCapture->isCaptureEnabled()) {
94 return '';
95 }
96 $copy = $this->trackingConsentCapture->getCopy(
97 SubscriberEntity::TRACKING_CONSENT_METHOD_COMMENT
98 );
99
100 return '<p class="comment-form-mailpoet-tracking-consent">
101 <label for="mailpoet_tracking_consent">
102 <input
103 type="checkbox"
104 id="mailpoet_tracking_consent"
105 value="1"
106 name="mailpoet[tracking_consent]"
107 />&nbsp;' . esc_html($copy) . '
108 </label>
109 </p>';
110 }
111
112 public function onSubmit($commentId, $commentStatus) {
113 if ($commentStatus === Comment::SPAM) return;
114
115 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- type narrowing only, value is read as bool
116 $mailpoetPost = isset($_POST['mailpoet']) && is_array($_POST['mailpoet']) ? $_POST['mailpoet'] : [];
117
118 // Independent of the subscribe checkbox, and only for a row that already
119 // exists: never creates one. Runs whatever the moderation status, because
120 // updating an existing row's consent does not depend on the comment being
121 // approved, unlike subscribing, which does.
122 $this->applyTrackingConsentToExistingSubscriber($mailpoetPost, $commentId);
123
124 if (
125 isset($mailpoetPost['subscribe_on_comment'])
126 && (bool)$mailpoetPost['subscribe_on_comment'] === true
127 ) {
128 $trackingConsent = !empty($mailpoetPost['tracking_consent']);
129 if ($commentStatus === Comment::PENDING_APPROVAL) {
130 // add a comment meta to remember to subscribe the user
131 // once the comment gets approved
132 WPFunctions::get()->addCommentMeta(
133 $commentId,
134 'mailpoet',
135 'subscribe_on_comment',
136 true
137 );
138 // Approval runs in a later request with no POST data, so the consent
139 // choice is stored alongside it rather than re-read then.
140 WPFunctions::get()->addCommentMeta(
141 $commentId,
142 self::TRACKING_CONSENT_META,
143 $trackingConsent ? '1' : '0',
144 true
145 );
146 } elseif ($commentStatus === Comment::APPROVED) {
147 $this->subscribeAuthorOfComment($commentId, $trackingConsent);
148 }
149 }
150 }
151
152 /**
153 * Records the comment form's tracking-consent choice for a commenter who is
154 * already a subscriber, whether or not they also ticked "add me to your
155 * mailing list". Deliberately never creates a subscriber: a comment is not a
156 * signup, and someone answering a tracking question should not thereby end
157 * up on a list they did not ask to join.
158 *
159 * isNewSubscriber is always false by construction here, since this only ever
160 * reaches the apply call when findOneBy just found an existing row. That
161 * means an unticked box on an existing row is dropped by getConsentData()'s
162 * own rule, with no extra logic needed.
163 */
164 private function applyTrackingConsentToExistingSubscriber(array $mailpoetPost, $commentId): void {
165 if (!isset($mailpoetPost['tracking_consent'])) {
166 return;
167 }
168 $comment = WPFunctions::get()->getComment($commentId);
169 $email = $comment ? $comment->comment_author_email : null; // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
170 if (empty($email)) {
171 return;
172 }
173 $subscriber = $this->subscribersRepository->findOneBy(['email' => $email]);
174 if (!$subscriber instanceof SubscriberEntity) {
175 return;
176 }
177 $method = SubscriberEntity::TRACKING_CONSENT_METHOD_COMMENT;
178 $this->trackingConsentCapture->applyToSubscriber(
179 $subscriber,
180 !empty($mailpoetPost['tracking_consent']),
181 $method,
182 $this->trackingConsentCapture->getCopy($method),
183 false
184 );
185 $this->subscribersRepository->flush();
186 }
187
188 public function onStatusUpdate($commentId, $action) {
189 if ($action === 'approve') {
190 // check if the comment's author wants to subscribe
191 $doSubscribe = (
192 WPFunctions::get()->getCommentMeta(
193 $commentId,
194 'mailpoet',
195 true
196 ) === 'subscribe_on_comment'
197 );
198
199 if ($doSubscribe === true) {
200 $trackingConsent = WPFunctions::get()->getCommentMeta(
201 $commentId,
202 self::TRACKING_CONSENT_META,
203 true
204 ) === '1';
205 $this->subscribeAuthorOfComment($commentId, $trackingConsent);
206
207 WPFunctions::get()->deleteCommentMeta($commentId, 'mailpoet');
208 WPFunctions::get()->deleteCommentMeta($commentId, self::TRACKING_CONSENT_META);
209 }
210 }
211 }
212
213 private function subscribeAuthorOfComment($commentId, bool $trackingConsent = false) {
214 $segmentIds = $this->settings->get('subscribe.on_comment.segments', []);
215
216 if (!empty($segmentIds)) {
217 $comment = WPFunctions::get()->getComment($commentId);
218 $email = $comment->comment_author_email; // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
219 $method = SubscriberEntity::TRACKING_CONSENT_METHOD_COMMENT;
220 $consentData = $this->trackingConsentCapture->getConsentData(
221 $trackingConsent,
222 $method,
223 $this->trackingConsentCapture->getCopy($method),
224 $this->trackingConsentCapture->isNewSubscriber($email)
225 );
226
227 $this->subscriberActions->subscribe(
228 array_merge([
229 'email' => $email,
230 'first_name' => $comment->comment_author, // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
231 ], $consentData),
232 $segmentIds
233 );
234 }
235 }
236 }
237