PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 10.1.4
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v10.1.4
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 10.1.4, at back/Controllers/Lists/Edition.php

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