PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 10.10.2
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v10.10.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.10.2, at back/Helpers/ScenarioHelper.php

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