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 +217 -135 10.11.1 → 11.1.0 View file →
@@ -1,48 +1,212 @@
1 1 <?php
2 2
3 3 trait TimeAutomationTriggers
4 4 {
5 - public function onAcymDeclareTriggers(&$triggers, &$defaultValues)
5 + protected function initAutomationTriggers(): void
6 6 {
7 - $dailyHour = $this->config->get('daily_hour', '12');
8 - $dailyMinute = $this->config->get('daily_minute', '00');
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 + }
9 15
10 - $triggers['classic']['asap'] = new stdClass();
11 - $triggers['classic']['asap']->name = acym_translation('ACYM_EACH_TIME');
12 - $triggers['classic']['asap']->option = '<input type="hidden" name="[triggers][classic][asap]" value="y">';
16 + public function displayTriggerOptions_asap(string $fieldName): void
17 + {
18 + echo '<input type="hidden" name="'.esc_attr($fieldName).'" value="y">';
19 + }
13 20
14 - $hour = [];
15 - $minutes = [];
16 - $i = 0;
17 - while ($i <= 59) {
18 - if ($i <= 23) {
19 - $hour[$i] = $i < 10 ? '0'.$i : $i;
20 - }
21 - $minutes[$i] = $i < 10 ? '0'.$i : $i;
22 - $i++;
23 - }
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 + }
24 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 + }
25 81
26 - $triggers['classic']['day'] = new stdClass();
27 - $triggers['classic']['day']->name = acym_translation('ACYM_EVERY_DAY_AT');
28 - $triggers['classic']['day']->option = '<div class="grid-x grid-margin-x" style="height: 40px;">';
29 - $triggers['classic']['day']->option .= '<div class="cell medium-shrink">'.acym_select(
30 - $hour,
31 - '[triggers][classic][day][hour]',
32 - empty($defaultValues['day']) ? date('H') : $defaultValues['day']['hour'],
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'),
33 172 ['data-class' => 'intext_select acym__select']
34 173 ).'</div>';
35 - $triggers['classic']['day']->option .= '<div class="cell medium-shrink acym_vcenter">:</div>';
36 - $triggers['classic']['day']->option .= '<div class="cell medium-auto">'.acym_select(
37 - $minutes,
38 - '[triggers][classic][day][minutes]',
39 - empty($defaultValues['day']) ? date('i') : $defaultValues['day']['minutes'],
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'),
40 179 ['data-class' => 'intext_select acym__select']
41 180 ).'</div>';
42 - $triggers['classic']['day']->option .= '</div>';
43 181
44 - $days = [
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 [
45 209 'monday' => acym_translation('ACYM_MONDAY'),
46 210 'tuesday' => acym_translation('ACYM_TUESDAY'),
47 211 'wednesday' => acym_translation('ACYM_WEDNESDAY'),
48 212 'thursday' => acym_translation('ACYM_THURSDAY'),
@@ -49,104 +213,8 @@
49 213 'friday' => acym_translation('ACYM_FRIDAY'),
50 214 'saturday' => acym_translation('ACYM_SATURDAY'),
51 215 'sunday' => acym_translation('ACYM_SUNDAY'),
52 216 ];
53 -
54 - $triggers['classic']['weeks_on'] = new stdClass();
55 - $triggers['classic']['weeks_on']->name = acym_translation('ACYM_EVERY_WEEK_ON');
56 - $triggers['classic']['weeks_on']->option = '<div class="grid-x">';
57 - $triggers['classic']['weeks_on']->option .= '<div class="cell">'.acym_selectMultiple(
58 - $days,
59 - '[triggers][classic][weeks_on][day]',
60 - empty($defaultValues['weeks_on']) ? ['monday'] : $defaultValues['weeks_on']['day'],
61 - ['data-class' => 'acym__select']
62 - ).'</div>';
63 - $triggers['classic']['weeks_on']->option .= '<div class="cell margin-top-1 acym_vcenter">';
64 - $triggers['classic']['weeks_on']->option .= acym_translationSprintf(
65 - 'ACYM_AT_DATE_TIME',
66 - '<div class="margin-left-1 margin-right-1">'.acym_select(
67 - $hour,
68 - '[triggers][classic][weeks_on][hour]',
69 - !isset($defaultValues['weeks_on']['hour']) ? $dailyHour : $defaultValues['weeks_on']['hour'],
70 - ['data-class' => 'intext_select acym__select']
71 - ).'</div>',
72 - '<div class="margin-left-1 margin-right-1">'.acym_select(
73 - $minutes,
74 - '[triggers][classic][weeks_on][minutes]',
75 - !isset($defaultValues['weeks_on']['minutes']) ? $dailyMinute : $defaultValues['weeks_on']['minutes'],
76 - ['data-class' => 'intext_select acym__select']
77 - ).'</div>'
78 - );
79 - $triggers['classic']['weeks_on']->option .= '</div>';
80 - $triggers['classic']['weeks_on']->option .= '</div>';
81 -
82 -
83 - $triggers['classic']['on_day_month'] = new stdClass();
84 - $triggers['classic']['on_day_month']->name = acym_translation('ACYM_ONTHE');
85 - $triggers['classic']['on_day_month']->option = '<div class="grid-x grid-margin-x margin-y">';
86 - $triggers['classic']['on_day_month']->option .= '<div class="cell medium-4">'.acym_select(
87 - [
88 - 'first' => acym_translation('ACYM_FIRST'),
89 - 'second' => acym_translation('ACYM_SECOND'),
90 - 'third' => acym_translation('ACYM_THIRD'),
91 - 'fourth' => acym_translation('ACYM_FOURTH'),
92 - 'last' => acym_translation('ACYM_LAST'),
93 - ],
94 - '[triggers][classic][on_day_month][number]',
95 - empty($defaultValues['on_day_month']) ? null : $defaultValues['on_day_month']['number'],
96 - ['data-class' => 'acym__select']
97 - ).'</div>';
98 - $triggers['classic']['on_day_month']->option .= '<div class="cell medium-4">'.acym_select(
99 - $days,
100 - '[triggers][classic][on_day_month][day]',
101 - empty($defaultValues['on_day_month']) ? null : $defaultValues['on_day_month']['day'],
102 - [
103 - 'data-class' => 'acym__select',
104 - 'style' => 'margin: 0 10px;',
105 - ]
106 - ).'</div>';
107 - $triggers['classic']['on_day_month']->option .= '<div class="cell medium-4 acym_vcenter">'.acym_translation('ACYM_DAYOFMONTH').'</div>';
108 - $triggers['classic']['on_day_month']->option .= '<div class="cell acym_vcenter">';
109 - $triggers['classic']['on_day_month']->option .= acym_translationSprintf(
110 - 'ACYM_AT_DATE_TIME',
111 - '<div class="margin-left-1 margin-right-1">'.acym_select(
112 - $hour,
113 - '[triggers][classic][on_day_month][hour]',
114 - !isset($defaultValues['on_day_month']['hour']) ? $dailyHour : $defaultValues['on_day_month']['hour'],
115 - ['data-class' => 'intext_select acym__select']
116 - ).'</div>',
117 - '<div class="margin-left-1 margin-right-1">'.acym_select(
118 - $minutes,
119 - '[triggers][classic][on_day_month][minutes]',
120 - !isset($defaultValues['on_day_month']['minutes']) ? $dailyMinute : $defaultValues['on_day_month']['minutes'],
121 - ['data-class' => 'intext_select acym__select']
122 - ).'</div>'
123 - );
124 - $triggers['classic']['on_day_month']->option .= '</div>';
125 - $triggers['classic']['on_day_month']->option .= '</div>';
126 -
127 -
128 - $every = [
129 - '3600' => acym_translation('ACYM_HOURS'),
130 - '86400' => acym_translation('ACYM_DAYS'),
131 - '604800' => acym_translation('ACYM_WEEKS'),
132 - '2628000' => acym_translation('ACYM_MONTHS'),
133 - ];
134 -
135 - $defaultEvery = empty($defaultValues['every']['number']) ? '1' : $defaultValues['every']['number'];
136 - $triggers['classic']['every'] = new stdClass();
137 - $triggers['classic']['every']->name = acym_translation('ACYM_EVERY');
138 - $triggers['classic']['every']->option = '<div class="grid-x grid-margin-x">';
139 - $triggers['classic']['every']->option .= '<div class="cell medium-shrink">';
140 - $triggers['classic']['every']->option .= '<input type="number" min="1" name="[triggers][classic][every][number]" class="intext_input" value="'.intval($defaultEvery).'">';
141 - $triggers['classic']['every']->option .= '</div>';
142 - $triggers['classic']['every']->option .= '<div class="cell medium-auto">'.acym_select(
143 - $every,
144 - '[triggers][classic][every][type]',
145 - empty($defaultValues['every']) ? '604800' : $defaultValues['every']['type'],
146 - ['data-class' => 'intext_select acym__select']
147 - ).'</div>';
148 - $triggers['classic']['every']->option .= '</div>';
149 217 }
150 218
151 219 public function onAcymExecuteTrigger(&$step, &$execute, &$data)
152 220 {
@@ -156,20 +224,25 @@
156 224
157 225 $time = $data['time'];
158 226 $triggers = $step->triggers;
159 227
228 + // For each trigger of the automation, we'll calculate the next execution date. In the end we take the closest one
160 229 $nextExecutionDate = [];
161 230
231 + // Get the time the auto tasks should be triggered
162 232 $dailyHour = $this->config->get('daily_hour', '12');
163 233 $dailyMinute = $this->config->get('daily_minute', '00');
164 234
165 235
236 + // Each time the cron is triggered
166 237 if (!empty($triggers['asap'])) {
167 238 $execute = true;
168 239 $nextExecutionDate[] = $time;
169 240 }
170 241
242 + // Every day at xx:xx
171 243 if (!empty($triggers['day'])) {
244 + // The day it is currently based on the timezone specified in the CMS configuration
172 245 $dayBasedOnCMSTimezone = acym_date('now', 'Y-m-d');
173 246
174 247 $hour = $triggers['day']['hour'];
175 248 $minutes = $triggers['day']['minutes'];
@@ -176,8 +249,9 @@
176 249
177 250 if (strlen($hour) < 2) $hour = '0'.$hour;
178 251 if (strlen($minutes) < 2) $minutes = '0'.$minutes;
179 252
253 + // The UTC timestamp of the current day based on the CMS timezone, at the specified hour
180 254 $dayBasedOnCMSTimezoneAtSpecifiedHour = acym_getTimeFromCMSDate($dayBasedOnCMSTimezone.' '.$hour.':'.$minutes);
181 255
182 256 if ($time < $dayBasedOnCMSTimezoneAtSpecifiedHour) {
183 257 $nextExecutionDate[] = $dayBasedOnCMSTimezoneAtSpecifiedHour;
@@ -183,12 +257,14 @@
183 257 $nextExecutionDate[] = $dayBasedOnCMSTimezoneAtSpecifiedHour;
184 258 } else {
185 259 $nextExecutionDate[] = $dayBasedOnCMSTimezoneAtSpecifiedHour + 86400;
186 260
261 + // First trigger: if the hour is passed we execute
187 262 if (empty($step->last_execution)) $execute = true;
188 263 }
189 264 }
190 265
266 + // Each week on Mondays and Wednesdays for example
191 267 if (!empty($triggers['weeks_on'])) {
192 268 if (isset($triggers['weeks_on']['hour'])) {
193 269 $hour = $triggers['weeks_on']['hour'];
194 270 $minutes = $triggers['weeks_on']['minutes'];
@@ -200,12 +276,15 @@
200 276 if (strlen($hour) < 2) $hour = '0'.$hour;
201 277 if (strlen($minutes) < 2) $minutes = '0'.$minutes;
202 278
203 279 foreach ($triggers['weeks_on']['day'] as $day) {
280 + // The day it is currently based on the timezone specified in the CMS configuration
204 281 $dayBasedOnCMSTimezone = acym_date('now', 'Y-m-d');
205 282
283 + // The UTC timestamp of the current day based on the CMS timezone, at the specified hour
206 284 $dayBasedOnCMSTimezoneAtSpecifiedHour = acym_getTimeFromCMSDate($dayBasedOnCMSTimezone.' '.$hour.':'.$minutes);
207 285
286 + // Only store the next Execution date if it's in the future
208 287 if ($day == strtolower(acym_date('now', 'l', true, false))) {
209 288 if ($time < $dayBasedOnCMSTimezoneAtSpecifiedHour) {
210 289 $nextExecutionDate[] = $dayBasedOnCMSTimezoneAtSpecifiedHour;
211 290 } else {
@@ -210,8 +289,9 @@
210 289 $nextExecutionDate[] = $dayBasedOnCMSTimezoneAtSpecifiedHour;
211 290 } else {
212 291 $nextExecutionDate[] = $dayBasedOnCMSTimezoneAtSpecifiedHour + 604800;
213 292
293 + // Current day is selected, the time is passed, and it's the first trigger
214 294 $lastExecutionIsNotToday = acym_date($step->last_execution, 'Y-m-d') !== $dayBasedOnCMSTimezone;
215 295 $nextExecutionIsToday = acym_date($step->next_execution, 'Y-m-d') === $dayBasedOnCMSTimezone;
216 296 if (empty($step->last_execution) || ($lastExecutionIsNotToday && $nextExecutionIsToday)) $execute = true;
217 297 }
@@ -235,8 +315,9 @@
235 315 }
236 316 }
237 317 }
238 318
319 + // On first Friday of the month for example
239 320 if (!empty($triggers['on_day_month'])) {
240 321 if (isset($triggers['on_day_month']['hour'])) {
241 322 $hour = $triggers['on_day_month']['hour'];
242 323 $minutes = $triggers['on_day_month']['minutes'];
@@ -249,28 +330,37 @@
249 330 if (strlen($minutes) < 2) $minutes = '0'.$minutes;
250 331
251 332 $today = acym_getTime('today '.$hour.':'.$minutes);
252 333
334 + // Get the current month's day
253 335 $execution = acym_getTime($triggers['on_day_month']['number'].' '.$triggers['on_day_month']['day'].' of this month '.$hour.':'.$minutes);
254 336
337 + //If it's before today, get the next date
255 338 if ($execution < $today) {
256 339 $execution = acym_getTime($triggers['on_day_month']['number'].' '.$triggers['on_day_month']['day'].' of next month '.$hour.':'.$minutes);
257 340 }
258 341
342 + // The next execution date is in the future
259 343 if ($execution > $time) {
260 344 $nextExecutionDate[] = $execution;
261 345 } else {
346 + // The next execution is today and is passed
262 347
263 348
349 + // If it's the first trigger we execute
264 350 if (empty($step->last_execution)) {
265 351 $execute = true;
266 352 }
267 353
354 + // Set the next execution time: recompute the real "next weekday of next month" occurrence.
268 355 $nextExecutionDate[] = acym_getTime($triggers['on_day_month']['number'].' '.$triggers['on_day_month']['day'].' of next month '.$hour.':'.$minutes);
269 356 }
270 357 }
271 358
359 + // WARNING : KEEP THIS TRIGGER AT THE END, ACTION PERFORMED IF WE EXECUTE
360 + // Every X hours/days/weeks/months
272 361 if (!empty($triggers['every'])) {
362 + // First trigger: we execute and set the next execution in X hours/days/weeks/months
273 363 if (empty($step->last_execution)) {
274 364 $execute = true;
275 365 } else {
276 366 if ($triggers['every']['type'] == 2628000) {
@@ -303,17 +393,9 @@
303 393 if (!empty($automation->triggers['type_trigger'])) {
304 394 unset($automation->triggers['type_trigger']);
305 395 }
306 396
307 - $days = [
308 - 'monday' => acym_translation('ACYM_MONDAY'),
309 - 'tuesday' => acym_translation('ACYM_TUESDAY'),
310 - 'wednesday' => acym_translation('ACYM_WEDNESDAY'),
311 - 'thursday' => acym_translation('ACYM_THURSDAY'),
312 - 'friday' => acym_translation('ACYM_FRIDAY'),
313 - 'saturday' => acym_translation('ACYM_SATURDAY'),
314 - 'sunday' => acym_translation('ACYM_SUNDAY'),
315 - ];
397 + $days = $this->getTriggerDays();
316 398
317 399 $this->summaryAsap($automation);
318 400 $this->summaryDay($automation);
319 401 $this->summaryWeeksOn($automation, $days);