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