- Merges all shortcode links under one category

- Adds filter hooks for custom links
- Renames old shortcodes in the editor and elsewhere
This commit is contained in:
Vlad
2016-04-30 22:02:22 -04:00
parent fa9d32c230
commit 5bad682879
19 changed files with 334 additions and 138 deletions

View File

@@ -16,7 +16,7 @@ define([
defaults: function() {
return this._getDefaults({
type: 'footer',
text: '<a href="[subscription:unsubscribe_url]">Unsubscribe</a> | <a href="[subscription:manage_url]">Manage subscription</a><br /><b>Add your postal address here!</b>',
text: '<a href="[link:subscription_unsubscribe_url]">Unsubscribe</a> | <a href="[link:subscription_manage_url]">Manage subscription</a><br /><b>Add your postal address here!</b>',
styles: {
block: {
backgroundColor: 'transparent',

View File

@@ -16,7 +16,7 @@ define([
defaults: function() {
return this._getDefaults({
type: 'header',
text: 'Display problems? <a href="[newsletter:view_in_browser_url]">View it in your browser</a>',
text: 'Display problems? <a href="[link:newsletter_view_in_browser_url]">View it in your browser</a>',
styles: {
block: {
backgroundColor: 'transparent',

View File

@@ -284,7 +284,7 @@ define([
}
if (App.getConfig().get('validation.validateUnsubscribeLinkPresent') &&
JSON.stringify(jsonObject).indexOf("[subscription:unsubscribe_url]") < 0) {
JSON.stringify(jsonObject).indexOf("[link:subscription_unsubscribe_url]") < 0) {
this.showValidationError(MailPoet.I18n.t('unsubscribeLinkMissing'));
return;
}

View File

@@ -142,7 +142,7 @@ class BlankTemplate {
"blocks" => array(
array(
"type" => "footer",
"text" => "<a href=\"[subscription:unsubscribe_url]\">Unsubscribe</a> | <a href=\"[subscription:manage_url]\">Manage subscription</a><br /><b>Add your postal address here!</b>",
"text" => "<a href=\"[link:subscription_unsubscribe_url]\">Unsubscribe</a> | <a href=\"[link:subscription_manage_url]\">Manage subscription</a><br /><b>Add your postal address here!</b>",
"styles" => array(
"block" => array(
"backgroundColor" => "transparent"

View File

@@ -46,7 +46,7 @@ class FranksRoastHouseTemplate {
"blocks" => array(
array(
"type" => "header",
"text" => __("Display problems?&nbsp;<a href=\"[newsletter:view_in_browser_url]\">View it in your browser</a>"),
"text" => __("Display problems?&nbsp;<a href=\"[link:newsletter_view_in_browser_url]\">View it in your browser</a>"),
"styles" => array(
"block" => array(
"backgroundColor" => "#ccc6c6"
@@ -280,7 +280,7 @@ class FranksRoastHouseTemplate {
"blocks" => array(
array(
"type" => "footer",
"text" => __("<p><a href=\"[subscription:unsubscribe_url]\">Unsubscribe</a> | <a href=\"[subscription:manage_url]\">Manage subscription</a><br />12345 MailPoet Drive, EmailVille, 76543</p>"),
"text" => __("<p><a href=\"[link:subscription_unsubscribe_url]\">Unsubscribe</a> | <a href=\"[link:subscription_manage_url]\">Manage subscription</a><br />12345 MailPoet Drive, EmailVille, 76543</p>"),
"styles" => array(
"block" => array(
"backgroundColor" => "#a9a7a7"

View File

@@ -242,7 +242,7 @@ class PostNotificationsBlankTemplate {
"blocks" => array(
array(
"type" => "footer",
"text" => __("<a href=\"[subscription:unsubscribe_url]\">Unsubscribe</a> | <a href=\"[subscription:manage_url]\">Manage subscription</a><br /><b>Add your postal address here!</b>"),
"text" => __("<a href=\"[link:subscription_unsubscribe_url]\">Unsubscribe</a> | <a href=\"[link:subscription_manage_url]\">Manage subscription</a><br /><b>Add your postal address here!</b>"),
"styles" => array(
"block" => array(
"backgroundColor" => "transparent"

View File

@@ -46,7 +46,7 @@ class WelcomeTemplate {
"blocks" => array(
array(
"type" => "header",
"text" => __("Display problems?&nbsp;<a href=\"[newsletter:view_in_browser_url]\">View it in your browser</a>"),
"text" => __("Display problems?&nbsp;<a href=\"[link:newsletter_view_in_browser_url]\">View it in your browser</a>"),
"styles" => array(
"block" => array(
"backgroundColor" => "transparent"
@@ -224,7 +224,7 @@ class WelcomeTemplate {
"blocks" => array(
array(
"type" => "footer",
"text" => __("<a href=\"[subscription:unsubscribe_url]\">Unsubscribe</a> | <a href=\"[subscription:manage_url]\">Manage subscription</a><br /><b>Add your postal address here!</b>"),
"text" => __("<a href=\"[link:subscription_unsubscribe_url]\">Unsubscribe</a> | <a href=\"[link:subscription_manage_url]\">Manage subscription</a><br /><b>Add your postal address here!</b>"),
"styles" => array(
"block" => array(
"backgroundColor" => "transparent"

View File

@@ -5,7 +5,9 @@ use MailPoet\Newsletter\Shortcodes\Shortcodes;
use MailPoet\Util\Security;
class Links {
static function extract($text) {
const DATA_TAG = '[mailpoet_data]';
static function extract($text, $process_link_shortcodes = false) {
// adopted from WP's wp_extract_urls() function & modified to work on hrefs
# match href=' or href="
$regex = '#(?:href.*?=.*?)(["\']?)('
@@ -18,22 +20,29 @@ class Links {
. '(?:'
. '\([\w\d]+\)|'
. '(?:'
. '[^`!()\[\]{};:\'".,<>«»“”‘’\s]|'
. '[^`!()\[\]{}:;\'".,<>«»“”‘’\s]|'
. '(?:[:]\d+)?/?'
. ')+'
. ')'
. ')\\1#';
$shortcodes = new Shortcodes();
// extract shortcodes with [url:*] format
$shortcodes = $shortcodes->extract($text, $limit = array('link'));
// extract links
preg_match_all($regex, $text, $links);
$shortcodes = new Shortcodes();;
$shortcodes = $shortcodes->extract($text, $limit = array('subscription'));
return array_merge(
array_unique($links[2]),
$shortcodes
);
}
static function replace($text, $links = false) {
$links = ($links) ? $links : self::extract($text);
static function process($text, $links = false, $process_link_shortcodes = false) {
if($process_link_shortcodes) {
// process shortcodes with [url:*] format
$shortcodes = new Shortcodes();
$text = $shortcodes->replace($text, $limit = array('link'));
}
$links = ($links) ? $links : self::extract($text, $process_link_shortcodes);
$processed_links = array();
foreach($links as $link) {
$hash = Security::generateRandomString(5);
@@ -42,13 +51,25 @@ class Links {
'url' => $link
);
$encoded_link = sprintf(
'%s/?mailpoet&endpoint=track&action=click&data=%s',
'%s/?mailpoet&endpoint=track&action=click&data=%s-%s',
home_url(),
'[mailpoet_data]-'.$hash
self::DATA_TAG,
$hash
);
$link_regex = '/' . preg_quote($link, '/') . '/';
$text = preg_replace($link_regex, $encoded_link, $text);
}
return array($text, $processed_links);
return array(
$text,
$processed_links
);
}
static function replaceSubscriberData($newsletter_id, $subscriber_id, $queue_id, $content) {
return str_replace(
'[mailpoet_data]',
sprintf('%s-%s-%s', $newsletter_id, $subscriber_id, $queue_id),
$content
);
}
}

View File

@@ -0,0 +1,168 @@
<?php
namespace MailPoet\Newsletter\Shortcodes\Categories;
use MailPoet\Models\Setting;
use MailPoet\Models\Subscriber;
use MailPoet\Statistics\Track\Unsubscribes;
use MailPoet\Subscription\Url as SubscriptionUrl;
class Link {
/*
{
text: '<%= __('Unsubscribe') %>',-
shortcode: 'subscription:unsubscribe',
},
{
text: '<%= __('Manage subscription') %>',
shortcode: 'subscription:manage',
},
{
text: '<%= __('View in browser link') %>',
shortcode: 'newsletter:view_in_browser',
}
*/
static function process($action,
$default_value = false,
$newsletter,
$subscriber,
$queue = false
) {
switch($action) {
case 'subscription_unsubscribe':
$action = 'subscription_unsubscribe_url';
$url = self::processUrl(
$action,
esc_attr(SubscriptionUrl::getUnsubscribeUrl($subscriber))
);
return sprintf(
'<a target="_blank" href="%s">%s</a>',
$url,
__('Unsubscribe')
);
break;
case 'subscription_unsubscribe_url':
return self::processUrl(
$action,
SubscriptionUrl::getUnsubscribeUrl($subscriber)
);
break;
case 'subscription_manage':
$url = self::processUrl(
$action = 'subscription_manage_url',
esc_attr(SubscriptionUrl::getManageUrl($subscriber))
);
return sprintf(
'<a target="_blank" href="%s">%s</a>',
$url,
__('Manage subscription')
);
break;
case 'subscription_manage_url':
return self::processUrl(
$action,
SubscriptionUrl::getManageUrl($subscriber)
);
break;
case 'newsletter_view_in_browser':
$action = 'view_in_browser_url';
$url = esc_attr(self::getViewInBrowserUrl($newsletter, $subscriber, $queue));
$url = self::processUrl($action, $url);
return sprintf(
'<a target="_blank" href="%s">%s</a>',
$url,
__('View in your browser')
);
break;
case 'newsletter_view_in_browser_url':
$url = self::getViewInBrowserUrl($newsletter, $subscriber, $queue);
return self::processUrl($action, $url);
break;
default:
$url = apply_filters(
'mailpoet_shortcode_link',
$action,
$newsletter,
$subscriber,
$queue
);
return ($url !== $action) ?
self::processUrl($action, $url) :
false;
break;
}
}
static function getViewInBrowserUrl(
$newsletter,
$subscriber = false,
$queue = false
) {
$data = array(
'newsletter' => (isset($newsletter['id'])) ?
$newsletter['id'] :
$newsletter,
'subscriber' => (isset($subscriber['id'])) ?
$subscriber['id'] :
$subscriber,
'subscriber_token' => (isset($subscriber['id'])) ?
Subscriber::generateToken($subscriber['email']) :
false,
'queue' => (isset($queue['id'])) ?
$queue['id'] :
$queue
);
$data = rtrim(base64_encode(serialize($data)), '=');
return home_url() . '/?mailpoet&endpoint=view_in_browser&data=' . $data;
}
static function processUrl($action, $url) {
// when invoked by the ViewInBrowser class and tracking is enabled,
// do nto return shortcode
foreach(debug_backtrace() as $trace) {
if(isset($trace['class']) && preg_match('/ViewInBrowser/', $trace['class'])) {
return $url;
}
}
return ((boolean) Setting::getValue('tracking.enabled')) ?
'[link:' . $action . ']' :
$url;
}
static function processShortcodeAction(
$shortcode_action, $newsletter, $subscriber, $queue
) {
switch($shortcode_action) {
case 'subscription_unsubscribe_url':
// track unsubscribe event
if((boolean) Setting::getValue('tracking.enabled')) {
$unsubscribe = new Unsubscribes();
$unsubscribe->track($subscriber['id'], $queue['id'], $newsletter['id']);
}
$url = SubscriptionUrl::getUnsubscribeUrl($subscriber);
break;
case 'subscription_manage_url':
$url = SubscriptionUrl::getManageUrl($subscriber);
break;
case 'view_in_browser_url':
$url = Link::getViewInBrowserUrl($newsletter, $subscriber, $queue);
break;
default:
$url = apply_filters(
'mailpoet_shortcode_link',
$shortcode_action,
$newsletter,
$subscriber,
$queue
);
$url = ($url !== $shortcode_action) ? $url : false;
break;
}
return $url;
}
}

View File

@@ -2,6 +2,7 @@
namespace MailPoet\Newsletter\Shortcodes\Categories;
use MailPoet\Models\SendingQueue;
use MailPoet\Newsletter\Shortcodes\ShortcodesHelper;
require_once( ABSPATH . "wp-includes/pluggable.php" );
@@ -26,18 +27,15 @@ class Newsletter {
{
text: '<%= __('Issue number') %>',
shortcode: 'newsletter:number',
},
{
text: '<%= __('View in browser link') %>',
shortcode: 'newsletter:view_in_browser',
}
*/
static function process($action,
$default_value = false,
$newsletter, $subscriber = false, $text) {
if(is_object($newsletter)) {
$newsletter = $newsletter->asArray();
}
$newsletter,
$subscriber,
$queue = false,
$text
) {
switch($action) {
case 'subject':
return ($newsletter) ? $newsletter['subject'] : false;
@@ -63,14 +61,6 @@ class Newsletter {
return ++$sent_newsletters;
break;
case 'view_in_browser':
return '<a href="#TODO">'.__('View in your browser').'</a>';
break;
case 'view_in_browser_url':
return '#TODO';
break;
default:
return false;
break;

View File

@@ -1,69 +0,0 @@
<?php
namespace MailPoet\Newsletter\Shortcodes\Categories;
use MailPoet\Models\Setting;
use MailPoet\Subscription\Url as SubscriptionUrl;
class Subscription {
/*
{
text: '<%= __('Unsubscribe') %>',-
shortcode: 'subscription:unsubscribe',
},
{
text: '<%= __('Manage subscriptions') %>',
shortcode: 'subscription:manage',
},
*/
static function process(
$action,
$default_value = false,
$newsletter = false,
$subscriber = false,
$text = false,
$shortcode
) {
switch($action) {
case 'unsubscribe':
return '<a target="_blank" href="'.
self::getShortcodeUrl(
$shortcode,
esc_attr(SubscriptionUrl::getUnsubscribeUrl($subscriber))
)
.'">'.__('Unsubscribe').'</a>';
break;
case 'unsubscribe_url':
return self::getShortcodeUrl(
$shortcode,
SubscriptionUrl::getUnsubscribeUrl($subscriber)
);
break;
case 'manage':
return '<a target="_blank" href="'.
self::getShortcodeUrl(
$shortcode,
esc_attr(SubscriptionUrl::getManageUrl($subscriber))
)
.'">'.__('Manage subscription').'</a>';
break;
case 'manage_url':
return self::getShortcodeUrl(
$shortcode,
SubscriptionUrl::getManageUrl($subscriber)
);
break;
default:
return false;
break;
}
}
static function getShortcodeUrl($shortcode, $url) {
return ((boolean) Setting::getValue('tracking.enabled')) ?
$shortcode :
$url;
}
}

View File

@@ -28,10 +28,12 @@ class User {
shortcode: 'user:count',
}
*/
static function process($action, $default_value, $newsletter = false, $subscriber) {
if(is_object($subscriber)) {
$subscriber = $subscriber->asArray();
}
static function process(
$action,
$default_value,
$newsletter = false,
$subscriber
) {
switch($action) {
case 'firstname':
return ($subscriber) ? $subscriber['first_name'] : $default_value;

View File

@@ -4,13 +4,22 @@ namespace MailPoet\Newsletter\Shortcodes;
class Shortcodes {
public $newsletter;
public $subscriber;
public $queue;
function __construct(
$newsletter = false,
$subscriber = false
$subscriber = false,
$queue = false
) {
$this->newsletter = $newsletter;
$this->subscriber = $subscriber;
$this->newsletter = (is_object($newsletter)) ?
$newsletter->toArray() :
$newsletter;
$this->subscriber = (is_object($subscriber)) ?
$subscriber->toArray() :
$subscriber;
$this->queue = (is_object($queue)) ?
$queue->toArray() :
$queue;
}
function extract($text, $limit = false) {
@@ -48,6 +57,7 @@ class Shortcodes {
$shortcode_default_value,
$this->newsletter,
$this->subscriber,
$this->queue,
$text,
$shortcode
);

View File

@@ -0,0 +1,61 @@
<?php
namespace MailPoet\Newsletter\Links;
use MailPoet\Newsletter\Shortcodes\Shortcodes;
use MailPoet\Util\Security;
class Links {
static function extract($text, $process_link_shortcodes = false) {
// adopted from WP's wp_extract_urls() function & modified to work on hrefs
# match href=' or href="
$regex = '#(?:href.*?=.*?)(["\']?)('
# match http://
. '(?:([\w-]+:)?//?)'
# match everything except for special characters # until .
. '[^\s()<>]+'
. '[.]'
# conditionally match everything except for special characters after .
. '(?:'
. '\([\w\d]+\)|'
. '(?:'
. '[^`!()\[\]{}:\'".,<>«»“”‘’\s]|'
. '(?:[:]\d+)?/?'
. ')+'
. ')'
. ')\\1#';
$shortcodes = new Shortcodes();
// extract shortcodes with [url:*] format
$shortcodes = $shortcodes->extract($text, $limit = array('link'));
// extract links
preg_match_all($regex, $text, $links);
return array_merge(
array_unique($links[2]),
$shortcodes
);
}
static function replace($text, $links = false, $process_link_shortcodes = false) {
if ($process_link_shortcodes) {
// process shortcodes with [url:*] format
$shortcodes = new Shortcodes();
$text = $shortcodes->replace($text, $limit = array('link'));
}
$links = ($links) ? $links : self::extract($text, $process_link_shortcodes);
$processed_links = array();
foreach($links as $link) {
$hash = Security::generateRandomString(5);
$processed_links[] = array(
'hash' => $hash,
'url' => $link
);
$encoded_link = sprintf(
'%s/?mailpoet&endpoint=track&action=click&data=%s',
home_url(),
'[mailpoet_data]-' . $hash
);
$link_regex = '/' . preg_quote($link, '/') . '/';
$text = preg_replace($link_regex, $encoded_link, $text);
}
return array($text, $processed_links);
}
}

View File

@@ -30,9 +30,8 @@ class Clicks {
->where('queue_id', $queue_id)
->findOne();
if(!$statistics) {
// track open action in case it did not register
$opens = new Opens($url, $display_image = false);
$opens->track();
// track open event in case it did not register
$this->trackOpen($url);
$statistics = StatisticsClicks::create();
$statistics->newsletter_id = $newsletter_id;
$statistics->link_id = $link->id;
@@ -44,28 +43,41 @@ class Clicks {
$statistics->count++;
$statistics->save();
}
$url = (preg_match('/\[subscription:.*?\]/', $link->url)) ?
$this->processSubscriptionUrl($link->url, $subscriber, $queue_id, $newsletter_id) :
$is_this_subscription = (preg_match('/\[link:(?P<action>.*?)\]/', $link->url, $action));
$url = ($is_this_subscription) ?
$this->getSubscriptionUrl($link->url, $subscriber, $queue_id, $newsletter_id) :
$link->url;
header('Location: ' . $url, true, 302);
exit;
}
function processSubscriptionUrl($url, $subscriber, $queue_id, $newsletter_id) {
preg_match('/\[subscription:(.*?)\]/', $url, $match);
$action = $match[1];
if(preg_match('/unsubscribe/', $action)) {
function getSubscriptionUrl(
$subscription_action, $subscriber, $queue_id, $newsletter_id
) {
if(!isset($subscription_action['action'])) self::abort();
switch($subscription_action['action']) {
case 'unsubscribe':
// track unsubscribe event
$this->trackUnsubscribe($subscriber->id, $queue_id, $newsletter_id);
$url = SubscriptionUrl::getUnsubscribeUrl($subscriber);
// track unsubscribe action
$unsubscribes = new Unsubscribes();
$unsubscribes->track($subscriber->id, $queue_id, $newsletter_id);
}
if(preg_match('/manage/', $action)) {
break;
case 'manage':
$url = SubscriptionUrl::getManageUrl($subscriber);
break;
}
return $url;
}
function trackUnsubscribe($subscriber, $queue, $newsletter) {
$unsubscribe = new Unsubscribes();
$unsubscribe->track($subscriber, $queue, $newsletter);
}
function trackOpen($url) {
$open = new Opens($url, $display_image = false);
$open->track();
}
private function abort() {
header('HTTP/1.0 404 Not Found');
exit;

View File

@@ -45,7 +45,7 @@ class Url {
$params = array(
'endpoint=subscription',
'action='.$action,
'data='.base64_encode(serialize($data))
'data='.rtrim(base64_encode(serialize($data)), '=')
);
// add parameters

View File

@@ -22,7 +22,7 @@
"blocks": [
{
"type": "header",
"text": "Display problems?&nbsp;<a href=\"[newsletter:view_in_browser_url]\">View it in your browser</a>",
"text": "Display problems?&nbsp;<a href=\"[link:newsletter_view_in_browser_url]\">View it in your browser</a>",
"styles": {
"block": {
"backgroundColor": "#ffffff"
@@ -1154,7 +1154,7 @@
"blocks": [
{
"type": "footer",
"text": "<p>You are receiving this email because you opted in on our website. <a href=\"[subscription:manage_url]\">Update your preferences</a> or <a href=\"[subscription:unsubscribe_url]\">Unsubscribe</a><a href=\"[subscription:manage_url]\"> </a><br />123 Maple Avenue<br />93102<br />Oakland, California </p>",
"text": "<p>You are receiving this email because you opted in on our website. <a href=\"[link:subscription_manage_url]\">Update your preferences</a> or <a href=\"[link:subscription_unsubscribe_url]\">Unsubscribe</a><a href=\"[link:subscription_manage_url]\"> </a><br />123 Maple Avenue<br />93102<br />Oakland, California </p>",
"styles": {
"block": {
"backgroundColor": "transparent"

View File

@@ -50,9 +50,9 @@ class ShortcodesTest extends MailPoetTest {
Month text: [date:mtext].
Year: [date:y]
You can unsubscribe here: [subscription:unsubscribe_url].
Manage your subscription here: [subscription:manage_url].
View this newsletter in browser: [newsletter:view_in_browser_url].";
You can unsubscribe here: [link:subscription_unsubscribe_url].
Manage your subscription here: [link:subscription_manage_url].
View this newsletter in browser: [link:newsletter_view_in_browser_url].";
$this->shortcodes_object = new MailPoet\Newsletter\Shortcodes\Shortcodes(
$this->newsletter,
$this->subscriber

View File

@@ -320,6 +320,7 @@
'customFieldsWindowTitle': __('Select a shortcode'),
'unsubscribeLinkMissing': __('All newsletters must include an "unsubscribe" link. Add a footer widget to your newsletter to continue.'),
'newsletterPreviewEmailMissing': __('Please enter an email where newsletter preview should be sent to.'),
'newsletterPreviewFailed': __('Preview failed. Pleae check console log.'),
'newsletterPreviewSent': __('Newsletter preview email has been successfully sent!'),
'newsletterPreviewFailedToSend': __('Attempt to send a newsletter preview email failed. Please verify that your sending method is configured correctly try again.'),
'templateNameMissing': __('Please add a template name'),
@@ -1024,7 +1025,7 @@
},
},
footer: {
text: '<a href="[subscription:unsubscribe_url]"><%= __('Unsubscribe') %></a> | <a href="[subscription:manage_url]"><%= __('Manage subscription') %></a><br /><b><%= __('Add your postal address here!') %></b>',
text: '<a href="[link:subscription_unsubscribe_url]"><%= __('Unsubscribe') %></a> | <a href="[link:subscription_manage_url]"><%= __('Manage subscription') %></a><br /><b><%= __('Add your postal address here!') %></b>',
styles: {
block: {
backgroundColor: 'transparent',
@@ -1149,7 +1150,7 @@
},
header: {
text: '<%= __('Display problems?') %>&nbsp;'+
'<a href="[newsletter:view_in_browser_url]"><%= __('View it in your browser') %></a>',
'<a href="[link:newsletter_view_in_browser_url]"><%= __('View it in your browser') %></a>',
styles: {
block: {
backgroundColor: 'transparent',
@@ -1239,15 +1240,15 @@
'<%= __('Links') %>': [
{
text: '<%= __('Unsubscribe link') %>',
shortcode: 'subscription:unsubscribe',
shortcode: 'link:subscription_unsubscribe',
},
{
text: '<%= __('Edit subscription page link') %>',
shortcode: 'subscription:manage',
shortcode: 'link:subscription_manage',
},
{
text: '<%= __('View in browser link') %>',
shortcode: 'newsletter:view_in_browser',
shortcode: 'link:newsletter_view_in_browser',
}
],
<% if customFields %>