Extract Newsletters::sendPreview() logic to a controller

[MAILPOET-2646]
This commit is contained in:
Jan Jakeš
2020-04-29 11:17:37 +02:00
committed by Veljko V
parent 6a44f820ee
commit 81dc5ad480
6 changed files with 195 additions and 130 deletions

View File

@ -12,24 +12,21 @@ use MailPoet\Cron\Workers\SendingQueue\Tasks\Newsletter as NewsletterQueueTask;
use MailPoet\DI\ContainerWrapper;
use MailPoet\Entities\NewsletterEntity;
use MailPoet\Listing;
use MailPoet\Mailer\Mailer as MailerFactory;
use MailPoet\Mailer\MetaInfo;
use MailPoet\Models\Newsletter;
use MailPoet\Models\NewsletterOption;
use MailPoet\Models\NewsletterOptionField;
use MailPoet\Models\NewsletterSegment;
use MailPoet\Models\SendingQueue;
use MailPoet\Models\Subscriber;
use MailPoet\Newsletter\Listing\NewsletterListingRepository;
use MailPoet\Newsletter\NewslettersRepository;
use MailPoet\Newsletter\Renderer\Renderer;
use MailPoet\Newsletter\Preview\SendPreviewController;
use MailPoet\Newsletter\Preview\SendPreviewException;
use MailPoet\Newsletter\Scheduler\PostNotificationScheduler;
use MailPoet\Newsletter\Scheduler\Scheduler;
use MailPoet\Newsletter\Url as NewsletterUrl;
use MailPoet\NewsletterTemplates\NewsletterTemplatesRepository;
use MailPoet\Services\AuthorizedEmailsController;
use MailPoet\Settings\SettingsController;
use MailPoet\Tasks\Sending as SendingTask;
use MailPoet\Util\License\Features\Subscribers as SubscribersFeature;
use MailPoet\WP\Emoji;
use MailPoet\WP\Functions as WPFunctions;
@ -71,18 +68,15 @@ class Newsletters extends APIEndpoint {
/** @var PostNotificationScheduler */
private $postNotificationScheduler;
/** @var MetaInfo */
private $mailerMetaInfo;
/** @var Emoji */
private $emoji;
/** @var MailerFactory */
private $mailer;
/** @var SubscribersFeature */
private $subscribersFeature;
/** @var SendPreviewController */
private $sendPreviewController;
public function __construct(
Listing\BulkActionController $bulkAction,
Listing\Handler $listingHandler,
@ -94,10 +88,9 @@ class Newsletters extends APIEndpoint {
NewsletterListingRepository $newsletterListingRepository,
NewslettersResponseBuilder $newslettersResponseBuilder,
PostNotificationScheduler $postNotificationScheduler,
MailerFactory $mailer,
MetaInfo $mailerMetaInfo,
Emoji $emoji,
SubscribersFeature $subscribersFeature
SubscribersFeature $subscribersFeature,
SendPreviewController $sendPreviewController
) {
$this->bulkAction = $bulkAction;
$this->listingHandler = $listingHandler;
@ -109,10 +102,9 @@ class Newsletters extends APIEndpoint {
$this->newsletterListingRepository = $newsletterListingRepository;
$this->newslettersResponseBuilder = $newslettersResponseBuilder;
$this->postNotificationScheduler = $postNotificationScheduler;
$this->mailer = $mailer;
$this->mailerMetaInfo = $mailerMetaInfo;
$this->emoji = $emoji;
$this->subscribersFeature = $subscribersFeature;
$this->sendPreviewController = $sendPreviewController;
}
public function get($data = []) {
@ -444,66 +436,20 @@ class Newsletters extends APIEndpoint {
$id = (isset($data['id'])) ? (int)$data['id'] : false;
$newsletter = Newsletter::findOne($id);
if ($newsletter instanceof Newsletter) {
$renderer = new Renderer($newsletter, $preview = true);
$renderedNewsletter = $renderer->render();
$divider = '***MailPoet***';
$dataForShortcodes = array_merge(
[$newsletter->subject],
$renderedNewsletter
);
$body = implode($divider, $dataForShortcodes);
$subscriber = Subscriber::getCurrentWPUser();
$subscriber = ($subscriber) ? $subscriber : false;
$shortcodes = new \MailPoet\Newsletter\Shortcodes\Shortcodes(
$newsletter,
$subscriber,
$queue = false,
$wpUserPreview = true
);
list(
$renderedNewsletter['subject'],
$renderedNewsletter['body']['html'],
$renderedNewsletter['body']['text']
) = explode($divider, $shortcodes->replace($body));
$renderedNewsletter['id'] = $newsletter->id;
try {
$extraParams = [
'unsubscribe_url' => WPFunctions::get()->homeUrl(),
'meta' => $this->mailerMetaInfo->getPreviewMetaInfo(),
];
$result = $this->mailer->send($renderedNewsletter, $data['subscriber'], $extraParams);
if ($result['response'] === false) {
$error = sprintf(
__('The email could not be sent: %s', 'mailpoet'),
$result['error']->getMessage()
);
return $this->errorResponse([APIError::BAD_REQUEST => $error]);
} else {
$newsletter = Newsletter::findOne($newsletter->id);
if(!$newsletter instanceof Newsletter) return $this->errorResponse();
return $this->successResponse(
$newsletter->asArray()
);
}
} catch (\Exception $e) {
return $this->errorResponse([
$e->getCode() => $e->getMessage(),
]);
}
} else {
if (!$newsletter) {
return $this->errorResponse([
APIError::NOT_FOUND => __('This email does not exist.', 'mailpoet'),
]);
}
try {
$this->sendPreviewController->sendPreview($newsletter, $data['subscriber']);
} catch (SendPreviewException $e) {
return $this->errorResponse([APIError::BAD_REQUEST => $e->getMessage()]);
} catch (\Throwable $e) {
return $this->errorResponse([$e->getCode() => $e->getMessage()]);
}
return $this->successResponse($newsletter->asArray());
}
public function listing($data = []) {

View File

@ -264,6 +264,7 @@ class ContainerConfigurator implements IContainerConfigurator {
$container->autowire(\MailPoet\Newsletter\AutomatedLatestContent::class)->setPublic(true);
$container->autowire(\MailPoet\Newsletter\NewslettersRepository::class)->setPublic(true);
$container->autowire(\MailPoet\Newsletter\Listing\NewsletterListingRepository::class)->setPublic(true);
$container->autowire(\MailPoet\Newsletter\Preview\SendPreviewController::class);
$container->autowire(\MailPoet\Newsletter\Statistics\NewsletterStatisticsRepository::class);
$container->autowire(\MailPoet\Newsletter\Scheduler\WelcomeScheduler::class)->setPublic(true);
$container->autowire(\MailPoet\Newsletter\Scheduler\PostNotificationScheduler::class);

View File

@ -0,0 +1,73 @@
<?php declare(strict_types = 1);
namespace MailPoet\Newsletter\Preview;
use MailPoet\Mailer\Mailer;
use MailPoet\Mailer\MetaInfo;
use MailPoet\Models\Newsletter;
use MailPoet\Models\Subscriber;
use MailPoet\Newsletter\Renderer\Renderer;
use MailPoet\Newsletter\Shortcodes\Shortcodes;
use MailPoet\WP\Functions as WPFunctions;
class SendPreviewController {
/** @var Mailer */
private $mailer;
/** @var MetaInfo */
private $mailerMetaInfo;
/** @var WPFunctions */
private $wp;
public function __construct(
Mailer $mailer,
MetaInfo $mailerMetaInfo,
WPFunctions $wp
) {
$this->mailer = $mailer;
$this->mailerMetaInfo = $mailerMetaInfo;
$this->wp = $wp;
}
public function sendPreview(Newsletter $newsletter, string $emailAddress) {
$renderer = new Renderer($newsletter, $preview = true);
$renderedNewsletter = $renderer->render();
$divider = '***MailPoet***';
$dataForShortcodes = array_merge(
[$newsletter->subject],
$renderedNewsletter
);
$body = implode($divider, $dataForShortcodes);
$subscriber = Subscriber::getCurrentWPUser() ?: false;
$shortcodes = new Shortcodes(
$newsletter,
$subscriber,
$queue = false,
$wpUserPreview = true
);
list(
$renderedNewsletter['subject'],
$renderedNewsletter['body']['html'],
$renderedNewsletter['body']['text']
) = explode($divider, $shortcodes->replace($body));
$renderedNewsletter['id'] = $newsletter->id;
$extraParams = [
'unsubscribe_url' => $this->wp->homeUrl(),
'meta' => $this->mailerMetaInfo->getPreviewMetaInfo(),
];
$result = $this->mailer->send($renderedNewsletter, $emailAddress, $extraParams);
if ($result['response'] === false) {
$error = sprintf(
__('The email could not be sent: %s', 'mailpoet'),
$result['error']->getMessage()
);
throw new SendPreviewException($error);
}
}
}

View File

@ -0,0 +1,6 @@
<?php declare(strict_types = 1);
namespace MailPoet\Newsletter\Preview;
class SendPreviewException extends \RuntimeException {
}

View File

@ -13,9 +13,6 @@ use MailPoet\Cron\CronHelper;
use MailPoet\DI\ContainerWrapper;
use MailPoet\Listing\BulkActionController;
use MailPoet\Listing\Handler;
use MailPoet\Mailer\Mailer;
use MailPoet\Mailer\MailerError;
use MailPoet\Mailer\MetaInfo;
use MailPoet\Models\Newsletter;
use MailPoet\Models\NewsletterOption;
use MailPoet\Models\NewsletterOptionField;
@ -26,6 +23,8 @@ use MailPoet\Models\SendingQueue;
use MailPoet\Models\SubscriberSegment;
use MailPoet\Newsletter\Listing\NewsletterListingRepository;
use MailPoet\Newsletter\NewslettersRepository;
use MailPoet\Newsletter\Preview\SendPreviewController;
use MailPoet\Newsletter\Preview\SendPreviewException;
use MailPoet\Newsletter\Scheduler\PostNotificationScheduler;
use MailPoet\Newsletter\Scheduler\Scheduler;
use MailPoet\Newsletter\Statistics\NewsletterStatisticsRepository;
@ -33,7 +32,6 @@ use MailPoet\Newsletter\Url;
use MailPoet\Router\Router;
use MailPoet\Services\AuthorizedEmailsController;
use MailPoet\Settings\SettingsController;
use MailPoet\Subscription\SubscriptionUrlFactory;
use MailPoet\Tasks\Sending as SendingTask;
use MailPoet\Util\License\Features\Subscribers as SubscribersFeature;
use MailPoet\WooCommerce\Helper as WCHelper;
@ -49,15 +47,11 @@ class NewslettersTest extends \MailPoetTest {
/** @var Newsletters */
private $endpoint;
/** @var SubscriptionUrlFactory */
private $subscriptionUrlFactory;
/** @var CronHelper */
private $cronHelper;
public function _before() {
parent::_before();
$this->subscriptionUrlFactory = SubscriptionUrlFactory::getInstance();
$this->cronHelper = ContainerWrapper::getInstance()->get(CronHelper::class);
$this->endpoint = Stub::copy(
ContainerWrapper::getInstance()->get(Newsletters::class),
@ -836,33 +830,11 @@ class NewslettersTest extends \MailPoetTest {
}
public function testItCanSendAPreview() {
$subscriber = 'test@subscriber.com';
$endpoint = Stub::copy($this->endpoint, [
'mailer' => $this->makeEmpty(
Mailer::class,
[
'send' => function ($newsletter, $subscriber, $extraParams) {
$unsubscribeLink = $this->subscriptionUrlFactory->getConfirmUnsubscribeUrl(null);
$manageLink = $this->subscriptionUrlFactory->getManageUrl(null);
$viewInBrowserLink = Url::getViewInBrowserUrl($this->newsletter);
$mailerMetaInfo = new MetaInfo;
expect(is_array($newsletter))->true();
expect($newsletter['body']['text'])->contains('Hello test');
expect($subscriber)->equals($subscriber);
expect($extraParams['unsubscribe_url'])->equals(home_url());
expect($extraParams['meta'])->equals($mailerMetaInfo->getPreviewMetaInfo());
// system links are replaced with hashes
expect($newsletter['body']['html'])->contains('href="' . $viewInBrowserLink . '">View in browser');
expect($newsletter['body']['html'])->contains('href="' . $unsubscribeLink . '">Unsubscribe');
expect($newsletter['body']['html'])->contains('href="' . $manageLink . '">Manage subscription');
return ['response' => true];
},
]
),
$endpoint = $this->createNewslettersEndpointWithMocks([
'sendPreviewController' => $this->make(SendPreviewController::class, [
'sendPreview' => null,
]),
]);
$data = [
@ -875,27 +847,12 @@ class NewslettersTest extends \MailPoetTest {
public function testItReturnsMailerErrorWhenSendingFailed() {
$subscriber = 'test@subscriber.com';
$endpoint = Stub::copy($this->endpoint, [
'mailer' => $this->makeEmpty(
Mailer::class,
[
'send' => function ($newsletter, $subscriber) {
expect(is_array($newsletter))->true();
expect($newsletter['body']['text'])->contains('Hello test');
expect($subscriber)->equals($subscriber);
return [
'response' => false,
'error' => $this->make(
MailerError::class,
[
'getMessage' => 'failed',
]
),
];
},
]
),
$endpoint = $this->createNewslettersEndpointWithMocks([
'sendPreviewController' => $this->make(SendPreviewController::class, [
'sendPreview' => Expected::once(function () {
throw new SendPreviewException('The email could not be sent: failed');
}),
]),
]);
$data = [
@ -1050,10 +1007,9 @@ class NewslettersTest extends \MailPoetTest {
$this->diContainer->get(NewsletterListingRepository::class),
$this->diContainer->get(NewslettersResponseBuilder::class),
$this->diContainer->get(PostNotificationScheduler::class),
$this->diContainer->get(Mailer::class),
$this->diContainer->get(MetaInfo::class),
$mocks['emoji'] ?? $this->diContainer->get(Emoji::class),
$mocks['subscribersFeature'] ?? $this->diContainer->get(SubscribersFeature::class)
$mocks['subscribersFeature'] ?? $this->diContainer->get(SubscribersFeature::class),
$mocks['sendPreviewController'] ?? $this->diContainer->get(SendPreviewController::class)
);
}
}

View File

@ -0,0 +1,83 @@
<?php declare(strict_types = 1);
namespace MailPoet\Newsletter\Preview;
use Codeception\Stub\Expected;
use Codeception\Util\Fixtures;
use MailPoet\Mailer\Mailer;
use MailPoet\Mailer\MailerError;
use MailPoet\Mailer\MetaInfo;
use MailPoet\Models\Newsletter;
use MailPoet\Newsletter\Url;
use MailPoet\Subscription\SubscriptionUrlFactory;
use MailPoet\WP\Functions as WPFunctions;
class SendPrevewControllerTest extends \MailPoetTest {
/** @var SubscriptionUrlFactory */
private $subscriptionUrlFactory;
/** @var Newsletter */
private $newsletter;
public function _before() {
parent::_before();
$this->subscriptionUrlFactory = SubscriptionUrlFactory::getInstance();
$this->newsletter = Newsletter::createOrUpdate([
'subject' => 'My Standard Newsletter',
'preheader' => 'preheader',
'body' => Fixtures::get('newsletter_body_template'),
'type' => Newsletter::TYPE_STANDARD,
]);
}
public function testItCanSendAPreview() {
$mailer = $this->makeEmpty(Mailer::class, [
'send' => Expected::once(
function ($newsletter, $subscriber, $extraParams) {
$unsubscribeLink = $this->subscriptionUrlFactory->getConfirmUnsubscribeUrl(null);
$manageLink = $this->subscriptionUrlFactory->getManageUrl(null);
$viewInBrowserLink = Url::getViewInBrowserUrl($this->newsletter);
$mailerMetaInfo = new MetaInfo;
expect(is_array($newsletter))->true();
expect($newsletter['body']['text'])->contains('Hello test');
expect($subscriber)->equals($subscriber);
expect($extraParams['unsubscribe_url'])->equals(home_url());
expect($extraParams['meta'])->equals($mailerMetaInfo->getPreviewMetaInfo());
// system links are replaced with hashes
expect($newsletter['body']['html'])->contains('href="' . $viewInBrowserLink . '">View in browser');
expect($newsletter['body']['html'])->contains('href="' . $unsubscribeLink . '">Unsubscribe');
expect($newsletter['body']['html'])->contains('href="' . $manageLink . '">Manage subscription');
return ['response' => true];
}
),
]);
$sendPreviewController = new SendPreviewController($mailer, new MetaInfo(), new WPFunctions());
$sendPreviewController->sendPreview($this->newsletter, 'test@subscriber.com');
}
public function testItThrowsWhenSendingFailed() {
$mailer = $this->makeEmpty(Mailer::class, [
'send' => function ($newsletter, $subscriber) {
expect(is_array($newsletter))->true();
expect($newsletter['body']['text'])->contains('Hello test');
expect($subscriber)->equals($subscriber);
return [
'response' => false,
'error' => $this->make(MailerError::class, [
'getMessage' => 'failed',
]),
];
},
]);
$this->expectException(SendPreviewException::class);
$this->expectExceptionMessage('The email could not be sent: failed');
$sendPreviewController = new SendPreviewController($mailer, new MetaInfo(), new WPFunctions());
$sendPreviewController->sendPreview($this->newsletter, 'test@subscriber.com');
}
}