PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 11.1.0
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v11.1.0
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
← All changes | back/dynamics/time/TimeAutomationTriggers.php +500 -0 10.10.1 → 11.1.0 View file →
@@ -1,0 +1,500 @@
1 +<?php
2 +
3 +trait TimeAutomationTriggers
4 +{
5 + protected function initAutomationTriggers(): void
6 + {
7 + $this->automationTriggers['classic'] = [
8 + 'asap' => acym_translation('ACYM_EACH_TIME'),
9 + 'day' => acym_translation('ACYM_EVERY_DAY_AT'),
10 + 'weeks_on' => acym_translation('ACYM_EVERY_WEEK_ON'),
11 + 'on_day_month' => acym_translation('ACYM_ONTHE'),
12 + 'every' => acym_translation('ACYM_EVERY'),
13 + ];
14 + }
15 +
16 + public function displayTriggerOptions_asap(string $fieldName): void
17 + {
18 + echo '<input type="hidden" name="'.esc_attr($fieldName).'" value="y">';
19 + }
20 +
21 + public function displayTriggerOptions_day(string $fieldName, array $defaultValues): void
22 + {
23 + ?>
24 + <div class="grid-x grid-margin-x" style="height: 40px;">
25 + <div class="cell medium-shrink">
26 + <?php
27 + acym_select(
28 + $this->getTriggerHours(),
29 + $fieldName.'[hour]',
30 + empty($defaultValues['day']) ? acym_date('now', 'H') : $defaultValues['day']['hour'],
31 + ['data-class' => 'intext_select acym__select'],
32 + 'value',
33 + 'text',
34 + null,
35 + false,
36 + true
37 + );
38 + ?>
39 + </div>
40 + <div class="cell medium-shrink acym_vcenter">:</div>
41 + <div class="cell medium-auto">
42 + <?php
43 + acym_select(
44 + $this->getTriggerMinutes(),
45 + $fieldName.'[minutes]',
46 + empty($defaultValues['day']) ? acym_date('now', 'i') : $defaultValues['day']['minutes'],
47 + ['data-class' => 'intext_select acym__select'],
48 + 'value',
49 + 'text',
50 + null,
51 + false,
52 + true
53 + );
54 + ?>
55 + </div>
56 + </div>
57 + <?php
58 + }
59 +
60 + public function displayTriggerOptions_weeks_on(string $fieldName, array $defaultValues): void
61 + {
62 + ?>
63 + <div class="grid-x">
64 + <div class="cell">
65 + <?php acym_selectMultiple(
66 + $this->getTriggerDays(),
67 + $fieldName.'[day]',
68 + empty($defaultValues['weeks_on']) ? ['monday'] : $defaultValues['weeks_on']['day'],
69 + ['data-class' => 'acym__select'],
70 + 'value',
71 + 'text',
72 + true
73 + ); ?>
74 + </div>
75 + <div class="cell margin-top-1 acym_vcenter">
76 + <?php $this->displayTriggerTimeSentence($fieldName, empty($defaultValues['weeks_on']) ? [] : $defaultValues['weeks_on']); ?>
77 + </div>
78 + </div>
79 + <?php
80 + }
81 +
82 + public function displayTriggerOptions_on_day_month(string $fieldName, array $defaultValues): void
83 + {
84 + $numbers = [
85 + 'first' => acym_translation('ACYM_FIRST'),
86 + 'second' => acym_translation('ACYM_SECOND'),
87 + 'third' => acym_translation('ACYM_THIRD'),
88 + 'fourth' => acym_translation('ACYM_FOURTH'),
89 + 'last' => acym_translation('ACYM_LAST'),
90 + ];
91 + ?>
92 + <div class="grid-x grid-margin-x margin-y">
93 + <div class="cell medium-4">
94 + <?php acym_select(
95 + $numbers,
96 + $fieldName.'[number]',
97 + empty($defaultValues['on_day_month']) ? null : $defaultValues['on_day_month']['number'],
98 + ['data-class' => 'acym__select'],
99 + 'value',
100 + 'text',
101 + null,
102 + false,
103 + true
104 + ); ?>
105 + </div>
106 + <div class="cell medium-4">
107 + <?php acym_select(
108 + $this->getTriggerDays(),
109 + $fieldName.'[day]',
110 + empty($defaultValues['on_day_month']) ? null : $defaultValues['on_day_month']['day'],
111 + [
112 + 'data-class' => 'acym__select',
113 + 'style' => 'margin: 0 10px;',
114 + ],
115 + 'value',
116 + 'text',
117 + null,
118 + false,
119 + true
120 + ); ?>
121 + </div>
122 + <div class="cell medium-4 acym_vcenter"><?php echo esc_html(acym_translation('ACYM_DAYOFMONTH')); ?></div>
123 + <div class="cell acym_vcenter">
124 + <?php $this->displayTriggerTimeSentence($fieldName, empty($defaultValues['on_day_month']) ? [] : $defaultValues['on_day_month']); ?>
125 + </div>
126 + </div>
127 + <?php
128 + }
129 +
130 + public function displayTriggerOptions_every(string $fieldName, array $defaultValues): void
131 + {
132 + $every = [
133 + '3600' => acym_translation('ACYM_HOURS'),
134 + '86400' => acym_translation('ACYM_DAYS'),
135 + '604800' => acym_translation('ACYM_WEEKS'),
136 + '2628000' => acym_translation('ACYM_MONTHS'),
137 + ];
138 +
139 + $defaultEvery = empty($defaultValues['every']['number']) ? '1' : $defaultValues['every']['number'];
140 + ?>
141 + <div class="grid-x grid-margin-x">
142 + <div class="cell medium-shrink">
143 + <input type="number"
144 + min="1"
145 + name="<?php echo esc_attr($fieldName.'[number]'); ?>"
146 + class="intext_input"
147 + value="<?php echo intval($defaultEvery); ?>">
148 + </div>
149 + <div class="cell medium-auto">
150 + <?php acym_select(
151 + $every,
152 + $fieldName.'[type]',
153 + empty($defaultValues['every']) ? '604800' : $defaultValues['every']['type'],
154 + ['data-class' => 'intext_select acym__select'],
155 + 'value',
156 + 'text',
157 + null,
158 + false,
159 + true
160 + ); ?>
161 + </div>
162 + </div>
163 + <?php
164 + }
165 +
166 + private function displayTriggerTimeSentence(string $fieldName, array $defaults): void
167 + {
168 + $hourSelect = '<div class="margin-left-1 margin-right-1">'.acym_select(
169 + $this->getTriggerHours(),
170 + $fieldName.'[hour]',
171 + $defaults['hour'] ?? $this->config->get('daily_hour', '12'),
172 + ['data-class' => 'intext_select acym__select']
173 + ).'</div>';
174 +
175 + $minuteSelect = '<div class="margin-left-1 margin-right-1">'.acym_select(
176 + $this->getTriggerMinutes(),
177 + $fieldName.'[minutes]',
178 + $defaults['minutes'] ?? $this->config->get('daily_minute', '00'),
179 + ['data-class' => 'intext_select acym__select']
180 + ).'</div>';
181 +
182 + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Both selects are built and escaped by acym_select just above, and the translation itself holds HTML entities on purpose.
183 + echo acym_translationSprintf('ACYM_AT_DATE_TIME', $hourSelect, $minuteSelect);
184 + }
185 +
186 + private function getTriggerHours(): array
187 + {
188 + $hours = [];
189 + for ($i = 0; $i <= 23; $i++) {
190 + $hours[$i] = $i < 10 ? '0'.$i : $i;
191 + }
192 +
193 + return $hours;
194 + }
195 +
196 + private function getTriggerMinutes(): array
197 + {
198 + $minutes = [];
199 + for ($i = 0; $i <= 59; $i++) {
200 + $minutes[$i] = $i < 10 ? '0'.$i : $i;
201 + }
202 +
203 + return $minutes;
204 + }
205 +
206 + private function getTriggerDays(): array
207 + {
208 + return [
209 + 'monday' => acym_translation('ACYM_MONDAY'),
210 + 'tuesday' => acym_translation('ACYM_TUESDAY'),
211 + 'wednesday' => acym_translation('ACYM_WEDNESDAY'),
212 + 'thursday' => acym_translation('ACYM_THURSDAY'),
213 + 'friday' => acym_translation('ACYM_FRIDAY'),
214 + 'saturday' => acym_translation('ACYM_SATURDAY'),
215 + 'sunday' => acym_translation('ACYM_SUNDAY'),
216 + ];
217 + }
218 +
219 + public function onAcymExecuteTrigger(&$step, &$execute, &$data)
220 + {
221 + if (!empty($step->is_scenario)) {
222 + return;
223 + }
224 +
225 + $time = $data['time'];
226 + $triggers = $step->triggers;
227 +
228 + // For each trigger of the automation, we'll calculate the next execution date. In the end we take the closest one
229 + $nextExecutionDate = [];
230 +
231 + // Get the time the auto tasks should be triggered
232 + $dailyHour = $this->config->get('daily_hour', '12');
233 + $dailyMinute = $this->config->get('daily_minute', '00');
234 +
235 +
236 + // Each time the cron is triggered
237 + if (!empty($triggers['asap'])) {
238 + $execute = true;
239 + $nextExecutionDate[] = $time;
240 + }
241 +
242 + // Every day at xx:xx
243 + if (!empty($triggers['day'])) {
244 + // The day it is currently based on the timezone specified in the CMS configuration
245 + $dayBasedOnCMSTimezone = acym_date('now', 'Y-m-d');
246 +
247 + $hour = $triggers['day']['hour'];
248 + $minutes = $triggers['day']['minutes'];
249 +
250 + if (strlen($hour) < 2) $hour = '0'.$hour;
251 + if (strlen($minutes) < 2) $minutes = '0'.$minutes;
252 +
253 + // The UTC timestamp of the current day based on the CMS timezone, at the specified hour
254 + $dayBasedOnCMSTimezoneAtSpecifiedHour = acym_getTimeFromCMSDate($dayBasedOnCMSTimezone.' '.$hour.':'.$minutes);
255 +
256 + if ($time < $dayBasedOnCMSTimezoneAtSpecifiedHour) {
257 + $nextExecutionDate[] = $dayBasedOnCMSTimezoneAtSpecifiedHour;
258 + } else {
259 + $nextExecutionDate[] = $dayBasedOnCMSTimezoneAtSpecifiedHour + 86400;
260 +
261 + // First trigger: if the hour is passed we execute
262 + if (empty($step->last_execution)) $execute = true;
263 + }
264 + }
265 +
266 + // Each week on Mondays and Wednesdays for example
267 + if (!empty($triggers['weeks_on'])) {
268 + if (isset($triggers['weeks_on']['hour'])) {
269 + $hour = $triggers['weeks_on']['hour'];
270 + $minutes = $triggers['weeks_on']['minutes'];
271 + } else {
272 + $hour = $dailyHour;
273 + $minutes = $dailyMinute;
274 + }
275 +
276 + if (strlen($hour) < 2) $hour = '0'.$hour;
277 + if (strlen($minutes) < 2) $minutes = '0'.$minutes;
278 +
279 + foreach ($triggers['weeks_on']['day'] as $day) {
280 + // The day it is currently based on the timezone specified in the CMS configuration
281 + $dayBasedOnCMSTimezone = acym_date('now', 'Y-m-d');
282 +
283 + // The UTC timestamp of the current day based on the CMS timezone, at the specified hour
284 + $dayBasedOnCMSTimezoneAtSpecifiedHour = acym_getTimeFromCMSDate($dayBasedOnCMSTimezone.' '.$hour.':'.$minutes);
285 +
286 + // Only store the next Execution date if it's in the future
287 + if ($day == strtolower(acym_date('now', 'l', true, false))) {
288 + if ($time < $dayBasedOnCMSTimezoneAtSpecifiedHour) {
289 + $nextExecutionDate[] = $dayBasedOnCMSTimezoneAtSpecifiedHour;
290 + } else {
291 + $nextExecutionDate[] = $dayBasedOnCMSTimezoneAtSpecifiedHour + 604800;
292 +
293 + // Current day is selected, the time is passed, and it's the first trigger
294 + $lastExecutionIsNotToday = acym_date($step->last_execution, 'Y-m-d') !== $dayBasedOnCMSTimezone;
295 + $nextExecutionIsToday = acym_date($step->next_execution, 'Y-m-d') === $dayBasedOnCMSTimezone;
296 + if (empty($step->last_execution) || ($lastExecutionIsNotToday && $nextExecutionIsToday)) $execute = true;
297 + }
298 + } else {
299 + $days = [
300 + 'monday',
301 + 'tuesday',
302 + 'wednesday',
303 + 'thursday',
304 + 'friday',
305 + 'saturday',
306 + 'sunday',
307 + ];
308 + $currentDayOfWeek = acym_date('now', 'N') - 1;
309 + $wantedDayOfWeek = array_search($day, $days);
310 +
311 + $shift = $wantedDayOfWeek - $currentDayOfWeek;
312 + if ($shift < 0) $shift += 7;
313 +
314 + $nextExecutionDate[] = $dayBasedOnCMSTimezoneAtSpecifiedHour + 86400 * $shift;
315 + }
316 + }
317 + }
318 +
319 + // On first Friday of the month for example
320 + if (!empty($triggers['on_day_month'])) {
321 + if (isset($triggers['on_day_month']['hour'])) {
322 + $hour = $triggers['on_day_month']['hour'];
323 + $minutes = $triggers['on_day_month']['minutes'];
324 + } else {
325 + $hour = $dailyHour;
326 + $minutes = $dailyMinute;
327 + }
328 +
329 + if (strlen($hour) < 2) $hour = '0'.$hour;
330 + if (strlen($minutes) < 2) $minutes = '0'.$minutes;
331 +
332 + $today = acym_getTime('today '.$hour.':'.$minutes);
333 +
334 + // Get the current month's day
335 + $execution = acym_getTime($triggers['on_day_month']['number'].' '.$triggers['on_day_month']['day'].' of this month '.$hour.':'.$minutes);
336 +
337 + //If it's before today, get the next date
338 + if ($execution < $today) {
339 + $execution = acym_getTime($triggers['on_day_month']['number'].' '.$triggers['on_day_month']['day'].' of next month '.$hour.':'.$minutes);
340 + }
341 +
342 + // The next execution date is in the future
343 + if ($execution > $time) {
344 + $nextExecutionDate[] = $execution;
345 + } else {
346 + // The next execution is today and is passed
347 +
348 +
349 + // If it's the first trigger we execute
350 + if (empty($step->last_execution)) {
351 + $execute = true;
352 + }
353 +
354 + // Set the next execution time: recompute the real "next weekday of next month" occurrence.
355 + $nextExecutionDate[] = acym_getTime($triggers['on_day_month']['number'].' '.$triggers['on_day_month']['day'].' of next month '.$hour.':'.$minutes);
356 + }
357 + }
358 +
359 + // WARNING : KEEP THIS TRIGGER AT THE END, ACTION PERFORMED IF WE EXECUTE
360 + // Every X hours/days/weeks/months
361 + if (!empty($triggers['every'])) {
362 + // First trigger: we execute and set the next execution in X hours/days/weeks/months
363 + if (empty($step->last_execution)) {
364 + $execute = true;
365 + } else {
366 + if ($triggers['every']['type'] == 2628000) {
367 + $nextDate = new \DateTime(acym_date($step->last_execution, 'Y-m-d H:i:s', false), new \DateTimeZone('UTC'));
368 + $nextDate = $nextDate->add(new \DateInterval('P'.$triggers['every']['number'].'M'));
369 + $nextDate = $nextDate->getTimestamp();
370 + } else {
371 + $nextDate = $step->last_execution + ($triggers['every']['number'] * $triggers['every']['type']);
372 + }
373 +
374 + if ($nextDate > $time) {
375 + $nextExecutionDate[] = $nextDate;
376 + } else {
377 + $execute = true;
378 + }
379 + }
380 +
381 + if ($execute) {
382 + $nextExecutionDate[] = $time + ($triggers['every']['number'] * $triggers['every']['type']);
383 + }
384 + }
385 +
386 + if (!empty($nextExecutionDate)) {
387 + $step->next_execution = min($nextExecutionDate);
388 + }
389 + }
390 +
391 + public function onAcymDeclareSummary_triggers(object $automation): void
392 + {
393 + if (!empty($automation->triggers['type_trigger'])) {
394 + unset($automation->triggers['type_trigger']);
395 + }
396 +
397 + $days = $this->getTriggerDays();
398 +
399 + $this->summaryAsap($automation);
400 + $this->summaryDay($automation);
401 + $this->summaryWeeksOn($automation, $days);
402 + $this->summaryOnDayMonth($automation, $days);
403 + $this->summaryEvery($automation);
404 + }
405 +
406 + private function summaryAsap(object $automation): void
407 + {
408 + if (!empty($automation->triggers['asap'])) {
409 + $automation->triggers['asap'] = acym_translation('ACYM_EACH_TIME');
410 + }
411 + }
412 +
413 + private function summaryDay(object $automation): void
414 + {
415 + if (empty($automation->triggers['day']) || !is_array($automation->triggers['day'])) {
416 + return;
417 + }
418 +
419 + $hour = sprintf('%02d', $automation->triggers['day']['hour']);
420 + $minutes = sprintf('%02d', $automation->triggers['day']['minutes']);
421 +
422 + $automation->triggers['day'] = acym_translationSprintf('ACYM_TRIGGER_DAY_SUMMARY', $hour, $minutes);
423 + }
424 +
425 + private function summaryWeeksOn(object $automation, array $days): void
426 + {
427 + if (empty($automation->triggers['weeks_on']) || !is_array($automation->triggers['weeks_on'])) {
428 + return;
429 + }
430 +
431 + foreach ($automation->triggers['weeks_on']['day'] as $i => $oneDay) {
432 + $automation->triggers['weeks_on']['day'][$i] = $days[$oneDay];
433 + }
434 +
435 + if (isset($automation->triggers['weeks_on']['hour'])) {
436 + $hour = $automation->triggers['weeks_on']['hour'];
437 + $minutes = $automation->triggers['weeks_on']['minutes'];
438 + } else {
439 + $hour = $this->config->get('daily_hour', '12');
440 + $minutes = $this->config->get('daily_minute', '00');
441 + }
442 +
443 + $hour = sprintf('%02d', $hour);
444 + $minutes = sprintf('%02d', $minutes);
445 +
446 + $automation->triggers['weeks_on'] = acym_translationSprintf(
447 + 'ACYM_TRIGGER_WEEKS_ON_SUMMARY',
448 + implode(', ', $automation->triggers['weeks_on']['day'])
449 + ).' '.acym_translationSprintf('ACYM_AT_DATE_TIME', $hour, $minutes);
450 + }
451 +
452 + private function summaryOnDayMonth(object $automation, array $days): void
453 + {
454 + if (empty($automation->triggers['on_day_month']) || !is_array($automation->triggers['on_day_month'])) {
455 + return;
456 + }
457 +
458 + $numbers = [
459 + 'first' => acym_translation('ACYM_FIRST'),
460 + 'second' => acym_translation('ACYM_SECOND'),
461 + 'third' => acym_translation('ACYM_THIRD'),
462 + 'fourth' => acym_translation('ACYM_FOURTH'),
463 + 'last' => acym_translation('ACYM_LAST'),
464 + ];
465 +
466 + if (isset($automation->triggers['on_day_month']['hour'])) {
467 + $hour = $automation->triggers['on_day_month']['hour'];
468 + $minutes = $automation->triggers['on_day_month']['minutes'];
469 + } else {
470 + $hour = $this->config->get('daily_hour', '12');
471 + $minutes = $this->config->get('daily_minute', '00');
472 + }
473 +
474 + $hour = sprintf('%02d', $hour);
475 + $minutes = sprintf('%02d', $minutes);
476 +
477 + $automation->triggers['on_day_month'] = acym_translationSprintf(
478 + 'ACYM_TRIGGER_ON_DAY_MONTH_SUMMARY',
479 + $numbers[$automation->triggers['on_day_month']['number']],
480 + $days[$automation->triggers['on_day_month']['day']]
481 + ).' '.acym_translationSprintf('ACYM_AT_DATE_TIME', $hour, $minutes);
482 + }
483 +
484 + private function summaryEvery(object $automation): void
485 + {
486 + if (empty($automation->triggers['every']) || !is_array($automation->triggers['every'])) {
487 + return;
488 + }
489 +
490 + if ($automation->triggers['every']['type'] == 3600) $automation->triggers['every']['type'] = acym_translation('ACYM_HOURS');
491 + if ($automation->triggers['every']['type'] == 86400) $automation->triggers['every']['type'] = acym_translation('ACYM_DAYS');
492 + if ($automation->triggers['every']['type'] == 604800) $automation->triggers['every']['type'] = acym_translation('ACYM_WEEKS');
493 + if ($automation->triggers['every']['type'] == 2628000) $automation->triggers['every']['type'] = acym_translation('ACYM_MONTHS');
494 + $automation->triggers['every'] = acym_translationSprintf(
495 + 'ACYM_TRIGGER_EVERY_SUMMARY',
496 + $automation->triggers['every']['number'],
497 + $automation->triggers['every']['type']
498 + );
499 + }
500 +}