diff --git a/lib/Cron/Workers/SendingQueue.php b/lib/Cron/Workers/SendingQueue.php index 36f1b43e4a..a996c46c27 100644 --- a/lib/Cron/Workers/SendingQueue.php +++ b/lib/Cron/Workers/SendingQueue.php @@ -6,6 +6,7 @@ use MailPoet\Models\Newsletter; use MailPoet\Models\NewsletterStatistics; use MailPoet\Models\Subscriber; use MailPoet\Newsletter\Renderer\Renderer; +use MailPoet\Newsletter\Shortcodes\Shortcodes; if(!defined('ABSPATH')) exit; @@ -19,17 +20,12 @@ class SendingQueue { function process() { // TODO: implement mailer sending frequency limits foreach($this->getQueues() as $queue) { - $newsletter = Newsletter::findOne($queue->newsletter_id); - - if($newsletter === false) { - //TODO: delete queue item if newsletter doesn't exist + $newsletter = Newsletter::findOne($queue->newsletter_id) + ->asArray(); + if(!$newsletter) { continue; - } else { - $newsletter = $newsletter->asArray(); - } - + }; $mailer = $this->configureMailerForNewsletter($newsletter); - $newsletter = $this->renderNewsletter($newsletter); $subscribers = json_decode($queue->subscribers, true); $subscribers_to_process = $subscribers['to_process']; if(!isset($subscribers['processed'])) $subscribers['processed'] = array(); @@ -41,7 +37,7 @@ class SendingQueue { $this->checkExecutionTimer(); $result = $this->sendNewsletter( $mailer, - $this->processNewsletter($newsletter), + $this->processNewsletter($newsletter, $db_subscriber), $db_subscriber); if($result) { $this->updateStatistics($newsletter['id'], $db_subscriber['id'], $queue->id); @@ -55,9 +51,11 @@ class SendingQueue { } } - function processNewsletter($newsletter) { - // TODO: replace shortcodes - return $newsletter; + function processNewsletter($newsletter, $subscriber) { + $rendered_newsletter = $this->renderNewsletter($newsletter); + $shortcodes = new Shortcodes($rendered_newsletter, $newsletter, $subscriber); + $processed_newsletter = $shortcodes->replace(); + return $processed_newsletter; } function sendNewsletter($mailer, $newsletter, $subscriber) { diff --git a/lib/Newsletter/Shortcodes/Categories/Date.php b/lib/Newsletter/Shortcodes/Categories/Date.php new file mode 100644 index 0000000000..f066c1d510 --- /dev/null +++ b/lib/Newsletter/Shortcodes/Categories/Date.php @@ -0,0 +1,43 @@ +', + shortcode: 'date:d', + }, + { + text: '<%= __('Current day of the month in ordinal, ie. 2nd, 3rd, etc.') %>', + shortcode: 'date:dordinal', + }, + { + text: '<%= __('Full name of current day') %>', + shortcode: 'date:dtext', + }, + { + text: '<%= __('Current month number') %>', + shortcode: 'date:m', + }, + { + text: '<%= __('Full name of current month') %>', + shortcode: 'date:mtext', + }, + { + text: '<%= __('Year') %>', + shortcode: 'date:y', + } + */ + static function process($action) { + $date = new \DateTime('now'); + $actions = array( + 'd' => $date->format('d'), + 'dordinal' => $date->format('dS'), + 'dtext' => $date->format('D'), + 'm' => $date->format('m'), + 'mtext' => $date->format('F'), + 'y' => $date->format('Y') + ); + return (isset($actions[$action])) ? $actions[$action] : false; + } +} \ No newline at end of file diff --git a/lib/Newsletter/Shortcodes/Categories/Link.php b/lib/Newsletter/Shortcodes/Categories/Link.php new file mode 100644 index 0000000000..6a7b2ddb89 --- /dev/null +++ b/lib/Newsletter/Shortcodes/Categories/Link.php @@ -0,0 +1,30 @@ +', + shortcode: 'global:unsubscribe', + }, + { + text: '<%= __('Edit subscription page link') %>', + shortcode: 'global:manage', + }, + { + text: '<%= __('View in browser link') %>', + shortcode: 'global:browser', + } + */ + static function process($action) { + // TODO: implement + $actions = array( + 'unsubscribe' => '', + 'manage' => '', + 'browser' => '' + ); + return (isset($actions[$action])) ? $actions[$action] : false; + } +} \ No newline at end of file diff --git a/lib/Newsletter/Shortcodes/Categories/Newsletter.php b/lib/Newsletter/Shortcodes/Categories/Newsletter.php new file mode 100644 index 0000000000..d4dae921c9 --- /dev/null +++ b/lib/Newsletter/Shortcodes/Categories/Newsletter.php @@ -0,0 +1,43 @@ +',- + shortcode: 'newsletter:subject', + }, + { + text: '<%= __('Total number of posts or pages') %>', + shortcode: 'newsletter:total', + }, + { + text: '<%= __('Latest post title') %>', + shortcode: 'newsletter:post_title', + }, + { + text: '<%= __('Issue number') %>', + shortcode: 'newsletter:number', + } + */ + static function process($action, $default_value = false, $newsletter) { + if(is_object($newsletter)) { + $newsletter = $newsletter->asArray(); + } + switch($action) { + case 'subject': + return ($newsletter) ? $newsletter['subject'] : false; + case 'total': + $posts = wp_count_posts(); + return $posts->publish; + case 'post_title': + $post = wp_get_recent_posts(array('numberposts' => 1)); + return (isset($post[0])) ? $post[0]['post_title'] : false; + case 'number': + // TODO: implement + return; + default: + return false; + } + } +} \ No newline at end of file diff --git a/lib/Newsletter/Shortcodes/Categories/User.php b/lib/Newsletter/Shortcodes/Categories/User.php new file mode 100644 index 0000000000..cf547cabf2 --- /dev/null +++ b/lib/Newsletter/Shortcodes/Categories/User.php @@ -0,0 +1,54 @@ +', + shortcode: 'user:firstname | default:reader', + }, + { + text: '<%= __('Last Name') %>', + shortcode: 'user:lastname | default:reader', + }, + { + text: '<%= __('Email Address') %>', + shortcode: 'user:email', + }, + { + text: '<%= __('Wordpress user display name') %>', + shortcode: 'user:displayname | default:member', + }, + { + text: '<%= __('Total of subscribers') %>', + shortcode: 'user:count', + } + */ + static function process($action, $default_value, $newsletter = false, $subscriber) { + if(is_object($subscriber)) { + $subscriber = $subscriber->asArray(); + } + switch($action) { + case 'firstname': + return ($subscriber) ? $subscriber['first_name'] : $default_value; + case 'lastname': + return ($subscriber) ? $subscriber['last_name'] : $default_value; + case 'email': + return ($subscriber) ? $subscriber['email'] : false; + case 'displayname': + if($subscriber && $subscriber['wp_user_id']) { + $wp_user = get_userdata($subscriber['wp_user_id']); + return $wp_user->user_login; + }; + return $default_value; + case 'count': + return Subscriber::count(); + default: + return false; + } + } +} \ No newline at end of file diff --git a/lib/Newsletter/Shortcodes/Shortcodes.php b/lib/Newsletter/Shortcodes/Shortcodes.php new file mode 100644 index 0000000000..8fa18ec402 --- /dev/null +++ b/lib/Newsletter/Shortcodes/Shortcodes.php @@ -0,0 +1,53 @@ +rendered_newsletter = $rendered_newsletter; + $this->newsletter = $newsletter; + $this->subscriber = $subscriber; + } + + function extract() { + preg_match_all('/\[(?:\w+):.*?\]/', $this->rendered_newsletter, $shortcodes); + return array_unique($shortcodes[0]); + } + + function process($shortcodes) { + $processed_shortcodes = array_map( + function ($shortcode) { + // TODO: discuss renaming "global". It is a reserved name in PHP. + if($shortcode === 'global') $shortcode = 'link'; + preg_match( + '/\[(?P\w+):(?P\w+)(?:.*?default:(?P.*?))?\]/', + $shortcode, + $shortcode_details + ); + $shortcode_class = + __NAMESPACE__ . '\\Categories\\' . ucfirst($shortcode_details['type']); + if(!class_exists($shortcode_class)) return false; + return $shortcode_class::process( + $shortcode_details['action'], + isset($shortcode_details['default']) + ? $shortcode_details['default'] : false, + $this->newsletter, + $this->subscriber + ); + }, $shortcodes); + return array_filter($processed_shortcodes); + } + + function replace() { + $shortcodes = $this->extract($this->rendered_newsletter); + $processed_shortcodes = $this->process($shortcodes); + $shortcodes = array_intersect_key($shortcodes, $processed_shortcodes); + return str_replace($shortcodes, $processed_shortcodes, $this->rendered_newsletter); + } +} \ No newline at end of file diff --git a/lib/Router/Newsletters.php b/lib/Router/Newsletters.php index 5d93cfb7ec..7956d16f6a 100644 --- a/lib/Router/Newsletters.php +++ b/lib/Router/Newsletters.php @@ -1,6 +1,7 @@ render(); - wp_send_json(array('rendered_body' => $rendered_body['html'])); + $shortcodes = new \MailPoet\Newsletter\Shortcodes\Shortcodes( + $rendered_body, + $data + ); + $rendered_body = $shortcodes->replace(); + wp_send_json(array('rendered_body' => $rendered_body)); } function sendPreview($data = array()) { @@ -236,10 +242,23 @@ class Newsletters { $newsletter = $newsletter->asArray(); $renderer = new Renderer($newsletter); - $newsletter['body'] = $renderer->render(); + $rendered_body = $renderer->render(); + $shortcodes = new \MailPoet\Newsletter\Shortcodes\Shortcodes( + $rendered_body, + $newsletter + ); + $rendered_body = $shortcodes->replace(); + $newsletter['body'] = array( + 'html' => $rendered_body, + 'text' => '', + ); try { - $mailer = new \MailPoet\Mailer\Mailer(false, false, false); + $mailer = new \MailPoet\Mailer\Mailer( + $mailer = false, + $sender = false, + $reply_to = false + ); wp_send_json(array( 'result' => $mailer->send($newsletter, $data['subscriber']) diff --git a/tests/unit/Newsletter/ShortcodesCest.php b/tests/unit/Newsletter/ShortcodesCest.php new file mode 100644 index 0000000000..3adfe2d3ce --- /dev/null +++ b/tests/unit/Newsletter/ShortcodesCest.php @@ -0,0 +1,105 @@ +wp_user = $this->_createWPUser(); + $this->subscriber = $this->_createSubscriber(); + $this->newsletter['subject'] = 'some subject'; + $this->rendered_newsletter = ' + Hello [user:displayname | default:member]. + Your first name is [user:firstname | default:First Name]. + Your last name is [user:lastname | default:Last Name]. + Thank you for subscribing with [user:email]. + We already have [user:count] users. + + There are [newsletter:total] posts on this blog. + You are reading [newsletter:subject]. + The latest post on this blog is called [newsletter:post_title]. + The issue number of this newsletter is [newsletter:number]. + + Date: [date:d]. + Ordinal date: [date:dordinal]. + Date text: [date:dtext]. + Month: [date:m]. + Month text: [date:mtext]. + Year: [date:y] + + You can usubscribe here: [global:unsubscribe]. + Manage your subscription here: [global:manage]. + View this newsletter in browser: [global:browser].'; + $this->shortcodes_object = new MailPoet\Newsletter\Shortcodes\Shortcodes( + $this->rendered_newsletter, + $this->newsletter, + $this->subscriber + ); + } + + function itCanProcessShortcodes() { + $shortcodes = $this->shortcodes_object->extract(); + expect(count($shortcodes))->equals(18); + $wp_user = get_userdata($this->wp_user); + $wp_post_count = wp_count_posts(); + $wp_latest_post = wp_get_recent_posts(array('numberposts' => 1)); + $wp_latest_post = (isset($wp_latest_post)) ? + $wp_latest_post[0]['post_title'] : + false; + $date = new \DateTime('now'); + $subscriber_count = Subscriber::count(); + $newsletter_with_replaced_shortcodes = $this->shortcodes_object->replace(); + expect($newsletter_with_replaced_shortcodes)->equals(" + Hello {$wp_user->user_login}. + Your first name is {$this->subscriber->first_name}. + Your last name is {$this->subscriber->last_name}. + Thank you for subscribing with {$this->subscriber->email}. + We already have {$subscriber_count} users. + + There are {$wp_post_count->publish} posts on this blog. + You are reading {$this->newsletter['subject']}. + The latest post on this blog is called {$wp_latest_post}. + The issue number of this newsletter is [newsletter:number]. + + Date: {$date->format('d')}. + Ordinal date: {$date->format('dS')}. + Date text: {$date->format('D')}. + Month: {$date->format('m')}. + Month text: {$date->format('F')}. + Year: {$date->format('Y')} + + You can usubscribe here: [global:unsubscribe]. + Manage your subscription here: [global:manage]. + View this newsletter in browser: [global:browser]."); + } + + function _createWPUser() { + $wp_user = wp_create_user('phoenix_test_user', 'pass', 'phoenix@test.com'); + if(is_wp_error($wp_user)) { + $wp_user = get_user_by('login', 'phoenix_test_user'); + $wp_user = $wp_user->ID; + } + return $wp_user; + } + + function _createSubscriber() { + $subscriber = Subscriber::create(); + $subscriber->hydrate( + array( + 'first_name' => 'Donald', + 'last_name' => 'Trump', + 'email' => 'mister@trump.com', + 'wp_user_id' => $this->wp_user + ) + ); + $subscriber->save(); + return Subscriber::findOne($subscriber->id); + } + + function _after() { + ORM::raw_execute('TRUNCATE ' . Subscriber::$_table); + } +} \ No newline at end of file