diff --git a/lib/Newsletter/Scheduler/WelcomeScheduler.php b/lib/Newsletter/Scheduler/WelcomeScheduler.php index d5beff71d5..20e46daadf 100644 --- a/lib/Newsletter/Scheduler/WelcomeScheduler.php +++ b/lib/Newsletter/Scheduler/WelcomeScheduler.php @@ -2,14 +2,32 @@ namespace MailPoet\Newsletter\Scheduler; +use MailPoet\Entities\SegmentEntity; +use MailPoet\Entities\SubscriberEntity; use MailPoet\Models\Newsletter; use MailPoet\Models\SendingQueue; +use MailPoet\Segments\SegmentsRepository; +use MailPoet\Subscribers\SubscribersRepository; use MailPoet\Tasks\Sending as SendingTask; class WelcomeScheduler { const WORDPRESS_ALL_ROLES = 'mailpoet_all'; + /** @var SubscribersRepository */ + private $subscribersRepository; + + /** @var SegmentsRepository */ + private $segmentsRepository; + + public function __construct( + SubscribersRepository $subscribersRepository, + SegmentsRepository $segmentsRepository + ) { + $this->subscribersRepository = $subscribersRepository; + $this->segmentsRepository = $segmentsRepository; + } + public function scheduleSubscriberWelcomeNotification($subscriberId, $segments) { $newsletters = Scheduler::getNewsletters(Newsletter::TYPE_WELCOME); if (empty($newsletters)) return false; @@ -18,10 +36,13 @@ class WelcomeScheduler { if ($newsletter->event === 'segment' && in_array($newsletter->segment, $segments) ) { - $result[] = $this->createWelcomeNotificationSendingTask($newsletter, $subscriberId); + $sendingTask = $this->createWelcomeNotificationSendingTask($newsletter, $subscriberId); + if ($sendingTask) { + $result[] = $sendingTask; + } } } - return $result; + return $result ?: false; } public function scheduleWPUserWelcomeNotification( @@ -53,6 +74,22 @@ class WelcomeScheduler { } public function createWelcomeNotificationSendingTask($newsletter, $subscriberId) { + $subscriber = $this->subscribersRepository->findOneById($subscriberId); + if (!($subscriber instanceof SubscriberEntity) || $subscriber->getDeletedAt() !== null) { + return; + } + if ($newsletter->event === 'segment') { + $segment = $this->segmentsRepository->findOneById((int)$newsletter->segment); + if ((!$segment instanceof SegmentEntity) || $segment->getDeletedAt() !== null) { + return; + } + } + if ($newsletter->event === 'user') { + $segment = $this->segmentsRepository->getWPUsersSegment(); + if ((!$segment instanceof SegmentEntity) || $segment->getDeletedAt() !== null) { + return; + } + } $previouslyScheduledNotification = SendingQueue::joinWithSubscribers() ->where('queues.newsletter_id', $newsletter->id) ->where('subscribers.subscriber_id', $subscriberId) diff --git a/lib/Segments/SegmentsRepository.php b/lib/Segments/SegmentsRepository.php index 1e45790081..8807695d4b 100644 --- a/lib/Segments/SegmentsRepository.php +++ b/lib/Segments/SegmentsRepository.php @@ -13,6 +13,10 @@ class SegmentsRepository extends Repository { return SegmentEntity::class; } + public function getWPUsersSegment() { + return $this->findOneBy(['type' => SegmentEntity::TYPE_WP_USERS]); + } + public function getCountsPerType(): array { $results = $this->doctrineRepository->createQueryBuilder('s') ->select('s.type, COUNT(s) as cnt') diff --git a/tests/integration/Newsletter/Scheduler/WelcomeTest.php b/tests/integration/Newsletter/Scheduler/WelcomeTest.php index a344af1328..10bf7d84c9 100644 --- a/tests/integration/Newsletter/Scheduler/WelcomeTest.php +++ b/tests/integration/Newsletter/Scheduler/WelcomeTest.php @@ -2,12 +2,15 @@ namespace MailPoet\Newsletter\Scheduler; +use MailPoet\Entities\SegmentEntity; +use MailPoet\Entities\SubscriberEntity; use MailPoet\Models\Newsletter; use MailPoet\Models\NewsletterOption; use MailPoet\Models\NewsletterOptionField; use MailPoet\Models\NewsletterPost; use MailPoet\Models\ScheduledTask; use MailPoet\Models\ScheduledTaskSubscriber; +use MailPoet\Models\Segment; use MailPoet\Models\SendingQueue; use MailPoet\Models\Subscriber; use MailPoet\Tasks\Sending as SendingTask; @@ -20,9 +23,21 @@ class WelcomeTest extends \MailPoetTest { /** @var WelcomeScheduler */ private $welcomeScheduler; + /** @var SubscriberEntity */ + private $subscriber; + + /** @var SegmentEntity */ + private $segment; + + /** @var SegmentEntity */ + private $wpSegment; + public function _before() { parent::_before(); - $this->welcomeScheduler = new WelcomeScheduler; + $this->welcomeScheduler = $this->diContainer->get(WelcomeScheduler::class); + $this->subscriber = $this->createSubscriber('welcome_test_1@example.com'); + $this->segment = $this->createSegment('welcome_segment'); + $this->wpSegment = $this->createSegment('Wordpress', SegmentEntity::TYPE_WP_USERS); } public function testItDoesNotCreateDuplicateWelcomeNotificationSendingTasks() { @@ -30,8 +45,10 @@ class WelcomeTest extends \MailPoetTest { 'id' => 1, 'afterTimeNumber' => 2, 'afterTimeType' => 'hours', + 'event' => 'segment', + 'segment' => $this->segment->getId(), ]; - $existingSubscriber = 678; + $existingSubscriber = $this->subscriber->getId(); $existingQueue = SendingTask::create(); $existingQueue->newsletterId = $newsletter->id; $existingQueue->setSubscribers([$existingSubscriber]); @@ -41,8 +58,9 @@ class WelcomeTest extends \MailPoetTest { $this->welcomeScheduler->createWelcomeNotificationSendingTask($newsletter, $existingSubscriber); expect(SendingQueue::findMany())->count(1); - // queue is not scheduled - $this->welcomeScheduler->createWelcomeNotificationSendingTask($newsletter, 1); + // queue is scheduled + $unscheduledSubscriber = $this->createSubscriber('welcome_test_2@example.com'); + $this->welcomeScheduler->createWelcomeNotificationSendingTask($newsletter, $unscheduledSubscriber->getId()); expect(SendingQueue::findMany())->count(2); } @@ -50,11 +68,13 @@ class WelcomeTest extends \MailPoetTest { $newsletter = (object)[ 'id' => 1, 'afterTimeNumber' => 2, + 'event' => 'segment', + 'segment' => $this->segment->getId(), ]; // queue is scheduled delivery in 2 hours $newsletter->afterTimeType = 'hours'; - $this->welcomeScheduler->createWelcomeNotificationSendingTask($newsletter, $subscriberId = 1); + $this->welcomeScheduler->createWelcomeNotificationSendingTask($newsletter, $this->subscriber->getId()); $queue = SendingQueue::findTaskByNewsletterId(1) ->findOne(); $currentTime = Carbon::createFromTimestamp(WPFunctions::get()->currentTime('timestamp')); @@ -69,11 +89,13 @@ class WelcomeTest extends \MailPoetTest { $newsletter = (object)[ 'id' => 1, 'afterTimeNumber' => 2, + 'event' => 'segment', + 'segment' => $this->segment->getId(), ]; // queue is scheduled for delivery in 2 days $newsletter->afterTimeType = 'days'; - $this->welcomeScheduler->createWelcomeNotificationSendingTask($newsletter, $subscriberId = 1); + $this->welcomeScheduler->createWelcomeNotificationSendingTask($newsletter, $this->subscriber->getId()); $currentTime = Carbon::createFromTimestamp(WPFunctions::get()->currentTime('timestamp')); Carbon::setTestNow($currentTime); // mock carbon to return current time $queue = SendingQueue::findTaskByNewsletterId(1) @@ -88,11 +110,13 @@ class WelcomeTest extends \MailPoetTest { $newsletter = (object)[ 'id' => 1, 'afterTimeNumber' => 2, + 'event' => 'segment', + 'segment' => $this->segment->getId(), ]; // queue is scheduled for delivery in 2 weeks $newsletter->afterTimeType = 'weeks'; - $this->welcomeScheduler->createWelcomeNotificationSendingTask($newsletter, $subscriberId = 1); + $this->welcomeScheduler->createWelcomeNotificationSendingTask($newsletter, $this->subscriber->getId()); $currentTime = Carbon::createFromTimestamp(WPFunctions::get()->currentTime('timestamp')); Carbon::setTestNow($currentTime); // mock carbon to return current time $queue = SendingQueue::findTaskByNewsletterId(1) @@ -107,11 +131,13 @@ class WelcomeTest extends \MailPoetTest { $newsletter = (object)[ 'id' => 1, 'afterTimeNumber' => 2, + 'event' => 'segment', + 'segment' => $this->segment->getId(), ]; // queue is scheduled for immediate delivery $newsletter->afterTimeType = null; - $this->welcomeScheduler->createWelcomeNotificationSendingTask($newsletter, $subscriberId = 1); + $this->welcomeScheduler->createWelcomeNotificationSendingTask($newsletter, $this->subscriber->getId()); $currentTime = Carbon::createFromTimestamp(WPFunctions::get()->currentTime('timestamp')); Carbon::setTestNow($currentTime); // mock carbon to return current time $queue = SendingQueue::findTaskByNewsletterId(1)->findOne(); @@ -125,7 +151,7 @@ class WelcomeTest extends \MailPoetTest { // do not schedule when subscriber is not in segment $newsletter = $this->_createNewsletter(); $this->welcomeScheduler->scheduleSubscriberWelcomeNotification( - $subscriberId = 10, + $this->subscriber->getId(), $segments = [] ); @@ -141,19 +167,22 @@ class WelcomeTest extends \MailPoetTest { $newsletter->id, [ 'event' => 'segment', - 'segment' => 2, + 'segment' => $this->segment->getId(), 'afterTimeType' => 'days', 'afterTimeNumber' => 1, ] ); + $segment2 = $this->createSegment('Segment 2'); + $segment3 = $this->createSegment('Segment 3'); + // queue is created and scheduled for delivery one day later $result = $this->welcomeScheduler->scheduleSubscriberWelcomeNotification( - $subscriberId = 10, + $this->subscriber->getId(), $segments = [ - 3, - 2, - 1, + $this->segment->getId(), + $segment2->getId(), + $segment3->getId(), ] ); $currentTime = Carbon::createFromTimestamp(WPFunctions::get()->currentTime('timestamp')); @@ -165,19 +194,61 @@ class WelcomeTest extends \MailPoetTest { expect($result[0]->id())->equals($queue->id()); } - public function itDoesNotScheduleAnythingWhenNewsletterDoesNotExist() { - + public function testItDoesNotScheduleWelcomeNotificationWhenSubscriberIsInTrash() { + $newsletter = $this->_createNewsletter(); + $this->_createNewsletterOptions( + $newsletter->id, + [ + 'event' => 'segment', + 'segment' => $this->segment->getId(), + 'afterTimeType' => 'days', + 'afterTimeNumber' => 1, + ] + ); + $trashedSubscriber = $this->createSubscriber('trashed@example.com'); + $trashedSubscriber->setDeletedAt(Carbon::now()); + $this->entityManager->flush(); // subscriber welcome notification is not scheduled $result = $this->welcomeScheduler->scheduleSubscriberWelcomeNotification( - $subscriberId = 10, + $trashedSubscriber->getId(), + $segments = [$this->segment->getId()] + ); + expect($result)->false(); + } + + public function testItDoesNotScheduleWelcomeNotificationWhenSegmentIsInTrash() { + $newsletter = $this->_createNewsletter(); + $this->_createNewsletterOptions( + $newsletter->id, + [ + 'event' => 'segment', + 'segment' => $this->segment->getId(), + 'afterTimeType' => 'days', + 'afterTimeNumber' => 1, + ] + ); + $this->segment->setDeletedAt(Carbon::now()); + $this->entityManager->flush(); + // subscriber welcome notification is not scheduled + $result = $this->welcomeScheduler->scheduleSubscriberWelcomeNotification( + $this->subscriber->getId(), + $segments = [$this->segment->getId()] + ); + expect($result)->false(); + } + + public function itDoesNotScheduleAnythingWhenNewsletterDoesNotExist() { + // subscriber welcome notification is not scheduled + $result = $this->welcomeScheduler->scheduleSubscriberWelcomeNotification( + $this->subscriber->getId(), $segments = [] ); expect($result)->false(); // WP user welcome notification is not scheduled - $result = $this->welcomeScheduler->scheduleSubscriberWelcomeNotification( - $subscriberId = 10, - $segments = [] + $result = $this->welcomeScheduler->scheduleWPUserWelcomeNotification( + $this->subscriber->getId(), + $wpUser = ['roles' => ['editor']] ); expect($result)->false(); } @@ -194,7 +265,7 @@ class WelcomeTest extends \MailPoetTest { ] ); $this->welcomeScheduler->scheduleWPUserWelcomeNotification( - $subscriberId = 10, + $subscriberId = $this->subscriber->getId(), $wpUser = ['roles' => ['editor']], $oldUserData = ['roles' => ['editor']] ); @@ -217,7 +288,7 @@ class WelcomeTest extends \MailPoetTest { ] ); $this->welcomeScheduler->scheduleWPUserWelcomeNotification( - $subscriberId = 10, + $subscriberId = $this->subscriber->getId(), $wpUser = ['roles' => ['administrator']] ); @@ -227,6 +298,57 @@ class WelcomeTest extends \MailPoetTest { expect($queue)->false(); } + public function testItDoesNotSchedulesWPUserWelcomeNotificationWhenSubscriberIsInTrash() { + $newsletter = $this->_createNewsletter(); + $this->_createNewsletterOptions( + $newsletter->id, + [ + 'event' => 'user', + 'role' => 'administrator', + 'afterTimeType' => 'days', + 'afterTimeNumber' => 1, + ] + ); + $trashedSubscriber = $this->createSubscriber('trashed@example.com'); + $trashedSubscriber->setDeletedAt(Carbon::now()); + $this->entityManager->flush(); + $this->welcomeScheduler->scheduleWPUserWelcomeNotification( + $subscriberId = $trashedSubscriber->getId(), + $wpUser = ['roles' => ['administrator']] + ); + $currentTime = Carbon::createFromTimestamp(WPFunctions::get()->currentTime('timestamp')); + Carbon::setTestNow($currentTime); // mock carbon to return current time + // queue is created and scheduled for delivery one day later + $queue = SendingQueue::findTaskByNewsletterId($newsletter->id) + ->findOne(); + expect($queue)->false(); + } + + public function testItDoesNotSchedulesWPUserWelcomeNotificationWhenWpSegmentIsInTrash() { + $newsletter = $this->_createNewsletter(); + $this->_createNewsletterOptions( + $newsletter->id, + [ + 'event' => 'user', + 'role' => 'administrator', + 'afterTimeType' => 'days', + 'afterTimeNumber' => 1, + ] + ); + $this->wpSegment->setDeletedAt(Carbon::now()); + $this->entityManager->flush(); + $this->welcomeScheduler->scheduleWPUserWelcomeNotification( + $subscriberId = $this->subscriber->getId(), + $wpUser = ['roles' => ['administrator']] + ); + $currentTime = Carbon::createFromTimestamp(WPFunctions::get()->currentTime('timestamp')); + Carbon::setTestNow($currentTime); // mock carbon to return current time + // queue is created and scheduled for delivery one day later + $queue = SendingQueue::findTaskByNewsletterId($newsletter->id) + ->findOne(); + expect($queue)->false(); + } + public function testItSchedulesWPUserWelcomeNotificationWhenUserRolesMatches() { $newsletter = $this->_createNewsletter(); $this->_createNewsletterOptions( @@ -239,7 +361,7 @@ class WelcomeTest extends \MailPoetTest { ] ); $this->welcomeScheduler->scheduleWPUserWelcomeNotification( - $subscriberId = 10, + $subscriberId = $this->subscriber->getId(), $wpUser = ['roles' => ['administrator']] ); $currentTime = Carbon::createFromTimestamp(WPFunctions::get()->currentTime('timestamp')); @@ -263,7 +385,7 @@ class WelcomeTest extends \MailPoetTest { ] ); $this->welcomeScheduler->scheduleWPUserWelcomeNotification( - $subscriberId = 10, + $this->subscriber->getId(), $wpUser = ['roles' => ['administrator']] ); $currentTime = Carbon::createFromTimestamp(WPFunctions::get()->currentTime('timestamp')); @@ -306,6 +428,22 @@ class WelcomeTest extends \MailPoetTest { } } + private function createSubscriber($email): SubscriberEntity { + $subscriber = new SubscriberEntity(); + $subscriber->setStatus(SubscriberEntity::STATUS_SUBSCRIBED); + $subscriber->setEmail($email); + $this->entityManager->persist($subscriber); + $this->entityManager->flush(); + return $subscriber; + } + + private function createSegment($name, $type = SegmentEntity::TYPE_DEFAULT): SegmentEntity { + $segment = new SegmentEntity($name, $type, $name); + $this->entityManager->persist($segment); + $this->entityManager->flush(); + return $segment; + } + public function _after() { Carbon::setTestNow(); ORM::raw_execute('TRUNCATE ' . Newsletter::$_table); @@ -316,5 +454,6 @@ class WelcomeTest extends \MailPoetTest { ORM::raw_execute('TRUNCATE ' . ScheduledTaskSubscriber::$_table); ORM::raw_execute('TRUNCATE ' . SendingQueue::$_table); ORM::raw_execute('TRUNCATE ' . Subscriber::$_table); + ORM::raw_execute('TRUNCATE ' . Segment::$_table); } }