diff --git a/assets/js/src/newsletters/types/notification.jsx b/assets/js/src/newsletters/types/notification.jsx index 92f1606cd1..9952590994 100644 --- a/assets/js/src/newsletters/types/notification.jsx +++ b/assets/js/src/newsletters/types/notification.jsx @@ -53,13 +53,13 @@ define( var weekDayField = { name: 'weekDay', values: { - 0: 'Monday', - 1: 'Tuesday', - 2: 'Wednesday', - 3: 'Thursday', - 4: 'Friday', - 5: 'Saturday', - 6: 'Sunday', + 0: 'Sunday', + 1: 'Monday', + 2: 'Tuesday', + 3: 'Wednesday', + 4: 'Thursday', + 5: 'Friday', + 6: 'Saturday' }, }; @@ -84,10 +84,10 @@ define( var nthWeekDayField = { name: 'nthWeekDay', values: { - '0': '1st', - '1': '2nd', - '2': '3rd', - '3': 'last', + '1': '1st', + '2': '2nd', + '3': '3rd', + 'L': 'last', }, }; @@ -99,9 +99,9 @@ define( return { intervalType: 'immediate', // 'immediate'|'daily'|'weekly'|'monthly' timeOfDay: 0, - weekDay: 0, + weekDay: 1, monthDay: 0, - nthWeekDay: 0, + nthWeekDay: 1, }; }, handleIntervalChange: function(event) { diff --git a/composer.json b/composer.json index 602b8112e0..3be91d72b0 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ "j4mie/paris": "1.5.4", "swiftmailer/swiftmailer": "^5.4", "phpseclib/phpseclib": "*", - "mtdowling/cron-expression": "^1.0", + "mtdowling/cron-expression": "^1.1", "nesbot/carbon": "^1.21", "soundasleep/html2text": "^0.3.0" }, diff --git a/composer.lock b/composer.lock index 61b632474f..84c3cf549c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "db2edea5fb720fcb8013ac470e924c19", - "content-hash": "85119d1ccd5193b6b08fda305a2427e7", + "hash": "2bed8395d84740d7c0ae644a6c6216fd", + "content-hash": "7b66e221814f3d5839ed4faabd2f50ad", "packages": [ { "name": "cerdic/css-tidy", @@ -2294,7 +2294,7 @@ }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/41ee6c70758f40fa1dbf90d019ae0a66c4a09e74", + "url": "https://api.github.com/repos/symfony/config/zipball/ee4cdda66aff834c8125e8f2c15932461667c521", "reference": "41ee6c70758f40fa1dbf90d019ae0a66c4a09e74", "shasum": "" }, @@ -2671,7 +2671,7 @@ }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/form/zipball/7fd5e4034cb8e215887136f5e176430bbf5ef085", + "url": "https://api.github.com/repos/symfony/form/zipball/b629051c77a4f37c625651d03002760385df4575", "reference": "7fd5e4034cb8e215887136f5e176430bbf5ef085", "shasum": "" }, @@ -2929,7 +2929,7 @@ }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/6f1979c3b0f4c22c77a8a8971afaa7dd07f082ac", + "url": "https://api.github.com/repos/symfony/process/zipball/d9d21cfcc3e202ee34777d6da38897695d4d208d", "reference": "6f1979c3b0f4c22c77a8a8971afaa7dd07f082ac", "shasum": "" }, diff --git a/lib/Config/Migrator.php b/lib/Config/Migrator.php index 1398bf72f4..9f7c751143 100644 --- a/lib/Config/Migrator.php +++ b/lib/Config/Migrator.php @@ -221,6 +221,7 @@ class Migrator { 'count_processed mediumint(9) NOT NULL DEFAULT 0,', 'count_to_process mediumint(9) NOT NULL DEFAULT 0,', 'count_failed mediumint(9) NOT NULL DEFAULT 0,', + 'scheduled_at TIMESTAMP NOT NULL DEFAULT 0,', 'processed_at TIMESTAMP NOT NULL DEFAULT 0,', 'created_at TIMESTAMP NOT NULL DEFAULT 0,', 'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,', diff --git a/lib/Config/Populator.php b/lib/Config/Populator.php index 9dd7c60594..02c9edc2b1 100644 --- a/lib/Config/Populator.php +++ b/lib/Config/Populator.php @@ -180,6 +180,14 @@ class Populator { 'name' => 'nthWeekDay', 'newsletter_type' => 'notification', ), + array( + 'name' => 'schedule', + 'newsletter_type' => 'notification', + ), + array( + 'name' => 'lastSentData', + 'newsletter_type' => 'notification', + ), ); } diff --git a/lib/Models/Subscriber.php b/lib/Models/Subscriber.php index 892266fbdd..60342a49da 100644 --- a/lib/Models/Subscriber.php +++ b/lib/Models/Subscriber.php @@ -1,6 +1,7 @@ save()) { $subscriber->addToSegments($segment_ids); + Scheduler::welcomeForSegmentSubscription($subscriber->id, $segment_ids); } } diff --git a/lib/Newsletter/Scheduler/Scheduler.php b/lib/Newsletter/Scheduler/Scheduler.php new file mode 100644 index 0000000000..7b46753057 --- /dev/null +++ b/lib/Newsletter/Scheduler/Scheduler.php @@ -0,0 +1,114 @@ +findOne($newsletter_id) + ->asArray(); + $interval_type = $newsletter['intervalType']; + $hour = (int) $newsletter['timeOfDay'] / self::seconds_in_hour; + $week_day = $newsletter['weekDay']; + $month_day = $newsletter['monthDay']; + $nth_week_day = ($newsletter['nthWeekDay'] === self::last_weekday_format) ? + $newsletter['nthWeekDay'] : + '#' . $newsletter['nthWeekDay']; + switch($interval_type) { + case 'immediately': + $cron = '* * * * *'; + break; + case 'immediate': //daily + $cron = sprintf('0 %s * * *', $hour); + break; + case 'weekly': + $cron = sprintf('0 %s * * %s', $hour, $week_day); + break; + case 'monthly': + $cron = sprintf('0 %s %s * *', $hour, $month_day); + break; + case 'nthWeekDay': + $cron = sprintf('0 %s ? * %s%s', $hour, $week_day, $nth_week_day); + break; + } + $option_field = NewsletterOptionField::where('name', 'schedule') + ->findOne() + ->asArray(); + $relation = NewsletterOption::create(); + $relation->newsletter_id = $newsletter['id']; + $relation->option_field_id = $option_field['id']; + $relation->value = $cron; + $relation->save(); + } + + static function welcomeForSegmentSubscription($subscriber_id, array $segments) { + $newsletters = self::getWelcomeNewsletters(); + if(!count($newsletters)) return; + foreach($newsletters as $newsletter) { + if($newsletter['event'] === 'segment' && + in_array($newsletter['segment'], $segments) + ) { + self::createSendingQueueEntry($newsletter, $subscriber_id); + } + } + } + + static function welcomeForNewWPUser($subscriber_id, array $wp_user) { + $newsletters = self::getWelcomeNewsletters(); + if(!count($newsletters)) return; + foreach($newsletters as $newsletter) { + if($newsletter['event'] === 'user' && + in_array($newsletter['role'], $wp_user['roles']) + ) { + self::createSendingQueueEntry($newsletter, $subscriber_id); + } + } + } + + private static function getWelcomeNewsletters() { + return Newsletter::where('type', 'welcome') + ->filter('filterWithOptions') + ->findArray(); + } + + private static function createSendingQueueEntry($newsletter, $subscriber_id) { + $queue = SendingQueue::create(); + $queue->newsletter_id = $newsletter['id']; + $queue->subscribers = serialize( + array( + 'to_process' => array($subscriber_id) + ) + ); + $queue->count_total = $queue->count_to_process = 1; + $after_time_type = $newsletter['afterTimeType']; + $after_time_number = $newsletter['afterTimeNumber']; + $scheduled_at = null; + switch($after_time_type) { + case 'hours': + $scheduled_at = Carbon::now() + ->addHours($after_time_number); + break; + case 'days': + $scheduled_at = Carbon::now() + ->addDays($after_time_number); + break; + case 'weeks': + $scheduled_at = Carbon::now() + ->addWeeks($after_time_number); + break; + } + if($scheduled_at) { + $queue->status = 'scheduled'; + $queue->scheduled_at = $scheduled_at; + } + $queue->save(); + } +} \ No newline at end of file diff --git a/lib/Router/Newsletters.php b/lib/Router/Newsletters.php index 2f40669a52..ce8e1cfebd 100644 --- a/lib/Router/Newsletters.php +++ b/lib/Router/Newsletters.php @@ -14,6 +14,7 @@ use MailPoet\Models\NewsletterOptionField; use MailPoet\Models\NewsletterOption; use MailPoet\Newsletter\Renderer\Renderer; use MailPoet\Models\SendingQueue; +use MailPoet\Newsletter\Scheduler\Scheduler; if(!defined('ABSPATH')) exit; @@ -265,6 +266,12 @@ class Newsletters { } } } + if(!isset($data['id']) && + isset($data['type']) && + $data['type'] === 'notification' + ) { + Scheduler::postNotification($newsletter->id); + } return array( 'result' => true, 'newsletter' => $newsletter->asArray() diff --git a/lib/Segments/WP.php b/lib/Segments/WP.php index 63d409fccb..dab1a9da6e 100644 --- a/lib/Segments/WP.php +++ b/lib/Segments/WP.php @@ -2,17 +2,15 @@ namespace MailPoet\Segments; use \MailPoet\Models\Subscriber; use \MailPoet\Models\Segment; +use MailPoet\Newsletter\Scheduler\Scheduler; class WP { static function synchronizeUser($wp_user_id) { - $wpUser = \get_userdata($wp_user_id); + $wp_user = \get_userdata($wp_user_id); $segment = Segment::getWPUsers(); - - if($wpUser === false or $segment === false) return; - - $subscriber = Subscriber::where('wp_user_id', $wpUser->ID) + if($wp_user === false or $segment === false) return; + $subscriber = Subscriber::where('wp_user_id', $wp_user->ID) ->findOne(); - switch(current_filter()) { case 'delete_user': case 'deleted_user': @@ -20,23 +18,22 @@ class WP { if($subscriber !== false && $subscriber->id()) { $subscriber->delete(); } - break; - + break; case 'user_register': + $new_user = (!$subscriber) ? true : false; case 'added_existing_user': case 'profile_update': default: // get first name & last name - $first_name = $wpUser->first_name; - $last_name = $wpUser->last_name; - if(empty($wpUser->first_name) && empty($wpUser->last_name)) { - $first_name = $wpUser->display_name; + $first_name = $wp_user->first_name; + $last_name = $wp_user->last_name; + if(empty($wp_user->first_name) && empty($wp_user->last_name)) { + $first_name = $wp_user->display_name; } - // subscriber data $data = array( - 'wp_user_id'=> $wpUser->ID, - 'email' => $wpUser->user_email, + 'wp_user_id' => $wp_user->ID, + 'email' => $wp_user->user_email, 'first_name' => $first_name, 'last_name' => $last_name, 'status' => 'subscribed' @@ -46,13 +43,18 @@ class WP { $data['id'] = $subscriber->id(); } $subscriber = Subscriber::createOrUpdate($data); - if($subscriber->getErrors() === false && $subscriber->id > 0) { if($segment !== false) { $segment->addSubscriber($subscriber->id); } + if(isset($new_user) && $new_user === true) { + Scheduler::welcomeForNewWPUser( + $subscriber->id, + (array) $wp_user + ); + } } - break; + break; } }