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 / Statistics / Track / Clicks.php
mailpoet / lib / Statistics / Track Last commit date
Clicks.php 4 days ago Opens.php 4 days ago PageViewCookie.php 2 months ago SubscriberActivityTracker.php 4 days ago SubscriberCookie.php 2 months ago SubscriberHandler.php 1 year ago Unsubscribes.php 2 months ago WooCommercePurchases.php 2 years ago index.php 3 years ago
Clicks.php
213 lines
1 <?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
2
3 namespace MailPoet\Statistics\Track;
4
5 if (!defined('ABSPATH')) exit;
6
7
8 use MailPoet\Entities\NewsletterEntity;
9 use MailPoet\Entities\NewsletterLinkEntity;
10 use MailPoet\Entities\SendingQueueEntity;
11 use MailPoet\Entities\StatisticsClickEntity;
12 use MailPoet\Entities\SubscriberEntity;
13 use MailPoet\Entities\UserAgentEntity;
14 use MailPoet\Newsletter\Shortcodes\Categories\Link as LinkShortcodeCategory;
15 use MailPoet\Newsletter\Shortcodes\Shortcodes;
16 use MailPoet\Settings\TrackingConfig;
17 use MailPoet\Statistics\StatisticsClicksRepository;
18 use MailPoet\Statistics\UserAgentsRepository;
19 use MailPoet\Subscribers\SubscribersRepository;
20 use MailPoet\Subscribers\TrackingConsentController;
21 use MailPoet\Util\Cookies;
22 use MailPoet\Util\Request;
23 use MailPoet\WP\Functions as WPFunctions;
24
25 class Clicks {
26
27 const REVENUE_TRACKING_COOKIE_NAME = 'mailpoet_revenue_tracking';
28 const REVENUE_TRACKING_COOKIE_EXPIRY = 60 * 60 * 24 * 14;
29
30 /** @var Cookies */
31 private $cookies;
32
33 /** @var SubscriberCookie */
34 private $subscriberCookie;
35
36 /** @var Shortcodes */
37 private $shortcodes;
38
39 /** @var LinkShortcodeCategory */
40 private $linkShortcodeCategory;
41
42 /** @var Opens */
43 private $opens;
44
45 /** @var StatisticsClicksRepository */
46 private $statisticsClicksRepository;
47
48 /** @var UserAgentsRepository */
49 private $userAgentsRepository;
50
51 /** @var SubscribersRepository */
52 private $subscribersRepository;
53
54 /** @var TrackingConfig */
55 private $trackingConfig;
56
57 /** @var Request */
58 private $request;
59
60 /** @var TrackingConsentController */
61 private $trackingConsentController;
62
63 public function __construct(
64 Cookies $cookies,
65 SubscriberCookie $subscriberCookie,
66 Shortcodes $shortcodes,
67 Opens $opens,
68 StatisticsClicksRepository $statisticsClicksRepository,
69 UserAgentsRepository $userAgentsRepository,
70 LinkShortcodeCategory $linkShortcodeCategory,
71 SubscribersRepository $subscribersRepository,
72 TrackingConfig $trackingConfig,
73 Request $request,
74 TrackingConsentController $trackingConsentController
75 ) {
76 $this->cookies = $cookies;
77 $this->subscriberCookie = $subscriberCookie;
78 $this->shortcodes = $shortcodes;
79 $this->linkShortcodeCategory = $linkShortcodeCategory;
80 $this->opens = $opens;
81 $this->statisticsClicksRepository = $statisticsClicksRepository;
82 $this->userAgentsRepository = $userAgentsRepository;
83 $this->subscribersRepository = $subscribersRepository;
84 $this->trackingConfig = $trackingConfig;
85 $this->request = $request;
86 $this->trackingConsentController = $trackingConsentController;
87 }
88
89 /**
90 * @param \stdClass|null $data
91 */
92 public function track($data) {
93 if (!$data || empty($data->link)) {
94 return $this->abort();
95 }
96 /** @var SubscriberEntity $subscriber */
97 $subscriber = $data->subscriber;
98 /** @var SendingQueueEntity $queue */
99 $queue = $data->queue;
100 /** @var NewsletterEntity $newsletter */
101 $newsletter = $data->newsletter;
102 /** @var NewsletterLinkEntity $link */
103 $link = $data->link;
104 $wpUserPreview = ($data->preview && ($subscriber->isWPUser()));
105 $trackingAllowed = $this->trackingConsentController->isTrackingAllowed($subscriber);
106 // log statistics only if the action did not come from
107 // a WP user previewing the newsletter
108 // No tracking consent (CNIL/Garante): skip all recording (stats, cookies,
109 // engagement) but keep the redirect below.
110 if (!$wpUserPreview && $trackingAllowed) {
111 $userAgent = !empty($data->userAgent) ? $this->userAgentsRepository->findOrCreate($data->userAgent) : null;
112 $statisticsClicks = $this->statisticsClicksRepository->createOrUpdateClickCount(
113 $link,
114 $subscriber,
115 $newsletter,
116 $queue,
117 $userAgent
118 );
119 if (
120 $userAgent instanceof UserAgentEntity &&
121 ($userAgent->getUserAgentType() === UserAgentEntity::USER_AGENT_TYPE_HUMAN
122 || $statisticsClicks->getUserAgentType() === UserAgentEntity::USER_AGENT_TYPE_MACHINE)
123 ) {
124 $statisticsClicks->setUserAgent($userAgent);
125 $statisticsClicks->setUserAgentType($userAgent->getUserAgentType());
126 }
127 $this->statisticsClicksRepository->flush();
128 $this->sendRevenueCookie($statisticsClicks);
129
130 $subscriberId = $subscriber->getId();
131 if ($subscriberId) {
132 $this->subscriberCookie->setSubscriberId($subscriberId);
133 }
134
135 // track open event
136 $this->opens->track($data, $displayImage = false);
137 // Update engagement date
138 $this->subscribersRepository->maybeUpdateLastClickAt($subscriber);
139 }
140 $url = $this->processUrl($link->getUrl(), $newsletter, $subscriber, $queue, $wpUserPreview);
141 if ($trackingAllowed) {
142 // Consumers of this hook (e.g. automation "clicked link" triggers) use
143 // clicks for follow-up personalization — exactly what consent covers.
144 do_action('mailpoet_link_clicked', $link, $subscriber, $wpUserPreview);
145 }
146 $this->redirectToUrl($url);
147 }
148
149 private function sendRevenueCookie(StatisticsClickEntity $clicks) {
150 if ($this->trackingConfig->isCookieTrackingEnabled()) {
151 $this->cookies->set(
152 self::REVENUE_TRACKING_COOKIE_NAME,
153 [
154 'statistics_clicks' => $clicks->getId(),
155 'created_at' => time(),
156 ],
157 [
158 'expires' => time() + self::REVENUE_TRACKING_COOKIE_EXPIRY,
159 'path' => '/',
160 ]
161 );
162 }
163 }
164
165 public function processUrl(
166 string $url,
167 NewsletterEntity $newsletter,
168 SubscriberEntity $subscriber,
169 SendingQueueEntity $queue,
170 bool $wpUserPreview
171 ) {
172 if (preg_match('/\[link:(?P<action>.*?)\]/', $url, $shortcode)) {
173 if (empty($shortcode['action'])) $this->abort();
174 $processedUrl = $this->linkShortcodeCategory->processShortcodeAction(
175 $shortcode[0],
176 $newsletter,
177 $subscriber,
178 $queue,
179 $wpUserPreview
180 );
181 // If shortcode was not processed, return original shortcode unchanged
182 if ($processedUrl === null) {
183 return $shortcode[0];
184 }
185 $url = $processedUrl;
186 // We need to know the original method for unsubscribe actions
187 if ($this->request->isPost() && $url) {
188 $url = $url . (parse_url($url, PHP_URL_QUERY) ? '&' : '?') . 'request_method=POST';
189 }
190 } else {
191 $this->shortcodes->setQueue($queue);
192 $this->shortcodes->setNewsletter($newsletter);
193 $this->shortcodes->setSubscriber($subscriber);
194 $this->shortcodes->setWpUserPreview($wpUserPreview);
195 $url = $this->shortcodes->replace($url);
196 }
197 return $url;
198 }
199
200 public function abort() {
201 global $wp_query;// phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
202 WPFunctions::get()->statusHeader(404);
203 $wp_query->set_404();// phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
204 WPFunctions::get()->getTemplatePart((string)404);
205 exit;
206 }
207
208 public function redirectToUrl($url) {
209 header('Location: ' . $url, true, 302);
210 exit;
211 }
212 }
213