From fcf6e738c7e811b8a6b44fd78addb8cd4f12ddb6 Mon Sep 17 00:00:00 2001 From: David Remer Date: Thu, 16 Feb 2023 08:31:43 +0200 Subject: [PATCH] Interrupt automation run creation if a run already exists and only_once setting is set [MAILPOET-4966] --- .../Engine/Storage/AutomationRunStorage.php | 21 ++++++++ .../Hooks/CreateAutomationRunHook.php | 49 +++++++++++++++++++ .../MailPoet/MailPoetIntegration.php | 10 +++- mailpoet/lib/DI/ContainerConfigurator.php | 1 + mailpoet/tasks/phpstan/phpstan.neon | 2 +- 5 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 mailpoet/lib/Automation/Integrations/MailPoet/Hooks/CreateAutomationRunHook.php diff --git a/mailpoet/lib/Automation/Engine/Storage/AutomationRunStorage.php b/mailpoet/lib/Automation/Engine/Storage/AutomationRunStorage.php index f2323e6931..411f49bed8 100644 --- a/mailpoet/lib/Automation/Engine/Storage/AutomationRunStorage.php +++ b/mailpoet/lib/Automation/Engine/Storage/AutomationRunStorage.php @@ -4,6 +4,7 @@ namespace MailPoet\Automation\Engine\Storage; use MailPoet\Automation\Engine\Data\Automation; use MailPoet\Automation\Engine\Data\AutomationRun; +use MailPoet\Automation\Engine\Data\Subject; use MailPoet\Automation\Engine\Exceptions; use wpdb; @@ -108,6 +109,26 @@ class AutomationRunStorage { ); } + /** + * @param Automation $automation + * @return int + */ + public function countRunsForAutomationAndSubject(Automation $automation, Subject $subject): int { + $table = esc_sql($this->table); + $subjectTable = esc_sql($this->subjectTable); + + $sql = "SELECT count(runs.id) as count from $table as runs + JOIN $subjectTable as subjects on runs.id = subjects.automation_run_id + WHERE runs.automation_id = %d + AND subjects.hash = %s"; + + $result = $this->wpdb->get_col( + (string)$this->wpdb->prepare($sql, $automation->getId(), $subject->hash()) + ); + + return $result ? (int)current($result) : 0; + } + public function getCountForAutomation(Automation $automation, string ...$status): int { if (!count($status)) { return 0; diff --git a/mailpoet/lib/Automation/Integrations/MailPoet/Hooks/CreateAutomationRunHook.php b/mailpoet/lib/Automation/Integrations/MailPoet/Hooks/CreateAutomationRunHook.php new file mode 100644 index 0000000000..c7ccd4285d --- /dev/null +++ b/mailpoet/lib/Automation/Integrations/MailPoet/Hooks/CreateAutomationRunHook.php @@ -0,0 +1,49 @@ +wp = $wp; + $this->automationRunStorage = $automationRunStorage; + } + + public function init(): void { + $this->wp->addAction(Hooks::AUTOMATION_RUN_CREATE, [$this, 'createAutomationRun'], 5, 2); + } + + public function createAutomationRun(bool $result, StepRunArgs $args): bool { + if (!$result) { + return $result; + } + + $automation = $args->getAutomation(); + $runOnlyOnce = $automation->getMeta('run_automation_once'); + if (!$runOnlyOnce) { + return true; + } + + $subscriberSubject = $args->getAutomationRun()->getSubjects(SubscriberSubject::KEY); + if (!$subscriberSubject) { + return true; + } + + return $this->automationRunStorage->countRunsForAutomationAndSubject($automation, current($subscriberSubject)) === 0; + } +} diff --git a/mailpoet/lib/Automation/Integrations/MailPoet/MailPoetIntegration.php b/mailpoet/lib/Automation/Integrations/MailPoet/MailPoetIntegration.php index 515c21fc23..c5ca3a8ccc 100644 --- a/mailpoet/lib/Automation/Integrations/MailPoet/MailPoetIntegration.php +++ b/mailpoet/lib/Automation/Integrations/MailPoet/MailPoetIntegration.php @@ -6,6 +6,7 @@ use MailPoet\Automation\Engine\Integration; use MailPoet\Automation\Engine\Registry; use MailPoet\Automation\Integrations\MailPoet\Actions\SendEmailAction; use MailPoet\Automation\Integrations\MailPoet\Hooks\AutomationEditorLoadingHooks; +use MailPoet\Automation\Integrations\MailPoet\Hooks\CreateAutomationRunHook; use MailPoet\Automation\Integrations\MailPoet\Subjects\SegmentSubject; use MailPoet\Automation\Integrations\MailPoet\Subjects\SubscriberSubject; use MailPoet\Automation\Integrations\MailPoet\Triggers\SomeoneSubscribesTrigger; @@ -30,8 +31,12 @@ class MailPoetIntegration implements Integration { /** @var SendEmailAction */ private $sendEmailAction; + /** @var AutomationEditorLoadingHooks */ private $automationEditorLoadingHooks; + /** @var CreateAutomationRunHook */ + private $createAutomationRunHook; + public function __construct( ContextFactory $contextFactory, SegmentSubject $segmentSubject, @@ -39,7 +44,8 @@ class MailPoetIntegration implements Integration { SomeoneSubscribesTrigger $someoneSubscribesTrigger, UserRegistrationTrigger $userRegistrationTrigger, SendEmailAction $sendEmailAction, - AutomationEditorLoadingHooks $automationEditorLoadingHooks + AutomationEditorLoadingHooks $automationEditorLoadingHooks, + CreateAutomationRunHook $createAutomationRunHook ) { $this->contextFactory = $contextFactory; $this->segmentSubject = $segmentSubject; @@ -48,6 +54,7 @@ class MailPoetIntegration implements Integration { $this->userRegistrationTrigger = $userRegistrationTrigger; $this->sendEmailAction = $sendEmailAction; $this->automationEditorLoadingHooks = $automationEditorLoadingHooks; + $this->createAutomationRunHook = $createAutomationRunHook; } public function register(Registry $registry): void { @@ -68,5 +75,6 @@ class MailPoetIntegration implements Integration { ); $this->automationEditorLoadingHooks->init(); + $this->createAutomationRunHook->init(); } } diff --git a/mailpoet/lib/DI/ContainerConfigurator.php b/mailpoet/lib/DI/ContainerConfigurator.php index 8b4eac9632..52f741a317 100644 --- a/mailpoet/lib/DI/ContainerConfigurator.php +++ b/mailpoet/lib/DI/ContainerConfigurator.php @@ -162,6 +162,7 @@ class ContainerConfigurator implements IContainerConfigurator { $container->autowire(\MailPoet\Automation\Integrations\MailPoet\Templates\AutomationBuilder::class)->setPublic(true)->setShared(false); $container->autowire(\MailPoet\Automation\Integrations\MailPoet\Actions\SendEmailAction::class)->setPublic(true); $container->autowire(\MailPoet\Automation\Integrations\MailPoet\Hooks\AutomationEditorLoadingHooks::class)->setPublic(true); + $container->autowire(\MailPoet\Automation\Integrations\MailPoet\Hooks\CreateAutomationRunHook::class)->setPublic(true); // Config $container->autowire(\MailPoet\Config\AccessControl::class)->setPublic(true); $container->autowire(\MailPoet\Config\Activator::class)->setPublic(true); diff --git a/mailpoet/tasks/phpstan/phpstan.neon b/mailpoet/tasks/phpstan/phpstan.neon index 5d0680a379..400bc4522c 100644 --- a/mailpoet/tasks/phpstan/phpstan.neon +++ b/mailpoet/tasks/phpstan/phpstan.neon @@ -44,7 +44,7 @@ parameters: - '/Call to method getName\(\) on an unknown class _generated\\([a-zA-Z])*Cookie/' # codeception generate incorrect return type in ../../tests/_support/_generated - message: "#^Cannot cast string|void to string\\.$#" - count: 9 + count: 10 path: ../../lib/Automation/Engine/Storage/AutomationRunStorage.php - message: "#^Cannot cast string|void to string\\.$#"