diff --git a/mailpoet/assets/js/src/newsletter-editor/blocks/coupon.tsx b/mailpoet/assets/js/src/newsletter-editor/blocks/coupon.tsx index b5c81f8d6a..a65f422513 100644 --- a/mailpoet/assets/js/src/newsletter-editor/blocks/coupon.tsx +++ b/mailpoet/assets/js/src/newsletter-editor/blocks/coupon.tsx @@ -19,6 +19,7 @@ Module.CouponBlockModel = base.BlockModel.extend({ return this._getDefaults( { isStandardEmail: App.getNewsletter().isStandardEmail(), + isAutomationEmail: App.getNewsletter().isAutomationEmail(), productIds: [], // selected product ids, excludedProductIds: [], productCategoryIds: [], // selected categories id @@ -33,6 +34,10 @@ Module.CouponBlockModel = base.BlockModel.extend({ minimumAmount: '', maximumAmount: '', emailRestrictions: '', + restrictToSubscriber: false, + showRestrictToSubscriber: + App.getNewsletter().isAutomationEmail() || + App.getNewsletter().isWelcomeEmail(), styles: { block: { backgroundColor: '#ffffff', diff --git a/mailpoet/assets/js/src/newsletter-editor/blocks/coupon/usage-restriction.tsx b/mailpoet/assets/js/src/newsletter-editor/blocks/coupon/usage-restriction.tsx index 650c0a2b61..fee556f060 100644 --- a/mailpoet/assets/js/src/newsletter-editor/blocks/coupon/usage-restriction.tsx +++ b/mailpoet/assets/js/src/newsletter-editor/blocks/coupon/usage-restriction.tsx @@ -31,6 +31,7 @@ type State = { productCategoryIds: Post[]; excludedProductCategoryIds: Post[]; emailRestrictions: string; + restrictToSubscriber: boolean; }; class UsageRestriction extends Component { @@ -62,6 +63,8 @@ class UsageRestriction extends Component { 'excludedProductCategoryIds', ).toJSON() as Post[], emailRestrictions: this.getValueCallback('emailRestrictions') as string, + restrictToSubscriber: + (this.getValueCallback('restrictToSubscriber') as boolean) || false, }; } @@ -262,6 +265,25 @@ class UsageRestriction extends Component { )} /> + {this.getValueCallback('showRestrictToSubscriber') && ( + + { + this.setValueCallback( + 'restrictToSubscriber', + restrictToSubscriber, + ); + this.setState({ restrictToSubscriber }); + }} + help={__( + 'Restrict coupon usage to the subscriber receiving this email.', + 'mailpoet', + )} + /> + + )} ); diff --git a/mailpoet/assets/js/src/newsletter-editor/components/content.js b/mailpoet/assets/js/src/newsletter-editor/components/content.js index 0eba43ee87..f79122ed60 100644 --- a/mailpoet/assets/js/src/newsletter-editor/components/content.js +++ b/mailpoet/assets/js/src/newsletter-editor/components/content.js @@ -35,6 +35,9 @@ Module.NewsletterModel = SuperModel.extend({ isStandardEmail: function isStandardEmail() { return this.get('type') === NewsletterType.Standard; }, + isWelcomeEmail: function isWelcomeEmail() { + return this.get('type') === NewsletterType.Welcome; + }, }); // Content block view and model handlers for different content types diff --git a/mailpoet/lib/Newsletter/Renderer/Preprocessor.php b/mailpoet/lib/Newsletter/Renderer/Preprocessor.php index 64c78d782c..e23b68d22f 100644 --- a/mailpoet/lib/Newsletter/Renderer/Preprocessor.php +++ b/mailpoet/lib/Newsletter/Renderer/Preprocessor.php @@ -53,7 +53,7 @@ class Preprocessor { return $content; } $contentBlocks = $content['blocks']; - $contentBlocks = $this->couponPreProcessor->processCoupons($newsletter, $contentBlocks, $preview); + $contentBlocks = $this->couponPreProcessor->processCoupons($newsletter, $contentBlocks, $preview, $sendingQueue); $content['blocks'] = $this->processContainer($newsletter, $contentBlocks, $preview, $sendingQueue); return $content; } diff --git a/mailpoet/lib/WooCommerce/CouponPreProcessor.php b/mailpoet/lib/WooCommerce/CouponPreProcessor.php index 119afc61df..e5024b247d 100644 --- a/mailpoet/lib/WooCommerce/CouponPreProcessor.php +++ b/mailpoet/lib/WooCommerce/CouponPreProcessor.php @@ -3,6 +3,7 @@ namespace MailPoet\WooCommerce; use MailPoet\Entities\NewsletterEntity; +use MailPoet\Entities\SendingQueueEntity; use MailPoet\Newsletter\NewslettersRepository; use MailPoet\Newsletter\Renderer\Blocks\Coupon; use MailPoet\NewsletterProcessingException; @@ -30,12 +31,12 @@ class CouponPreProcessor { /** * @throws NewsletterProcessingException */ - public function processCoupons(NewsletterEntity $newsletter, array $blocks, bool $preview = false): array { + public function processCoupons(NewsletterEntity $newsletter, array $blocks, bool $preview = false, ?SendingQueueEntity $sendingQueue = null): array { if ($preview) { return $blocks; } - $generated = $this->ensureCouponForBlocks($blocks, $newsletter); + $generated = $this->ensureCouponForBlocks($blocks, $newsletter, $sendingQueue); $body = $newsletter->getBody(); if ($generated && $body && $this->shouldPersist($newsletter)) { @@ -54,11 +55,10 @@ class CouponPreProcessor { return $blocks; } - private function ensureCouponForBlocks(array &$blocks, NewsletterEntity $newsletter): bool { - + private function ensureCouponForBlocks(array &$blocks, NewsletterEntity $newsletter, ?SendingQueueEntity $sendingQueue): bool { foreach ($blocks as &$innerBlock) { if (isset($innerBlock['blocks']) && !empty($innerBlock['blocks'])) { - $this->ensureCouponForBlocks($innerBlock['blocks'], $newsletter); + $this->ensureCouponForBlocks($innerBlock['blocks'], $newsletter, $sendingQueue); } if (isset($innerBlock['type']) && $innerBlock['type'] === Coupon::TYPE) { if (!$this->wcHelper->isWooCommerceActive()) { @@ -66,7 +66,7 @@ class CouponPreProcessor { } if ($this->shouldGenerateCoupon($innerBlock)) { try { - $innerBlock['couponId'] = $this->addOrUpdateCoupon($innerBlock, $newsletter); + $innerBlock['couponId'] = $this->addOrUpdateCoupon($innerBlock, $newsletter, $sendingQueue); $this->generated = true; } catch (\Exception $e) { throw NewsletterProcessingException::create()->withMessage($e->getMessage())->withCode($e->getCode()); @@ -81,10 +81,11 @@ class CouponPreProcessor { /** * @param array $couponBlock * @param NewsletterEntity $newsletter + * @param SendingQueueEntity|null $sendingQueue * @return int * @throws \WC_Data_Exception|\Exception */ - private function addOrUpdateCoupon(array $couponBlock, NewsletterEntity $newsletter) { + private function addOrUpdateCoupon(array $couponBlock, NewsletterEntity $newsletter, ?SendingQueueEntity $sendingQueue) { $coupon = $this->wcHelper->createWcCoupon($couponBlock['couponId'] ?? ''); if ($this->shouldGenerateCoupon($couponBlock)) { $code = isset($couponBlock['code']) && $couponBlock['code'] !== Coupon::CODE_PLACEHOLDER ? $couponBlock['code'] : $this->generateRandomCode(); @@ -128,7 +129,24 @@ class CouponPreProcessor { $coupon->set_product_categories($this->getItemIds($couponBlock['productCategoryIds'] ?? [])); $coupon->set_excluded_product_categories($this->getItemIds($couponBlock['excludedProductCategoryIds'] ?? [])); - $coupon->set_email_restrictions(explode(',', $couponBlock['emailRestrictions'] ?? '')); + $emailRestrictions = []; + if (!empty($couponBlock['emailRestrictions'])) { + $emailRestrictions = explode(',', $couponBlock['emailRestrictions']); + } + + if (!empty($couponBlock['restrictToSubscriber']) && $sendingQueue && $sendingQueue->getTask()) { + $subscribers = $sendingQueue->getTask()->getSubscribers(); + if (is_iterable($subscribers) && count($subscribers) === 1) { // Only apply to single-subscriber sending queues + foreach ($subscribers as $taskSubscriber) { + $subscriber = $taskSubscriber->getSubscriber(); + if ($subscriber && $subscriber->getEmail()) { + $emailRestrictions[] = $subscriber->getEmail(); + } + } + } + } + + $coupon->set_email_restrictions(array_unique(array_filter($emailRestrictions))); // usage limit $coupon->set_usage_limit($couponBlock['usageLimit'] ?? 0);