PluginProbe ʕ •ᴥ•ʔ
MailPoet – Newsletters, Email Marketing, and Automation / 5.34.2
MailPoet – Newsletters, Email Marketing, and Automation v5.34.2
5.34.2 5.34.1 5.34.0 5.33.1 5.33.0 5.32.0 5.31.0 5.30.0 5.29.0 5.28.1 5.28.0 5.27.0 5.26.0 5.26.1 5.25.0 5.24.0 4.43.0 4.43.1 4.44.0 4.44.1 4.45.0 4.46.0 4.47.0 4.48.0 4.48.1 4.48.2 4.49.0 4.49.1 4.5.0 4.5.1 4.5.2 4.50.0 4.50.1 4.51.0 4.51.1 4.51.2 4.52.0 4.53.0 4.54.0 4.55.0 4.56.0 4.57.0 4.58.0 4.58.1 4.58.2 4.6.0 4.6.1 4.6.2 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 5.0.0 5.0.1 5.0.2 5.1.0 5.1.1 5.10.0 5.10.1 5.11.0 5.12.0 5.12.1 5.12.10 5.12.11 5.12.12 5.12.13 5.12.2 5.12.3 5.12.4 5.12.5 5.12.6 5.12.7 5.12.8 5.12.9 5.13.0 5.13.1 5.13.2 5.14.0 5.14.1 5.14.2 5.14.3 5.15.0 5.15.1 5.16.0 5.16.1 5.16.2 5.16.3 5.16.4 5.17.0 5.17.1 5.17.2 5.17.3 5.17.4 5.17.5 5.17.6 5.18.0 5.19.0 5.2.0 5.2.1 5.2.2 5.2.3 5.20.0 5.21.0 5.21.1 5.21.2 5.21.3 5.22.0 5.22.1 5.22.2 5.22.3 5.22.4 5.23.0 5.23.1 5.23.2 5.3.0 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 5.4.0 5.4.1 5.4.2 5.5.0 5.5.1 5.5.2 5.6.0 5.6.1 5.6.2 5.6.3 5.6.4 5.7.0 5.7.1 5.8.0 5.8.1 5.9.0 3.0.0-beta.15 3.7.1 3.0.0-beta.16 3.7.2 3.0.0-beta.17 3.7.3 3.0.0-beta.18 3.7.4 3.0.0-beta.19 3.7.5 3.0.0-beta.2 3.7.6 3.0.0-beta.20 3.7.8 3.0.0-beta.21 3.70.0 3.0.0-beta.22 3.71.0 3.0.0-beta.23 3.71.1 3.0.0-beta.23.1 3.71.2 3.0.0-beta.23.2 3.71.3 3.0.0-beta.24 3.72.0 3.0.0-beta.25 3.73.0 3.0.0-beta.26 3.73.1 3.0.0-beta.27 3.73.2 3.0.0-beta.28 3.74.0 3.0.0-beta.29 3.74.1 3.0.0-beta.3 3.74.2 3.0.0-beta.30 3.74.3 3.0.0-beta.31 3.75.0 3.0.0-beta.32 3.75.1 3.0.0-beta.33 3.76.0 3.0.0-beta.33.1 3.77.0 3.0.0-beta.34.0.0 3.77.1 3.0.0-beta.36.0.0 3.78.0 3.0.0-beta.36.0.1 3.79.0 3.0.0-beta.36.2.0 3.8 3.0.0-beta.36.3.0 3.8.1 3.0.0-beta.36.3.1 3.8.2 3.0.0-beta.37.0.0 3.8.3 3.0.0-beta.4 3.8.4 3.0.0-beta.5 3.8.5 3.0.0-beta.6 3.8.6 3.0.0-beta.7 3.80.0 3.0.0-beta.7.1 3.81.0 3.0.0-beta.8 3.82.0 3.0.0-beta.9 3.83.0 3.0.0-rc.1.0.0 3.84.0 3.0.0-rc.1.0.1 3.84.1 3.0.0-rc.1.0.2 3.85.0 3.0.0-rc.1.0.3 3.85.1 3.0.0-rc.1.0.4 3.86.0 3.0.0-rc.2.0.0 3.87.0 3.0.0-rc.2.0.1 3.87.1 3.0.0-rc.2.0.2 3.87.2 3.0.0-rc.2.0.3 3.88.0 3.0.1 3.88.1 3.0.2 3.88.2 3.0.3 3.89.0 3.0.4 3.89.1 3.0.5 3.89.2 3.0.6 3.89.3 3.0.7 3.89.4 3.0.8 3.9.0 3.0.9 3.9.1 3.1.0 3.90.0 3.10 3.90.1 3.10.1 3.90.2 3.100.0 3.91.0 3.100.1 3.91.1 3.100.2 3.92.0 3.101.0 3.92.1 3.101.1 3.93.0 3.102.0 3.93.1 3.102.1 3.94.0 3.103.0 3.95.0 3.103.1 3.95.1 3.11.0 3.96.0 3.11.1 3.96.1 3.11.2 3.97.0 3.11.3 3.98.0 3.11.4 3.98.1 3.11.5 3.99.0 3.12.0 3.99.1 3.12.1 4.0.0 3.13.0 4.0.1 3.14.0 4.1.0 3.14.1 4.1.1 3.15.0 4.10.0 3.16.0 4.11.0 3.16.1 4.11.1 3.16.2 4.12.0 3.16.3 4.12.1 3.17.0 4.12.2 3.17.1 4.13.0 3.17.2 4.14.0 3.18.0 4.15.0 3.18.1 4.16.0 3.18.2 4.17.0 3.19.0 4.17.1 3.19.1 4.18.0 3.19.2 4.18.1 3.19.3 4.19.0 3.2.0 4.2.0 3.2.1 4.20.0 3.2.2 4.20.1 3.2.3 4.20.2 3.2.4 4.21.0 3.2.5 4.22.0 3.20.0 4.22.1 3.21.0 4.22.2 3.21.1 4.23.0 3.22.0 4.24.0 3.23.0 4.25.0 3.23.1 4.26.0 3.23.2 4.26.1 3.24.0 4.27.0 3.25.0 4.28.0 3.25.1 4.29.0 3.26.0 4.3.0 3.26.1 4.3.1 3.27.0 4.30.0 3.28.0 4.31.0 3.29.0 4.31.1 3.3.0 4.32.0 3.3.1 4.33.0 3.3.2 4.34.0 3.3.3 4.35.0 3.3.4 4.35.1 3.3.5 4.36.0 3.3.6 4.37.0 3.30.0 4.38.0 3.31.0 4.39.0 3.31.1 4.4.0 3.32.0 4.40.0 3.32.1 4.41.0 3.32.2 4.41.1 3.33.0 4.41.2 3.34.0 4.41.3 3.34.1 4.42.0 3.34.2 4.42.1 3.34.3 3.34.4 3.35.0 3.35.1 3.35.3 3.35.4 3.36.0 3.37.0 3.37.1 3.37.2 3.37.3 3.38.0 3.38.1 3.39.0 3.39.1 3.39.2 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.40.0 3.40.1 3.41.0 3.41.1 3.41.2 3.42.0 3.42.1 3.42.2 3.42.3 3.43.0 3.43.1 3.44.0 3.45.0 3.45.1 3.46.0 3.46.1 3.46.10 3.46.11 3.46.12 3.46.13 3.46.14 3.46.2 3.46.3 3.46.4 3.46.5 3.46.6 3.46.7 3.46.8 3.46.9 3.47.0 3.47.1 3.47.10 3.47.11 3.47.2 3.47.3 3.47.5 3.47.6 3.47.7 3.47.9 3.48.0 3.48.1 3.49.0 3.49.1 3.5.0 3.5.1 3.50.0 3.51.0 3.51.1 3.51.2 3.52.0 3.53.0 3.54.0 3.54.1 3.54.2 3.54.3 3.55.0 3.55.1 3.56.0 3.56.1 3.56.2 3.57.0 3.57.1 3.58.0 3.59.0 3.59.1 3.59.2 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.60.0 3.60.1 3.60.10 3.60.11 3.60.12 3.60.2 3.60.3 3.60.4 3.60.6 3.60.7 3.60.8 3.60.9 3.61.0 3.62.0 3.62.1 3.63.0 3.64.0 3.64.1 3.64.2 3.64.3 3.65.0 trunk 3.65.1 3.0.0 3.66.0 3.0.0-beta.1 3.67.0 3.0.0-beta.10 3.67.1 3.0.0-beta.11 3.68.0 3.0.0-beta.12 3.69.0 3.0.0-beta.13 3.69.1 3.0.0-beta.14 3.7.0
mailpoet / lib / Automation / Engine / Builder / UpdateAutomationController.php
mailpoet / lib / Automation / Engine / Builder Last commit date
CreateAutomationFromTemplateController.php 2 months ago DeleteAutomationController.php 2 months ago DuplicateAutomationController.php 2 months ago UpdateAutomationController.php 2 months ago UpdateStepsController.php 11 months ago index.php 3 years ago
UpdateAutomationController.php
193 lines
1 <?php declare(strict_types = 1);
2
3 namespace MailPoet\Automation\Engine\Builder;
4
5 if (!defined('ABSPATH')) exit;
6
7
8 use ActionScheduler_Store;
9 use MailPoet\Automation\Engine\Control\ActionScheduler;
10 use MailPoet\Automation\Engine\Data\Automation;
11 use MailPoet\Automation\Engine\Data\AutomationRun;
12 use MailPoet\Automation\Engine\Data\Step;
13 use MailPoet\Automation\Engine\Exceptions;
14 use MailPoet\Automation\Engine\Exceptions\UnexpectedValueException;
15 use MailPoet\Automation\Engine\Hooks;
16 use MailPoet\Automation\Engine\Storage\AutomationRunStorage;
17 use MailPoet\Automation\Engine\Storage\AutomationStorage;
18 use MailPoet\Automation\Engine\Validation\AutomationValidator;
19
20 class UpdateAutomationController {
21 /** @var Hooks */
22 private $hooks;
23
24 /** @var AutomationStorage */
25 private $storage;
26
27 /** @var AutomationValidator */
28 private $automationValidator;
29
30 /** @var UpdateStepsController */
31 private $updateStepsController;
32
33 private AutomationRunStorage $automationRunStorage;
34
35 private ActionScheduler $actionScheduler;
36
37 public function __construct(
38 Hooks $hooks,
39 AutomationStorage $storage,
40 AutomationValidator $automationValidator,
41 AutomationRunStorage $automationRunStorage,
42 ActionScheduler $actionScheduler,
43 UpdateStepsController $updateStepsController
44 ) {
45 $this->hooks = $hooks;
46 $this->storage = $storage;
47 $this->automationValidator = $automationValidator;
48 $this->updateStepsController = $updateStepsController;
49 $this->automationRunStorage = $automationRunStorage;
50 $this->actionScheduler = $actionScheduler;
51 }
52
53 public function updateAutomation(int $id, array $data): Automation {
54 $automation = $this->storage->getAutomation($id);
55 if (!$automation) {
56 throw Exceptions::automationNotFound($id);
57 }
58 $previousAutomation = clone $automation;
59
60 if (array_key_exists('name', $data)) {
61 $automation->setName($data['name']);
62 }
63 $originalStatus = $automation->getStatus();
64
65 if (array_key_exists('status', $data)) {
66 $this->checkAutomationStatus($data['status']);
67 $automation->setStatus($data['status']);
68 }
69
70 if (array_key_exists('steps', $data)) {
71 $this->validateTriggerInvariants($automation, $data['steps']);
72 $this->validateAutomationSteps($automation, $data['steps']);
73 $this->updateStepsController->updateSteps($automation, $data['steps']);
74 foreach ($automation->getSteps() as $step) {
75 $this->hooks->doAutomationStepBeforeSave($step, $automation);
76 $this->hooks->doAutomationStepByKeyBeforeSave($step, $automation);
77 }
78 }
79
80 $shouldUnscheduleAutomationRuns = (
81 ($automation->getStatus() === Automation::STATUS_DRAFT)
82 && ($originalStatus === Automation::STATUS_ACTIVE)
83 ) || !empty($data['cancel_running_runs']);
84
85 if (array_key_exists('meta', $data)) {
86 $automation->deleteAllMetas();
87 foreach ($data['meta'] as $key => $value) {
88 $automation->setMeta($key, $value);
89 }
90 }
91
92 $this->hooks->doAutomationBeforeSave($automation);
93
94 $this->automationValidator->validate($automation);
95 $this->storage->updateAutomation($automation);
96
97 if ($shouldUnscheduleAutomationRuns) {
98 $this->unscheduleAutomationRuns($automation);
99 }
100
101 $automation = $this->storage->getAutomation($id);
102 if (!$automation) {
103 throw Exceptions::automationNotFound($id);
104 }
105 $this->hooks->doAutomationAfterUpdate($automation, $previousAutomation);
106 return $automation;
107 }
108
109 private function checkAutomationStatus(string $status): void {
110 if (!in_array($status, Automation::STATUS_ALL, true)) {
111 // translators: %s is the status.
112 throw UnexpectedValueException::create()->withMessage(sprintf(__('Invalid status: %s', 'mailpoet'), $status));
113 }
114 }
115
116 private function validateTriggerInvariants(Automation $automation, array $steps): void {
117 $existingTriggers = [];
118 foreach ($automation->getSteps() as $step) {
119 if ($step->getType() === Step::TYPE_TRIGGER) {
120 $existingTriggers[$step->getId()] = $step;
121 }
122 }
123
124 $newTriggers = [];
125 foreach ($steps as $id => $data) {
126 if (($data['type'] ?? null) === Step::TYPE_TRIGGER) {
127 $newTriggers[$id] = $data;
128 }
129 }
130
131 $triggersChanged = false;
132 if (count($newTriggers) !== count($existingTriggers)) {
133 $triggersChanged = true;
134 }
135
136 if (!$triggersChanged) {
137 foreach ($existingTriggers as $id => $existingTrigger) {
138 $newTrigger = $newTriggers[$id] ?? null;
139 if (!$newTrigger || ($newTrigger['key'] ?? '') !== $existingTrigger->getKey()) {
140 $triggersChanged = true;
141 break;
142 }
143 }
144 }
145
146 if ($triggersChanged && $this->automationRunStorage->getCountForAutomation($automation) > 0) {
147 throw Exceptions::automationTriggerModificationNotSupported();
148 }
149 }
150
151 protected function validateAutomationSteps(Automation $automation, array $steps): void {
152 $existingSteps = $automation->getSteps();
153 if (count($steps) !== count($existingSteps)) {
154 throw Exceptions::automationStructureModificationNotSupported();
155 }
156
157 foreach ($steps as $id => $data) {
158 $existingStep = $existingSteps[$id] ?? null;
159 if (!$existingStep || !$this->stepChanged(Step::fromArray($data), $existingStep)) {
160 throw Exceptions::automationStructureModificationNotSupported();
161 }
162 }
163 }
164
165 private function stepChanged(Step $a, Step $b): bool {
166 $aData = $a->toArray();
167 $bData = $b->toArray();
168 unset($aData['args'], $aData['filters']);
169 unset($bData['args'], $bData['filters']);
170 return $aData === $bData;
171 }
172
173 private function unscheduleAutomationRuns(Automation $automation): void {
174 $runIds = [];
175 $runs = $this->automationRunStorage->getAutomationRunsForAutomation($automation);
176 foreach ($runs as $run) {
177 if ($run->getStatus() === AutomationRun::STATUS_RUNNING) {
178 $this->automationRunStorage->updateStatus($run->getId(), AutomationRun::STATUS_CANCELLED);
179 }
180 $runIds[$run->getId()] = $run;
181 }
182
183 $actions = $this->actionScheduler->getScheduledActions(['hook' => Hooks::AUTOMATION_STEP, 'status' => ActionScheduler_Store::STATUS_PENDING]);
184 foreach ($actions as $action) {
185 $args = $action->get_args();
186 $automationArgs = reset($args);
187 if (isset($automationArgs['automation_run_id']) && isset($runIds[$automationArgs['automation_run_id']])) {
188 $this->actionScheduler->unscheduleAction(Hooks::AUTOMATION_STEP, $args);
189 }
190 }
191 }
192 }
193