aximumAmount']); $coupon->set_individual_use($validatedAttrs['individualUse']); $coupon->set_exclude_sale_items($validatedAttrs['excludeSaleItems']); $coupon->set_product_ids($validatedAttrs['productIds']); $coupon->set_excluded_product_ids($validatedAttrs['excludedProductIds']); $coupon->set_product_categories($validatedAttrs['productCategoryIds']); $coupon->set_excluded_product_categories($validatedAttrs['excludedProductCategoryIds']); $coupon->set_email_restrictions($validatedAttrs['emailRestrictions']); $coupon->set_usage_limit($validatedAttrs['usageLimit']); $coupon->set_usage_limit_per_user($validatedAttrs['usageLimitPerUser']); $couponId = $coupon->save(); if (!$couponId) { throw new \RuntimeException('WooCommerce did not save the generated coupon.'); } return $code; } catch (\Throwable $e) { $this->recordFailure('generation_failed', $e->getMessage(), $attrs, $context); return self::SAFE_PLACEHOLDER; } } public function registerEmailRenderer(): void { if (!class_exists('\WP_Block_Type_Registry')) { return; } $blockType = \WP_Block_Type_Registry::get_instance()->get_registered(CouponBlock::NAME); $renderEmailCallbackProperty = 'render_email_callback'; $currentCallback = $blockType ? (get_object_vars($blockType)[$renderEmailCallbackProperty] ?? null) : null; if (!$blockType || $this->usesWooCommerceEmailEditorRenderer($currentCallback)) { return; } // WooCommerce 10.8 development builds can expose the block attributes // without wiring the matching email renderer. Patch only that gap. // @phpstan-ignore-next-line -- WooCommerce email editor reads this dynamic block setting. $blockType->{$renderEmailCallbackProperty} = [$this, 'renderEmailCouponBlock']; } public function renderEmailCouponBlock(string $blockContent, array $parsedBlock, Rendering_Context $renderingContext): string { $attrs = $parsedBlock['attrs'] ?? []; if (!is_array($attrs)) { $attrs = []; } if (!CouponBlock::isCreateNew($attrs, strpos($blockContent, self::SAFE_PLACEHOLDER) !== false)) { return $blockContent; } $couponCode = $this->wp->applyFilters( 'woocommerce_coupon_code_block_auto_generate', '', $attrs, $renderingContext ); $couponCode = is_scalar($couponCode) ? (string)$couponCode : ''; if ($couponCode === '') { return ''; } return str_replace(self::SAFE_PLACEHOLDER, $this->wp->escHtml($couponCode), $blockContent); } private function usesWooCommerceEmailEditorRenderer($callback): bool { if (!is_array($callback) || !isset($callback[0]) || !is_object($callback[0])) { return false; } return strpos(get_class($callback[0]), 'Automattic\\WooCommerce\\EmailEditor\\Integrations\\WooCommerce\\') === 0; } private function getUnsupportedRealSendContextMessage(array $attrs, Rendering_Context $renderingContext): ?string { $requiresRecipientEmail = CouponBlockAttributeParser::toBoolean($attrs['restrictToSubscriber'] ?? false); if ($this->isSupportedAutomationContext($renderingContext, $requiresRecipientEmail)) { return null; } if ($requiresRecipientEmail) { return 'Recipient-restricted generated coupons are only supported in automation emails sent to one subscriber at a time.'; } if ($this->isSupportedStandardContext($renderingContext)) { return null; } return 'Auto-generated coupon codes are only supported in regular newsletters and automation emails sent to one subscriber at a time.'; } private function isSupportedAutomationContext(Rendering_Context $renderingContext, bool $requiresRecipientEmail): bool { $context = $renderingContext->get_email_context(); $recipientEmail = $renderingContext->get_recipient_email(); $automationTypes = [ NewsletterEntity::TYPE_AUTOMATION, NewsletterEntity::TYPE_AUTOMATION_NOTIFICATION, NewsletterEntity::TYPE_AUTOMATION_TRANSACTIONAL, ]; return ($context['integration'] ?? null) === 'mailpoet' && ($context['is_real_send'] ?? null) === true && ($context['is_preview'] ?? null) === false && ($context['is_single_recipient'] ?? null) === true && is_numeric($context['subscriber_count'] ?? null) && (int)$context['subscriber_count'] === 1 && ($context['mailpoet_is_automation'] ?? null) === true && in_array($context['email_type'] ?? '', $automationTypes, true) && ( !$requiresRecipientEmail || (is_string($recipientEmail) && (bool)$this->wp->isEmail($recipientEmail)) ); } private function isSupportedStandardContext(Rendering_Context $renderingContext): bool { $context = $renderingContext->get_email_context(); return ($context['integration'] ?? null) === 'mailpoet' && ($context['is_real_send'] ?? null) === true && ($context['is_preview'] ?? null) === false && ($context['email_type'] ?? '') === NewsletterEntity::TYPE_STANDARD && ($context['mailpoet_is_automation'] ?? null) === false && is_numeric($context['subscriber_count'] ?? null) && (int)$context['subscriber_count'] >= 1; } private function generateUniqueCode(): string { for ($i = 0; $i < self::MAX_CODE_RETRIES; $i++) { $code = $this->randomCouponCodeGenerator->generate(); if (!$this->wcHelper->wcGetCouponIdByCode($code)) { return $code; } } throw new \RuntimeException('Failed to generate a unique coupon code.'); } private function getCouponDescription(array $context): string { return sprintf( // translators: %1$d is the MailPoet email ID. _x('Auto-generated coupon by MailPoet for email: %1$d', 'Coupon block code generation', 'mailpoet'), (int)($context['newsletter_id'] ?? 0) ); } private function recordFailure(string $code, string $message, array $attrs, array $context): void { $safeContext = array_intersect_key($context, array_flip([ 'integration', 'newsletter_id', 'queue_id', 'email_type', 'is_real_send', 'is_preview', 'is_single_recipient', 'subscriber_count', 'mailpoet_is_automation', ])); $this->failureCollector->record($code, $message, $attrs, $safeContext); } }
Fatal error: Uncaught Error: Class "MailPoet\EmailEditor\Integrations\MailPoet\Coupons\CouponBlockGenerator" not found in /htdocs/wp-content/plugins/mailpoet/generated/FreeCachedContainer.php:4213 Stack trace: #0 /htdocs/wp-content/plugins/mailpoet/generated/FreeCachedContainer.php(3187): MailPoetGenerated\FreeCachedContainer->getCouponBlockGeneratorService() #1 /htdocs/wp-content/plugins/mailpoet/generated/FreeCachedContainer.php(3217): MailPoetGenerated\FreeCachedContainer->getHooks2Service() #2 /htdocs/wp-content/plugins/mailpoet/vendor-prefixed/symfony/dependency-injection/Container.php(122): MailPoetGenerated\FreeCachedContainer->getInitializerService() #3 /htdocs/wp-content/plugins/mailpoet/vendor-prefixed/symfony/dependency-injection/Container.php(110): MailPoetVendor\Symfony\Component\DependencyInjection\Container->make('MailPoet\\Config...', 1) #4 /htdocs/wp-content/plugins/mailpoet/lib/DI/ContainerWrapper.php(39): MailPoetVendor\Symfony\Component\DependencyInjection\Container->get('MailPoet\\Config...') #5 /htdocs/wp-content/plugins/mailpoet/mailpoet_initializer.php(125): MailPoet\DI\ContainerWrapper->get('MailPoet\\Config...') #6 /htdocs/wp-content/plugins/mailpoet/mailpoet.php(181): require_once('/htdocs/wp-cont...') #7 /htdocs/wp-settings.php(589): include_once('/htdocs/wp-cont...') #8 /htdocs/wp-config.php(123): require_once('/htdocs/wp-sett...') #9 /htdocs/wp-load.php(50): require_once('/htdocs/wp-conf...') #10 /htdocs/wp-blog-header.php(13): require_once('/htdocs/wp-load...') #11 /htdocs/index.php(17): require('/htdocs/wp-blog...') #12 {main} thrown in /htdocs/wp-content/plugins/mailpoet/generated/FreeCachedContainer.php on line 4213