diff --git a/mailpoet/lib/Automation/Engine/Exceptions.php b/mailpoet/lib/Automation/Engine/Exceptions.php index 852d60bf57..204412c6f7 100644 --- a/mailpoet/lib/Automation/Engine/Exceptions.php +++ b/mailpoet/lib/Automation/Engine/Exceptions.php @@ -2,6 +2,7 @@ namespace MailPoet\Automation\Engine; +use MailPoet\Automation\Engine\Data\Step; use MailPoet\Automation\Engine\Exceptions\InvalidStateException; use MailPoet\Automation\Engine\Exceptions\NotFoundException; use MailPoet\Automation\Engine\Exceptions\UnexpectedValueException; @@ -24,6 +25,7 @@ class Exceptions { private const MULTIPLE_SUBJECTS_FOUND = 'mailpoet_automation_multiple_subjects_found'; private const WORKFLOW_STRUCTURE_MODIFICATION_NOT_SUPPORTED = 'mailpoet_automation_workflow_structure_modification_not_supported'; private const WORKFLOW_STRUCTURE_NOT_VALID = 'mailpoet_automation_workflow_structure_not_valid'; + private const WORKFLOW_STEP_MODIFIED_WHEN_UNKNOWN = 'mailpoet_automation_workflow_step_modified_when_unknon'; public function __construct() { throw new InvalidStateException( @@ -147,4 +149,18 @@ class Exceptions { // translators: %s is a detailed information ->withMessage(sprintf(__("Invalid workflow structure: %s", 'mailpoet'), $detail)); } + + public static function workflowStepModifiedWhenUnknown(Step $step): UnexpectedValueException { + return UnexpectedValueException::create() + ->withErrorCode(self::WORKFLOW_STEP_MODIFIED_WHEN_UNKNOWN) + // translators: %1$s is the key of the step, %2$s is the type of the step, %3\$s is its ID. + ->withMessage( + sprintf( + __("Modification of step '%1\$s' of type '%2\$s' with ID '%3\$s' is not supported when the related plugin is not active.", 'mailpoet'), + $step->getKey(), + $step->getType(), + $step->getId() + ) + ); + } } diff --git a/mailpoet/lib/Automation/Engine/Validation/WorkflowStepsValidator.php b/mailpoet/lib/Automation/Engine/Validation/WorkflowStepsValidator.php new file mode 100644 index 0000000000..79ed89ec03 --- /dev/null +++ b/mailpoet/lib/Automation/Engine/Validation/WorkflowStepsValidator.php @@ -0,0 +1,61 @@ +validator = $validator; + $this->registry = $registry; + $this->workflowStorage = $workflowStorage; + } + + public function validateSteps(Workflow $workflow): void { + foreach ($workflow->getSteps() as $step) { + $this->validateStep($workflow, $step); + } + } + + private function validateStep(Workflow $workflow, Step $step): void { + $registryStep = $this->registry->getStep($step->getKey()); + if ($registryStep) { + $this->validator->validate($registryStep->getArgsSchema(), $step->getArgs()); + } else { + // step not registered (e.g. plugin was deactivated) - allow saving it only if it hasn't changed + $currentWorkflow = $this->getCurrentWorkflow($workflow->getId()); + $currentStep = $currentWorkflow ? ($currentWorkflow->getSteps()[$step->getId()] ?? null) : null; + if (!$currentStep || $step->toArray() !== $currentStep->toArray()) { + throw Exceptions::workflowStepModifiedWhenUnknown($step); + } + } + } + + private function getCurrentWorkflow(int $id): ?Workflow { + if ($this->cachedExistingWorkflow === false) { + $this->cachedExistingWorkflow = $this->workflowStorage->getWorkflow($id); + } + return $this->cachedExistingWorkflow; + } +} diff --git a/mailpoet/lib/Automation/Engine/Validation/WorkflowValidator.php b/mailpoet/lib/Automation/Engine/Validation/WorkflowValidator.php index fff608f709..d957dc049b 100644 --- a/mailpoet/lib/Automation/Engine/Validation/WorkflowValidator.php +++ b/mailpoet/lib/Automation/Engine/Validation/WorkflowValidator.php @@ -13,16 +13,22 @@ use MailPoet\Automation\Engine\Validation\WorkflowRules\NoUnreachableStepsRule; use MailPoet\Automation\Engine\Validation\WorkflowRules\TriggersUnderRootRule; class WorkflowValidator { + /** @var WorkflowStepsValidator */ + private $stepsValidator; + /** @var WorkflowWalker */ private $workflowWalker; public function __construct( + WorkflowStepsValidator $stepsValidator, WorkflowWalker $workflowWalker ) { $this->workflowWalker = $workflowWalker; + $this->stepsValidator = $stepsValidator; } public function validate(Workflow $workflow): void { + // validate graph $this->workflowWalker->walk($workflow, [ new NoUnreachableStepsRule(), new ConsistentStepMapRule(), @@ -32,5 +38,8 @@ class WorkflowValidator { new NoJoinRule(), new NoSplitRule(), ]); + + // validate steps + $this->stepsValidator->validateSteps($workflow); } } diff --git a/mailpoet/lib/DI/ContainerConfigurator.php b/mailpoet/lib/DI/ContainerConfigurator.php index 0afa036538..326a3af953 100644 --- a/mailpoet/lib/DI/ContainerConfigurator.php +++ b/mailpoet/lib/DI/ContainerConfigurator.php @@ -129,6 +129,7 @@ class ContainerConfigurator implements IContainerConfigurator { $container->autowire(\MailPoet\Automation\Engine\Storage\WorkflowTemplateStorage::class)->setPublic(true); $container->autowire(\MailPoet\Automation\Engine\Storage\WorkflowStorage::class)->setPublic(true); $container->autowire(\MailPoet\Automation\Engine\Validation\WorkflowGraph\WorkflowWalker::class)->setPublic(true); + $container->autowire(\MailPoet\Automation\Engine\Validation\WorkflowStepsValidator::class)->setPublic(true); $container->autowire(\MailPoet\Automation\Engine\Validation\WorkflowValidator::class)->setPublic(true); $container->autowire(\MailPoet\Automation\Engine\WordPress::class)->setPublic(true); // Automation - API endpoints