8889841cPKд[OYShortcodes.phpnu[dateCategory = $dateCategory; $this->linkCategory = $linkCategory; $this->newsletterCategory = $newsletterCategory; $this->subscriberCategory = $subscriberCategory; $this->siteCategory = $siteCategory; $this->wp = $wp; } public function setNewsletter(NewsletterEntity $newsletter = null): void { $this->newsletter = $newsletter; } public function setSubscriber(SubscriberEntity $subscriber = null): void { $this->subscriber = $subscriber; } public function setQueue(SendingQueueEntity $queue = null): void { $this->queue = $queue; } public function setWpUserPreview(bool $wpUserPreview): void { $this->wpUserPreview = $wpUserPreview; } public function extract($content, $categories = false) { $categories = (is_array($categories)) ? implode('|', $categories) : false; // match: [category:shortcode] or [category|category|...:shortcode] // dot not match: [category://shortcode] - avoids matching http/ftp links $regex = sprintf( '/\[%s:(?!\/\/).*?\]/i', ($categories) ? '(?:' . $categories . ')' : '(?:\w+)' ); preg_match_all($regex, (string)$content, $shortcodes); $shortcodes = $shortcodes[0]; return (count($shortcodes)) ? array_values(array_unique($shortcodes)) : false; } /** * Parse a MailPoet-style shortcode. * The syntax is [category:action | argument:argument_value], it can have a single argument. */ public function match($shortcode) { preg_match( '/\[(?P\w+)?:(?P\w+)(?:.*?\|.*?(?P\w+):(?P.*?))?\]/', $shortcode, $match ); // If argument exists, copy it to the arguments array if (!empty($match['argument'])) { $match['arguments'] = [$match['argument'] => isset($match['argument_value']) ? $match['argument_value'] : '']; } return $match; } /** * Parse a WordPress-style shortcode. * The syntax is [category:action arg1="value1" arg2="value2"], it can have multiple arguments. */ public function matchWPShortcode($shortcode) { $atts = $this->wp->shortcodeParseAtts(trim($shortcode, '[]/')); if (empty($atts[0])) { return []; } $shortcodeName = $atts[0]; list($category, $action) = explode(':', $shortcodeName); $shortcodeDetails = []; $shortcodeDetails['category'] = $category; $shortcodeDetails['action'] = $action; $shortcodeDetails['arguments'] = []; foreach ($atts as $attrName => $attrValue) { if (is_numeric($attrName)) { continue; // Skip unnamed attributes } $shortcodeDetails['arguments'][$attrName] = $attrValue; // Make a shortcut to the first argument if (!isset($shortcodeDetails['argument'])) { $shortcodeDetails['argument'] = $attrName; $shortcodeDetails['argument_value'] = $attrValue; } } return $shortcodeDetails; } public function process($shortcodes, $content = '') { $processedShortcodes = []; foreach ($shortcodes as $shortcode) { $shortcodeDetails = $this->match($shortcode); if (empty($shortcodeDetails)) { // Wrong MailPoet shortcode syntax, try to parse as a native WP shortcode $shortcodeDetails = $this->matchWPShortcode($shortcode); } $shortcodeDetails['shortcode'] = $shortcode; $shortcodeDetails['category'] = !empty($shortcodeDetails['category']) ? $shortcodeDetails['category'] : ''; $shortcodeDetails['action'] = !empty($shortcodeDetails['action']) ? $shortcodeDetails['action'] : ''; $shortcodeDetails['action_argument'] = !empty($shortcodeDetails['argument']) ? $shortcodeDetails['argument'] : ''; $shortcodeDetails['action_argument_value'] = !empty($shortcodeDetails['argument_value']) ? $shortcodeDetails['argument_value'] : ''; $shortcodeDetails['arguments'] = !empty($shortcodeDetails['arguments']) ? $shortcodeDetails['arguments'] : []; $category = strtolower($shortcodeDetails['category']); $categoryClass = $this->getCategoryObject($category); if ($categoryClass instanceof CategoryInterface) { $processedShortcodes[] = $categoryClass->process( $shortcodeDetails, $this->newsletter, $this->subscriber, $this->queue, $content, $this->wpUserPreview ); } else { $customShortcode = $this->wp->applyFilters( 'mailpoet_newsletter_shortcode', $shortcode, $this->newsletter, $this->subscriber, $this->queue, $content, $shortcodeDetails['arguments'], $this->wpUserPreview ); $processedShortcodes[] = ($customShortcode === $shortcode) ? false : $customShortcode; } } return $processedShortcodes; } public function replace($content, $contentSource = null, $categories = null) { $shortcodes = $this->extract($content, $categories); if (!$shortcodes) { return $content; } // if content contains only shortcodes (e.g., [newsletter:post_title]) but their processing // depends on some other content (e.g., "post_id" inside a rendered newsletter), // then we should use that content source when processing shortcodes $processedShortcodes = $this->process( $shortcodes, ($contentSource) ? $contentSource : $content ); return str_replace($shortcodes, $processedShortcodes, $content); } private function getCategoryObject($category): ?CategoryInterface { if ($category === 'link') { return $this->linkCategory; } elseif ($category === 'date') { return $this->dateCategory; } elseif ($category === 'newsletter') { return $this->newsletterCategory; } elseif ($category === 'subscriber') { return $this->subscriberCategory; } elseif ($category === 'site') { return $this->siteCategory; } return null; } } PKд[ index.phpnu[customFieldsRepository = $customFieldsRepository; } public function getShortcodes(): array { $shortcodes = [ __('Subscriber', 'mailpoet') => [ [ 'text' => __('First Name', 'mailpoet'), 'shortcode' => '[subscriber:firstname | default:reader]', ], [ 'text' => __('Last Name', 'mailpoet'), 'shortcode' => '[subscriber:lastname | default:reader]', ], [ 'text' => __('Email Address', 'mailpoet'), 'shortcode' => '[subscriber:email]', ], [ 'text' => __('WordPress User Display Name', 'mailpoet'), 'shortcode' => '[subscriber:displayname | default:member]', ], [ 'text' => __('Total Number of Subscribers', 'mailpoet'), 'shortcode' => '[subscriber:count]', ], ], __('Newsletter', 'mailpoet') => [ [ 'text' => __('Newsletter Subject', 'mailpoet'), 'shortcode' => '[newsletter:subject]', ], ], __('Post Notifications', 'mailpoet') => [ [ 'text' => __('Total Number of Posts or Pages', 'mailpoet'), 'shortcode' => '[newsletter:total]', ], [ 'text' => __('Most Recent Post Title', 'mailpoet'), 'shortcode' => '[newsletter:post_title]', ], [ 'text' => __('Issue Number', 'mailpoet'), 'shortcode' => '[newsletter:number]', ], ], __('Date', 'mailpoet') => [ [ 'text' => __('Current day of the month number', 'mailpoet'), 'shortcode' => '[date:d]', ], [ 'text' => __('Current day of the month in ordinal form, i.e. 2nd, 3rd, 4th, etc.', 'mailpoet'), 'shortcode' => '[date:dordinal]', ], [ 'text' => __('Full name of current day', 'mailpoet'), 'shortcode' => '[date:dtext]', ], [ 'text' => __('Current month number', 'mailpoet'), 'shortcode' => '[date:m]', ], [ 'text' => __('Full name of current month', 'mailpoet'), 'shortcode' => '[date:mtext]', ], [ 'text' => __('Year', 'mailpoet'), 'shortcode' => '[date:y]', ], ], __('Links', 'mailpoet') => [ [ 'text' => __('Unsubscribe link', 'mailpoet'), 'shortcode' => sprintf( '%s', NewsletterLinkEntity::UNSUBSCRIBE_LINK_SHORT_CODE, __('Unsubscribe', 'mailpoet') ), ], [ 'text' => __('Edit subscription page link', 'mailpoet'), 'shortcode' => sprintf( '%s', '[link:subscription_manage_url]', __('Manage subscription', 'mailpoet') ), ], [ 'text' => __('View in browser link', 'mailpoet'), 'shortcode' => sprintf( '%s', '[link:newsletter_view_in_browser_url]', __('View in your browser', 'mailpoet') ), ], ], __('Site', 'mailpoet') => [ [ 'text' => __('Site title', 'mailpoet'), 'shortcode' => '[site:title]', ], [ 'text' => __('Homepage link', 'mailpoet'), 'shortcode' => sprintf( '%s', '[site:homepage_url]', '[site:title]' ), ], [ 'text' => __('Homepage URL', 'mailpoet'), 'shortcode' => '[site:homepage_url]', ], ], ]; $customFields = $this->getCustomFields(); if (count($customFields) > 0) { $shortcodes[__('Subscriber', 'mailpoet')] = array_merge( $shortcodes[__('Subscriber', 'mailpoet')], $customFields ); } return $shortcodes; } public function getCustomFields(): array { $customFields = $this->customFieldsRepository->findAll(); return array_map(function($customField) { return [ 'text' => $customField->getName(), 'shortcode' => '[subscriber:cf_' . $customField->getId() . ']', ]; }, $customFields); } } PKд[SL Categories/Newsletter.phpnu[newslettersRepository = $newslettersRepository; } public function process( array $shortcodeDetails, NewsletterEntity $newsletter = null, SubscriberEntity $subscriber = null, SendingQueueEntity $queue = null, string $content = '', bool $wpUserPreview = false ): ?string { switch ($shortcodeDetails['action']) { case 'subject': return ($newsletter instanceof NewsletterEntity) ? $newsletter->getSubject() : null; case 'total': return (string)substr_count($content, 'data-post-id'); case 'post_title': preg_match_all('/data-post-id="(\d+)"/ism', $content, $posts); $postIds = array_unique($posts[1]); $latestPost = (!empty($postIds)) ? $this->getLatestWPPost($postIds) : null; return ($latestPost) ? $latestPost['post_title'] : null; case 'number': if (!($newsletter instanceof NewsletterEntity)) return null; if ($newsletter->getType() !== NewsletterEntity::TYPE_NOTIFICATION_HISTORY) { return null; } $sentNewsletters = $this->newslettersRepository->countBy([ 'parent' => $newsletter->getParent(), 'status' => NewsletterEntity::STATUS_SENT, ]); return (string)++$sentNewsletters; default: return null; } } public function ensureConsistentQueryType(\WP_Query $query) { // Queries with taxonomies are autodetected as 'is_archive=true' and 'is_home=false' // while queries without them end up being 'is_archive=false' and 'is_home=true'. // This is to fix that by always enforcing constistent behavior. $query->is_archive = true; // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps $query->is_home = false; // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps } private function getLatestWPPost($postIds) { // set low priority to execute 'ensureConstistentQueryType' before any other filter $filterPriority = defined('PHP_INT_MIN') ? constant('PHP_INT_MIN') : ~PHP_INT_MAX; WPFunctions::get()->addAction('pre_get_posts', [$this, 'ensureConsistentQueryType'], $filterPriority); $posts = new \WP_Query( [ 'post_type' => WPPosts::getTypes(), 'post__in' => $postIds, 'posts_per_page' => 1, 'ignore_sticky_posts' => true, 'orderby' => 'post_date', 'order' => 'DESC', ] ); WPFunctions::get()->removeAction('pre_get_posts', [$this, 'ensureConsistentQueryType'], $filterPriority); return (!empty($posts->posts[0])) && ($posts->posts[0] instanceof \WP_Post) ? $posts->posts[0]->to_array() : false; } } PKд[%X X Categories/Subscriber.phpnu[subscribersRepository = $subscribersRepository; $this->subscriberCustomFieldRepository = $subscriberCustomFieldRepository; } public function process( array $shortcodeDetails, NewsletterEntity $newsletter = null, SubscriberEntity $subscriber = null, SendingQueueEntity $queue = null, string $content = '', bool $wpUserPreview = false ): ?string { if (!($subscriber instanceof SubscriberEntity)) { return $shortcodeDetails['shortcode']; } $defaultValue = ($shortcodeDetails['action_argument'] === 'default') ? $shortcodeDetails['action_argument_value'] : ''; switch ($shortcodeDetails['action']) { case 'firstname': return (!empty($subscriber->getFirstName())) ? htmlspecialchars($subscriber->getFirstName()) : $defaultValue; case 'lastname': return !empty($subscriber->getLastName()) ? htmlspecialchars($subscriber->getLastName()) : $defaultValue; case 'email': return $subscriber->getEmail(); case 'displayname': if ($subscriber->getWpUserId()) { $wpUser = WPFunctions::get()->getUserdata($subscriber->getWpUserId()); return $wpUser->user_login; // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps } return $defaultValue; case 'count': return (string)$this->getSubscribersCountWithSubscribedStatus(); default: if ( preg_match('/cf_(\d+)/', $shortcodeDetails['action'], $customField) && !empty($subscriber->getId()) ) { $customField = $this->subscriberCustomFieldRepository->findOneBy([ 'subscriber' => $subscriber, 'customField' => $customField[1], ]); return ($customField instanceof SubscriberCustomFieldEntity) ? $customField->getValue() : null; } return null; } } private function getSubscribersCountWithSubscribedStatus(): int { return $this->subscribersRepository->countBy(['status' => SubscriberEntity::STATUS_SUBSCRIBED, 'deletedAt' => null]); } } PKд[Zc#OO Categories/CategoryInterface.phpnu[settings = $settings; $this->newsletterUrl = $newsletterUrl; $this->wp = $wp; $this->trackingConfig = $trackingConfig; } public function process( array $shortcodeDetails, NewsletterEntity $newsletter = null, SubscriberEntity $subscriber = null, SendingQueueEntity $queue = null, string $content = '', bool $wpUserPreview = false ): ?string { $subscriptionUrlFactory = SubscriptionUrlFactory::getInstance(); switch ($shortcodeDetails['action']) { case 'subscription_unsubscribe_url': return self::processUrl( $shortcodeDetails['action'], $subscriptionUrlFactory->getConfirmUnsubscribeUrl( $wpUserPreview ? null : $subscriber, $queue ? $queue->getId() : null ), $queue, $wpUserPreview ); case 'subscription_instant_unsubscribe_url': return self::processUrl( $shortcodeDetails['action'], $subscriptionUrlFactory->getUnsubscribeUrl( $wpUserPreview ? null : $subscriber, $queue ? $queue->getId() : null ), $queue, $wpUserPreview ); case 'subscription_manage_url': return self::processUrl( $shortcodeDetails['action'], $subscriptionUrlFactory->getManageUrl($wpUserPreview ? null : $subscriber), $queue, $wpUserPreview ); case 'newsletter_view_in_browser_url': $url = $this->newsletterUrl->getViewInBrowserUrl( $newsletter, $wpUserPreview ? null : $subscriber, $queue, $wpUserPreview ); return self::processUrl($shortcodeDetails['action'], $url, $queue, $wpUserPreview); case 'subscription_re_engage_url': $url = $subscriptionUrlFactory->getReEngagementUrl($wpUserPreview ? null : $subscriber); return self::processUrl($shortcodeDetails['action'], $url, $queue, $wpUserPreview); default: $shortcode = self::getFullShortcode($shortcodeDetails['action']); $url = $this->wp->applyFilters( 'mailpoet_newsletter_shortcode_link', $shortcode, $newsletter, $subscriber, $queue, $shortcodeDetails['arguments'], $wpUserPreview ); return ($url !== $shortcode) ? self::processUrl($shortcodeDetails['action'], $url, $queue, $wpUserPreview) : null; } } public function processUrl($action, $url, ?SendingQueueEntity $queue, $wpUserPreview = false): string { if ($wpUserPreview) return $url; return ($queue && $this->trackingConfig->isEmailTrackingEnabled()) ? self::getFullShortcode($action) : $url; } public function processShortcodeAction( $shortcodeAction, NewsletterEntity $newsletter = null, SubscriberEntity $subscriber = null, SendingQueueEntity $queue = null, $wpUserPreview = false ): ?string { $subscriptionUrlFactory = SubscriptionUrlFactory::getInstance(); switch ($shortcodeAction) { case 'subscription_unsubscribe_url': $url = $subscriptionUrlFactory->getConfirmUnsubscribeUrl( $subscriber, $queue ? $queue->getId() : null ); break; case 'subscription_instant_unsubscribe_url': $url = $subscriptionUrlFactory->getUnsubscribeUrl( $subscriber, $queue ? $queue->getId() : null ); break; case 'subscription_manage_url': $url = $subscriptionUrlFactory->getManageUrl($subscriber); break; case 'newsletter_view_in_browser_url': $url = $this->newsletterUrl->getViewInBrowserUrl( $newsletter, $subscriber, $queue, false ); break; case 'subscription_re_engage_url': $url = $subscriptionUrlFactory->getReEngagementUrl($subscriber); break; default: $shortcode = self::getFullShortcode($shortcodeAction); $url = $this->wp->applyFilters( 'mailpoet_newsletter_shortcode_link', $shortcode, $newsletter, $subscriber, $queue, $wpUserPreview ); $url = ($url !== $shortcodeAction) ? $url : null; break; } return $url; } private function getFullShortcode($action): string { return sprintf('[link:%s]', $action); } } PKд[YCategories/Date.phpnu[ 'd', 'dordinal' => 'jS', 'dtext' => 'l', 'm' => 'm', 'mtext' => 'F', 'y' => 'Y', ]; $wp = new WPFunctions(); $date = $wp->currentTime('timestamp'); if ( ($newsletter instanceof NewsletterEntity) && ($newsletter->getSentAt() instanceof \DateTimeInterface) && ($newsletter->getStatus() === NewsletterEntity::STATUS_SENT) ) { $date = $newsletter->getSentAt()->getTimestamp(); } if (!empty($actionMapping[$shortcodeDetails['action']])) { return WPFunctions::get()->dateI18n($actionMapping[$shortcodeDetails['action']], $date); } return ($shortcodeDetails['action'] === 'custom' && $shortcodeDetails['action_argument'] === 'format') ? WPFunctions::get()->dateI18n($shortcodeDetails['action_argument_value'], $date) : null; } } PKд[Categories/index.phpnu[wp = $wp; } public function process( array $shortcodeDetails, NewsletterEntity $newsletter = null, SubscriberEntity $subscriber = null, SendingQueueEntity $queue = null, string $content = '', bool $wpUserPreview = false ): ?string { switch ($shortcodeDetails['action']) { case 'title': return $this->wp->getBloginfo('name'); case 'homepage_url': return $this->wp->getBloginfo('url'); case 'homepage_link': return sprintf( '%s', $this->wp->escUrl($this->wp->getBloginfo('url')), $this->wp->escHtml($this->wp->getBloginfo('name')) ); default: return null; } } } PKд[OYShortcodes.phpnu[PKд[ Rindex.phpnu[PKд[ΒNShortcodesHelper.phpnu[PKд[SL m0Categories/Newsletter.phpnu[PKд[%X X =Categories/Subscriber.phpnu[PKд[Zc#OO VICategories/CategoryInterface.phpnu[PKд[o$T2KCategories/Link.phpnu[PKд[YWaCategories/Date.phpnu[PKд[gCategories/index.phpnu[PKд[fRgCategories/Site.phpnu[PK Jl