PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 11.0.0
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v11.0.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
acymailing / back / Controllers / Lists / Edition.php

Edition.php in AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress 11.0.0, at back/Controllers/Lists/Edition.php

476 lines 18.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace AcyMailing\Controllers\Lists;
4
5 use AcyMailing\Classes\HistoryClass;
6 use AcyMailing\Classes\ListClass;
7 use AcyMailing\Classes\MailClass;
8 use AcyMailing\Classes\MailStatClass;
9 use AcyMailing\Classes\TagClass;
10 use AcyMailing\Classes\UrlClickClass;
11 use AcyMailing\Controllers\ListsController;
12 use AcyMailing\Helpers\EntitySelectHelper;
13 use AcyMailing\Helpers\WorkflowHelper;
14
15 trait Edition
16 {
17 public function settings(): void
18 {
19 acym_setVar('layout', 'settings');
20
21 $tab = acym_getVar('string', 'step', ListsController::LIST_EDITION_TABS_GENERAL);
22
23 // In the front with the SEF activated, the tab is not always set
24 if (empty($tab)) {
25 $tab = ListsController::LIST_EDITION_TABS_GENERAL;
26 }
27
28 $data = [];
29 $data['workflowHelper'] = new WorkflowHelper();
30 $data['currentTab'] = $tab;
31 $data['tabs'] = [
32 'general' => ListsController::LIST_EDITION_TABS_GENERAL,
33 'subscribers' => ListsController::LIST_EDITION_TABS_SUBSCRIBERS,
34 'unsubscriptions' => ListsController::LIST_EDITION_TABS_UNSUBSCRIPTIONS,
35 ];
36
37 $listId = acym_getVar('int', 'listId', 0);
38
39 if (!$this->prepareListSettings($data, $listId)) return;
40
41 if ($tab === ListsController::LIST_EDITION_TABS_GENERAL) {
42 $this->prepareTagsSettings($data, $listId);
43 $this->prepareListStat($data, $listId);
44 $this->prepareListStatEvolution($data, $listId);
45 $this->prepareWelcomeUnsubData($data);
46 $this->prepareMultilingualOption($data);
47 } elseif ($tab === ListsController::LIST_EDITION_TABS_SUBSCRIBERS) {
48 $this->prepareSubscribersEntitySelect($data, $listId);
49 $this->prepareSubscribersSettings($data, $listId);
50 } elseif ($tab === ListsController::LIST_EDITION_TABS_UNSUBSCRIPTIONS) {
51 $this->prepareUnsubReasons($data);
52 }
53
54 parent::display($data);
55 }
56
57 private function prepareListSettings(array &$data, int $listId): bool
58 {
59 if (empty($listId)) {
60 $listInformation = new \stdClass();
61 $listInformation->id = '';
62 $listInformation->name = '';
63 $listInformation->frontLabel = '';
64 $listInformation->description = '';
65 $listInformation->active = 1;
66 $listInformation->visible = 1;
67 $listInformation->color = '#'.implode('', [
68 ListClass::COLOR_PARTS[acym_rand(0, 15)],
69 ListClass::COLOR_PARTS[acym_rand(0, 15)],
70 ListClass::COLOR_PARTS[acym_rand(0, 15)],
71 ListClass::COLOR_PARTS[acym_rand(0, 15)],
72 ListClass::COLOR_PARTS[acym_rand(0, 15)],
73 ListClass::COLOR_PARTS[acym_rand(0, 15)],
74 ]);
75 $listInformation->welcome_id = '';
76 $listInformation->unsubscribe_id = '';
77 $listInformation->access = [];
78 $listInformation->tracking = 1;
79 $listInformation->translation = [];
80 $listInformation->display_name = '';
81
82 $this->breadcrumb[acym_translation('ACYM_NEW_LIST')] = acym_completeLink('lists&task=settings');
83 } else {
84 $listClass = new ListClass();
85 $listInformation = $listClass->getOneById($listId);
86 if (is_null($listInformation)) {
87 acym_enqueueMessage(acym_translation('ACYM_LIST_DOESNT_EXIST'), 'error');
88 $this->listing();
89
90 return false;
91 }
92
93
94 $subscribersCount = $listClass->getSubscribersCountPerStatusByListIds([$listId]);
95
96 $this->breadcrumb[$listInformation->name] = acym_completeLink('lists&task=settings&listId='.$listId);
97
98 $listInformation->access = empty($listInformation->access) ? [] : explode(',', $listInformation->access);
99
100 $currentUser = acym_currentUserId();
101 if (!acym_isAdmin() && ($listInformation->cms_user_id != $currentUser)) {
102 $userGroups = acym_getGroupsByUser($currentUser);
103 $canAccess = false;
104
105 foreach ($userGroups as $group) {
106 if (in_array($group, $listInformation->access)) $canAccess = true;
107 }
108
109 if (!$canAccess) {
110 acym_enqueueMessage(acym_translation('ACYM_YOU_DONT_HAVE_ACCESS_TO_THIS_LIST'), 'error');
111
112 $this->listing();
113
114 return false;
115 }
116 }
117 }
118
119 $listInformation->subscribers = [
120 'unsubscribed_users' => 0,
121 'sendable_users' => 0,
122 'unconfirmed_users' => 0,
123 'inactive_users' => 0,
124 ];
125 if (!empty($subscribersCount)) {
126 $listStats = array_shift($subscribersCount);
127 $listInformation->subscribers = [
128 'unsubscribed_users' => $listStats->unsubscribed_users,
129 'sendable_users' => $listStats->sendable_users,
130 'unconfirmed_users' => $listStats->unconfirmed_users,
131 'inactive_users' => $listStats->inactive_users,
132 ];
133 }
134
135 $data['listInformation'] = $listInformation;
136
137 return true;
138 }
139
140 private function prepareTagsSettings(array &$data, int $listId): void
141 {
142 $tagClass = new TagClass();
143 $data['allTags'] = $tagClass->getAllTagsByType(TagClass::TYPE_LIST);
144 $data['listTagsName'] = [];
145 $listsTags = $tagClass->getAllTagsByElementId(TagClass::TYPE_LIST, $listId);
146 foreach ($listsTags as $oneTag) {
147 $data['listTagsName'][] = $oneTag;
148 }
149 }
150
151 private function prepareSubscribersSettings(array &$data, int $listId): void
152 {
153 $data['ordering'] = acym_getVar('string', 'users_ordering', 'id');
154 $data['orderingSortOrder'] = acym_getVar('string', 'users_ordering_sort_order', 'desc');
155 $data['classSortOrder'] = $data['orderingSortOrder'] == 'asc' ? 'acymicon-sort-amount-asc' : 'acymicon-sort-amount-desc';
156 $listClass = new ListClass();
157 $data['subscribers'] = $listClass->getSubscribersForList(
158 [
159 'listIds' => [$listId],
160 'offset' => 0,
161 'limit' => 500,
162 'status' => 1,
163 'orderBy' => $data['ordering'],
164 'orderBySort' => $data['orderingSortOrder'],
165 ]
166 );
167 foreach ($data['subscribers'] as &$oneSub) {
168 if ($oneSub->subscription_date == '0000-00-00 00:00:00') continue;
169 $oneSub->subscription_date = acym_date(strtotime($oneSub->subscription_date), acym_translation('ACYM_DATE_FORMAT_LC2'));
170 }
171 }
172
173 private function prepareSubscribersEntitySelect(array &$data, int $listId): void
174 {
175 if (empty($listId)) {
176 $data['subscribersEntitySelect'] = '';
177
178 return;
179 }
180
181 $data['subscribersEntitySelect'] = $listId;
182 }
183
184 private function prepareListStat(array &$data, int $listId): void
185 {
186 $data['listStats'] = ['deliveryRate' => 0, 'openRate' => 0, 'clickRate' => 0, 'failRate' => 0, 'bounceRate' => 0];
187 if (empty($listId)) return;
188 $listClass = new ListClass();
189 $mails = $listClass->getMailsByListId($listId);
190 if (empty($mails)) return;
191
192 $mailStatClass = new MailStatClass();
193 $mailsStat = $mailStatClass->getCumulatedStatsByMailIds($mails);
194
195 if (intval($mailsStat->sent) + intval($mailsStat->fails) === 0) return;
196
197 $totalSent = intval($mailsStat->sent) + intval($mailsStat->fails);
198 if (empty($mailsStat->open)) $mailsStat->open = 0;
199 if (empty($mailsStat->fails)) $mailsStat->fails = 0;
200 if (empty($mailsStat->bounces)) $mailsStat->bounces = 0;
201
202 $data['listStats']['openRate'] = number_format($mailsStat->open / $totalSent * 100, 2);
203 $data['listStats']['deliveryRate'] = number_format(($mailsStat->sent - $mailsStat->bounces) / $totalSent * 100, 2);
204 $data['listStats']['failRate'] = number_format($mailsStat->fails / $totalSent * 100, 2);
205 $data['listStats']['bounceRate'] = number_format($mailsStat->bounces / $totalSent * 100, 2);
206
207 $urlClickClass = new UrlClickClass();
208 $nbClicks = $urlClickClass->getClickRateByMailIds($mails);
209 $data['listStats']['clickRate'] = number_format($nbClicks / $totalSent * 100, 2);
210 }
211
212 private function prepareListStatEvolution(array &$data, int $listId): void
213 {
214 $data['evol'] = [];
215 $listClass = new ListClass();
216 $subEvolStat = $listClass->getYearSubEvolutionPerList($listId);
217 if (empty($subEvolStat['subscribers']) && empty($subEvolStat['unsubscribers'])) return;
218
219 // Init tables ordered by month number starting on month from one year ago
220 $firstMonth = gmdate('n') + 1;
221 $zeroReached = false;
222 $evolSub = [];
223 $evolUnsub = [];
224 for ($i = 0; $i < 12; $i++) {
225 $month = ($firstMonth + $i) % 13;
226 if ($month == 0) $zeroReached = true;
227 if ($zeroReached) $month += 1;
228 $evolSub[$month] = $month.'_0';
229 $evolUnsub[$month] = $month.'_0';
230 }
231
232 foreach ($subEvolStat['subscribers'] as $unit => $monthData) {
233 $evolSub[$monthData->monthSub] = $monthData->monthSub.'_'.$monthData->nbUser;
234 }
235
236 foreach ($subEvolStat['unsubscribers'] as $unit => $monthData) {
237 $evolUnsub[$monthData->monthUnsub] = $monthData->monthUnsub.'_'.$monthData->nbUser;
238 }
239
240 foreach ($evolSub as $month => $oneEvol) {
241 $data['evol'][0][] = $oneEvol;
242 $data['evol'][1][] = $evolUnsub[$month];
243 }
244 }
245
246 protected function prepareWelcomeUnsubData(array &$data): void
247 {
248 $data['tmpls'] = [];
249 if (empty($data['listInformation']->id)) return;
250 $listClass = new ListClass();
251 $mailClass = new MailClass();
252
253 foreach ([MailClass::TYPE_WELCOME => 'welcome', MailClass::TYPE_UNSUBSCRIBE => 'unsub'] as $full => $short) {
254 $mailId = acym_getVar('int', $short.'mailid', 0);
255 if (empty($data['listInformation']->{$full.'_id'}) && !empty($mailId)) {
256 $data['listInformation']->{$full.'_id'} = $mailId;
257 $listInfoSave = clone $data['listInformation'];
258 unset($listInfoSave->subscribers);
259 $savedListId = $listClass->save($listInfoSave);
260 if (empty($savedListId)) {
261 acym_enqueueMessage(acym_translation('ACYM_ERROR_SAVE_LIST'), 'error');
262 }
263 }
264
265 $returnLink = acym_completeLink('lists&task=settings&listId='.$data['listInformation']->id.'&edition=1&'.$short.'mailid={mailid}');
266 $favoriteTemplate = $this->config->get('favorite_template', 0);
267 $startFrom = empty($favoriteTemplate) ? '' : '&from='.$favoriteTemplate;
268
269 if (empty($data['listInformation']->{$full.'_id'})) {
270 $data['tmpls'][$short.'TmplUrl'] = acym_completeLink(
271 'mails&task=edit&step=editEmail&type='.$full.'&type_editor=acyEditor&list_id='.$data['listInformation']->id.'&return='.urlencode(
272 base64_encode($returnLink)
273 ).$startFrom
274 );
275 } else {
276 $data['tmpls'][$short.'TmplUrl'] = acym_completeLink(
277 'mails&task=edit&id='.$data['listInformation']->{$full.'_id'}.'&type='.$full.'&list_id='.$data['listInformation']->id.'&return='.urlencode(
278 base64_encode($returnLink)
279 ).$startFrom
280 );
281 }
282
283 $data['tmpls'][$full] = !empty($data['listInformation']->{$full.'_id'}) ? $mailClass->getOneById($data['listInformation']->{$full.'_id'}) : '';
284 }
285 }
286
287 public function unsetMail(string $type): void
288 {
289 wp_verify_nonce(acym_getVar('cmd', '_wpnonce'), 'acymnonce') || die('Invalid Token');
290
291 $listClass = new ListClass();
292 $id = acym_getVar('int', 'listId', 0);
293 $list = $listClass->getOneById($id);
294
295 if (empty($list)) {
296 acym_enqueueMessage(acym_translation('ACYM_ERROR_SAVE_LIST'), 'error');
297 $this->listing();
298
299 return;
300 }
301
302 if (!$listClass->hasUserAccess($id)) {
303 die('Access denied for list '.esc_html($id));
304 }
305
306 $list->$type = null;
307
308 $savedListId = $listClass->save($list);
309 if (!empty($savedListId)) {
310 acym_setVar('listId', $id);
311 $this->settings();
312 } else {
313 acym_enqueueMessage(acym_translation('ACYM_ERROR_SAVE_LIST'), 'error');
314 $this->listing();
315 }
316 }
317
318 public function unsetWelcome(): void
319 {
320 $this->unsetMail('welcome_id');
321 }
322
323 public function unsetUnsubscribe(): void
324 {
325 $this->unsetMail('unsubscribe_id');
326 }
327
328 public function apply(): void
329 {
330 $this->save(false);
331 }
332
333 public function save(bool $goToListing = true): void
334 {
335 wp_verify_nonce(acym_getVar('cmd', '_wpnonce'), 'acymnonce') || die('Invalid Token');
336
337 $step = acym_getVar('string', 'step');
338 if ($step === self::LIST_EDITION_TABS_GENERAL) {
339 $listClass = new ListClass();
340 $formData = (object)acym_getVar('array', 'list', []);
341
342 $listId = acym_getVar('int', 'listId', 0);
343 if (!empty($listId)) {
344 $formData->id = $listId;
345 }
346
347 $allowedFields = acym_getColumns('list');
348 $listInformation = new \stdClass();
349 if (empty($formData->welcome_id)) unset($formData->welcome_id);
350 if (empty($formData->unsubscribe_id)) unset($formData->unsubscribe_id);
351 foreach ($formData as $name => $data) {
352 if (!in_array($name, $allowedFields)) {
353 continue;
354 }
355 $listInformation->{$name} = $data;
356 }
357
358 $listInformation->tags = acym_getVar('array', 'list_tags', []);
359
360 if (acym_isAdmin()) {
361 $listInformation->access = empty($listInformation->access) ? '' : ','.implode(',', $listInformation->access).',';
362 } elseif (!empty($formData->id) && !$listClass->hasUserAccess($formData->id)) {
363 die('Cannot save list '.esc_html($formData->id));
364 }
365
366 $listId = $listClass->save($listInformation);
367
368 if (!empty($listId)) {
369 acym_setVar('listId', $listId);
370 acym_enqueueMessage(acym_translationSprintf('ACYM_LIST_IS_SAVED', $listInformation->name), 'success');
371 $this->saveSubscribersTolist();
372 } else {
373 acym_enqueueMessage(acym_translation('ACYM_ERROR_SAVING'), 'error');
374 if (!empty($listClass->errors)) {
375 acym_enqueueMessage($listClass->errors, 'error');
376 }
377 }
378 }
379
380 if ($goToListing) {
381 $this->listing();
382 } else {
383 $this->settings();
384 }
385 }
386
387 private function saveSubscribersTolist(): bool
388 {
389 $selected = acym_getVar('string', 'acym__entity_select__selected', '[]');
390 $unselected = acym_getVar('string', 'acym__entity_select__unselected', '[]');
391 $usersIds = json_decode(empty($selected) ? '[]' : $selected, true);
392 $usersIdsUnselected = json_decode(empty($unselected) ? '[]' : $unselected, true);
393 $listId = acym_getVar('int', 'listId', 0);
394
395 $listClass = new ListClass();
396 if (empty($listId) || !$listClass->hasUserAccess($listId)) return false;
397
398 acym_arrayToInteger($usersIdsUnselected);
399 if (!empty($usersIdsUnselected)) {
400 acym_query(
401 'UPDATE #__acym_user_has_list
402 SET status = 0, unsubscribe_date = '.acym_escapeDB(acym_date(time(), 'Y-m-d H:i:s')).'
403 WHERE list_id = '.intval($listId).'
404 AND user_id IN ('.implode(', ', $usersIdsUnselected).')'
405 );
406 }
407
408 acym_arrayToInteger($usersIds);
409 if (!empty($usersIds)) {
410 acym_query(
411 'INSERT IGNORE #__acym_user_has_list (`user_id`, `list_id`, `status`, `subscription_date`) (SELECT id, '.intval($listId).', 1, '.acym_escapeDB(
412 acym_date(time(), 'Y-m-d H:i:s')
413 ).' FROM #__acym_user AS user WHERE user.id IN ('.implode(', ', $usersIds).')) ON DUPLICATE KEY UPDATE status = 1'
414 );
415 }
416
417 return true;
418 }
419
420 public function saveSubscribers(): void
421 {
422 wp_verify_nonce(acym_getVar('cmd', '_wpnonce'), 'acymnonce') || die('Invalid Token');
423
424 $this->saveSubscribersTolist();
425 $listId = acym_getVar('int', 'listId', 0);
426 acym_setVar('listId', $listId);
427
428 $this->settings();
429 }
430
431 private function sortDataByList(array $data): array
432 {
433 $sortedData = [];
434
435 foreach ($data as $entry) {
436 $pattern = '/(?<!EXECUTED_BY::)\b\d+\b/';
437 preg_match_all($pattern, $entry->data, $listNumbers);
438
439 foreach ($listNumbers[0] as $listNumber) {
440 if (!isset($sortedData[$listNumber])) {
441 $sortedData[$listNumber] = [];
442 }
443
444 $sortedData[$listNumber][] = $entry->unsubscribe_reason;
445 }
446 }
447
448 return $sortedData;
449 }
450
451 private function prepareUnsubReasons(array &$data): void
452 {
453 $historyClass = new HistoryClass();
454 $allReasonsData = $historyClass->getAllUnsubReasons();
455
456 $sortedData = $this->sortDataByList($allReasonsData);
457 $listId = acym_getVar('int', 'listId', 0);
458 $currentListData = $sortedData[$listId] ?? [];
459 $reasonCounts = array_count_values($currentListData);
460
461 $allAnswers = $historyClass->getAllMainLanguageUnsubReasons();
462 $newReasonCounts = [];
463 foreach ($reasonCounts as $key => $value) {
464 if (is_numeric($key) && isset($allAnswers[$key - 1])) {
465 $newReasonCounts[$allAnswers[$key - 1]] = $value;
466 } else {
467 $newReasonCounts[$key] = $value;
468 }
469 }
470
471 arsort($newReasonCounts);
472
473 $data['unsubReasons'] = $newReasonCounts;
474 }
475 }
476