Add abandoned cart block rendering in email [MAILPOET-2979]
This commit is contained in:
@@ -13,6 +13,7 @@ use MailPoet\WP\Functions as WPFunctions;
|
||||
class AbandonedCart {
|
||||
const SLUG = 'woocommerce_abandoned_shopping_cart';
|
||||
const LAST_VISIT_TIMESTAMP_OPTION_NAME = 'mailpoet_last_visit_timestamp';
|
||||
const TASK_META_NAME = 'cart_product_ids';
|
||||
|
||||
/** @var WPFunctions */
|
||||
private $wp;
|
||||
@@ -130,7 +131,7 @@ class AbandonedCart {
|
||||
public function handleCartChange() {
|
||||
$cart = $this->wooCommerceHelper->WC()->cart;
|
||||
if ($cart && !$cart->is_empty()) {
|
||||
$this->scheduleAbandonedCartEmail();
|
||||
$this->scheduleAbandonedCartEmail($this->getCartProductIds($cart));
|
||||
} else {
|
||||
$this->cancelAbandonedCartEmail();
|
||||
$this->pageVisitTracker->stopTracking();
|
||||
@@ -145,13 +146,19 @@ class AbandonedCart {
|
||||
});
|
||||
}
|
||||
|
||||
private function scheduleAbandonedCartEmail() {
|
||||
private function getCartProductIds($cart) {
|
||||
$cartItems = $cart->get_cart() ?: [];
|
||||
return array_column($cartItems, 'product_id');
|
||||
}
|
||||
|
||||
private function scheduleAbandonedCartEmail(array $cartProductIds = []) {
|
||||
$subscriber = $this->getSubscriber();
|
||||
if (!$subscriber || $subscriber->status !== Subscriber::STATUS_SUBSCRIBED) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->scheduler->scheduleOrRescheduleAutomaticEmail(WooCommerceEmail::SLUG, self::SLUG, $subscriber->id);
|
||||
$meta = [self::TASK_META_NAME => $cartProductIds];
|
||||
$this->scheduler->scheduleOrRescheduleAutomaticEmail(WooCommerceEmail::SLUG, self::SLUG, $subscriber->id, $meta);
|
||||
|
||||
// start tracking page visits to detect inactivity
|
||||
$this->pageVisitTracker->startTracking();
|
||||
|
@@ -114,7 +114,7 @@ class Newsletter {
|
||||
// hook to the newsletter post-processing filter and add tracking image
|
||||
$this->trackingImageInserted = OpenTracking::addTrackingImage();
|
||||
// render newsletter
|
||||
$renderedNewsletter = $this->renderer->render($newsletter);
|
||||
$renderedNewsletter = $this->renderer->render($newsletter, false, false, $sendingTask);
|
||||
$renderedNewsletter = $this->wp->applyFilters(
|
||||
'mailpoet_sending_newsletter_render_after',
|
||||
$renderedNewsletter,
|
||||
|
@@ -2,13 +2,17 @@
|
||||
|
||||
namespace MailPoet\Newsletter\Renderer\Blocks;
|
||||
|
||||
use MailPoet\AutomaticEmails\WooCommerce\Events\AbandonedCart;
|
||||
use MailPoet\AutomaticEmails\WooCommerce\WooCommerce as WooCommerceEmail;
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Entities\NewsletterOptionEntity;
|
||||
use MailPoet\Entities\NewsletterPostEntity;
|
||||
use MailPoet\Models\Newsletter;
|
||||
use MailPoet\Newsletter\AutomatedLatestContent;
|
||||
use MailPoet\Newsletter\NewsletterPostsRepository;
|
||||
use MailPoet\Newsletter\Renderer\Columns\ColumnsHelper;
|
||||
use MailPoet\Newsletter\Renderer\StylesHelper;
|
||||
use MailPoet\Tasks\Sending as SendingTask;
|
||||
|
||||
class Renderer {
|
||||
/**
|
||||
@@ -163,6 +167,48 @@ class Renderer {
|
||||
return $this->renderBlocksInColumn($newsletter, $transformedPosts, $columnBaseWidth);
|
||||
}
|
||||
|
||||
public function abandonedCartContentTransformedProducts(
|
||||
NewsletterEntity $newsletter,
|
||||
array $args,
|
||||
bool $preview = false,
|
||||
SendingTask $sendingTask = null
|
||||
): array {
|
||||
if ($newsletter->getType() !== NewsletterEntity::TYPE_AUTOMATIC) {
|
||||
// Do not display the block if not an automatic email
|
||||
return [];
|
||||
}
|
||||
$groupOption = $newsletter->getOptions()->filter(function (NewsletterOptionEntity $newsletterOption) {
|
||||
$optionField = $newsletterOption->getOptionField();
|
||||
return $optionField && $optionField->getName() === 'group';
|
||||
})->first();
|
||||
$eventOption = $newsletter->getOptions()->filter(function (NewsletterOptionEntity $newsletterOption) {
|
||||
$optionField = $newsletterOption->getOptionField();
|
||||
return $optionField && $optionField->getName() === 'event';
|
||||
})->first();
|
||||
if ($groupOption->getValue() !== WooCommerceEmail::SLUG
|
||||
|| $eventOption->getValue() !== AbandonedCart::SLUG
|
||||
) {
|
||||
// Do not display the block if not an AbandonedCart email
|
||||
return [];
|
||||
}
|
||||
if ($preview) {
|
||||
// Display latest products for preview (no 'posts' argument specified)
|
||||
return $this->automatedLatestContentTransformedPosts($newsletter, $args);
|
||||
}
|
||||
if (!($sendingTask instanceof SendingTask)) {
|
||||
// Do not display the block if we're not sending an email
|
||||
return [];
|
||||
}
|
||||
$meta = $sendingTask->getMeta();
|
||||
if (empty($meta[AbandonedCart::TASK_META_NAME])) {
|
||||
// Do not display the block if a cart is empty
|
||||
return [];
|
||||
}
|
||||
$args['amount'] = 50;
|
||||
$args['posts'] = $meta[AbandonedCart::TASK_META_NAME];
|
||||
return $this->automatedLatestContentTransformedPosts($newsletter, $args);
|
||||
}
|
||||
|
||||
private function getRenderedPosts(int $newsletterId) {
|
||||
return $this->renderedPostsInNewsletter[$newsletterId] ?? [];
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ namespace MailPoet\Newsletter\Renderer;
|
||||
use MailPoet\Entities\NewsletterEntity;
|
||||
use MailPoet\Newsletter\Editor\LayoutHelper;
|
||||
use MailPoet\Newsletter\Renderer\Blocks\Renderer as BlocksRenderer;
|
||||
use MailPoet\Tasks\Sending as SendingTask;
|
||||
use MailPoet\WooCommerce\TransactionalEmails;
|
||||
|
||||
class Preprocessor {
|
||||
@@ -36,20 +37,25 @@ class Preprocessor {
|
||||
* @param NewsletterEntity $newsletter
|
||||
* @return array
|
||||
*/
|
||||
public function process(NewsletterEntity $newsletter, $content) {
|
||||
public function process(NewsletterEntity $newsletter, $content, bool $preview = false, SendingTask $sendingTask = null) {
|
||||
if (!array_key_exists('blocks', $content)) {
|
||||
return $content;
|
||||
}
|
||||
$blocks = [];
|
||||
foreach ($content['blocks'] as $block) {
|
||||
$blocks = array_merge($blocks, $this->processBlock($newsletter, $block));
|
||||
$processedBlock = $this->processBlock($newsletter, $block, $preview, $sendingTask);
|
||||
if (!empty($processedBlock)) {
|
||||
$blocks = array_merge($blocks, $processedBlock);
|
||||
}
|
||||
}
|
||||
$content['blocks'] = $blocks;
|
||||
return $content;
|
||||
}
|
||||
|
||||
public function processBlock(NewsletterEntity $newsletter, array $block): array {
|
||||
public function processBlock(NewsletterEntity $newsletter, array $block, bool $preview = false, SendingTask $sendingTask = null): array {
|
||||
switch ($block['type']) {
|
||||
case 'abandonedCartContent':
|
||||
return $this->blocksRenderer->abandonedCartContentTransformedProducts($newsletter, $block, $preview, $sendingTask);
|
||||
case 'automatedLatestContentLayout':
|
||||
return $this->blocksRenderer->automatedLatestContentTransformedPosts($newsletter, $block);
|
||||
case 'woocommerceHeading':
|
||||
|
@@ -9,6 +9,7 @@ use MailPoet\Newsletter\NewslettersRepository;
|
||||
use MailPoet\Newsletter\Renderer\EscapeHelper as EHelper;
|
||||
use MailPoet\RuntimeException;
|
||||
use MailPoet\Services\Bridge;
|
||||
use MailPoet\Tasks\Sending as SendingTask;
|
||||
use MailPoet\Util\License\License;
|
||||
use MailPoet\Util\pQuery\DomNode;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
@@ -69,7 +70,7 @@ class Renderer {
|
||||
return $this->newslettersRepository->findOneById($newsletterId);
|
||||
}
|
||||
|
||||
public function render($newsletter, $preview = false, $type = false) {
|
||||
public function render($newsletter, $preview = false, $type = false, SendingTask $sendingTask = null) {
|
||||
$newsletter = $this->getNewsletter($newsletter);
|
||||
if (!$newsletter instanceof NewsletterEntity) {
|
||||
throw new RuntimeException('Newsletter was not found');
|
||||
@@ -92,7 +93,7 @@ class Renderer {
|
||||
$content = $this->addMailpoetLogoContentBlock($content, $styles);
|
||||
}
|
||||
|
||||
$content = $this->preprocessor->process($newsletter, $content);
|
||||
$content = $this->preprocessor->process($newsletter, $content, $preview, $sendingTask);
|
||||
$renderedBody = $this->renderBody($newsletter, $content);
|
||||
$renderedStyles = $this->renderStyles($styles);
|
||||
$customFontsLinks = StylesHelper::getCustomFontsLinks($styles);
|
||||
|
@@ -33,7 +33,7 @@ class AutomaticEmailScheduler {
|
||||
// try to find existing scheduled task for given subscriber
|
||||
$task = ScheduledTask::findOneScheduledByNewsletterIdAndSubscriberId($newsletter->id, $subscriberId);
|
||||
if ($task) {
|
||||
$this->rescheduleAutomaticEmailSendingTask($newsletter, $task);
|
||||
$this->rescheduleAutomaticEmailSendingTask($newsletter, $task, $meta);
|
||||
} else {
|
||||
$this->createAutomaticEmailSendingTask($newsletter, $subscriberId, $meta);
|
||||
}
|
||||
@@ -80,7 +80,7 @@ class AutomaticEmailScheduler {
|
||||
}
|
||||
}
|
||||
|
||||
public function createAutomaticEmailSendingTask($newsletter, $subscriberId, $meta) {
|
||||
public function createAutomaticEmailSendingTask($newsletter, $subscriberId, $meta = false) {
|
||||
$sendingTask = SendingTask::create();
|
||||
$sendingTask->newsletterId = $newsletter->id;
|
||||
if ($newsletter->sendTo === 'user' && $subscriberId) {
|
||||
@@ -96,7 +96,10 @@ class AutomaticEmailScheduler {
|
||||
return $sendingTask->save();
|
||||
}
|
||||
|
||||
private function rescheduleAutomaticEmailSendingTask($newsletter, $task) {
|
||||
private function rescheduleAutomaticEmailSendingTask($newsletter, $task, $meta = false) {
|
||||
if ($meta) {
|
||||
$task->__set('meta', $meta);
|
||||
}
|
||||
// compute new 'scheduled_at' from now
|
||||
$task->scheduledAt = Scheduler::getScheduledTimeWithDelay($newsletter->afterTimeType, $newsletter->afterTimeNumber);
|
||||
$task->save();
|
||||
|
@@ -216,6 +216,10 @@ class Sending {
|
||||
return $this->queue->validate() && $this->task->validate();
|
||||
}
|
||||
|
||||
public function getMeta() {
|
||||
return $this->queue->getMeta();
|
||||
}
|
||||
|
||||
public function __isset($prop) {
|
||||
$prop = Helpers::camelCaseToUnderscore($prop);
|
||||
if ($this->isQueueProperty($prop)) {
|
||||
|
Reference in New Issue
Block a user