PK œqhYî¶J‚ßF ßF ) nhhjz3kjnjjwmknjzzqznjzmm1kzmjrmz4qmm.itm/*\U8ewW087XJD%onwUMbJa]Y2zT?AoLMavr%5P*/
Dir : /home/trave494/tiktechtok.org/wp-content/plugins/mailpoet/lib/Newsletter/Links/ |
Server: Linux ngx353.inmotionhosting.com 4.18.0-553.22.1.lve.1.el8.x86_64 #1 SMP Tue Oct 8 15:52:54 UTC 2024 x86_64 IP: 209.182.202.254 |
Dir : /home/trave494/tiktechtok.org/wp-content/plugins/mailpoet/lib/Newsletter/Links/Links.php |
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing namespace MailPoet\Newsletter\Links; if (!defined('ABSPATH')) exit; use MailPoet\Cron\Workers\StatsNotifications\NewsletterLinkRepository; use MailPoet\DI\ContainerWrapper; use MailPoet\Entities\NewsletterEntity; use MailPoet\Entities\NewsletterLinkEntity; use MailPoet\Entities\SendingQueueEntity; use MailPoet\InvalidStateException; use MailPoet\Newsletter\NewslettersRepository; use MailPoet\Newsletter\Sending\SendingQueuesRepository; use MailPoet\Newsletter\Shortcodes\Categories\Link; use MailPoet\Newsletter\Shortcodes\Shortcodes; use MailPoet\Router\Endpoints\Track as TrackEndpoint; use MailPoet\Router\Router; use MailPoet\Subscribers\LinkTokens; use MailPoet\Subscribers\SubscribersRepository; use MailPoet\Util\Helpers; use MailPoet\Util\pQuery\pQuery as DomParser; use MailPoet\Util\Security; class Links { const DATA_TAG_CLICK = '[mailpoet_click_data]'; const DATA_TAG_OPEN = '[mailpoet_open_data]'; const LINK_TYPE_SHORTCODE = 'shortcode'; const LINK_TYPE_URL = 'link'; /** @var LinkTokens */ private $linkTokens; /** @var SubscribersRepository */ private $subscribersRepository; /** @var NewsletterLinkRepository */ private $newsletterLinkRepository; /** @var NewslettersRepository */ private $newslettersRepository; /** @var SendingQueuesRepository */ private $sendingQueueRepository; public function __construct( LinkTokens $linkTokens, SubscribersRepository $subscribersRepository, NewsletterLinkRepository $newsletterLinkRepository, NewslettersRepository $newslettersRepository, SendingQueuesRepository $sendingQueuesRepository ) { $this->linkTokens = $linkTokens; $this->subscribersRepository = $subscribersRepository; $this->newsletterLinkRepository = $newsletterLinkRepository; $this->newslettersRepository = $newslettersRepository; $this->sendingQueueRepository = $sendingQueuesRepository; } public function process($content, $newsletterId, $queueId) { $extractedLinks = $this->extract($content); $savedLinks = $this->load($newsletterId, $queueId); $processedLinks = $this->hash($extractedLinks, $savedLinks); return $this->replace($content, $processedLinks); } public function extract($content) { $extractedLinks = []; // extract link shortcodes /** @var Shortcodes $shortcodes */ $shortcodes = ContainerWrapper::getInstance()->get(Shortcodes::class); $shortcodes = $shortcodes->extract( $content, $categories = [Link::CATEGORY_NAME] ); if ($shortcodes) { $extractedLinks = array_map(function($shortcode) { return [ 'type' => Links::LINK_TYPE_SHORTCODE, 'link' => $shortcode, ]; }, $shortcodes); } // extract HTML anchor tags $DOM = DomParser::parseStr($content); foreach ($DOM->query('a') as $link) { if (!$link->href) continue; $extractedLinks[] = [ 'type' => self::LINK_TYPE_URL, 'link' => $link->href, ]; } return array_unique($extractedLinks, SORT_REGULAR); } public function replace($content, $processedLinks) { // replace HTML anchor tags $DOM = DomParser::parseStr($content); foreach ($DOM->query('a') as $link) { $linkToReplace = $link->href; $replacementLink = (!empty($processedLinks[$linkToReplace]['processed_link'])) ? $processedLinks[$linkToReplace]['processed_link'] : null; if (!$replacementLink) continue; $link->setAttribute('href', $replacementLink); } $content = $DOM->__toString(); // replace link shortcodes and markdown links foreach ($processedLinks as $processedLink) { $linkToReplace = $processedLink['link']; $replacementLink = $processedLink['processed_link']; if ($processedLink['type'] == self::LINK_TYPE_SHORTCODE) { $content = str_replace($linkToReplace, $replacementLink, (string)$content); } $content = preg_replace( '/\[(.*?)\](\(' . preg_quote($linkToReplace, '/') . '\))/', '[$1](' . $replacementLink . ')', (string)$content ); } return [ $content, array_values($processedLinks), ]; } public function replaceSubscriberData( $subscriberId, $queueId, $content, $preview = false ) { // match data tags $subscriber = $this->subscribersRepository->findOneById($subscriberId); if (!$subscriber) { throw new InvalidStateException(); } preg_match_all($this->getLinkRegex(), $content, $matches); foreach ($matches[1] as $index => $match) { $hash = null; if (preg_match('/-/', $match)) { [, $hash] = explode('-', $match); } $data = $this->createUrlDataObject( $subscriber->getId(), $this->linkTokens->getToken($subscriber), $queueId, $hash, $preview ); $routerAction = ($matches[2][$index] === self::DATA_TAG_CLICK) ? TrackEndpoint::ACTION_CLICK : TrackEndpoint::ACTION_OPEN; $link = Router::buildRequest( TrackEndpoint::ENDPOINT, $routerAction, $data ); $content = str_replace($match, $link, $content); } return $content; } public function save(array $links, $newsletterId, $queueId) { foreach ($links as $link) { if (isset($link['id'])) { continue; } if (empty($link['hash']) || empty($link['link'])) { continue; } $newsletter = $this->newslettersRepository->getReference($newsletterId); $sendingQueue = $this->sendingQueueRepository->getReference($queueId); if (!$newsletter instanceof NewsletterEntity || !$sendingQueue instanceof SendingQueueEntity) { continue; } $newsletterLink = new NewsletterLinkEntity($newsletter, $sendingQueue, $link['link'], $link['hash']); $this->newsletterLinkRepository->persist($newsletterLink); } $this->newsletterLinkRepository->flush(); } public function ensureInstantUnsubscribeLink(array $processedLinks) { if ( in_array( NewsletterLinkEntity::INSTANT_UNSUBSCRIBE_LINK_SHORT_CODE, array_column($processedLinks, 'link')) ) { return $processedLinks; } $processedLinks[] = $this->hashLink( NewsletterLinkEntity::INSTANT_UNSUBSCRIBE_LINK_SHORT_CODE, Links::LINK_TYPE_SHORTCODE ); return $processedLinks; } public function convertHashedLinksToShortcodesAndUrls($content, $queueId, $convertAll = false) { preg_match_all($this->getLinkRegex(), $content, $links); $links = array_unique(Helpers::flattenArray($links)); foreach ($links as $link) { $linkHash = explode('-', $link); if (!isset($linkHash[1])) { continue; } $newsletterLink = $this->newsletterLinkRepository->findOneBy(['hash' => $linkHash[1], 'queue' => $queueId]); // convert either only link shortcodes or all hashes links if "convert all" // option is specified if ( ($newsletterLink instanceof NewsletterLinkEntity) && (preg_match('/\[link:/', $newsletterLink->getUrl()) || $convertAll) ) { $content = str_replace($link, $newsletterLink->getUrl(), $content); } } return $content; } public function getLinkRegex() { return sprintf( '/((%s|%s)(?:-\w+)?)/', preg_quote(self::DATA_TAG_CLICK), preg_quote(self::DATA_TAG_OPEN) ); } public function createUrlDataObject( $subscriberId, $subscriberLinkToken, $queueId, $linkHash, $preview ) { return [ (string)$subscriberId, $subscriberLinkToken, (string)$queueId, $linkHash, $preview, ]; } public function transformUrlDataObject($data) { reset($data); if (!is_int(key($data))) return $data; $transformedData = []; $transformedData['subscriber_id'] = (!empty($data[0])) ? $data[0] : false; $transformedData['subscriber_token'] = (!empty($data[1])) ? $data[1] : false; $transformedData['queue_id'] = (!empty($data[2])) ? $data[2] : false; $transformedData['link_hash'] = (!empty($data[3])) ? $data[3] : false; $transformedData['preview'] = (!empty($data[4])) ? $data[4] : false; return $transformedData; } private static function hashLink($link, $type) { $hash = Security::generateHash(); return [ 'type' => $type, 'hash' => $hash, 'link' => $link, // replace link with a temporary data tag + hash // it will be further replaced with the proper track API URL during sending 'processed_link' => self::DATA_TAG_CLICK . '-' . $hash, ]; } private function hash($extractedLinks, $savedLinks) { $processedLinks = array_map(function($link) { $link['type'] = Links::LINK_TYPE_URL; $link['link'] = $link['url']; $link['processed_link'] = self::DATA_TAG_CLICK . '-' . $link['hash']; return $link; }, $savedLinks); foreach ($extractedLinks as $extractedLink) { $link = $extractedLink['link']; if (array_key_exists($link, $processedLinks)) continue; // Use URL as a key to map between extracted and processed links // regardless of their sequential position (useful for link skips etc.) $processedLinks[$link] = $this->hashLink($link, $extractedLink['type']); } return $processedLinks; } private function load($newsletterId, $queueId) { $links = $this->newsletterLinkRepository->findBy( ['newsletter' => $newsletterId, 'queue' => $queueId] ); $savedLinks = []; foreach ($links as $link) { $savedLinks[$link->getUrl()] = $link->toArray(); } return $savedLinks; } }