PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 11.0.4
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v11.0.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 / Classes / ListClass.php

ListClass.php in AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress 11.0.4, at back/Classes/ListClass.php

1,061 lines 36.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\Classes;
4
5 use AcyMailing\Helpers\MailerHelper;
6 use AcyMailing\Helpers\PaginationHelper;
7 use AcyMailing\Core\AcymClass;
8
9 class ListClass extends AcymClass
10 {
11 const LIST_TYPE_STANDARD = 'standard';
12 const LIST_TYPE_FRONT = 'front';
13 const LIST_TYPE_FOLLOWUP = 'followup';
14 const COLOR_PARTS = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
15
16 public function __construct()
17 {
18 parent::__construct();
19
20 $this->table = 'list';
21 $this->pkey = 'id';
22 }
23
24 public function getMatchingElements(array $settings = []): array
25 {
26 $columns = 'list.*';
27 if (!empty($settings['columns'])) {
28 foreach ($settings['columns'] as $key => $value) {
29 if ($value == 'lists.list_id') {
30 unset($settings['columns'][$key]);
31 continue;
32 }
33 $settings['columns'][$key] = $key === 'join' ? acym_secureDBColumn($value) : 'list.'.acym_secureDBColumn($value);
34 }
35 $columns = implode(', ', $settings['columns']);
36 }
37
38 $query = 'SELECT '.$columns.' FROM #__acym_list AS list';
39 $queryCount = 'SELECT COUNT(list.id) AS total FROM #__acym_list AS list';
40 if (!empty($settings['join'])) $query .= $this->getJoinForQuery($settings['join']);
41 /*
42 This query will return for example:
43
44 array(
45 6 => 0,
46 2 => 1,
47 5 => 2,
48 12 => 3
49 )
50
51 It would mean that:
52
53 6 lists are disabled and invisible 0 + (0*2) = 0
54 2 lists are active and invisible 1 + (0*2) = 1
55 5 lists are inactive and visible 0 + (1*2) = 2
56 12 lists are active and visible 1 + (1*2) = 3
57
58 So there are 2 + 12 active lists and 5 + 12 visible lists, get it?
59 */
60 $queryStatus = 'SELECT COUNT(id) AS number, active + (visible*2) AS score FROM #__acym_list AS list';
61 $filters = [];
62 $listsId = [];
63
64 if (!empty($settings['tag'])) {
65 $tagJoin = ' JOIN #__acym_tag AS tag ON list.id = tag.id_element';
66 $query .= $tagJoin;
67 $queryCount .= $tagJoin;
68 $queryStatus .= $tagJoin;
69 $filters[] = 'tag.name = '.acym_escapeDB($settings['tag']);
70 $filters[] = 'tag.type = "list"';
71 }
72
73 if (!empty($settings['search'])) {
74 $filters[] = 'list.name LIKE '.acym_escapeDB('%'.$settings['search'].'%');
75 }
76
77 if (!empty($settings['creator_id']) && !acym_isAdmin()) {
78 $userGroups = acym_getGroupsByUser($settings['creator_id']);
79 $groupCondition = '(list.access LIKE "%,'.implode(',%" OR list.access LIKE "%,', $userGroups).',%")';
80
81 $filters[] = 'list.cms_user_id = '.intval($settings['creator_id']).' OR '.$groupCondition.'';
82 }
83
84 $filters[] = 'list.type = '.acym_escapeDB(self::LIST_TYPE_STANDARD);
85
86 if (!empty($filters)) {
87 $queryStatus .= ' WHERE ('.implode(') AND (', $filters).')';
88 }
89
90 if (!empty($settings['status'])) {
91 $allowedStatus = [
92 'active' => 'active = 1',
93 'inactive' => 'active = 0',
94 'visible' => 'visible = 1',
95 'invisible' => 'visible = 0',
96 ];
97 if (empty($allowedStatus[$settings['status']])) {
98 die('Injection denied');
99 }
100 $filters[] = 'list.'.$allowedStatus[$settings['status']];
101 }
102
103 if (!empty($settings['where'])) {
104 $filters[] = $settings['where'];
105 }
106
107
108 if (!empty($filters)) {
109 $query .= ' WHERE ('.implode(') AND (', $filters).')';
110 $queryCount .= ' WHERE ('.implode(') AND (', $filters).')';
111 }
112
113 if (!empty($settings['ordering']) && !empty($settings['ordering_sort_order'])) {
114 $query .= ' ORDER BY list.'.acym_secureDBColumn($settings['ordering']).' '.acym_secureDBColumn(strtoupper($settings['ordering_sort_order']));
115 }
116
117 if (empty($settings['offset']) || $settings['offset'] < 0) {
118 $settings['offset'] = 0;
119 }
120
121 if (empty($settings['elementsPerPage']) || $settings['elementsPerPage'] < 1) {
122 $pagination = new PaginationHelper();
123 $settings['elementsPerPage'] = $pagination->getListLimit();
124 }
125
126 $results['elements'] = acym_loadObjectList($query, '', $settings['offset'], $settings['elementsPerPage']);
127 if (!empty($settings['join'])) {
128 $this->setSelectedList($results['elements'], $settings['join']);
129 }
130 foreach ($results['elements'] as $i => $oneList) {
131 $listsId[] = $oneList->id;
132 $results['elements'][$i]->sendable_users = 0;
133 $results['elements'][$i]->unconfirmed_users = 0;
134 $results['elements'][$i]->unsubscribed_users = 0;
135 $results['elements'][$i]->inactive_users = 0;
136 $results['elements'][$i]->newSub = 0;
137 $results['elements'][$i]->newUnsub = 0;
138 }
139
140 $countUserByList = [];
141 $countEvolByList = [];
142 if (!empty($listsId) && empty($settings['entitySelect'])) {
143 $countUserByList = $this->getSubscribersCountPerStatusByListIds($listsId);
144 $countEvolByList = $this->getSubscribersEvolutionByListIds($listsId);
145 }
146
147 foreach ($results['elements'] as $i => $list) {
148 $results['elements'][$i]->tags = [];
149 foreach ($countUserByList as $userList) {
150 if ($list->id == $userList->list_id) {
151 $results['elements'][$i]->sendable_users = $userList->sendable_users;
152 $results['elements'][$i]->unconfirmed_users = $userList->unconfirmed_users;
153 $results['elements'][$i]->unsubscribed_users = $userList->unsubscribed_users;
154 $results['elements'][$i]->inactive_users = $userList->inactive_users;
155 }
156 }
157 if (isset($countEvolByList[$list->id]->newSub)) $results['elements'][$i]->newSub = $countEvolByList[$list->id]->newSub;
158 if (isset($countEvolByList[$list->id]->newUnsub)) $results['elements'][$i]->newUnsub = $countEvolByList[$list->id]->newUnsub;
159 }
160
161 $results['total'] = acym_loadObject($queryCount);
162
163 $listsPerStatus = acym_loadObjectList($queryStatus.' GROUP BY score', 'score');
164 for ($i = 0; $i < 4; $i++) {
165 $listsPerStatus[$i] = empty($listsPerStatus[$i]) ? 0 : $listsPerStatus[$i]->number;
166 }
167
168 $results['status'] = [
169 'all' => array_sum($listsPerStatus),
170 'active' => $listsPerStatus[1] + $listsPerStatus[3],
171 'inactive' => $listsPerStatus[0] + $listsPerStatus[2],
172 'visible' => $listsPerStatus[2] + $listsPerStatus[3],
173 'invisible' => $listsPerStatus[0] + $listsPerStatus[1],
174 ];
175
176 return $results;
177 }
178
179 public function getOneById(int $id): ?object
180 {
181 $list = parent::getOneById($id);
182
183 if (!empty($list)) {
184 $list->translation = empty($list->translation) ? [] : json_decode($list->translation, true);
185 }
186
187 return $list;
188 }
189
190 private function setSelectedList(array $elements, string $join): void
191 {
192 if (strpos($join, 'join_list') === false) {
193 return;
194 }
195
196 $listsIds = explode('-', $join);
197 $listsIds = $listsIds[1];
198 $listsIds = explode(',', $listsIds);
199 foreach ($elements as $element) {
200 $element->list_id = in_array($element->id, $listsIds) ? $element->id : null;
201 }
202 }
203
204 private function getJoinForQuery(string $joinType): string
205 {
206 if (strpos($joinType, 'join_mail') !== false) {
207 $mailId = explode('-', $joinType);
208
209 return ' LEFT JOIN #__acym_mail_has_list as maillist ON list.id = maillist.list_id AND maillist.mail_id = '.intval($mailId[1]);
210 }
211 if (strpos($joinType, 'join_user') !== false) {
212 $userId = explode('-', $joinType);
213
214 return ' LEFT JOIN #__acym_user_has_list as userlist ON list.id = userlist.list_id AND userlist.status = 1 AND userlist.user_id = '.intval($userId[1]);
215 }
216
217 return '';
218 }
219
220 public function getListsWithIdNameCount(array $settings): array
221 {
222 $filters = [];
223
224 if (isset($settings['ids'])) {
225 if (empty($settings['ids'])) {
226 return ['lists' => [], 'total' => 0];
227 } else {
228 acym_arrayToInteger($settings['ids']);
229 $filters[] = 'list.id IN ('.implode(',', $settings['ids']).')';
230 }
231 }
232
233 $query = 'SELECT list.id, list.name, list.color, list.active, COUNT(user.id) AS subscribers
234 FROM #__acym_list AS list
235 LEFT JOIN #__acym_user_has_list AS userList
236 ON list.id = userList.list_id
237 AND userList.status = 1
238 LEFT JOIN #__acym_user AS user
239 ON user.id = userList.user_id
240 AND user.active = 1 ';
241
242 if (intval($this->config->get('require_confirmation', 1)) === 1) {
243 $query .= ' AND user.confirmed = 1 ';
244 }
245
246 $queryCount = 'SELECT COUNT(list.id) FROM #__acym_list AS list';
247
248 if (!acym_isAdmin()) {
249 $currentUserId = acym_currentUserId();
250 if (empty($currentUserId)) {
251 return [
252 'lists' => [],
253 'total' => 0,
254 ];
255 }
256
257 $userGroups = acym_getGroupsByUser($currentUserId);
258 $groupCondition = '(list.access LIKE "%,'.implode(',%" OR list.access LIKE "%,', $userGroups).',%")';
259
260 $filters[] = 'list.cms_user_id = '.intval($currentUserId).' OR '.$groupCondition;
261 }
262
263 if (!empty($settings['search'])) {
264 $filters[] = 'list.name LIKE '.acym_escapeDB('%'.$settings['search'].'%');
265 }
266
267 if (!empty($settings['already'])) {
268 acym_arrayToInteger($settings['already']);
269 $filters[] = 'list.id NOT IN('.implode(',', $settings['already']).')';
270 }
271 $filters[] = 'list.type = '.acym_escapeDB(self::LIST_TYPE_STANDARD);
272
273 if (!empty($filters)) {
274 $query .= ' WHERE ('.implode(') AND (', $filters).')';
275 $queryCount .= ' WHERE ('.implode(') AND (', $filters).')';
276 }
277
278 $query .= ' GROUP BY list.id ';
279
280 return [
281 'lists' => acym_loadObjectList($query, '', $settings['offset'], $settings['listsPerPage']),
282 'total' => (int)acym_loadResult($queryCount),
283 ];
284 }
285
286 public function getListsByIds(array $ids): array
287 {
288 acym_arrayToInteger($ids);
289 if (empty($ids)) {
290 return [];
291 }
292
293 $query = 'SELECT * FROM #__acym_list WHERE id IN ('.implode(', ', $ids).')';
294
295 return acym_loadObjectList($query);
296 }
297
298 public function getSubscribersCount(array $listsIds): int
299 {
300 acym_arrayToInteger($listsIds);
301 if (empty($listsIds)) {
302 return 0;
303 }
304
305 $query = 'SELECT COUNT(DISTINCT user.id)
306 FROM #__acym_user AS user
307 JOIN #__acym_user_has_list AS userList ON user.id = userList.user_id
308 WHERE userList.list_id IN ('.implode(",", $listsIds).') AND userList.status = 1 AND user.active = 1';
309
310 if ($this->config->get('require_confirmation', 1) == 1) {
311 $query .= ' AND user.confirmed = 1';
312 }
313
314 return (int)acym_loadResult($query);
315 }
316
317 public function getSubscribersForList(array $options = []): array
318 {
319 $listIds = $options['listIds'] ?? [];
320 $offset = $options['offset'] ?? 0;
321 $limit = $options['limit'] ?? 100;
322 $status = $options['status'] ?? null;
323 $orderBy = $options['orderBy'] ?? '';
324 $orderBySort = $options['orderBySort'] ?? '';
325 $subscribedAfter = $options['subscribed_after'] ?? null;
326 $unsubscribedAfter = $options['unsubscribed_after'] ?? null;
327
328 if (empty($listIds)) {
329 return [];
330 }
331
332 acym_arrayToInteger($listIds);
333
334 $statusCondition = '';
335 if (!is_null($status)) {
336 $statusCondition .= ' AND user_list.status = '.intval($status);
337 }
338
339 if (!empty($subscribedAfter)) {
340 $statusCondition .= ' AND user_list.subscription_date > '.acym_escapeDB($subscribedAfter);
341 }
342
343 if (!empty($unsubscribedAfter)) {
344 $statusCondition .= ' AND user_list.unsubscribe_date > '.acym_escapeDB($unsubscribedAfter);
345 }
346
347 $requestSub = 'SELECT user.*, user_list.status, user_list.subscription_date, user_list.unsubscribe_date FROM #__acym_user AS user';
348 $requestSub .= ' LEFT JOIN #__acym_user_has_list AS user_list ON user.id = user_list.user_id';
349 $requestSub .= ' WHERE user.active = 1 AND user_list.list_id IN ('.implode(', ', $listIds).')'.$statusCondition;
350
351 if (!empty($orderBy)) {
352 if (empty($orderBySort)) $orderBySort = 'desc';
353 $requestSub .= ' ORDER BY '.acym_secureDBColumn($orderBy).' '.acym_secureDBColumn($orderBySort);
354 }
355
356 return acym_loadObjectList(
357 $requestSub,
358 '',
359 $offset,
360 $limit
361 );
362 }
363
364 public function delete(array $elements, bool $force = false): int
365 {
366 if (!$force) {
367 $this->onlyManageableLists($elements);
368 }
369
370 if (empty($elements)) {
371 return 0;
372 }
373
374 foreach ($elements as $id) {
375 acym_query('DELETE FROM #__acym_mail_has_list WHERE list_id = '.intval($id));
376 acym_query('DELETE FROM #__acym_user_has_list WHERE list_id = '.intval($id));
377 acym_query('DELETE FROM #__acym_tag WHERE `id_element` = '.intval($id).' AND `type` = "list"');
378 }
379
380 return parent::delete($elements);
381 }
382
383 public function synchDeleteCmsList(int $userId): void
384 {
385 $query = 'SELECT * FROM #__acym_list WHERE type = "'.self::LIST_TYPE_FRONT.'" AND cms_user_id = '.intval($userId);
386 $listFrontManagement = acym_loadObject($query);
387 if (!empty($listFrontManagement)) {
388 $this->delete([$listFrontManagement->id]);
389 }
390 }
391
392 public function save(object $list): ?int
393 {
394 if (isset($list->tags)) {
395 $tags = $list->tags;
396 unset($list->tags);
397 }
398
399 $oldList = null;
400
401 if (empty($list->id)) {
402 if (empty($list->cms_user_id)) {
403 $list->cms_user_id = acym_currentUserId();
404 }
405
406 $list->creation_date = acym_date('now', 'Y-m-d H:i:s', false);
407 } else {
408 $oldList = $this->getOneById($list->id);
409 }
410
411 if (!empty($list->translation)) {
412 if (is_array($list->translation)) $list->translation = json_encode($list->translation);
413 } else {
414 $list->translation = '';
415 }
416
417 foreach ($list as $oneAttribute => $value) {
418 if (empty($value)) continue;
419
420 $list->$oneAttribute = acym_stripTags($value);
421 }
422
423 if (empty($list->description)) {
424 $list->description = '';
425 }
426
427 if (!isset($list->access) && empty($list->id)) {
428 $list->access = '';
429 }
430
431 if (empty($list->id)) {
432 acym_trigger('onAcymBeforeListCreate', [&$list]);
433 } else {
434 acym_trigger('onAcymBeforeListModify', [&$list]);
435 }
436
437 $listID = parent::save($list);
438
439 if (empty($listID)) {
440 return null;
441 }
442
443 if (isset($tags)) {
444 $tagClass = new TagClass();
445 $tagClass->setTags(TagClass::TYPE_LIST, intval($listID), $tags);
446 }
447
448 $list->id = $listID;
449
450 if (is_null($oldList)) {
451 acym_trigger('onAcymAfterListCreate', [&$list]);
452 } else {
453 acym_trigger('onAcymAfterListModify', [&$list, &$oldList]);
454 }
455
456 return $listID;
457 }
458
459 public function getAllWithIdName(): array
460 {
461 $endQuery = '';
462 if (!acym_isAdmin()) {
463 $creatorId = acym_currentUserId();
464
465 $userGroups = acym_getGroupsByUser($creatorId);
466 $groupCondition = '(access LIKE "%,'.implode(',%" OR access LIKE "%,', $userGroups).',%")';
467
468 $endQuery = ' AND (cms_user_id = '.intval($creatorId).' OR '.$groupCondition.')';
469 }
470 $lists = acym_loadObjectList('SELECT id, name FROM #__acym_list WHERE type = '.acym_escapeDB(self::LIST_TYPE_STANDARD).$endQuery, 'id');
471
472 $listsToReturn = [];
473
474 foreach ($lists as $key => $list) {
475 $listsToReturn[$key] = $list->name;
476 }
477
478 return $listsToReturn;
479 }
480
481 public function getAllForSelect(bool $emptyFirst = true, int $userFrontID = 0, bool $needTranslation = false, bool $needFrontLabel = false): array
482 {
483 $groupCondition = '';
484 if (!empty($userFrontID)) {
485 $userGroups = acym_getGroupsByUser($userFrontID);
486 $groupCondition = ' AND (cms_user_id = '.intval($userFrontID).' OR (access LIKE "%,'.implode(',%" OR access LIKE "%,', $userGroups).',%"))';
487 }
488
489 $lists = acym_loadObjectList('SELECT * FROM #__acym_list WHERE type = '.acym_escapeDB(self::LIST_TYPE_STANDARD).$groupCondition, 'id');
490
491 if (acym_isMultilingual() && $needTranslation) {
492 $lists = $this->getTranslatedNameDescription($lists);
493 }
494
495 $return = [];
496
497 if ($emptyFirst) {
498 $return[''] = acym_translation('ACYM_SELECT_A_LIST');
499 }
500
501 foreach ($lists as $key => $list) {
502 $return[$key] = $needFrontLabel && !empty($list->display_name) ? $list->display_name : $list->name;
503 }
504
505 return $return;
506 }
507
508 public function getAllWithoutManagement(bool $needTranslation = false): array
509 {
510 $lists = acym_loadObjectList('SELECT * FROM #__acym_list WHERE `type` = '.acym_escapeDB(self::LIST_TYPE_STANDARD), 'id');
511
512 if (acym_isMultilingual() && $needTranslation) {
513 $lists = $this->getTranslatedNameDescription($lists);
514 }
515
516 return $lists;
517 }
518
519 public function getTranslatedNameDescription(array $lists): array
520 {
521 $currentLanguageTag = acym_getLanguageTag();
522 foreach ($lists as $id => $list) {
523 if (empty($list->translation)) continue;
524
525 $list->translation = json_decode($list->translation, true);
526 if (!empty($list->translation[$currentLanguageTag])) {
527 if (!empty($list->translation[$currentLanguageTag]['name'])) {
528 $list->name = $list->translation[$currentLanguageTag]['name'];
529 }
530
531 if (!empty($list->translation[$currentLanguageTag]['display_name'])) {
532 $list->display_name = $list->translation[$currentLanguageTag]['display_name'];
533 }
534
535 if (!empty($list->translation[$currentLanguageTag]['description'])) {
536 $list->description = $list->translation[$currentLanguageTag]['description'];
537 }
538 }
539 }
540
541 return $lists;
542 }
543
544 /**
545 * Sends the welcome emails attached to the specified lists
546 */
547 public function sendWelcome(int $userID, array $listIDs, bool $forceFront = false): void
548 {
549 if (!$forceFront && acym_isAdmin()) {
550 return;
551 }
552
553 acym_arrayToInteger($listIDs);
554 if (empty($listIDs)) {
555 return;
556 }
557
558 $messages = acym_loadObjectList('SELECT `welcome_id` FROM #__acym_list WHERE `id` IN ('.implode(',', $listIDs).') AND `active` = 1');
559
560 if (empty($messages)) {
561 return;
562 }
563
564 $alreadySent = [];
565 $mailerHelper = new MailerHelper();
566 $mailerHelper->report = (bool)$this->config->get('welcome_message', 1);
567 foreach ($messages as $oneMessage) {
568 $mailid = $oneMessage->welcome_id;
569 if (empty($mailid)) continue;
570
571 //We don't send twice the same message
572 if (isset($alreadySent[$mailid])) {
573 continue;
574 }
575
576 $mailerHelper->trackEmail = true;
577 $mailerHelper->sendOne($mailid, $userID);
578 $alreadySent[$mailid] = true;
579 }
580 }
581
582 /**
583 * Sends the unsubscribe emails attached to the specified lists
584 */
585 public function sendUnsubscribe(int $userID, array $listIDs): void
586 {
587 if (acym_isAdmin()) {
588 return;
589 }
590
591 acym_arrayToInteger($listIDs);
592 if (empty($listIDs)) {
593 return;
594 }
595
596 $messages = acym_loadObjectList('SELECT `unsubscribe_id` FROM #__acym_list WHERE `id` IN ('.implode(',', $listIDs).') AND `active` = 1');
597
598 if (empty($messages)) {
599 return;
600 }
601
602 $alreadySent = [];
603 $mailerHelper = new MailerHelper();
604 $mailerHelper->report = (bool)$this->config->get('unsub_message', 1);
605 foreach ($messages as $oneMessage) {
606 if (!empty($oneMessage->unsubscribe_id)) {
607 $mailid = $oneMessage->unsubscribe_id;
608
609 //We don't send twice the same message
610 if (isset($alreadySent[$mailid])) {
611 continue;
612 }
613
614 $mailerHelper->trackEmail = true;
615 $mailerHelper->sendOne($mailid, $userID);
616 $alreadySent[$mailid] = true;
617 }
618 }
619 }
620
621 public function addDefaultList()
622 {
623 $listId = acym_loadResult('SELECT `id` FROM #__acym_list LIMIT 1');
624 if (empty($listId)) {
625 $defaultList = new \stdClass();
626 $defaultList->name = 'Newsletters';
627 $defaultList->color = '#3366ff';
628 $defaultList->description = '';
629
630 $this->save($defaultList);
631 }
632 }
633
634 public function getTotalSubCount(array $ids): int
635 {
636 if (empty($ids)) {
637 return 0;
638 }
639
640 acym_arrayToInteger($ids);
641 $this->onlyManageableLists($ids);
642 $query = 'SELECT COUNT(DISTINCT hasList.user_id)
643 FROM #__acym_user_has_list AS hasList
644 JOIN #__acym_user AS user
645 ON hasList.user_id = user.id
646 WHERE hasList.status = 1
647 AND user.active = 1
648 AND hasList.list_id IN ('.implode(',', $ids).')';
649
650
651 if ($this->config->get('require_confirmation', 1) == 1) {
652 $query .= ' AND user.confirmed = 1 ';
653 }
654
655 return intval(acym_loadResult($query));
656 }
657
658 /**
659 * Get all mails attached to a list
660 */
661 public function getMailsByListId(int $listId): array
662 {
663 $mailIds = acym_loadResultArray('SELECT mail_id FROM #__acym_mail_has_list WHERE list_id = '.intval($listId));
664
665 return empty($mailIds) ? [] : $mailIds;
666 }
667
668 public function getSubscribersCountPerStatusByListIds(array $listIds = []): array
669 {
670 $condList = '';
671 if (!empty($listIds)) {
672 acym_arrayToInteger($listIds);
673 $condList = 'AND userList.list_id IN ('.implode(',', $listIds).')';
674 }
675
676 /*
677 * score:
678 * An unconfirmed and active user will have a score of 0 + 1*2 = 2
679 * A confirmed and active user will have a score of 1 + 1*2 = 3
680 * A confirmed and inactive user will have a score of 1 + 0*2 = 1
681 */
682 $query = 'SELECT userList.list_id, COUNT(userList.user_id) AS users, acyuser.confirmed + acyuser.active*2 AS score
683 FROM #__acym_user_has_list AS userList
684 JOIN #__acym_user AS acyuser
685 ON acyuser.id = userList.user_id
686 WHERE userList.status = 1
687 '.$condList.'
688 GROUP BY score, userList.list_id';
689 $results = acym_loadObjectList($query);
690
691 $confirmationRequired = $this->config->get('require_confirmation', 1);
692 $listsUserStats = [];
693 foreach ($results as $oneResult) {
694 if (!isset($listsUserStats[$oneResult->list_id])) {
695 $listsUserStats[$oneResult->list_id] = $this->initList($oneResult->list_id);
696 }
697
698 // Joomla 4 casts the result into an int
699 $oneResult->score = (string)$oneResult->score;
700
701 if (in_array($oneResult->score, ['0', '1'])) {
702 $listsUserStats[$oneResult->list_id]->inactive_users += $oneResult->users;
703 }
704
705 if (in_array($oneResult->score, ['0', '2'])) {
706 $listsUserStats[$oneResult->list_id]->unconfirmed_users += $oneResult->users;
707 }
708
709 if ($oneResult->score === '3' || ($confirmationRequired != 1 && $oneResult->score == '2')) {
710 $listsUserStats[$oneResult->list_id]->sendable_users += $oneResult->users;
711 }
712 }
713
714 $query = 'SELECT userList.list_id, COUNT(userList.user_id) AS users
715 FROM #__acym_user_has_list AS userList
716 WHERE userList.status = 0
717 '.$condList.'
718 GROUP BY userList.list_id';
719 $unsubscribed = acym_loadObjectList($query);
720
721 foreach ($unsubscribed as $oneList) {
722 if (!isset($listsUserStats[$oneList->list_id])) {
723 $listsUserStats[$oneList->list_id] = $this->initList($oneList->list_id);
724 }
725 $listsUserStats[$oneList->list_id]->unsubscribed_users = $oneList->users;
726 }
727
728 return $listsUserStats;
729 }
730
731 public function getSubscribersEvolutionByListIds(array $listIds): array
732 {
733 $condList = '';
734 if (!empty($listIds)) {
735 acym_arrayToInteger($listIds);
736 $condList = ' AND list_id IN ('.implode(',', $listIds).')';
737 }
738 $dateNow = acym_loadResult('SELECT DATE_SUB(NOW(), INTERVAL 1 MONTH)');
739 $queryEvolSub = 'SELECT list_id, COUNT(*) AS newSub FROM #__acym_user_has_list';
740 $queryEvolSub .= ' WHERE subscription_date > '.acym_escapeDB($dateNow).' '.$condList;
741 $queryEvolSub .= ' GROUP BY list_id';
742 $evolSubscibers = acym_loadObjectList($queryEvolSub, 'list_id');
743
744 $queryEvolUnsub = 'SELECT list_id, COUNT(*) AS newUnsub FROM #__acym_user_has_list';
745 $queryEvolUnsub .= ' WHERE unsubscribe_date > '.acym_escapeDB($dateNow).' '.$condList;
746 $queryEvolUnsub .= ' GROUP BY list_id';
747 $newUnsubscribers = acym_loadObjectList($queryEvolUnsub, 'list_id');
748
749 foreach ($newUnsubscribers as $listId => $oneUnsub) {
750 if (empty($evolSubscibers[$listId])) {
751 $evolSubscibers[$listId] = new \stdClass();
752 }
753 $evolSubscibers[$listId]->newUnsub = $oneUnsub->newUnsub;
754 }
755
756 return $evolSubscibers;
757 }
758
759 public function getYearSubEvolutionPerList(int $listId): array
760 {
761 // Get next month from 1 year ago
762 $month = gmdate('n') + 1;
763 $year = gmdate('Y') - 1;
764 $initDate = $year.'-'.$month.'-01';
765 if ($month == 13) {
766 $initDate = gmdate('Y').'-01-01';
767 }
768
769 // Get new subscribers per month
770 $queryEvolSub = 'SELECT MONTH(subscription_date) as monthSub, DATE_FORMAT(subscription_date,"%Y_%m") AS unit, COUNT(user_id) AS nbUser';
771 $queryEvolSub .= ' FROM `#__acym_user_has_list`';
772 $queryEvolSub .= ' WHERE subscription_date >= "'.$initDate.'" AND list_id = '.$listId;
773 $queryEvolSub .= ' GROUP BY unit';
774 $evolSubscibers = acym_loadObjectList($queryEvolSub, 'unit');
775
776 // Get unsubscribers per month
777 $queryEvolUnsub = 'SELECT MONTH(unsubscribe_date) as monthUnsub, DATE_FORMAT(unsubscribe_date,"%Y_%m") AS unit, COUNT(user_id) AS nbUser';
778 $queryEvolUnsub .= ' FROM `#__acym_user_has_list`';
779 $queryEvolUnsub .= ' WHERE unsubscribe_date >= "'.$initDate.'" AND list_id = '.$listId;
780 $queryEvolUnsub .= ' GROUP BY unit';
781 $evolUnsubscibers = acym_loadObjectList($queryEvolUnsub, 'unit');
782
783 return [
784 'subscribers' => $evolSubscibers,
785 'unsubscribers' => $evolUnsubscibers,
786 ];
787 }
788
789 private function initList(int $listId): object
790 {
791 $list = new \stdClass();
792 $list->list_id = $listId;
793 $list->sendable_users = 0;
794 $list->unconfirmed_users = 0;
795 $list->inactive_users = 0;
796 $list->unsubscribed_users = 0;
797
798 return $list;
799 }
800
801 public function getManageableLists(): array
802 {
803 $idCurrentUser = acym_currentUserId();
804 if (empty($idCurrentUser)) {
805 return [];
806 }
807
808 $userGroups = acym_getGroupsByUser($idCurrentUser);
809
810 return acym_loadResultArray(
811 'SELECT id
812 FROM #__acym_list
813 WHERE cms_user_id = '.intval($idCurrentUser).'
814 OR (access LIKE "%,'.implode(',%" OR access LIKE "%,', $userGroups).',%")'
815 );
816 }
817
818 public function onlyManageableLists(&$elements): void
819 {
820 if (acym_isAdmin()) {
821 return;
822 }
823
824 $manageableLists = $this->getManageableLists();
825 $elements = array_intersect($elements, $manageableLists);
826 }
827
828 public function hasUserAccess(int $listId): bool
829 {
830 if (acym_isAdmin() && acym_isAllowed('lists')) {
831 return true;
832 }
833
834 return in_array($listId, $this->getManageableLists());
835 }
836
837 public function getfrontManagementList(): int
838 {
839 $idCurrentUser = acym_currentUserId();
840 if (empty($idCurrentUser)) {
841 return 0;
842 }
843
844 $frontListId = acym_loadResult('SELECT id FROM #__acym_list WHERE type = '.acym_escapeDB(self::LIST_TYPE_FRONT).' AND cms_user_id = '.intval($idCurrentUser));
845
846 if (!empty($frontListId)) {
847 return $frontListId;
848 }
849
850 $frontList = new \stdClass();
851 $frontList->name = 'frontlist_'.$idCurrentUser;
852 $frontList->active = 1;
853 $frontList->visible = 0;
854 $frontList->cms_user_id = $idCurrentUser;
855 $frontList->type = self::LIST_TYPE_FRONT;
856
857 return (int)$this->save($frontList);
858 }
859
860 public function setWelcomeUnsubEmail(array $listIds, int $mailId, string $type): bool
861 {
862 if (!in_array($type, [MailClass::TYPE_WELCOME, MailClass::TYPE_UNSUBSCRIBE]) || empty($mailId)) {
863 return false;
864 }
865
866 $column = esc_attr($type).'_id';
867 $columnsList = acym_getColumns('list');
868 if (!in_array($column, $columnsList)) {
869 return false;
870 }
871
872 acym_arrayToInteger($listIds);
873 $listIds = array_filter($listIds);
874
875 if (!$this->removeWelcomeUnsubByMailId($mailId, $listIds, $column)) {
876 return false;
877 }
878
879 foreach ($listIds as $listId) {
880 $list = $this->getOneById($listId);
881 if (empty($list)) {
882 continue;
883 }
884
885 $list->{$column} = $mailId;
886
887 $savedListId = $this->save($list);
888 if (empty($savedListId)) {
889 return false;
890 }
891 }
892
893 return true;
894 }
895
896 private function removeWelcomeUnsubByMailId(int $mailId, array $listIds, string $column): bool
897 {
898 if (empty($mailId)) {
899 return false;
900 }
901
902 $query = 'SELECT * FROM #__acym_list WHERE '.acym_secureDBColumn($column).' = '.$mailId;
903
904 if (!empty($listIds)) {
905 acym_arrayToInteger($listIds);
906 $query .= ' AND `id` NOT IN ('.implode(',', $listIds).')';
907 }
908
909 $lists = acym_loadObjectList($query);
910 if (empty($lists)) {
911 return true;
912 }
913
914 foreach ($lists as $list) {
915 $list->{$column} = null;
916
917 $savedListId = $this->save($list);
918 if (empty($savedListId)) {
919 return false;
920 }
921 }
922
923 return true;
924 }
925
926 public function getListIdsByWelcomeUnsub(int $mailId, bool $welcome = true): array
927 {
928 $type = $welcome ? 'welcome_id' : 'unsubscribe_id';
929
930 $return = acym_loadResultArray('SELECT id FROM #__acym_list WHERE '.$type.' = '.intval($mailId));
931
932 return empty($return) ? [] : $return;
933 }
934
935 public function getUsersForSummaryModal(int $id, int $offset, int $limit, string $search): array
936 {
937 $whereQuery = ' AND user_list.status = 1 AND user.active = 1';
938
939 if (!empty($search)) {
940 $search = acym_escapeDB('%'.$search.'%');
941 $whereQuery .= ' AND (user.email LIKE '.$search.' OR user.name LIKE '.$search.' OR user.id LIKE '.$search.') ';
942 }
943
944 if ($this->config->get('require_confirmation', '1') === '1') {
945 $whereQuery .= ' AND user.confirmed = 1';
946 }
947
948 $query = 'SELECT user.email, user.name, user.id
949 FROM #__acym_user as user
950 JOIN #__acym_user_has_list as user_list ON user.id = user_list.user_id
951 WHERE user_list.list_id = '.intval($id).$whereQuery.' LIMIT '.intval($offset).', '.intval($limit);
952
953
954 return acym_loadObjectList($query, 'id');
955 }
956
957 public function getXLists(array $options = []): array
958 {
959 $limit = $options['limit'] ?? 10;
960 $offset = $options['offset'] ?? 0;
961 $filters = $options['filters'] ?? ['type' => self::LIST_TYPE_STANDARD];
962
963 $conditions = [];
964 foreach ($filters as $column => $filter) {
965 switch ($column) {
966 case 'id':
967 case 'active':
968 case 'visible':
969 case 'clean':
970 case 'welcome_id':
971 case 'unsubscribe_id':
972 case 'cms_user_id':
973 case 'tracking':
974 $conditions[] = acym_secureDBColumn($column).' = '.intval($filter);
975 break;
976 default:
977 $conditions[] = acym_secureDBColumn($column).' LIKE '.acym_escapeDB('%'.$filter.'%');
978 }
979 }
980
981 $query = 'SELECT * FROM #__acym_list';
982 if (!empty($conditions)) {
983 $query .= ' WHERE '.implode(' AND ', $conditions);
984 }
985
986 return acym_loadObjectList($query, $this->pkey, $offset, $limit);
987 }
988
989 public function getOneList(): bool
990 {
991 $firstList = acym_loadObject('SELECT * FROM #__acym_list WHERE `id` != 1');
992
993 return !empty($firstList);
994 }
995
996 public function getMostUsedLists(): array
997 {
998 return acym_loadObjectList(
999 'SELECT list_id
1000 FROM #__acym_mail_has_list
1001 GROUP BY list_id
1002 ORDER BY COUNT(*) DESC
1003 LIMIT 5'
1004 );
1005 }
1006
1007 public function getListsSubscribersAndUnsubscribeUsersFromUserHasList(): array
1008 {
1009 $query = 'SELECT list_id,
1010 SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) AS subscribers,
1011 SUM(CASE WHEN status = 0 THEN 1 ELSE 0 END) AS unsubscribed_users
1012 FROM `#__acym_user_has_list`
1013 GROUP BY list_id';
1014
1015 return acym_loadObjectList($query);
1016 }
1017
1018 public function updateListsSubscribersAndUnsubscribeUsers(int $listId, int $subscribers, int $unsubscribeUsers, int $new_sub, int $new_unsub): void
1019 {
1020 acym_query(
1021 'UPDATE `#__acym_list`
1022 SET `subscribers` = '.$subscribers.', `unsubscribed_users` = '.$unsubscribeUsers.', `new_sub` = '.$new_sub.', `new_unsub` = '.$new_unsub.'
1023 WHERE id = '.$listId
1024 );
1025 }
1026
1027 public function getMostUsedListsByIdsWithSubscribersData(array $ids = []): array
1028 {
1029 if (empty($ids)) {
1030 return [];
1031 }
1032 acym_arrayToInteger($ids);
1033 $query = 'SELECT name, subscribers, unsubscribed_users, new_sub, new_unsub FROM `#__acym_list` WHERE id IN ('.implode(', ', $ids).')';
1034
1035 return acym_loadObjectList($query);
1036 }
1037
1038 public function getUserListEntries(array $userIds, array $listIds): array
1039 {
1040 if (empty($userIds) || empty($listIds)) {
1041 return [];
1042 }
1043
1044 $userIdsStr = implode(',', array_map('intval', $userIds));
1045 $listIdsStr = implode(',', array_map('intval', $listIds));
1046
1047 $query = 'SELECT * FROM `#__acym_user_has_list`
1048 WHERE user_id IN ('.$userIdsStr.') AND list_id IN ('.$listIdsStr.')';
1049
1050 $results = acym_loadObjectList($query);
1051
1052 $entries = [];
1053 foreach ($results as $row) {
1054 $key = $row->user_id.'_'.$row->list_id;
1055 $entries[$key] = $row;
1056 }
1057
1058 return $entries;
1059 }
1060 }
1061