PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 10.2.2
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v10.2.2
11.1.1 11.1.0 11.0.5 11.0.4 11.0.3 11.0.2 11.0.1 11.0.0 10.11.1 10.11.0 10.10.2 10.10.1 10.10.0 10.9.1 trunk 10.0.0 10.0.1 10.1.0 10.1.1 10.1.2 10.1.3 10.1.4 10.2.0 10.2.1 10.2.2 All 62 releases
acymailing / back / Helpers / ScenarioHelper.php

ScenarioHelper.php in AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress 10.2.2, at back/Helpers/ScenarioHelper.php

353 lines 12.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace AcyMailing\Helpers;
4
5 use AcyMailing\Classes\ScenarioClass;
6 use AcyMailing\Classes\ScenarioHistoryLineClass;
7 use AcyMailing\Classes\ScenarioProcessClass;
8 use AcyMailing\Classes\ScenarioStepClass;
9 use AcyMailing\Classes\ScenarioQueueClass;
10 use AcyMailing\Classes\UserClass;
11 use AcyMailing\Core\AcymObject;
12
13 class ScenarioHelper extends AcymObject
14 {
15 private ScenarioClass $scenarioClass;
16 private ScenarioStepClass $scenarioStepClass;
17 private ScenarioQueueClass $scenarioQueueClass;
18 private ScenarioHistoryLineClass $scenarioHistoryLineClass;
19 private ScenarioProcessClass $scenarioProcessClass;
20
21 public function __construct()
22 {
23 parent::__construct();
24 $this->scenarioClass = new ScenarioClass();
25 $this->scenarioStepClass = new ScenarioStepClass();
26 $this->scenarioQueueClass = new ScenarioQueueClass();
27 $this->scenarioHistoryLineClass = new ScenarioHistoryLineClass();
28 $this->scenarioProcessClass = new ScenarioProcessClass();
29 }
30
31 public function trigger(string $trigger, array $options): void
32 {
33 if (!acym_level(ACYM_ENTERPRISE) || empty($trigger)) {
34 acym_logError('ScenarioHelper::trigger - Missing trigger or not allowed, trigger: '.$trigger);
35
36 return;
37 }
38
39 if (empty($options['userId'])) {
40 acym_logError('ScenarioHelper::trigger - Missing userId, trigger: '.$trigger);
41
42 return;
43 }
44
45 $userId = intval($options['userId']);
46
47 $userClass = new UserClass();
48 if (empty($userClass->getOneById($userId))) {
49 acym_logError('ScenarioHelper::trigger - User not found, userId: '.$userId);
50
51 return;
52 }
53
54 $scenarios = $this->scenarioClass->getAllActiveByTriggers([$trigger]);
55
56 foreach ($scenarios as $scenario) {
57 if ($scenario->trigger_once) {
58 if (!empty($this->scenarioProcessClass->getOneByScenarioIdUserId($scenario->id, $userId))) {
59 continue;
60 }
61 }
62
63 $scenario->is_scenario = true;
64 $scenario->triggers = [$scenario->trigger => 1];
65 $execute = false;
66 $options = $this->formatOptions($scenario->trigger_params, 'trigger');
67 $options['userId'] = $userId;
68 $options['time'] = time();
69 acym_trigger('onAcymExecuteTrigger', [&$scenario, &$execute, &$options]);
70
71 if (!$execute) {
72 continue;
73 }
74
75 $firstStep = $this->scenarioStepClass->getFirstStepByScenarioId($scenario->id);
76
77 if (empty($firstStep)) {
78 acym_logError('ScenarioHelper::trigger - No first step found for scenario '.$scenario->id);
79 continue;
80 }
81
82 $scenarioProcess = new \stdClass();
83 $scenarioProcess->scenario_id = $scenario->id;
84 $scenarioProcess->user_id = $userId;
85 $scenarioProcess->start_at = acym_date('now', 'Y-m-d H:i:s', false);
86 $scenarioProcess->id = $this->scenarioProcessClass->save($scenarioProcess);
87
88 $this->createHistory($scenarioProcess->id, 'trigger', 'success');
89
90 $this->handleStep($firstStep, $userId, $scenarioProcess->id);
91 }
92 }
93
94 private function handleStep(object $step, int $userId, int $scenarioProcessId): void
95 {
96 switch ($step->type) {
97 case ScenarioClass::TYPE_DELAY:
98 $this->handleDelay($step, $userId, $scenarioProcessId);
99 break;
100 case ScenarioClass::TYPE_CONDITION:
101 $this->handleCondition($step, $userId, $scenarioProcessId);
102 break;
103 case ScenarioClass::TYPE_ACTION:
104 $this->handleAction($step, $userId, $scenarioProcessId);
105 break;
106 }
107 }
108
109 private function handleDelay(object $step, int $userId, int $scenarioProcessId): void
110 {
111 $nextStep = $this->scenarioStepClass->getStepByPreviousStepId($step->id);
112
113 if (empty($nextStep)) {
114 $this->ending($scenarioProcessId);
115
116 return;
117 }
118
119 $scenarioQueue = new \stdClass();
120 $scenarioQueue->scenario_process_id = $scenarioProcessId;
121 $scenarioQueue->step_id = $nextStep->id;
122 $timestampNextAction = time() + $this->calculateDelaySecondsToAdd($step->params);
123 $scenarioQueue->execution_date = acym_date($timestampNextAction, 'Y-m-d H:i:s', false);
124
125 $this->scenarioQueueClass->save($scenarioQueue);
126 $this->scenarioQueueClass->deleteByStepIds([$step->id]);
127
128 $this->createHistory($scenarioProcessId, ScenarioClass::TYPE_DELAY, 'success', $step->id, $step->params);
129 }
130
131 private function calculateDelaySecondsToAdd(array $stepDelayParams): int
132 {
133 if (empty($stepDelayParams['delay']) || empty($stepDelayParams['unit'])) {
134 return 0;
135 }
136
137 return $stepDelayParams['delay'] * $stepDelayParams['unit'];
138 }
139
140 private function handleCondition(object $step, int $userId, int $scenarioProcessId): void
141 {
142 $conditionNotValidCount = 0;
143
144 $query = new AutomationHelper();
145 $query->where = ['user.id = '.$userId];
146
147 $conditionOptions = $this->formatOptions($step->params['option'], $step->type);
148 $number = 0;
149 acym_trigger('onAcymProcessCondition_'.$step->params['condition'], [&$query, &$conditionOptions, &$number, &$conditionNotValidCount]);
150 $this->scenarioQueueClass->deleteByStepIds([$step->id]);
151
152 $conditionValid = $conditionNotValidCount === 0;
153
154 $historyOptions = array_merge($conditionOptions, ['scenario_condition' => $step->params['condition']]);
155 $this->createHistory($scenarioProcessId, ScenarioClass::TYPE_CONDITION, 'Condition is '.($conditionValid ? 'valid' : 'not valid'), $step->id, $historyOptions);
156
157 $nextStep = $this->scenarioStepClass->getStepByPreviousConditionId($step->id, $conditionValid);
158
159 if (empty($nextStep)) {
160 $this->ending($scenarioProcessId);
161
162 return;
163 }
164
165 $this->handleStep($nextStep, $userId, $scenarioProcessId);
166 }
167
168 private function formatOptions(array $options, string $type): array
169 {
170 $formattedOptions = [];
171
172 switch ($type) {
173 case ScenarioClass::TYPE_CONDITION:
174 $regex = '/acym_condition\[conditions\]\[__numor__\]\[__numand__\]\[(.+)\]\[(.+)\]/';
175 break;
176 case ScenarioClass::TYPE_ACTION:
177 $regex = '/acym_action\[actions\]\[__and__\]\[(.+)\]\[(.+)\]/';
178 break;
179 case 'trigger':
180 $regex = '/\[triggers\]\[user\]\[(.+)\]\[(.+)\]/';
181 break;
182 default:
183 $regex = '';
184 }
185
186 if (empty($regex)) {
187 return $formattedOptions;
188 }
189
190 foreach ($options as $key => $option) {
191 $formattedKey = preg_replace($regex, '$2', $key);
192 if ($formattedKey === $key) {
193 continue;
194 }
195 $formattedOptions[$formattedKey] = $option;
196 }
197
198 return $formattedOptions;
199 }
200
201 private function handleAction(object $step, int $userId, int $scenarioProcessId): void
202 {
203 $query = new AutomationHelper();
204 $query->where = ['user.id = '.$userId];
205
206 $actionOptions = $this->formatOptions($step->params['option'], $step->type);
207 $log = '';
208 $report = '';
209 try {
210 $report = acym_trigger('onAcymProcessAction_'.$step->params['action'], [&$query, &$actionOptions]);
211 $report = is_array($report) ? implode(', ', $report) : $report;
212 } catch (\Exception $e) {
213 $log = $e->getMessage();
214 }
215
216 $this->scenarioQueueClass->deleteByStepIds([$step->id]);
217
218 $historyOptions = array_merge($actionOptions, ['scenario_action' => $step->params['action']]);
219 $this->createHistory($scenarioProcessId, ScenarioClass::TYPE_CONDITION, 'Action completed: '.$report, $step->id, $historyOptions, $log);
220
221 $nextStep = $this->scenarioStepClass->getStepByPreviousStepId($step->id);
222
223 if (empty($nextStep)) {
224 $this->ending($scenarioProcessId);
225
226 return;
227 }
228
229 $this->handleStep($nextStep, $userId, $scenarioProcessId);
230 }
231
232 private function ending(int $scenarioProcessId): void
233 {
234 $this->createHistory($scenarioProcessId, 'ending', acym_translation('ACYM_SCENARIO_ENDED'));
235
236 $this->scenarioProcessClass->endProcess($scenarioProcessId);
237 }
238
239 public function executeAvailableSteps(): void
240 {
241 $dateNowUtc = acym_date('now', 'Y-m-d H:i:s', false);
242
243 $steps = $this->scenarioStepClass->getAvailableStepsByDate($dateNowUtc);
244
245 if (empty($steps)) {
246 return;
247 }
248
249 foreach ($steps as $step) {
250 $this->handleStep($step, $step->user_id, $step->scenario_process_id);
251 }
252 }
253
254 public function triggerTimeScenarios(): void
255 {
256 $userStatusTriggers = [];
257 acym_trigger('onAcymDefineUserStatusCheckTriggers', [&$userStatusTriggers]);
258
259 if (empty($userStatusTriggers)) {
260 return;
261 }
262
263 $scenarioClass = new ScenarioClass();
264 $scenarios = $scenarioClass->getAllActiveByTriggers($userStatusTriggers);
265
266 if (empty($scenarios)) {
267 return;
268 }
269
270 foreach ($scenarios as $scenario) {
271 $nextExecution = $scenario->trigger_params['next_execution'] ?? null;
272 $scenario->next_execution = $nextExecution;
273
274 if (!empty($nextExecution) && $nextExecution > time()) {
275 continue;
276 }
277
278 $firstStep = $this->scenarioStepClass->getFirstStepByScenarioId($scenario->id);
279 if (empty($firstStep)) {
280 acym_logError('ScenarioHelper::triggerTimeScenarios - No first step found for scenario '.$scenario->id);
281 continue;
282 }
283
284 $execute = false;
285 $options = $this->formatOptions($scenario->trigger_params['option'] ?? [], 'trigger');
286 $options['time'] = time();
287
288 $scenario->is_scenario = true;
289 $scenario->triggers = [$scenario->trigger => $options];
290
291 acym_trigger('onAcymExecuteTrigger', [&$scenario, &$execute, &$options]);
292
293 if ($nextExecution !== $scenario->next_execution) {
294 $scenario->trigger_params['next_execution'] = $scenario->next_execution;
295 $scenarioClass->save($scenario);
296 }
297
298 if (empty($options['userIds'])) {
299 continue;
300 }
301
302 foreach ($options['userIds'] as $userId) {
303 if ($scenario->trigger_once && !empty($this->scenarioProcessClass->getOneByScenarioIdUserId($scenario->id, $userId))) {
304 continue;
305 }
306
307 $scenarioProcess = new \stdClass();
308 $scenarioProcess->scenario_id = $scenario->id;
309 $scenarioProcess->user_id = $userId;
310 $scenarioProcess->start_at = acym_date('now', 'Y-m-d H:i:s', false);
311 $scenarioProcess->id = $this->scenarioProcessClass->save($scenarioProcess);
312
313 $this->createHistory($scenarioProcess->id, 'trigger', 'success');
314
315 $this->handleStep($firstStep, $userId, $scenarioProcess->id);
316 }
317 }
318 }
319
320 private function createHistory(int $scenarioProcessId, string $type, string $result, string $scenarioStepId = '', array $params = [], string $log = ''): void
321 {
322 $scenarioHistoryLine = new \stdClass();
323 $scenarioHistoryLine->scenario_process_id = $scenarioProcessId;
324 $scenarioHistoryLine->date = acym_date('now', 'Y-m-d H:i:s', false);
325 $scenarioHistoryLine->type = $type;
326 $scenarioHistoryLine->result = $result;
327
328 if (!empty($scenarioStepId)) {
329 $scenarioHistoryLine->scenario_step_id = $scenarioStepId;
330 }
331
332 if (!empty($params)) {
333 $scenarioHistoryLine->params = $params;
334 }
335
336 if (!empty($log)) {
337 $scenarioHistoryLine->log = $log;
338 }
339
340 $this->scenarioHistoryLineClass->save($scenarioHistoryLine);
341 }
342
343 public function getConditionValue(object $step): string
344 {
345 return $step->params['condition'];
346 }
347
348 public function getActionValue(object $step): string
349 {
350 return $step->params['action'];
351 }
352 }
353