init
This commit is contained in:
+21
@@ -0,0 +1,21 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\Newsletter\Shortcodes\Categories;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Entities\SendingQueueEntity;
|
||||
use MailPoet\Entities\SubscriberEntity;
|
||||
|
||||
interface CategoryInterface {
|
||||
public function process(
|
||||
array $shortcodeDetails,
|
||||
NewsletterEntity $newsletter = null,
|
||||
SubscriberEntity $subscriber = null,
|
||||
SendingQueueEntity $queue = null,
|
||||
string $content = '',
|
||||
bool $wpUserPreview = false
|
||||
): ?string;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\Newsletter\Shortcodes\Categories;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Entities\SendingQueueEntity;
|
||||
use MailPoet\Entities\SubscriberEntity;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
|
||||
class Date implements CategoryInterface {
|
||||
public function process(
|
||||
array $shortcodeDetails,
|
||||
NewsletterEntity $newsletter = null,
|
||||
SubscriberEntity $subscriber = null,
|
||||
SendingQueueEntity $queue = null,
|
||||
string $content = '',
|
||||
bool $wpUserPreview = false
|
||||
): ?string {
|
||||
$actionMapping = [
|
||||
'd' => '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 $wp->dateI18n($actionMapping[$shortcodeDetails['action']], $date);
|
||||
}
|
||||
return ($shortcodeDetails['action'] === 'custom' && $shortcodeDetails['action_argument'] === 'format') ?
|
||||
$wp->dateI18n($shortcodeDetails['action_argument_value'], $date) :
|
||||
null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\Newsletter\Shortcodes\Categories;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Entities\SendingQueueEntity;
|
||||
use MailPoet\Entities\SubscriberEntity;
|
||||
use MailPoet\Newsletter\Url as NewsletterUrl;
|
||||
use MailPoet\Settings\SettingsController;
|
||||
use MailPoet\Settings\TrackingConfig;
|
||||
use MailPoet\Subscription\SubscriptionUrlFactory;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
|
||||
class Link implements CategoryInterface {
|
||||
const CATEGORY_NAME = 'link';
|
||||
|
||||
/** @var SettingsController */
|
||||
private $settings;
|
||||
|
||||
/** @var NewsletterUrl */
|
||||
private $newsletterUrl;
|
||||
|
||||
/** @var WPFunctions */
|
||||
private $wp;
|
||||
|
||||
/** @var TrackingConfig */
|
||||
private $trackingConfig;
|
||||
|
||||
public function __construct(
|
||||
SettingsController $settings,
|
||||
NewsletterUrl $newsletterUrl,
|
||||
WPFunctions $wp,
|
||||
TrackingConfig $trackingConfig
|
||||
) {
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\Newsletter\Shortcodes\Categories;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Entities\SendingQueueEntity;
|
||||
use MailPoet\Entities\SubscriberEntity;
|
||||
use MailPoet\Newsletter\NewslettersRepository;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
use MailPoet\WP\Posts as WPPosts;
|
||||
|
||||
class Newsletter implements CategoryInterface {
|
||||
/** @var NewslettersRepository */
|
||||
private $newslettersRepository;
|
||||
|
||||
private WPFunctions $wp;
|
||||
|
||||
public function __construct(
|
||||
NewslettersRepository $newslettersRepository,
|
||||
WPFunctions $wp
|
||||
) {
|
||||
$this->newslettersRepository = $newslettersRepository;
|
||||
$this->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 '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;
|
||||
if ($latestPost) {
|
||||
// When a user with role author publish a post containing "&" in the title, the character is saved as "&" in the database.
|
||||
// Removing HTML tags from the title because
|
||||
$title = $this->wp->wpStripAllTags($latestPost['post_title']);
|
||||
// Decoding special characters such as & to &, etc.
|
||||
return htmlspecialchars_decode($title);
|
||||
}
|
||||
return 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;
|
||||
$this->wp->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',
|
||||
]
|
||||
);
|
||||
$this->wp->removeAction('pre_get_posts', [$this, 'ensureConsistentQueryType'], $filterPriority);
|
||||
return (!empty($posts->posts[0])) && ($posts->posts[0] instanceof \WP_Post) ?
|
||||
$posts->posts[0]->to_array() :
|
||||
false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\Newsletter\Shortcodes\Categories;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Entities\SendingQueueEntity;
|
||||
use MailPoet\Entities\SubscriberEntity;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
|
||||
class Site implements CategoryInterface {
|
||||
/** @var WPFunctions */
|
||||
private $wp;
|
||||
|
||||
public function __construct(
|
||||
WPFunctions $wp
|
||||
) {
|
||||
$this->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':
|
||||
// Decoding special characters such as & to &, etc.
|
||||
return htmlspecialchars_decode($this->wp->getBloginfo('name'));
|
||||
|
||||
case 'homepage_url':
|
||||
return $this->wp->getBloginfo('url');
|
||||
|
||||
case 'homepage_link':
|
||||
return sprintf(
|
||||
'<a target="_blank" href="%s">%s</a>',
|
||||
$this->wp->escUrl($this->wp->getBloginfo('url')),
|
||||
$this->wp->escHtml($this->wp->getBloginfo('name'))
|
||||
);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace MailPoet\Newsletter\Shortcodes\Categories;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Entities\SendingQueueEntity;
|
||||
use MailPoet\Entities\SubscriberCustomFieldEntity;
|
||||
use MailPoet\Entities\SubscriberEntity;
|
||||
use MailPoet\Subscribers\SubscriberCustomFieldRepository;
|
||||
use MailPoet\Subscribers\SubscribersRepository;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
|
||||
class Subscriber implements CategoryInterface {
|
||||
|
||||
/** @var SubscribersRepository */
|
||||
private $subscribersRepository;
|
||||
|
||||
/** @var SubscriberCustomFieldRepository */
|
||||
private $subscriberCustomFieldRepository;
|
||||
|
||||
public function __construct(
|
||||
SubscribersRepository $subscribersRepository,
|
||||
SubscriberCustomFieldRepository $subscriberCustomFieldRepository
|
||||
) {
|
||||
$this->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 && !empty($customField->getValue()))
|
||||
? htmlspecialchars($customField->getValue())
|
||||
: $defaultValue;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private function getSubscribersCountWithSubscribedStatus(): int {
|
||||
return $this->subscribersRepository->countBy(['status' => SubscriberEntity::STATUS_SUBSCRIBED, 'deletedAt' => null]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<?php
|
||||
@@ -0,0 +1,227 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\Newsletter\Shortcodes;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Entities\SendingQueueEntity;
|
||||
use MailPoet\Entities\SubscriberEntity;
|
||||
use MailPoet\Newsletter\Shortcodes\Categories\CategoryInterface;
|
||||
use MailPoet\Newsletter\Shortcodes\Categories\Date;
|
||||
use MailPoet\Newsletter\Shortcodes\Categories\Link;
|
||||
use MailPoet\Newsletter\Shortcodes\Categories\Newsletter;
|
||||
use MailPoet\Newsletter\Shortcodes\Categories\Site;
|
||||
use MailPoet\Newsletter\Shortcodes\Categories\Subscriber;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
|
||||
class Shortcodes {
|
||||
/** @var NewsletterEntity|null */
|
||||
private $newsletter;
|
||||
|
||||
/** @var SubscriberEntity|null */
|
||||
private $subscriber;
|
||||
|
||||
/** @var SendingQueueEntity|null */
|
||||
private $queue;
|
||||
|
||||
/** @var bool */
|
||||
private $wpUserPreview = false;
|
||||
|
||||
/** @var Date */
|
||||
private $dateCategory;
|
||||
|
||||
/** @var Link */
|
||||
private $linkCategory;
|
||||
|
||||
/** @var Newsletter */
|
||||
private $newsletterCategory;
|
||||
|
||||
/** @var Subscriber */
|
||||
private $subscriberCategory;
|
||||
|
||||
/** @var Site */
|
||||
private $siteCategory;
|
||||
|
||||
/** @var WPFunctions */
|
||||
private $wp;
|
||||
|
||||
public function __construct(
|
||||
Date $dateCategory,
|
||||
Link $linkCategory,
|
||||
Newsletter $newsletterCategory,
|
||||
Subscriber $subscriberCategory,
|
||||
Site $siteCategory,
|
||||
WPFunctions $wp
|
||||
) {
|
||||
$this->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<category>\w+)?:(?P<action>\w+)(?:.*?\|.*?(?P<argument>\w+):(?P<argument_value>.*?))?\]/',
|
||||
$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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\Newsletter\Shortcodes;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\CustomFields\CustomFieldsRepository;
|
||||
use MailPoet\Entities\NewsletterLinkEntity;
|
||||
|
||||
class ShortcodesHelper {
|
||||
/** @var CustomFieldsRepository */
|
||||
private $customFieldsRepository;
|
||||
|
||||
public function __construct(
|
||||
CustomFieldsRepository $customFieldsRepository
|
||||
) {
|
||||
$this->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(
|
||||
'<a target="_blank" href="%s">%s</a>',
|
||||
NewsletterLinkEntity::UNSUBSCRIBE_LINK_SHORT_CODE,
|
||||
__('Unsubscribe', 'mailpoet')
|
||||
),
|
||||
],
|
||||
[
|
||||
'text' => __('Edit subscription page link', 'mailpoet'),
|
||||
'shortcode' => sprintf(
|
||||
'<a target="_blank" href="%s">%s</a>',
|
||||
'[link:subscription_manage_url]',
|
||||
__('Manage subscription', 'mailpoet')
|
||||
),
|
||||
],
|
||||
[
|
||||
'text' => __('View in browser link', 'mailpoet'),
|
||||
'shortcode' => sprintf(
|
||||
'<a target="_blank" href="%s">%s</a>',
|
||||
'[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(
|
||||
'<a target="_blank" href="%s">%s</a>',
|
||||
'[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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<?php
|
||||
Reference in New Issue
Block a user