PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 10.2.2
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v10.2.2
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 / UserClass.php

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

1,677 lines 62.3 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\AutomationHelper;
6 use AcyMailing\Helpers\MailerHelper;
7 use AcyMailing\Helpers\PaginationHelper;
8 use AcyMailing\Core\AcymClass;
9
10 class UserClass extends AcymClass
11 {
12 var $checkVisitor = true;
13 var $restrictedFields = ['id', 'key', 'confirmed', 'active', 'cms_id', 'creation_date'];
14 var $allowModif = false;
15 var $requireId = false;
16 var $sendConf = true;
17 var $forceConf = false;
18 public bool $confirmationSentSuccess = false;
19 var $newUser = false;
20 var $blockNotifications = false;
21 var $subscribed = false;
22 var $confirmationSentError;
23 var $triggers = true;
24
25 var $forceConfAdmin = false;
26
27 public $sendWelcomeEmail = true;
28 public $sendUnsubscribeEmail = true;
29
30 public function __construct()
31 {
32 parent::__construct();
33
34 $this->table = 'user';
35 $this->pkey = 'id';
36 }
37
38 public function getMatchingElements(array $settings = []): array
39 {
40 $results = [];
41 $columns = '`user`.*';
42 if (!empty($settings['columns'])) {
43 foreach ($settings['columns'] as $key => $value) {
44 $settings['columns'][$key] = $key === 'join' ? $value : 'user.'.$value;
45 }
46 $columns = implode(', ', $settings['columns']);
47 }
48
49 $query = $columns.' FROM #__acym_user AS `user`';
50 $queryCount = 'SELECT COUNT(DISTINCT user.id) AS total FROM #__acym_user AS user';
51 $queryStatus = 'SELECT COUNT(DISTINCT user.id) AS number, user.confirmed + user.active*2 AS score FROM #__acym_user AS user';
52 $filters = [];
53
54 if (!empty($settings['join'])) {
55 $query .= $this->getJoinForQuery($settings['join']);
56 }
57 $this->handleSegmentFilter($settings, $filters, $query, $queryCount, $queryStatus);
58 $this->handleSubscriptionFilter($settings, $filters, $query, $queryCount, $queryStatus);
59 $this->handleFrontend($settings, $query, $queryCount, $queryStatus);
60 $this->handleSearchFilter($settings, $query, $queryCount, $queryStatus, $filters);
61 $results['status'] = $this->handleUserStatusFilter($settings, $queryStatus, $filters);
62 $entityResult = $this->handleEntitySelect($settings, $filters);
63
64 if (!empty($entityResult)) return $entityResult;
65
66 if (!empty($filters)) {
67 $query .= ' WHERE ('.implode(') AND (', $filters).')';
68 $queryCount .= ' WHERE ('.implode(') AND (', $filters).')';
69 }
70
71 if (!empty($settings['ordering']) && !empty($settings['ordering_sort_order'])) {
72 $query .= ' ORDER BY user.'.acym_secureDBColumn($settings['ordering']).' '.acym_secureDBColumn(strtoupper($settings['ordering_sort_order']));
73 }
74
75 if (empty($settings['offset']) || $settings['offset'] < 0) {
76 $settings['offset'] = 0;
77 }
78
79 if (empty($settings['elementsPerPage']) || $settings['elementsPerPage'] < 1) {
80 $pagination = new PaginationHelper();
81 $settings['elementsPerPage'] = $pagination->getListLimit();
82 }
83
84 acym_query('SET SQL_BIG_SELECTS=1');
85 $results['elements'] = acym_loadObjectList('SELECT DISTINCT '.$query, '', $settings['offset'], $settings['elementsPerPage']);
86 $results['total'] = acym_loadObject($queryCount);
87
88 if (!empty($results['elements']) && !empty($settings['cms_username'])) {
89 $cmsIds = array_diff(array_column($results['elements'], 'cms_id'), [0, '']);
90 if (!empty($cmsIds)) {
91 global $acymCmsUserVars;
92 $userNames = acym_loadObjectList(
93 'SELECT '.$acymCmsUserVars->id.' AS id, '.$acymCmsUserVars->username.' AS `cms_username`
94 FROM '.$acymCmsUserVars->table.'
95 WHERE id IN ('.implode(', ', array_values($cmsIds)).')',
96 'id'
97 );
98 foreach ($results['elements'] as $key => $oneElement) {
99 if (empty($results['elements'][$key]->cms_id)) continue;
100 if (empty($userNames[$results['elements'][$key]->cms_id])) {
101 $oneUser = $this->getOneById($results['elements'][$key]->id);
102 $oneUser->cms_id = 0;
103 $this->save($oneUser);
104 $results['elements'][$key]->cms_id = 0;
105 continue;
106 }
107 $results['elements'][$key]->cms_username = $userNames[$results['elements'][$key]->cms_id]->cms_username;
108 }
109 }
110 }
111
112 return $results;
113 }
114
115 private function handleSubscriptionFilter($settings, &$filters, &$query, &$queryCount, &$queryStatus)
116 {
117 if (empty($settings['list'])) return;
118
119 $listJoin = ' JOIN #__acym_user_has_list AS list ON user.id = list.user_id AND list.list_id = '.intval($settings['list']).' ';
120
121 if ($settings['list_status'] === 'none') {
122 $listJoin = ' LEFT'.$listJoin;
123 $filters[] = 'list.user_id IS NULL';
124 } else {
125 $filters[] = 'list.status = '.($settings['list_status'] === 'sub' ? 1 : 0);
126 }
127
128 $query .= $listJoin;
129 $queryCount .= $listJoin;
130 $queryStatus .= $listJoin;
131 }
132
133 private function handleFrontend($settings, &$query, &$queryCount, &$queryStatus)
134 {
135 if (!acym_isAdmin()) {
136 $settings['creator_id'] = acym_currentUserId();
137 if (empty($settings['creator_id'])) $settings['creator_id'] = '-1';
138 }
139
140 if (!empty($settings['creator_id'])) {
141 $userGroups = acym_getGroupsByUser($settings['creator_id']);
142 $groupCondition = 'list.access LIKE "%,'.implode(',%" OR list.access LIKE "%,', $userGroups).',%"';
143 $joinList = ' JOIN #__acym_user_has_list as user_list ON user_list.user_id = user.id JOIN #__acym_list AS list ON list.id = user_list.list_id AND (list.cms_user_id = '.intval(
144 $settings['creator_id']
145 ).' OR '.$groupCondition.')';
146
147 $query .= $joinList;
148 $queryCount .= $joinList;
149 $queryStatus .= $joinList;
150 }
151 }
152
153 private function handleSearchFilter($settings, &$query, &$queryCount, &$queryStatus, &$filters)
154 {
155 if (empty($settings['search'])) return;
156
157 $searchValue = acym_escapeDB('%'.$settings['search'].'%');
158 $searchFilter = 'user.email LIKE '.$searchValue.' OR user.name LIKE '.$searchValue.' OR user.id = '.intval($settings['search']);
159
160 $listingFields = acym_loadResultArray('SELECT `id` FROM #__acym_field WHERE `'.(acym_isAdmin() ? 'back' : 'front').'end_listing` = 1');
161 if (!empty($listingFields)) {
162 $query = 'DISTINCT '.$query;
163 $join = ' LEFT JOIN #__acym_user_has_field AS userfield ON user.id = userfield.user_id AND userfield.field_id IN ('.implode(', ', $listingFields).') AND ';
164 $cfSearch = 'userfield.value LIKE '.$searchValue;
165
166 $fieldsWithMultipleValues = acym_loadObjectList(
167 'SELECT *
168 FROM #__acym_field
169 WHERE id IN ('.implode(', ', $listingFields).')
170 AND `value` LIKE '.acym_escapeDB('%"title":"'.$settings['search'].'"%')
171 );
172
173 if (!empty($fieldsWithMultipleValues)) {
174 foreach ($fieldsWithMultipleValues as $oneField) {
175 $oneField->value = json_decode($oneField->value, true);
176 foreach ($oneField->value as $value) {
177 if (stripos($value['title'], $settings['search']) !== false) {
178 $cfSearch .= ' OR (userfield.field_id = '.intval($oneField->id).' AND userfield.value LIKE '.acym_escapeDB('%'.$value['value'].'%').')';
179 }
180 }
181 }
182 }
183
184 $join .= '('.$cfSearch.')';
185 $searchFilter .= ' OR userfield.field_id IS NOT NULL';
186
187 $query .= $join;
188 $queryCount .= $join;
189 $queryStatus .= $join;
190 }
191
192 $filters[] = $searchFilter;
193 }
194
195 private function handleUserStatusFilter($settings, $queryStatus, &$filters)
196 {
197 if (!empty($filters)) {
198 $queryStatus .= ' WHERE ('.implode(') AND (', $filters).')';
199 }
200
201 $usersPerStatusObject = acym_loadObjectList($queryStatus.' GROUP BY score', 'score');
202
203 $usersPerStatus = [];
204 for ($i = 0 ; $i < 4 ; $i++) {
205 $usersPerStatus[$i] = empty($usersPerStatusObject[$i]) ? 0 : $usersPerStatusObject[$i]->number;
206 }
207
208 if (!empty($settings['status'])) {
209 $allowedStatus = [
210 'active' => 'active = 1',
211 'inactive' => 'active = 0',
212 'confirmed' => 'confirmed = 1',
213 'unconfirmed' => 'confirmed = 0',
214 ];
215 if (empty($allowedStatus[$settings['status']])) {
216 die('Injection denied');
217 }
218 $filters[] = 'user.'.$allowedStatus[$settings['status']];
219 }
220
221 return [
222 'all' => array_sum($usersPerStatus),
223 'active' => $usersPerStatus[2] + $usersPerStatus[3],
224 'inactive' => $usersPerStatus[0] + $usersPerStatus[1],
225 'confirmed' => $usersPerStatus[1] + $usersPerStatus[3],
226 'unconfirmed' => $usersPerStatus[0] + $usersPerStatus[2],
227 ];
228 }
229
230 private function handleEntitySelect($settings, &$filters)
231 {
232 if (isset($settings['hiddenElements'])) {
233 if (empty($settings['hiddenElements'])) {
234 $filters[] = 'user.id IS NOT NULL';
235 } else {
236 acym_arrayToInteger($settings['hiddenElements']);
237 $filters[] = 'user.id NOT IN('.implode(',', $settings['hiddenElements']).')';
238 }
239 }
240
241 if (isset($settings['onlyElements'])) {
242 if (empty($settings['onlyElements'])) {
243 $filters[] = 'user.id IS NULL';
244 } else {
245 acym_arrayToInteger($settings['onlyElements']);
246 $filters[] = 'user.id IN('.implode(',', $settings['onlyElements']).')';
247 }
248 }
249
250 if (!empty($settings['showOnlySelected'])) {
251 if (!empty($settings['selectedUsers'])) {
252 acym_arrayToInteger($settings['selectedUsers']);
253 $filters[] = 'user.id IN('.implode(',', $settings['selectedUsers']).')';
254 } else {
255 return ['users' => [], 'total' => 0];
256 }
257 }
258
259 return null;
260 }
261
262 private function handleSegmentFilter($settings, &$filters, &$query, &$queryCount, &$queryStatus)
263 {
264 if (!array_key_exists('segment', $settings)) return;
265 if ($settings['segment'] == 0) return;
266
267 $segmentClass = (new SegmentClass())->getOneById($settings['segment']);
268 $automationHelpers = [];
269 if (!empty($segmentClass) && !empty($segmentClass->filters)) {
270 foreach ($segmentClass->filters as $or => $orValues) {
271 if (empty($orValues)) continue;
272 $automationHelpers[$or] = new AutomationHelper();
273 foreach ($orValues as $and => $andValues) {
274 $and = intval($and);
275 foreach ($andValues as $filterName => $options) {
276 acym_trigger('onAcymProcessFilter_'.$filterName, [&$automationHelpers[$or], &$options, $and.'_'.$or]);
277 }
278 }
279 }
280 }
281
282 $where = '';
283 $join = '';
284 foreach ($automationHelpers as $index => $automationHelper) {
285 if (!empty($automationHelper->where)) {
286 $where .= ' ('.implode(') and (', $automationHelper->where).')';
287 if ($index != count($automationHelpers) - 1) $where .= ' OR ';
288 }
289 if (!empty($automationHelper->join)) $join .= ' JOIN '.implode(' JOIN ', $automationHelper->join);
290 if (!empty($automationHelper->leftjoin)) $join .= ' LEFT JOIN '.implode(' LEFT JOIN ', $automationHelper->leftjoin);
291 }
292 if (!empty($where)) {
293 $filters[] = $where;
294 }
295 $query .= $join;
296 $queryCount .= $join;
297 $queryStatus .= $join;
298 }
299
300 public function getJoinForQuery($joinType)
301 {
302 if (strpos($joinType, 'join_list') !== false) {
303 $listId = explode('-', $joinType);
304
305 return ' LEFT JOIN #__acym_user_has_list AS userlist ON user.id = userlist.user_id AND userlist.status = 1 AND userlist.list_id = '.intval($listId[1]);
306 }
307
308 return '';
309 }
310
311 public function getOneByCMSId(?int $id)
312 {
313 $query = 'SELECT * FROM #__acym_user WHERE cms_id = '.intval($id);
314
315 return acym_loadObject($query);
316 }
317
318 public function getOneByEmail(?string $email)
319 {
320 $query = 'SELECT * FROM #__acym_user WHERE email = '.acym_escapeDB($email);
321
322 return acym_loadObject($query);
323 }
324
325 public function getByColumnValue($column, $value)
326 {
327 $userColumns = acym_getColumns('user');
328 if (!in_array($column, $userColumns)) return [];
329
330 $query = 'SELECT * FROM #__acym_user WHERE '.acym_secureDBColumn($column).' = '.acym_escapeDB($value);
331
332 return acym_loadObjectList($query, $this->pkey);
333 }
334
335 public function getUserSubscriptionById(
336 $userId,
337 $key = 'id',
338 $includeManagement = false,
339 $visible = false,
340 $needTranslation = false,
341 $sortBySubscription = false,
342 $mailId = 0,
343 $campaignOnly = false
344 ) {
345 $mailType = null;
346 if (!empty($mailId)) {
347 $mailType = acym_loadResult('SELECT type FROM #__acym_mail WHERE id = '.intval($mailId));
348 }
349
350 $query = 'SELECT list.id, list.translation, list.name, list.display_name, list.color, list.active, list.visible, list.description, userlist.status, userlist.subscription_date, userlist.unsubscribe_date
351 FROM #__acym_list AS list
352 JOIN #__acym_user_has_list AS userlist
353 ON list.id = userlist.list_id ';
354
355 if (!empty($mailId) && $campaignOnly && !empty($mailType) && $mailType !== MailClass::TYPE_FOLLOWUP) {
356 $query .= 'JOIN #__acym_mail_has_list AS mail_list ON list.id = mail_list.list_id AND mail_list.mail_id = '.intval($mailId);
357 }
358
359 $query .= ' WHERE userlist.user_id = '.intval($userId);
360
361 if (!$includeManagement && !empty($mailType) && $mailType === MailClass::TYPE_FOLLOWUP) {
362 $query .= ' AND list.type = '.acym_escapeDB(ListClass::LIST_TYPE_FOLLOWUP);
363 } elseif (!$includeManagement) {
364 $types = [ListClass::LIST_TYPE_STANDARD, ListClass::LIST_TYPE_FOLLOWUP];
365 $query .= ' AND list.type in ("'.implode('","', $types).'")';
366 }
367
368 if ($visible) {
369 $query .= ' AND list.visible = 1';
370 }
371
372 if ($sortBySubscription) {
373 $query .= ' ORDER BY userlist.subscription_date DESC';
374 }
375
376 $lists = acym_loadObjectList($query, $key);
377
378 if (acym_isMultilingual() && $needTranslation) {
379 $listClass = new ListClass();
380 $lists = $listClass->getTranslatedNameDescription($lists);
381 }
382
383 return $lists;
384 }
385
386 public function getUserStandardListIdById(int $userId)
387 {
388 $query = 'SELECT list.id FROM #__acym_list AS list
389 JOIN #__acym_user_has_list AS user_list ON list.id = user_list.list_id AND user_list.status = 1 AND user_list.user_id = '.$userId.'
390 WHERE list.visible = 1 AND list.type = '.acym_escapeDB(ListClass::LIST_TYPE_STANDARD);
391
392 return acym_loadResultArray($query);
393 }
394
395 public function getAllListsUserSubscriptionById($userId, $key = 'id', $needTranslation = false)
396 {
397 $query = 'SELECT list.id, list.translation, list.name, list.color, list.active, list.visible, userlist.status, userlist.subscription_date, userlist.unsubscribe_date
398 FROM #__acym_list AS list
399 LEFT JOIN #__acym_user_has_list AS userlist
400 ON list.id = userlist.list_id
401 AND userlist.user_id = '.intval($userId);
402
403 $lists = acym_loadObjectList($query, $key);
404
405 if (acym_isMultilingual() && $needTranslation) {
406 $listClass = new ListClass();
407 $lists = $listClass->getTranslatedNameDescription($lists);
408 }
409
410 return $lists;
411 }
412
413 public function getUsersSubscriptionsByIds($usersId, $creatorId = 0)
414 {
415 $query = 'SELECT `list`.`id`, `list`.`color`, `list`.`name`, `userlist`.`user_id`, `userlist`.`status`
416 FROM #__acym_list AS `list`
417 JOIN #__acym_user_has_list AS `userlist`
418 ON `list`.`id` = `userlist`.`list_id`
419 WHERE `userlist`.`user_id` IN ('.implode(',', $usersId).')
420 AND `list`.`type` = '.acym_escapeDB(ListClass::LIST_TYPE_STANDARD);
421
422 if (!empty($creatorId)) {
423 $userGroups = acym_getGroupsByUser($creatorId);
424 $groupCondition = '`list`.`access` LIKE "%,'.implode(',%" OR `list`.`access` LIKE "%,', $userGroups).',%"';
425 $query .= ' AND (`list`.`cms_user_id` = '.intval($creatorId).' OR '.$groupCondition.')';
426 }
427 $query .= ' ORDER BY `userlist`.`status` DESC, `list`.`id` ASC';
428
429 return acym_loadObjectList($query);
430 }
431
432 public function getCountTotalUsers(): int
433 {
434 $numberOfUsers = acym_loadResult('SELECT COUNT(id) FROM #__acym_user');
435
436 return empty($numberOfUsers) ? 0 : $numberOfUsers;
437 }
438
439 public function getSubscriptionStatus($userId, $listIds = [], $wantedStatus = null)
440 {
441 $query = 'SELECT status, list_id
442 FROM #__acym_user_has_list
443 WHERE user_id = '.intval($userId);
444 if (!empty($listIds)) {
445 acym_arrayToInteger($listIds);
446 $query .= ' AND list_id IN ('.implode(',', $listIds).')';
447 }
448
449 if ($wantedStatus !== null) {
450 $query .= ' AND status = '.intval($wantedStatus);
451 }
452
453 return acym_loadObjectList($query, 'list_id');
454 }
455
456 public function identify(bool $onlyValue = false, $idName = null, $keyName = null)
457 {
458 $id = acym_getVar('int', empty($idName) ? 'id' : $idName, 0);
459 $key = acym_getVar('string', empty($keyName) ? 'key' : $keyName, '');
460
461 if (empty($id) && !empty($idName) && empty($key) && !empty($keyName)) {
462 $id = acym_getVar('int', 'id', 0);
463 $key = acym_getVar('string', 'key', 0);
464 }
465
466 if (empty($id) || empty($key)) {
467 $currentUserid = acym_currentUserId();
468 if (!empty($currentUserid)) {
469 return $this->getOneByCMSId($currentUserid);
470 }
471 if (!$onlyValue) {
472 acym_enqueueMessage(acym_translation('ACYM_LOGIN'), 'error');
473 }
474
475 return false;
476 }
477
478 $userIdentified = acym_loadObject('SELECT * FROM #__acym_user WHERE `id` = '.intval($id).' AND `key` = '.acym_escapeDB($key));
479
480 if (!empty($userIdentified)) {
481 return $userIdentified;
482 }
483
484 if (!$onlyValue) {
485 acym_enqueueMessage(acym_translation('ACYM_USER_NOT_FOUND'), 'error');
486 }
487
488 return false;
489 }
490
491 public function subscribe($userIds, $addLists, $trigger = true, $forceFront = false)
492 {
493 if (empty($addLists)) {
494 return false;
495 }
496
497 if (!is_array($userIds)) {
498 $userIds = [$userIds];
499 }
500
501 if (!is_array($addLists)) {
502 $addLists = [$addLists];
503 }
504
505 $listClass = new ListClass();
506 $historyClass = new HistoryClass();
507
508 $confirmationRequired = $this->config->get('require_confirmation', 1);
509 $subscribedToLists = false;
510 $historyData = acym_translationSprintf('ACYM_LISTS_NUMBERS', implode(', ', $addLists));
511
512 foreach ($userIds as $userId) {
513 $user = $this->getOneById($userId);
514 if (empty($user)) continue;
515 $currentSubscription = $this->getUserSubscriptionById($userId, 'id', true);
516
517 $currentlySubscribed = [];
518 $currentlyUnsubscribed = [];
519 foreach ($currentSubscription as $oneList) {
520 if ($oneList->status == 1) {
521 $currentlySubscribed[$oneList->id] = $oneList;
522 }
523 if ($oneList->status == 0) {
524 $currentlyUnsubscribed[$oneList->id] = $oneList;
525 }
526 }
527
528 $subscribedLists = [];
529 $listsNotExisting = [];
530 foreach ($addLists as $oneListId) {
531 if (empty($oneListId) || !empty($currentlySubscribed[$oneListId])) {
532 continue;
533 }
534
535 $list = $listClass->getOneById($oneListId);
536 if (empty($list)) {
537 $listsNotExisting[] = $oneListId;
538 continue;
539 }
540
541 $subscription = new \stdClass();
542 $subscription->user_id = $userId;
543 $subscription->list_id = $oneListId;
544 $subscription->status = 1;
545 $subscription->subscription_date = date('Y-m-d H:i:s', time() - date('Z'));
546
547 if (empty($currentSubscription[$oneListId])) {
548 acym_insertObject('#__acym_user_has_list', $subscription);
549 } elseif (!empty($currentlyUnsubscribed[$oneListId])) {
550 acym_updateObject('#__acym_user_has_list', $subscription, ['user_id', 'list_id']);
551 }
552
553 $subscribedLists[] = $oneListId;
554 $subscribedToLists = true;
555 }
556
557 if (!empty($listsNotExisting)) {
558 $this->errors['list_not_existing'] = acym_translationSprintf('ACYM_LIST_NOT_FOUND', implode(', ', $listsNotExisting));
559 }
560
561 $historyClass->insert($userId, 'subscribed', [$historyData]);
562
563 if ($trigger) {
564 acym_trigger('onAcymAfterUserSubscribe', [&$user, &$subscribedLists]);
565 }
566
567 if ($this->sendWelcomeEmail && ($confirmationRequired == 0 || $user->confirmed == 1) && $user->active == 1) {
568 $listClass->sendWelcome($userId, $subscribedLists, $forceFront);
569 }
570 }
571
572 return $subscribedToLists;
573 }
574
575 private function registerUnsubUser($userIds)
576 {
577 $mailId = acym_getVar('int', 'mail_id', 0);
578 if (empty($mailId)) return;
579
580 $mailStatClass = new MailStatClass();
581 $userStatClass = new UserStatClass();
582
583 $countUnsubscribe = 0;
584
585 foreach ($userIds as $id) {
586 $userStat = $userStatClass->getOneByMailAndUserId($mailId, $id);
587 if (empty($userStat)) continue;
588
589 $newUserStat = [
590 'mail_id' => $mailId,
591 'user_id' => $id,
592 'unsubscribe' => empty($userStat->unsubscribe) ? 1 : $userStat->unsubscribe + 1,
593 ];
594
595 if (empty($userStat->unsubscribe)) $countUnsubscribe++;
596
597 $userStatClass->save($newUserStat);
598 }
599
600 $mailStat = [
601 'mail_id' => $mailId,
602 'unsubscribe_total' => $countUnsubscribe,
603 ];
604
605 $mailStatClass->save($mailStat);
606 }
607
608 public function unsubscribe($userIds, $lists)
609 {
610 if (empty($lists)) return false;
611
612 if (!is_array($userIds)) $userIds = [$userIds];
613 if (!is_array($lists)) $lists = [$lists];
614
615 $listClass = new ListClass();
616 $unsubscribedFromLists = false;
617 foreach ($userIds as $userId) {
618 $user = $this->getOneById($userId);
619 if (empty($user)) continue;
620
621 $currentSubscription = $this->getUserSubscriptionById($userId);
622
623 $currentlySubscribed = [];
624 $currentlyUnsubscribed = [];
625 foreach ($currentSubscription as $oneList) {
626 if ($oneList->status == 0) {
627 $currentlyUnsubscribed[$oneList->id] = $oneList;
628 } elseif ($oneList->status == 1) {
629 $currentlySubscribed[$oneList->id] = $oneList;
630 }
631 }
632
633 $unsubscribedLists = [];
634 $listsNotExisting = [];
635 foreach ($lists as $oneListId) {
636 if (empty($oneListId) || !empty($currentlyUnsubscribed[$oneListId])) continue;
637
638 if (empty($currentlySubscribed[$oneListId])) continue;
639
640 $list = $listClass->getOneById($oneListId);
641 if (empty($list)) {
642 $listsNotExisting[] = $oneListId;
643 continue;
644 }
645
646 $subscription = new \stdClass();
647 $subscription->user_id = $userId;
648 $subscription->list_id = $oneListId;
649 $subscription->status = 0;
650 $subscription->unsubscribe_date = date('Y-m-d H:i:s', time() - date('Z'));
651 if (empty($currentSubscription[$oneListId])) {
652 acym_insertObject('#__acym_user_has_list', $subscription);
653 } else {
654 acym_updateObject('#__acym_user_has_list', $subscription, ['user_id', 'list_id']);
655 }
656
657 $unsubscribedLists[] = $oneListId;
658 $unsubscribedFromLists = true;
659 }
660
661 if (!empty($listsNotExisting)) {
662 $this->errors['list_not_existing'] = acym_translationSprintf('ACYM_LIST_NOT_FOUND', implode(', ', $listsNotExisting));
663 }
664
665 acym_query(
666 'DELETE FROM #__acym_queue WHERE user_id = '.intval($userId).' AND mail_id IN (
667 SELECT followup_mail.mail_id FROM #__acym_followup_has_mail AS followup_mail
668 JOIN #__acym_followup AS followup ON followup.id = followup_mail.followup_id AND followup.list_id IN ('.implode(',', $lists).')
669 )'
670 );
671
672 $historyClass = new HistoryClass();
673 $historyData = acym_translationSprintf('ACYM_LISTS_NUMBERS', implode(', ', $lists));
674 $historyClass->insert($userId, 'unsubscribed', [$historyData], 0, acym_getVar('string', 'unsubscribe_reason'));
675
676 if ($this->triggers) {
677 acym_trigger('onAcymAfterUserUnsubscribe', [&$user, &$unsubscribedLists]);
678 }
679
680 if ($this->sendUnsubscribeEmail) {
681 $listClass->sendUnsubscribe($userId, $unsubscribedLists);
682 }
683
684 if (!empty($unsubscribedLists)) {
685 foreach ($unsubscribedLists as $i => $oneListId) {
686 $currentList = $listClass->getOneById($oneListId);
687 $unsubscribedLists[$i] = $currentList->name;
688 }
689 $this->sendNotification($user->id, 'acy_notification_unsub', ['lists' => implode(', ', $unsubscribedLists)]);
690 }
691 }
692
693 if ($unsubscribedFromLists) $this->registerUnsubUser($userIds);
694
695 return $unsubscribedFromLists;
696 }
697
698 public function unsubscribeOnSubscriptions($userId, $listIds)
699 {
700 if (empty($listIds)) {
701 return false;
702 }
703
704 if (!is_array($listIds)) {
705 $listIds = [$listIds];
706 }
707 acym_arrayToInteger($listIds);
708
709 $subscribedLists = acym_loadResultArray('SELECT list_id FROM #__acym_user_has_list WHERE user_id = '.intval($userId).' AND list_id IN ('.implode(',', $listIds).')');
710
711 if (!empty($subscribedLists)) $this->unsubscribe($userId, $subscribedLists);
712
713 return true;
714 }
715
716 public function removeSubscription($userIds, $listIds = [])
717 {
718 if (!is_array($userIds)) {
719 $userIds = [$userIds];
720 }
721
722 if (!is_array($listIds) || empty($listIds) || empty($userIds)) {
723 return false;
724 }
725
726 acym_arrayToInteger($listIds);
727
728 return acym_query(
729 'DELETE FROM #__acym_user_has_list
730 WHERE user_id IN ('.implode(',', $userIds).')
731 AND list_id IN ('.implode(',', $listIds).')'
732 );
733 }
734
735 public function onlyManageableUsers(&$elements)
736 {
737 if (acym_isAdmin()) {
738 return;
739 }
740
741 $listClass = new ListClass();
742 $manageableLists = $listClass->getManageableLists();
743 if (empty($manageableLists)) {
744 return;
745 }
746
747 $currentUserId = acym_currentUserId();
748 acym_arrayToInteger($elements);
749
750 $selfManagement = in_array($currentUserId, $elements);
751
752 $elements = acym_loadResultArray(
753 'SELECT DISTINCT user_id
754 FROM #__acym_user_has_list
755 WHERE `list_id` IN ('.implode(',', $manageableLists).')
756 AND `user_id` IN ('.implode(',', $elements).')'
757 );
758
759 if ($selfManagement && !in_array($currentUserId, $elements)) {
760 $elements[] = $currentUserId;
761 }
762 }
763
764 public function delete($elements, $forceDelete = false)
765 {
766 if (!is_array($elements)) {
767 $elements = [$elements];
768 }
769
770 acym_arrayToInteger($elements);
771 $this->onlyManageableUsers($elements);
772
773 if (empty($elements)) {
774 return 0;
775 }
776
777 if (acym_isAdmin() || $forceDelete || 'delete' === $this->config->get('frontend_delete_button', 'delete')) {
778 acym_query('DELETE FROM #__acym_user_has_list WHERE user_id IN ('.implode(',', $elements).')');
779 acym_query('DELETE FROM #__acym_queue WHERE user_id IN ('.implode(',', $elements).')');
780 acym_query('DELETE FROM #__acym_user_has_field WHERE user_id IN ('.implode(',', $elements).')');
781 acym_query('DELETE FROM #__acym_history WHERE user_id IN ('.implode(',', $elements).')');
782 acym_query('DELETE FROM #__acym_user_stat WHERE user_id IN ('.implode(',', $elements).')');
783
784 $scenarioProcessIds = acym_loadResultArray('SELECT id FROM #__acym_scenario_process WHERE user_id IN ('.implode(',', $elements).')');
785
786 if (!empty($scenarioProcessIds)) {
787 acym_query('DELETE FROM #__acym_scenario_history_line WHERE scenario_process_id IN ('.implode(',', $scenarioProcessIds).')');
788 acym_query('DELETE FROM #__acym_scenario_queue WHERE scenario_process_id IN ('.implode(',', $scenarioProcessIds).')');
789 acym_query('DELETE FROM #__acym_scenario_process WHERE id IN ('.implode(',', $scenarioProcessIds).')');
790 }
791
792 return parent::delete($elements);
793 } else {
794 $listClass = new ListClass();
795 $manageableLists = $listClass->getManageableLists();
796
797 if (!empty($manageableLists)) {
798 $this->removeSubscription($elements, $manageableLists);
799 acym_query(
800 'DELETE queue
801 FROM #__acym_queue AS queue
802 JOIN #__acym_mail_has_list AS maillist
803 ON queue.mail_id = maillist.mail_id
804 WHERE queue.user_id IN ('.implode(',', $elements).')
805 AND maillist.list_id IN ('.implode(',', $manageableLists).')'
806 );
807 }
808
809 return count($elements);
810 }
811 }
812
813 public function save($user, $customFields = [], $ajax = false)
814 {
815 if (empty($user->email) && empty($user->id)) {
816 return false;
817 }
818
819 if (isset($user->email)) {
820 $user->email = strtolower($user->email);
821 if (!acym_isValidEmail($user->email)) {
822 $this->errors[] = acym_translation('ACYM_VALID_EMAIL');
823
824 return false;
825 }
826 }
827
828 if (empty($user->id)) {
829 if (!isset($user->active)) {
830 $user->active = 1;
831 }
832
833 if (empty($user->language)) {
834 if (!acym_isAdmin()) {
835 $cmsUserLanguage = acym_getCmsUserLanguage();
836 }
837
838 if (!empty($cmsUserLanguage)) {
839 $user->language = $cmsUserLanguage;
840 } elseif (acym_isMultilingual()) {
841 $configUserLanguage = $this->config->get('multilingual_user_default', 'current_language');
842 $user->language = $configUserLanguage === 'current_language' ? acym_getLanguageTag() : $configUserLanguage;
843 }
844 }
845
846 $currentUserid = acym_currentUserId();
847 $currentEmail = acym_currentUserEmail();
848 $allowVisitors = $this->config->get('allow_visitor', 1) == 1;
849 if ($this->checkVisitor && !acym_isAdmin() && !$allowVisitors && (empty($currentUserid) || strtolower($currentEmail) !== $user->email)) {
850 $this->errors[] = acym_translation('ACYM_ONLY_LOGGED');
851
852 return false;
853 }
854
855 if (empty($user->name) && $this->config->get('generate_name', 1)) {
856 $user->name = ucwords(trim(str_replace(['.', '_', ')', ',', '(', '-', 1, 2, 3, 4, 5, 6, 7, 8, 9, 0], ' ', substr($user->email, 0, strpos($user->email, '@')))));
857 }
858
859 $source = acym_getVar('string', 'acy_source', '');
860 if (empty($user->source) && !empty($source)) $user->source = $source;
861
862 if (empty($user->key)) $user->key = acym_generateKey(14);
863
864 $user->creation_date = date('Y-m-d H:i:s', time() - date('Z'));
865 } elseif (!empty($user->confirmed)) {
866 $oldUser = $this->getOneByIdWithCustomFields($user->id);
867 if (!empty($oldUser) && empty($oldUser['confirmed'])) {
868 $user->confirmation_date = date('Y-m-d H:i:s', time() - date('Z'));
869 $user->confirmation_ip = acym_getIP();
870
871 if ($this->triggers) {
872 acym_trigger('onAcymAfterUserConfirm', [&$user]);
873 }
874 }
875 } else {
876 $oldUser = $this->getOneByIdWithCustomFields($user->id);
877 }
878
879 foreach ($user as $oneAttribute => $value) {
880 if (empty($value)) continue;
881
882 $oneAttribute = trim(strtolower($oneAttribute));
883 if (!in_array($oneAttribute, $this->restrictedFields)) {
884 $user->$oneAttribute = strip_tags($value);
885 }
886
887 if (is_numeric($user->$oneAttribute)) continue;
888
889 if (function_exists('mb_detect_encoding')) {
890 if (mb_detect_encoding($user->$oneAttribute, 'UTF-8', true) !== 'UTF-8') {
891 $user->$oneAttribute = acym_utf8Encode($user->$oneAttribute);
892 }
893 } elseif (!preg_match(
894 '%^(?:[\x09\x0A\x0D\x20-\x7E]|[\xC2-\xDF][\x80-\xBF]|\xE0[\xA0-\xBF][\x80-\xBF]|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}|\xED[\x80-\x9F][\x80-\xBF]|\xF0[\x90-\xBF][\x80-\xBF]{2}|[\xF1-\xF3][\x80-\xBF]{3}|\xF4[\x80-\x8F][\x80-\xBF]{2})*$%xs',
895 $user->$oneAttribute
896 )) {
897 $user->$oneAttribute = acym_utf8Encode($user->$oneAttribute);
898 }
899 }
900
901 if (empty($user->id)) {
902 if (empty($user->cms_id) && !empty($user->email)) {
903 global $acymCmsUserVars;
904 $userCmsID = acym_loadResult(
905 'SELECT '.acym_secureDBColumn($acymCmsUserVars->id).' FROM '.$acymCmsUserVars->table.' WHERE '.acym_secureDBColumn(
906 $acymCmsUserVars->email
907 ).' = '.acym_escapeDB($user->email)
908 );
909 if (!empty($userCmsID)) $user->cms_id = $userCmsID;
910 }
911 if ($this->triggers) {
912 $result = acym_trigger('onAcymBeforeUserCreate', [&$user]);
913 if (in_array(false, $result)) {
914 $acycheckerError = acym_getVar('string', 'acychecker_error');
915 if (!empty($acycheckerError)) {
916 if ($ajax) {
917 $this->errors[] = $acycheckerError;
918 } else {
919 acym_enqueueMessage($acycheckerError, 'error');
920 }
921 }
922
923 return false;
924 }
925 }
926 } else {
927 if ($this->triggers) {
928 acym_trigger('onAcymBeforeUserModify', [&$user]);
929 }
930 }
931
932 $userID = parent::save($user);
933
934 $fieldClass = new FieldClass();
935 if (!empty($customFields)) {
936 foreach ($customFields as $key => $value) {
937 if (!is_string($value)) {
938 continue;
939 }
940
941 $value = preg_replace('/[\x{10000}-\x{10FFFF}]/u', '', $value);
942
943 $field = $fieldClass->getOneById($key);
944 if (empty($field) || $field->type !== 'textarea') {
945 $value = preg_replace('/\s+/', ' ', $value);
946 }
947 $customFields[$key] = $value;
948 }
949 }
950 $fieldClass->store($userID, $customFields, $ajax);
951 if (!empty($fieldClass->errors)) {
952 $this->errors = array_merge($this->errors, $fieldClass->errors);
953 }
954
955 $historyClass = new HistoryClass();
956 if (empty($user->id)) {
957 $user->id = $userID;
958 $historyClass->insert($user->id, 'created');
959 if ($this->triggers) {
960 acym_trigger('onAcymAfterUserCreate', [&$user]);
961 acym_triggerCmsHook('onAcymAfterUserSave', [$user, true]);
962 }
963 } else {
964 $historyClass->insert($user->id, 'modified');
965 if ($this->triggers) {
966 acym_trigger('onAcymAfterUserModify', [&$user, &$oldUser]);
967 acym_triggerCmsHook('onAcymAfterUserSave', [$user, false]);
968 }
969 }
970
971 $this->sendConfirmation($userID);
972
973 return $userID;
974 }
975
976 public function saveForm($ajax = false)
977 {
978 $allowUserModifications = (bool)($this->config->get('allow_modif', 'data') == 'all') || $this->allowModif;
979 $allowSubscriptionModifications = (bool)($this->config->get('allow_modif', 'data') != 'none') || $this->allowModif;
980
981 $user = new \stdClass();
982 $connectedUser = $this->identify(true, 'userId', 'userKey');
983
984 $userData = acym_getVar('array', 'user', []);
985 if (!empty($userData)) {
986 foreach ($userData as $attribute => $value) {
987 $user->$attribute = $value;
988 }
989 }
990
991 if (empty($user->email)) {
992 if (!empty($connectedUser->email)) {
993 $user->email = $connectedUser->email;
994 }
995 }
996
997 if (empty($user->email)) {
998 $this->errors[] = acym_translation('ACYM_VALID_EMAIL');
999
1000 return false;
1001 }
1002
1003 if (!$allowUserModifications && !empty($connectedUser)) {
1004 if ($connectedUser->email != $user->email) {
1005 $this->errors[] = acym_translation('ACYM_NOT_ALLOWED_MODIFY_USER');
1006
1007 return false;
1008 }
1009
1010 $allowUserModifications = true;
1011 $allowSubscriptionModifications = true;
1012 }
1013
1014 if (!empty($user->email)) {
1015 if (empty($user->id)) {
1016 $user->id = 0;
1017 }
1018 $existUser = acym_loadObject('SELECT * FROM #__acym_user WHERE email = '.acym_escapeDB($user->email).' AND id != '.intval($user->id));
1019 if (!empty($existUser->id)) {
1020 if (!$this->allowModif && !$allowSubscriptionModifications) {
1021 $this->errors[] = acym_translation('ACYM_ADDRESS_TAKEN');
1022
1023 return false;
1024 } else {
1025 $user->id = $existUser->id;
1026 }
1027 }
1028 }
1029
1030 if (!empty($user->id) && !empty($user->email)) {
1031 $existUser = $this->getOneById($user->id);
1032 if (trim(strtolower($user->email)) != strtolower($existUser->email)) {
1033 $user->confirmed = 0;
1034 }
1035 }
1036
1037 $this->newUser = empty($user->id);
1038 if (empty($user->id) || $allowUserModifications) {
1039 if (isset($user->confirmed) && $user->confirmed != 1) {
1040 $user->confirmed = 0;
1041 }
1042 if (isset($user->active) && $user->active != 1) {
1043 $user->active = 0;
1044 }
1045 $customFieldData = acym_getVar('array', 'customField', []);
1046 $id = $this->save($user, $customFieldData, $ajax);
1047 $allowSubscriptionModifications = true;
1048 } else {
1049 $id = $user->id;
1050 if (isset($user->confirmed) && empty($user->confirmed)) {
1051 $this->sendConfirmation($id);
1052 }
1053 }
1054
1055 if (empty($id)) return false;
1056
1057 $formData = acym_getVar('array', 'data', []);
1058 acym_setVar('userId', $id);
1059
1060 if (!acym_isAdmin()) {
1061 $hiddenlistsString = acym_getVar('string', 'hiddenlists', '');
1062 $hiddenlists = explode(',', $hiddenlistsString);
1063 acym_arrayToInteger($hiddenlists);
1064 $visibleSubscription = acym_getVar('array', 'subscription', []);
1065 $subscribeLists = array_merge($hiddenlists, $visibleSubscription);
1066 if (!empty($subscribeLists)) {
1067 foreach ($subscribeLists as $oneListId) {
1068 $formData['listsub'][$oneListId] = ['status' => 1];
1069 }
1070 }
1071 }
1072
1073 $fromProfile = acym_getVar('int', 'acyprofile', 0) == 1;
1074
1075 if (empty($formData['listsub'])) {
1076 if (!$fromProfile) $this->sendNotification($id, 'acy_notification_subform');
1077
1078 $this->sendNotification(
1079 $id,
1080 $this->newUser ? 'acy_notification_create' : 'acy_notification_profile'
1081 );
1082
1083 return true;
1084 }
1085
1086 if (!$allowSubscriptionModifications) {
1087 $this->requireId = true;
1088 $this->errors[] = acym_translation('ACYM_NOT_ALLOWED_MODIFY_USER');
1089
1090 return false;
1091 }
1092
1093 $addLists = [];
1094 $unsubLists = [];
1095 foreach ($formData['listsub'] as $listID => $oneList) {
1096 if ($oneList['status'] == 1) {
1097 $addLists[] = $listID;
1098 } else {
1099 $unsubLists[] = $listID;
1100 }
1101 }
1102
1103 $this->subscribed = $this->subscribe($id, $addLists);
1104
1105 if (!$fromProfile) $this->sendNotification($id, 'acy_notification_subform');
1106
1107 $this->sendNotification(
1108 $id,
1109 $this->newUser ? 'acy_notification_create' : 'acy_notification_profile'
1110 );
1111
1112 if (!$this->newUser) $this->unsubscribe($id, $unsubLists);
1113
1114 return true;
1115 }
1116
1117 public function sendConfirmation($userID)
1118 {
1119 if (!$this->forceConf && !$this->sendConf) {
1120 return;
1121 }
1122
1123 if ($this->config->get('require_confirmation', 1) != 1 || (acym_isAdmin() && !$this->forceConfAdmin)) {
1124 return;
1125 }
1126
1127 $myUser = $this->getOneById($userID);
1128
1129 if (!empty($myUser->confirmed)) {
1130 return;
1131 }
1132
1133 $mailerHelper = new MailerHelper();
1134 $mailerHelper->report = (bool)$this->config->get('confirm_message', 0);
1135
1136 $mailClass = new MailClass();
1137 $confirmationEmail = $mailClass->getOneByName('acy_confirm');
1138 $this->confirmationSentSuccess = !empty($confirmationEmail) && $mailerHelper->sendOne($confirmationEmail->id, $myUser);
1139 $this->confirmationSentError = $mailerHelper->reportMessage;
1140 }
1141
1142 public function deactivate($userId)
1143 {
1144 acym_query('UPDATE `#__acym_user` SET `active` = 0 WHERE `id` = '.intval($userId));
1145 }
1146
1147 public function confirm($userId)
1148 {
1149 $user = $this->getOneById($userId);
1150 if (empty($user)) return;
1151
1152 $confirmDate = date('Y-m-d H:i:s', time() - date('Z'));
1153 $ip = acym_getIP();
1154 $query = 'UPDATE `#__acym_user`';
1155 $query .= ' SET `confirmed` = 1, `confirmation_date` = '.acym_escapeDB($confirmDate).', `confirmation_ip` = '.acym_escapeDB($ip);
1156 $query .= ' WHERE `id` = '.intval($userId).' LIMIT 1';
1157
1158 try {
1159 $res = acym_query($query);
1160 } catch (\Exception $e) {
1161 $res = false;
1162 }
1163 if ($res === false) {
1164 $errorMessage = isset($e) ? $e->getMessage() : acym_getDBError();
1165 $msg = acym_translation('ACYM_CONTACT_ADMIN_ERROR').'<br />'.substr(strip_tags($errorMessage), 0, 200).'...';
1166 acym_display($msg, 'error');
1167 exit;
1168 }
1169
1170 acym_trigger('onAcymAfterUserConfirm', [&$user]);
1171 $this->sendNotification($userId, 'acy_notification_confirm');
1172
1173 $historyClass = new HistoryClass();
1174 $historyClass->insert($userId, 'confirmed');
1175
1176 if (empty($this->config->get('require_confirmation', 1))) return;
1177
1178 $listIDs = acym_loadResultArray('SELECT `list_id` FROM `#__acym_user_has_list` WHERE `status` = 1 AND `user_id` = '.intval($userId));
1179
1180 if (empty($listIDs)) return;
1181
1182 $listClass = new ListClass();
1183 $listClass->sendWelcome($userId, $listIDs);
1184 }
1185
1186 public function getOneByIdWithCustomFields($id): array
1187 {
1188 $user = $this->getOneById($id);
1189 if (empty($user)) {
1190 return [];
1191 }
1192
1193 $user = get_object_vars($user);
1194
1195 $fieldsValue = acym_loadObjectList(
1196 'SELECT user_field.`value`, field.`name`
1197 FROM #__acym_user_has_field AS `user_field`
1198 LEFT JOIN #__acym_field AS `field` ON user_field.`field_id` = field.`id`
1199 WHERE user_field.`user_id` = '.intval($id),
1200 'name'
1201 );
1202
1203 foreach ($fieldsValue as $key => $value) {
1204 $fieldsValue[$key] = $value->value;
1205 }
1206
1207 return array_merge($user, $fieldsValue);
1208 }
1209
1210 public function getOneWithCustomFields($column, $value): array
1211 {
1212 switch ($column) {
1213 case 'id':
1214 $user = $this->getOneById($value);
1215 break;
1216 case 'email':
1217 $user = $this->getOneByEmail($value);
1218 break;
1219 case 'cms_id':
1220 $user = $this->getOneByCMSId($value);
1221 break;
1222 default:
1223 return [];
1224 }
1225
1226 if (empty($user)) {
1227 return [];
1228 }
1229 $user = get_object_vars($user);
1230
1231 $fieldsValue = acym_loadObjectList(
1232 'SELECT user_field.`value`, field.`name`
1233 FROM #__acym_user_has_field AS `user_field`
1234 LEFT JOIN #__acym_field AS `field` ON user_field.`field_id` = field.`id`
1235 WHERE user_field.`user_id` = '.intval($user['id']),
1236 'name'
1237 );
1238
1239 foreach ($fieldsValue as $key => $value) {
1240 $fieldsValue[$key] = $value->value;
1241 }
1242
1243 return array_merge($user, $fieldsValue);
1244 }
1245
1246 public function getCustomFieldValueById($id)
1247 {
1248 $fieldsValue = acym_loadObjectList(
1249 'SELECT user_field.value AS value, field.name AS name, field.type AS type, field.value AS field_params, field.id AS id
1250 FROM #__acym_user_has_field as user_field
1251 LEFT JOIN #__acym_field as field ON user_field.field_id = field.id
1252 WHERE user_field.user_id = '.intval($id),
1253 'id'
1254 );
1255
1256 $fieldReturn = [];
1257
1258 foreach ($fieldsValue as $fieldId => $field) {
1259 if (in_array($field->type, ['checkbox', 'radio', 'single_dropdown', 'multiple_dropdown'])) {
1260 $field->field_params = json_decode($field->field_params, true);
1261 $field->value = explode(',', $field->value);
1262 $values = [];
1263 foreach ($field->value as $oneValue) {
1264 $key = array_search($oneValue, array_column($field->field_params, 'value'));
1265 $values[] = $field->field_params[$key]['title'];
1266 }
1267 $fieldReturn[$field->name] = implode(',', $values);
1268 } else {
1269 $fieldReturn[$field->name] = $field->value;
1270 }
1271 }
1272
1273 return $fieldReturn;
1274 }
1275
1276 public function getAllColumnsUserAndCustomField($inAction = false)
1277 {
1278 $return = [];
1279
1280 $userFields = acym_getColumns('user');
1281 foreach ($userFields as $value) {
1282 $return[$value] = $value;
1283 }
1284
1285 $languageFieldId = $this->config->get(FieldClass::LANGUAGE_FIELD_ID_KEY, 0);
1286
1287 $customFields = acym_loadObjectList(
1288 'SELECT * FROM #__acym_field WHERE id NOT IN (1, 2, '.intval($languageFieldId).') '.($inAction ? 'AND type != "phone"' : ''),
1289 'id'
1290 );
1291 if (!empty($customFields)) {
1292 foreach ($customFields as $key => $value) {
1293 $return[$key] = $value->name;
1294 }
1295 }
1296
1297 return $return;
1298 }
1299
1300 public function getAllUserFields($user)
1301 {
1302 if (empty($user->id)) return $user;
1303 $query = 'SELECT field.*, field.value AS field_value, userfield.*
1304 FROM #__acym_field AS field
1305 LEFT JOIN #__acym_user_has_field AS userfield ON field.id = userfield.field_id AND userfield.user_id = '.intval($user->id).'
1306 WHERE field.id NOT IN(1, 2)';
1307
1308 $allFields = acym_loadObjectList($query);
1309
1310 foreach ($allFields as $oneField) {
1311 $fieldOptions = @json_decode(empty($oneField->option) ? '{}' : $oneField->option);
1312 if (in_array($oneField->type, ['multiple_dropdown', 'radio', 'checkbox', 'single_dropdown'])) {
1313 $oneField->field_value = json_decode($oneField->field_value, true);
1314 if (!empty($oneField->value)) {
1315 $oneField->value = explode(',', $oneField->value);
1316 $values = [];
1317 foreach ($oneField->field_value as $oneFieldValue) {
1318 foreach ($oneField->value as $oneValue) {
1319 if ($oneFieldValue['value'] == $oneValue) {
1320 $values[] = $oneFieldValue['title'];
1321 }
1322 }
1323 }
1324 $oneField->value = implode(',', $values);
1325 }
1326 } elseif ($oneField->type === 'file' && !empty($oneField->value)) {
1327 $oneField->value = json_decode($oneField->value);
1328 if (!empty($oneField->value)) {
1329 $oneField->value = $oneField->value[0];
1330 }
1331 } elseif ($oneField->type === 'date') {
1332 if (empty($fieldOptions->format)) {
1333 acym_logError('Date field with ID '.$oneField->id.' without format option');
1334 continue;
1335 }
1336 $formatToDisplay = explode('%', str_replace('y', 'Y', $fieldOptions->format));
1337 unset($formatToDisplay[0]);
1338 if (!empty($oneField->value)) {
1339 $oneField->value = acym_date($oneField->value, implode('-', $formatToDisplay));
1340 }
1341 }
1342 $user->{$oneField->namekey} = empty($oneField->value) ? '' : $oneField->value;
1343 }
1344
1345 return $user;
1346 }
1347
1348 public function getAllSimpleData()
1349 {
1350 return acym_loadObjectList('SELECT email, name FROM #__acym_user');
1351 }
1352
1353 public function synchSaveCmsUser($user, $isnew, $oldUser = null)
1354 {
1355 $source = acym_getVar('string', 'acy_source', '');
1356 if (empty($source)) acym_setVar('acy_source', ACYM_CMS);
1357
1358 if (!$this->config->get('regacy', 0)) return;
1359
1360 $this->checkVisitor = false;
1361 $this->sendConf = false;
1362
1363 $regacyForceConf = $this->config->get('regacy_forceconf', 0);
1364 $cmsUser = new \stdClass();
1365 $cmsUser->email = trim(strip_tags($user['email']));
1366 if (!acym_isValidEmail($cmsUser->email)) return;
1367 if (!empty($user['name'])) $cmsUser->name = trim(strip_tags($user['name']));
1368 if (!$regacyForceConf) $cmsUser->confirmed = 1;
1369 $cmsUser->active = 1 - intval($user['block']);
1370 $cmsUser->cms_id = $user['id'];
1371
1372 if (!$isnew && !empty($oldUser['email']) && $user['email'] != $oldUser['email']) {
1373 $acyUser = $this->getOneByEmail($oldUser['email']);
1374 if (!empty($acyUser)) $cmsUser->id = $acyUser->id;
1375 }
1376
1377 if (empty($cmsUser->id) && !empty($cmsUser->cms_id)) {
1378 $acyUser = $this->getOneByCMSId($cmsUser->cms_id);
1379 if (!empty($acyUser)) $cmsUser->id = $acyUser->id;
1380 }
1381
1382 $acyUser = $this->getOneByEmail($cmsUser->email);
1383 if (!empty($acyUser)) {
1384 if (empty($cmsUser->id)) {
1385 $cmsUser->id = $acyUser->id;
1386 } elseif ($cmsUser->id != $acyUser->id) {
1387 $this->delete($acyUser->id);
1388 }
1389 } else {
1390 $cmsUser->source = $source;
1391 }
1392
1393 $isnew = $isnew || empty($cmsUser->id);
1394
1395 $id = $this->save($cmsUser);
1396
1397 $confirmationRequired = $this->config->get('require_confirmation', 1);
1398 if (!$isnew && !$regacyForceConf && $user['block'] == 0 && !empty($oldUser['block']) && $confirmationRequired == 1) {
1399 $this->confirm($id);
1400 }
1401
1402
1403 $currentSubscription = $this->getSubscriptionStatus($id);
1404
1405 $autoLists = $isnew ? $this->config->get('regacy_autolists', '') : '';
1406 $autoLists = explode(',', $autoLists);
1407 acym_arrayToInteger($autoLists);
1408
1409 $listsClass = new ListClass();
1410 $allLists = $listsClass->getAll();
1411
1412 $visibleLists = acym_getVar('string', 'regacy_visible_lists', '');
1413 $visibleLists = explode(',', $visibleLists);
1414 acym_arrayToInteger($visibleLists);
1415
1416 $visibleListsChecked = acym_getVar('array', 'regacy_visible_lists_checked', []);
1417 acym_arrayToInteger($visibleListsChecked);
1418
1419
1420 if (!$isnew && !empty($visibleLists)) {
1421 $currentlySubscribedLists = [];
1422 foreach ($currentSubscription as $oneSubscription) {
1423 if ($oneSubscription->status == 1) $currentlySubscribedLists[] = $oneSubscription->list_id;
1424 }
1425 $unsubscribeLists = array_intersect($currentlySubscribedLists, array_diff($visibleLists, $visibleListsChecked));
1426 $this->unsubscribe($id, $unsubscribeLists);
1427 }
1428
1429 $listsToSubscribe = [];
1430 foreach ($allLists as $oneList) {
1431 if (!$oneList->active) continue;
1432 if (!empty($currentSubscription[$oneList->id]) && $currentSubscription[$oneList->id]->status == 1) continue;
1433
1434 if (in_array($oneList->id, $visibleListsChecked) || (in_array($oneList->id, $autoLists) && !in_array(
1435 $oneList->id,
1436 $visibleLists
1437 ) && empty($currentSubscription[$oneList->id]))) {
1438 $listsToSubscribe[] = $oneList->id;
1439 }
1440 }
1441
1442 if (!empty($listsToSubscribe)) $this->subscribe($id, $listsToSubscribe);
1443
1444 if ($isnew) $this->sendNotification($id, 'acy_notification_create');
1445
1446 $acymailingUser = $this->getOneById($id);
1447 if (!empty($user['block']) || !empty($acymailingUser->confirmed)) return;
1448
1449 if ($isnew || !empty($oldUser['block'])) {
1450 if ($confirmationRequired && $regacyForceConf) {
1451 $this->forceConf = true;
1452 $this->sendConfirmation($id);
1453 }
1454
1455 if (!$confirmationRequired && !empty($oldUser['email'])) {
1456 $listIDs = acym_loadResultArray('SELECT `list_id` FROM `#__acym_user_has_list` WHERE `status` = 1 AND `user_id` = '.intval($id));
1457 if (empty($listIDs)) return;
1458 $listsClass->sendWelcome($id, $listIDs);
1459 }
1460 }
1461 }
1462
1463 public function synchDeleteCmsUser($userEmail)
1464 {
1465 $acyUser = $this->getOneByEmail($userEmail);
1466
1467 if (empty($acyUser)) return;
1468
1469 if ($this->config->get('regacy', '0') == 1 && $this->config->get('regacy_delete', '0') == 1) {
1470 $this->delete($acyUser->id);
1471 } else {
1472 acym_query('UPDATE #__acym_user SET `cms_id` = 0 WHERE `id` = '.intval($acyUser->id));
1473 }
1474 }
1475
1476 public function getUsersLikeEmail($pattern)
1477 {
1478 $query = 'SELECT id, email FROM #__acym_user WHERE email LIKE '.acym_escapeDB('%'.$pattern.'%');
1479
1480 return acym_loadObjectList($query);
1481 }
1482
1483 public function sendNotification($userId, string $notification, array $params = [])
1484 {
1485 if (empty($userId) || $this->blockNotifications) return;
1486
1487 $notifyUsers = explode(',', $this->config->get($notification));
1488 if (acym_isAdmin() || empty($notifyUsers)) return;
1489
1490 $mailerHelper = new MailerHelper();
1491 $mailerHelper->report = false;
1492 $mailerHelper->autoAddUser = true;
1493
1494 $user = $this->getOneById($userId);
1495 $userField = $this->getAllUserFields($user);
1496 if (!empty($userField)) {
1497 foreach ($userField as $map => $value) {
1498 $mailerHelper->addParam('user:'.$map, $value);
1499 }
1500 }
1501
1502 $rawSubscription = $this->getUserSubscriptionById($userId);
1503 $subscription = [''];
1504 foreach ($rawSubscription as $listId => $listData) {
1505 $currentList = $listData->name.' => ';
1506 if (intval($listData->status) === 1) {
1507 $currentList .= acym_translation('ACYM_SUBSCRIBED').' - '.$listData->subscription_date;
1508 } else {
1509 $currentList .= acym_translation('ACYM_UNSUBSCRIBED').' - '.$listData->unsubscribe_date;
1510 }
1511 $subscription[] = $currentList;
1512 }
1513 $mailerHelper->addParam('user:subscription', implode('<br/>', $subscription));
1514
1515 if (!empty($params)) {
1516 foreach ($params as $name => $value) {
1517 $mailerHelper->addParam($name, $value);
1518 }
1519 }
1520
1521 $mailClass = new MailClass();
1522 foreach ($notifyUsers as $oneUser) {
1523 if (!acym_isValidEmail($oneUser)) {
1524 continue;
1525 }
1526
1527 $notificationEmail = $mailClass->getOneByName($notification);
1528 if (!empty($notificationEmail)) {
1529 $mailerHelper->sendOne($notificationEmail->id, $oneUser);
1530 }
1531 }
1532 }
1533
1534 public function getMailHistory($userId)
1535 {
1536 $query = 'SELECT user_stat.*, mail.id, mail.subject, SUM(url_click.click) as click FROM #__acym_user_stat AS user_stat
1537 JOIN #__acym_mail AS mail ON mail.id = user_stat.mail_id
1538 LEFT JOIN #__acym_url_click AS url_click ON user_stat.mail_id = url_click.mail_id AND url_click.user_id = '.intval($userId).'
1539 WHERE user_stat.user_id = '.intval($userId).' GROUP BY mail_id ORDER BY send_date DESC LIMIT 50';
1540
1541 $mailHistory = acym_loadObjectList($query, 'mail_id');
1542 $mailClass = new MailClass();
1543
1544 return $mailClass->decode($mailHistory);
1545 }
1546
1547 public function deleteHistoryPeriod()
1548 {
1549 if (empty($this->config->get('delete_user_history_enabled', 0))) return;
1550 $deleteOverSecond = $this->config->get('delete_user_history', 0);
1551 if (empty($deleteOverSecond)) return;
1552 $date = time() - $deleteOverSecond;
1553
1554 $query = 'DELETE FROM #__acym_history WHERE date < '.intval($date);
1555
1556 try {
1557 $status = acym_query($query);
1558 $message = empty($status) ? '' : acym_translationSprintf('ACYM_DELETE_X_ROWS_TABLE_X', $status, strtolower(acym_translation('ACYM_USER_HISTORY')));
1559 } catch (\Exception $e) {
1560 $status = false;
1561 $message = $e->getMessage();
1562 }
1563
1564 return ['status' => $status !== false, 'message' => $message];
1565 }
1566
1567 public function resetSubscription($userIds, $lists)
1568 {
1569 if (empty($lists)) return false;
1570
1571 if (!is_array($userIds)) $userIds = [$userIds];
1572 if (!is_array($lists)) $lists = [$lists];
1573
1574 acym_arrayToInteger($userIds);
1575 acym_arrayToInteger($lists);
1576
1577 foreach ($userIds as $userId) {
1578 acym_query(
1579 'DELETE FROM `#__acym_user_has_list`
1580 WHERE user_id = '.$userId.'
1581 AND list_id IN ('.implode(',', $lists).')'
1582 );
1583
1584 acym_query(
1585 'DELETE FROM #__acym_queue WHERE user_id = '.$userId.' AND mail_id IN (
1586 SELECT followup_mail.mail_id FROM #__acym_followup_has_mail AS followup_mail
1587 JOIN #__acym_followup AS followup ON followup.id = followup_mail.followup_id AND followup.list_id IN ('.implode(',', $lists).')
1588 )'
1589 );
1590
1591 $historyClass = new HistoryClass();
1592 $historyData = acym_translationSprintf('ACYM_LISTS_NUMBERS', implode(', ', $lists));
1593 $historyClass->insert($userId, 'reset_subscription', [$historyData]);
1594 }
1595
1596 return true;
1597 }
1598
1599 public function addMissingKeys()
1600 {
1601 $usersMissingKey = acym_loadResultArray('SELECT `id` FROM #__acym_user WHERE `key` IS NULL OR `key` = ""');
1602 foreach ($usersMissingKey as $oneUserId) {
1603 acym_query('UPDATE #__acym_user SET `key` = '.acym_escapeDB(acym_generateKey(14)).' WHERE `id` = '.intval($oneUserId));
1604 }
1605
1606 return count($usersMissingKey);
1607 }
1608
1609 public function hasUserAccess($subscriberId): bool
1610 {
1611 $userId = acym_currentUserId();
1612 if (empty($userId)) {
1613 return false;
1614 }
1615
1616 if (acym_isAdmin()) {
1617 return true;
1618 }
1619
1620 $user = $this->getOneById($subscriberId);
1621 $userEmail = acym_currentUserEmail();
1622 if (!empty($user) && $user->email === $userEmail) {
1623 return true;
1624 }
1625
1626 $listClass = new ListClass();
1627 $allowedListIds = $listClass->getManageableLists();
1628 if (empty($allowedListIds)) {
1629 return false;
1630 }
1631
1632 $query = 'SELECT COUNT(*) FROM #__acym_user AS user
1633 JOIN #__acym_user_has_list AS map ON map.user_id = user.id
1634 WHERE map.list_id IN ('.implode(',', $allowedListIds).')
1635 AND user.id = '.intval($subscriberId);
1636
1637 return acym_loadResult($query) > 0;
1638 }
1639
1640 public function getXUsers(array $options = [])
1641 {
1642 $limit = $options['limit'] ?? 10;
1643 $offset = $options['offset'] ?? 0;
1644 $filters = $options['filters'] ?? [];
1645
1646 $conditions = [];
1647 foreach ($filters as $column => $filter) {
1648 switch ($column) {
1649 case 'id':
1650 case 'cms_id':
1651 case 'active':
1652 case 'confirmed':
1653 case 'tracking':
1654 $conditions[] = acym_secureDBColumn($column).' = '.intval($filter);
1655 break;
1656 default:
1657 $conditions[] = acym_secureDBColumn($column).' LIKE '.acym_escapeDB('%'.$filter.'%');
1658 }
1659 }
1660
1661 if (!empty($options['created_after'])) {
1662 $conditions[] = 'creation_date > '.acym_escapeDB($options['created_after']);
1663 }
1664
1665 if (!empty($options['confirmed_after'])) {
1666 $conditions[] = 'confirmation_date > '.acym_escapeDB($options['confirmed_after']);
1667 }
1668
1669 $query = 'SELECT * FROM #__acym_user';
1670 if (!empty($conditions)) {
1671 $query .= ' WHERE '.implode(' AND ', $conditions);
1672 }
1673
1674 return acym_loadObjectList($query, $this->pkey, $offset, $limit);
1675 }
1676 }
1677