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 / Classes / UserClass.php

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

1,650 lines 61.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\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()
433 {
434 $query = 'SELECT COUNT(id) FROM #__acym_user';
435
436 return acym_loadResult($query);
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 = null)
717 {
718 if (!is_array($userIds)) $userIds = [$userIds];
719 if (!is_array($listIds) || empty($listIds) || empty($userIds)) return false;
720
721 acym_arrayToInteger($listIds);
722 $query = 'DELETE FROM #__acym_user_has_list WHERE user_id IN ('.implode(',', $userIds).')';
723 if (!empty($listIds)) $query .= ' AND list_id IN ('.implode(',', $listIds).')';
724
725 return acym_query($query);
726 }
727
728 public function onlyManageableUsers(&$elements)
729 {
730 if (acym_isAdmin()) return;
731
732 $listClass = new ListClass();
733 $manageableLists = $listClass->getManageableLists();
734 if (empty($manageableLists)) return;
735
736 acym_arrayToInteger($elements);
737 $elements = acym_loadResultArray(
738 'SELECT DISTINCT user_id
739 FROM #__acym_user_has_list
740 WHERE `list_id` IN ('.implode(',', $manageableLists).')
741 AND `user_id` IN ('.implode(',', $elements).')'
742 );
743 }
744
745 public function delete($elements, $fromAutomations = false)
746 {
747 if (!is_array($elements)) $elements = [$elements];
748 acym_arrayToInteger($elements);
749 $this->onlyManageableUsers($elements);
750
751 if (empty($elements)) return 0;
752
753 if (acym_isAdmin() || $fromAutomations || 'delete' === $this->config->get('frontend_delete_button', 'delete')) {
754 acym_query('DELETE FROM #__acym_user_has_list WHERE user_id IN ('.implode(',', $elements).')');
755 acym_query('DELETE FROM #__acym_queue WHERE user_id IN ('.implode(',', $elements).')');
756 acym_query('DELETE FROM #__acym_user_has_field WHERE user_id IN ('.implode(',', $elements).')');
757 acym_query('DELETE FROM #__acym_history WHERE user_id IN ('.implode(',', $elements).')');
758 acym_query('DELETE FROM #__acym_user_stat WHERE user_id IN ('.implode(',', $elements).')');
759
760 $scenarioProcessIds = acym_loadResultArray('SELECT id FROM #__acym_scenario_process WHERE user_id IN ('.implode(',', $elements).')');
761
762 if (!empty($scenarioProcessIds)) {
763 acym_query('DELETE FROM #__acym_scenario_history_line WHERE scenario_process_id IN ('.implode(',', $scenarioProcessIds).')');
764 acym_query('DELETE FROM #__acym_scenario_queue WHERE scenario_process_id IN ('.implode(',', $scenarioProcessIds).')');
765 acym_query('DELETE FROM #__acym_scenario_process WHERE id IN ('.implode(',', $scenarioProcessIds).')');
766 }
767
768 return parent::delete($elements);
769 } else {
770 $listClass = new ListClass();
771 $manageableLists = $listClass->getManageableLists();
772 $this->removeSubscription($elements, $manageableLists);
773 acym_query(
774 'DELETE queue
775 FROM #__acym_queue AS queue
776 JOIN #__acym_mail_has_list AS maillist
777 ON queue.mail_id = maillist.mail_id
778 WHERE queue.user_id IN ('.implode(',', $elements).')
779 AND maillist.list_id IN ('.implode(',', $manageableLists).')'
780 );
781
782 return count($elements);
783 }
784 }
785
786 public function save($user, $customFields = [], $ajax = false)
787 {
788 if (empty($user->email) && empty($user->id)) {
789 return false;
790 }
791
792 if (isset($user->email)) {
793 $user->email = strtolower($user->email);
794 if (!acym_isValidEmail($user->email)) {
795 $this->errors[] = acym_translation('ACYM_VALID_EMAIL');
796
797 return false;
798 }
799 }
800
801 if (empty($user->id)) {
802 if (!isset($user->active)) {
803 $user->active = 1;
804 }
805
806 if (empty($user->language)) {
807 if (!acym_isAdmin()) {
808 $cmsUserLanguage = acym_getCmsUserLanguage();
809 }
810
811 if (!empty($cmsUserLanguage)) {
812 $user->language = $cmsUserLanguage;
813 } elseif (acym_isMultilingual()) {
814 $configUserLanguage = $this->config->get('multilingual_user_default', 'current_language');
815 $user->language = $configUserLanguage === 'current_language' ? acym_getLanguageTag() : $configUserLanguage;
816 }
817 }
818
819 $currentUserid = acym_currentUserId();
820 $currentEmail = acym_currentUserEmail();
821 $allowVisitors = $this->config->get('allow_visitor', 1) == 1;
822 if ($this->checkVisitor && !acym_isAdmin() && !$allowVisitors && (empty($currentUserid) || strtolower($currentEmail) !== $user->email)) {
823 $this->errors[] = acym_translation('ACYM_ONLY_LOGGED');
824
825 return false;
826 }
827
828 if (empty($user->name) && $this->config->get('generate_name', 1)) {
829 $user->name = ucwords(trim(str_replace(['.', '_', ')', ',', '(', '-', 1, 2, 3, 4, 5, 6, 7, 8, 9, 0], ' ', substr($user->email, 0, strpos($user->email, '@')))));
830 }
831
832 $source = acym_getVar('string', 'acy_source', '');
833 if (empty($user->source) && !empty($source)) $user->source = $source;
834
835 if (empty($user->key)) $user->key = acym_generateKey(14);
836
837 $user->creation_date = date('Y-m-d H:i:s', time() - date('Z'));
838 } elseif (!empty($user->confirmed)) {
839 $oldUser = $this->getOneByIdWithCustomFields($user->id);
840 if (!empty($oldUser) && empty($oldUser['confirmed'])) {
841 $user->confirmation_date = date('Y-m-d H:i:s', time() - date('Z'));
842 $user->confirmation_ip = acym_getIP();
843
844 if ($this->triggers) {
845 acym_trigger('onAcymAfterUserConfirm', [&$user]);
846 }
847 }
848 } else {
849 $oldUser = $this->getOneByIdWithCustomFields($user->id);
850 }
851
852 foreach ($user as $oneAttribute => $value) {
853 if (empty($value)) continue;
854
855 $oneAttribute = trim(strtolower($oneAttribute));
856 if (!in_array($oneAttribute, $this->restrictedFields)) {
857 $user->$oneAttribute = strip_tags($value);
858 }
859
860 if (is_numeric($user->$oneAttribute)) continue;
861
862 if (function_exists('mb_detect_encoding')) {
863 if (mb_detect_encoding($user->$oneAttribute, 'UTF-8', true) !== 'UTF-8') {
864 $user->$oneAttribute = acym_utf8Encode($user->$oneAttribute);
865 }
866 } elseif (!preg_match(
867 '%^(?:[\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',
868 $user->$oneAttribute
869 )) {
870 $user->$oneAttribute = acym_utf8Encode($user->$oneAttribute);
871 }
872 }
873
874 if (empty($user->id)) {
875 if (empty($user->cms_id) && !empty($user->email)) {
876 global $acymCmsUserVars;
877 $userCmsID = acym_loadResult(
878 'SELECT '.acym_secureDBColumn($acymCmsUserVars->id).' FROM '.$acymCmsUserVars->table.' WHERE '.acym_secureDBColumn(
879 $acymCmsUserVars->email
880 ).' = '.acym_escapeDB($user->email)
881 );
882 if (!empty($userCmsID)) $user->cms_id = $userCmsID;
883 }
884 if ($this->triggers) {
885 $result = acym_trigger('onAcymBeforeUserCreate', [&$user]);
886 if (in_array(false, $result)) {
887 $acycheckerError = acym_getVar('string', 'acychecker_error');
888 if (!empty($acycheckerError)) {
889 if ($ajax) {
890 $this->errors[] = $acycheckerError;
891 } else {
892 acym_enqueueMessage($acycheckerError, 'error');
893 }
894 }
895
896 return false;
897 }
898 }
899 } else {
900 if ($this->triggers) {
901 acym_trigger('onAcymBeforeUserModify', [&$user]);
902 }
903 }
904
905 $userID = parent::save($user);
906
907 $fieldClass = new FieldClass();
908 if (!empty($customFields)) {
909 foreach ($customFields as $key => $value) {
910 if (!is_string($value)) {
911 continue;
912 }
913
914 $value = preg_replace('/[\x{10000}-\x{10FFFF}]/u', '', $value);
915
916 $field = $fieldClass->getOneById($key);
917 if (empty($field) || $field->type !== 'textarea') {
918 $value = preg_replace('/\s+/', ' ', $value);
919 }
920 $customFields[$key] = $value;
921 }
922 }
923 $fieldClass->store($userID, $customFields, $ajax);
924 if (!empty($fieldClass->errors)) {
925 $this->errors = array_merge($this->errors, $fieldClass->errors);
926 }
927
928 $historyClass = new HistoryClass();
929 if (empty($user->id)) {
930 $user->id = $userID;
931 $historyClass->insert($user->id, 'created');
932 if ($this->triggers) {
933 acym_trigger('onAcymAfterUserCreate', [&$user]);
934 acym_triggerCmsHook('onAcymAfterUserSave', [$user, true]);
935 }
936 } else {
937 $historyClass->insert($user->id, 'modified');
938 if ($this->triggers) {
939 acym_trigger('onAcymAfterUserModify', [&$user, &$oldUser]);
940 acym_triggerCmsHook('onAcymAfterUserSave', [$user, false]);
941 }
942 }
943
944 $this->sendConfirmation($userID);
945
946 return $userID;
947 }
948
949 public function saveForm($ajax = false)
950 {
951 $allowUserModifications = (bool)($this->config->get('allow_modif', 'data') == 'all') || $this->allowModif;
952 $allowSubscriptionModifications = (bool)($this->config->get('allow_modif', 'data') != 'none') || $this->allowModif;
953
954 $user = new \stdClass();
955 $connectedUser = $this->identify(true, 'userId', 'userKey');
956
957 $userData = acym_getVar('array', 'user', []);
958 if (!empty($userData)) {
959 foreach ($userData as $attribute => $value) {
960 $user->$attribute = $value;
961 }
962 }
963
964 if (empty($user->email)) {
965 if (!empty($connectedUser->email)) {
966 $user->email = $connectedUser->email;
967 }
968 }
969
970 if (empty($user->email)) {
971 $this->errors[] = acym_translation('ACYM_VALID_EMAIL');
972
973 return false;
974 }
975
976 if (!$allowUserModifications && !empty($connectedUser)) {
977 if ($connectedUser->email != $user->email) {
978 $this->errors[] = acym_translation('ACYM_NOT_ALLOWED_MODIFY_USER');
979
980 return false;
981 }
982
983 $allowUserModifications = true;
984 $allowSubscriptionModifications = true;
985 }
986
987 if (!empty($user->email)) {
988 if (empty($user->id)) {
989 $user->id = 0;
990 }
991 $existUser = acym_loadObject('SELECT * FROM #__acym_user WHERE email = '.acym_escapeDB($user->email).' AND id != '.intval($user->id));
992 if (!empty($existUser->id)) {
993 if (!$this->allowModif && !$allowSubscriptionModifications) {
994 $this->errors[] = acym_translation('ACYM_ADDRESS_TAKEN');
995
996 return false;
997 } else {
998 $user->id = $existUser->id;
999 }
1000 }
1001 }
1002
1003 if (!empty($user->id) && !empty($user->email)) {
1004 $existUser = $this->getOneById($user->id);
1005 if (trim(strtolower($user->email)) != strtolower($existUser->email)) {
1006 $user->confirmed = 0;
1007 }
1008 }
1009
1010 $this->newUser = empty($user->id);
1011 if (empty($user->id) || $allowUserModifications) {
1012 if (isset($user->confirmed) && $user->confirmed != 1) {
1013 $user->confirmed = 0;
1014 }
1015 if (isset($user->active) && $user->active != 1) {
1016 $user->active = 0;
1017 }
1018 $customFieldData = acym_getVar('array', 'customField', []);
1019 $id = $this->save($user, $customFieldData, $ajax);
1020 $allowSubscriptionModifications = true;
1021 } else {
1022 $id = $user->id;
1023 if (isset($user->confirmed) && empty($user->confirmed)) {
1024 $this->sendConfirmation($id);
1025 }
1026 }
1027
1028 if (empty($id)) return false;
1029
1030 $formData = acym_getVar('array', 'data', []);
1031 acym_setVar('userId', $id);
1032
1033 if (!acym_isAdmin()) {
1034 $hiddenlistsString = acym_getVar('string', 'hiddenlists', '');
1035 $hiddenlists = explode(',', $hiddenlistsString);
1036 acym_arrayToInteger($hiddenlists);
1037 $visibleSubscription = acym_getVar('array', 'subscription', []);
1038 $subscribeLists = array_merge($hiddenlists, $visibleSubscription);
1039 if (!empty($subscribeLists)) {
1040 foreach ($subscribeLists as $oneListId) {
1041 $formData['listsub'][$oneListId] = ['status' => 1];
1042 }
1043 }
1044 }
1045
1046 $fromProfile = acym_getVar('int', 'acyprofile', 0) == 1;
1047
1048 if (empty($formData['listsub'])) {
1049 if (!$fromProfile) $this->sendNotification($id, 'acy_notification_subform');
1050
1051 $this->sendNotification(
1052 $id,
1053 $this->newUser ? 'acy_notification_create' : 'acy_notification_profile'
1054 );
1055
1056 return true;
1057 }
1058
1059 if (!$allowSubscriptionModifications) {
1060 $this->requireId = true;
1061 $this->errors[] = acym_translation('ACYM_NOT_ALLOWED_MODIFY_USER');
1062
1063 return false;
1064 }
1065
1066 $addLists = [];
1067 $unsubLists = [];
1068 foreach ($formData['listsub'] as $listID => $oneList) {
1069 if ($oneList['status'] == 1) {
1070 $addLists[] = $listID;
1071 } else {
1072 $unsubLists[] = $listID;
1073 }
1074 }
1075
1076 $this->subscribed = $this->subscribe($id, $addLists);
1077
1078 if (!$fromProfile) $this->sendNotification($id, 'acy_notification_subform');
1079
1080 $this->sendNotification(
1081 $id,
1082 $this->newUser ? 'acy_notification_create' : 'acy_notification_profile'
1083 );
1084
1085 if (!$this->newUser) $this->unsubscribe($id, $unsubLists);
1086
1087 return true;
1088 }
1089
1090 public function sendConfirmation($userID)
1091 {
1092 if (!$this->forceConf && !$this->sendConf) {
1093 return;
1094 }
1095
1096 if ($this->config->get('require_confirmation', 1) != 1 || (acym_isAdmin() && !$this->forceConfAdmin)) {
1097 return;
1098 }
1099
1100 $myUser = $this->getOneById($userID);
1101
1102 if (!empty($myUser->confirmed)) {
1103 return;
1104 }
1105
1106 $mailerHelper = new MailerHelper();
1107 $mailerHelper->report = (bool)$this->config->get('confirm_message', 0);
1108
1109 $mailClass = new MailClass();
1110 $confirmationEmail = $mailClass->getOneByName('acy_confirm');
1111 $this->confirmationSentSuccess = !empty($confirmationEmail) && $mailerHelper->sendOne($confirmationEmail->id, $myUser);
1112 $this->confirmationSentError = $mailerHelper->reportMessage;
1113 }
1114
1115 public function deactivate($userId)
1116 {
1117 acym_query('UPDATE `#__acym_user` SET `active` = 0 WHERE `id` = '.intval($userId));
1118 }
1119
1120 public function confirm($userId)
1121 {
1122 $user = $this->getOneById($userId);
1123 if (empty($user)) return;
1124
1125 $confirmDate = date('Y-m-d H:i:s', time() - date('Z'));
1126 $ip = acym_getIP();
1127 $query = 'UPDATE `#__acym_user`';
1128 $query .= ' SET `confirmed` = 1, `confirmation_date` = '.acym_escapeDB($confirmDate).', `confirmation_ip` = '.acym_escapeDB($ip);
1129 $query .= ' WHERE `id` = '.intval($userId).' LIMIT 1';
1130
1131 try {
1132 $res = acym_query($query);
1133 } catch (\Exception $e) {
1134 $res = false;
1135 }
1136 if ($res === false) {
1137 $errorMessage = isset($e) ? $e->getMessage() : acym_getDBError();
1138 $msg = acym_translation('ACYM_CONTACT_ADMIN_ERROR').'<br />'.substr(strip_tags($errorMessage), 0, 200).'...';
1139 acym_display($msg, 'error');
1140 exit;
1141 }
1142
1143 acym_trigger('onAcymAfterUserConfirm', [&$user]);
1144 $this->sendNotification($userId, 'acy_notification_confirm');
1145
1146 $historyClass = new HistoryClass();
1147 $historyClass->insert($userId, 'confirmed');
1148
1149 if (empty($this->config->get('require_confirmation', 1))) return;
1150
1151 $listIDs = acym_loadResultArray('SELECT `list_id` FROM `#__acym_user_has_list` WHERE `status` = 1 AND `user_id` = '.intval($userId));
1152
1153 if (empty($listIDs)) return;
1154
1155 $listClass = new ListClass();
1156 $listClass->sendWelcome($userId, $listIDs);
1157 }
1158
1159 public function getOneByIdWithCustomFields($id): array
1160 {
1161 $user = $this->getOneById($id);
1162 if (empty($user)) {
1163 return [];
1164 }
1165
1166 $user = get_object_vars($user);
1167
1168 $fieldsValue = acym_loadObjectList(
1169 'SELECT user_field.`value`, field.`name`
1170 FROM #__acym_user_has_field AS `user_field`
1171 LEFT JOIN #__acym_field AS `field` ON user_field.`field_id` = field.`id`
1172 WHERE user_field.`user_id` = '.intval($id),
1173 'name'
1174 );
1175
1176 foreach ($fieldsValue as $key => $value) {
1177 $fieldsValue[$key] = $value->value;
1178 }
1179
1180 return array_merge($user, $fieldsValue);
1181 }
1182
1183 public function getOneWithCustomFields($column, $value): array
1184 {
1185 switch ($column) {
1186 case 'id':
1187 $user = $this->getOneById($value);
1188 break;
1189 case 'email':
1190 $user = $this->getOneByEmail($value);
1191 break;
1192 case 'cms_id':
1193 $user = $this->getOneByCMSId($value);
1194 break;
1195 default:
1196 return [];
1197 }
1198
1199 if (empty($user)) {
1200 return [];
1201 }
1202 $user = get_object_vars($user);
1203
1204 $fieldsValue = acym_loadObjectList(
1205 'SELECT user_field.`value`, field.`name`
1206 FROM #__acym_user_has_field AS `user_field`
1207 LEFT JOIN #__acym_field AS `field` ON user_field.`field_id` = field.`id`
1208 WHERE user_field.`user_id` = '.intval($user['id']),
1209 'name'
1210 );
1211
1212 foreach ($fieldsValue as $key => $value) {
1213 $fieldsValue[$key] = $value->value;
1214 }
1215
1216 return array_merge($user, $fieldsValue);
1217 }
1218
1219 public function getCustomFieldValueById($id)
1220 {
1221 $fieldsValue = acym_loadObjectList(
1222 'SELECT user_field.value AS value, field.name AS name, field.type AS type, field.value AS field_params, field.id AS id
1223 FROM #__acym_user_has_field as user_field
1224 LEFT JOIN #__acym_field as field ON user_field.field_id = field.id
1225 WHERE user_field.user_id = '.intval($id),
1226 'id'
1227 );
1228
1229 $fieldReturn = [];
1230
1231 foreach ($fieldsValue as $fieldId => $field) {
1232 if (in_array($field->type, ['checkbox', 'radio', 'single_dropdown', 'multiple_dropdown'])) {
1233 $field->field_params = json_decode($field->field_params, true);
1234 $field->value = explode(',', $field->value);
1235 $values = [];
1236 foreach ($field->value as $oneValue) {
1237 $key = array_search($oneValue, array_column($field->field_params, 'value'));
1238 $values[] = $field->field_params[$key]['title'];
1239 }
1240 $fieldReturn[$field->name] = implode(',', $values);
1241 } else {
1242 $fieldReturn[$field->name] = $field->value;
1243 }
1244 }
1245
1246 return $fieldReturn;
1247 }
1248
1249 public function getAllColumnsUserAndCustomField($inAction = false)
1250 {
1251 $return = [];
1252
1253 $userFields = acym_getColumns('user');
1254 foreach ($userFields as $value) {
1255 $return[$value] = $value;
1256 }
1257
1258 $languageFieldId = $this->config->get(FieldClass::LANGUAGE_FIELD_ID_KEY, 0);
1259
1260 $customFields = acym_loadObjectList(
1261 'SELECT * FROM #__acym_field WHERE id NOT IN (1, 2, '.intval($languageFieldId).') '.($inAction ? 'AND type != "phone"' : ''),
1262 'id'
1263 );
1264 if (!empty($customFields)) {
1265 foreach ($customFields as $key => $value) {
1266 $return[$key] = $value->name;
1267 }
1268 }
1269
1270 return $return;
1271 }
1272
1273 public function getAllUserFields($user)
1274 {
1275 if (empty($user->id)) return $user;
1276 $query = 'SELECT field.*, field.value AS field_value, userfield.*
1277 FROM #__acym_field AS field
1278 LEFT JOIN #__acym_user_has_field AS userfield ON field.id = userfield.field_id AND userfield.user_id = '.intval($user->id).'
1279 WHERE field.id NOT IN(1, 2)';
1280
1281 $allFields = acym_loadObjectList($query);
1282
1283 foreach ($allFields as $oneField) {
1284 $fieldOptions = @json_decode(empty($oneField->option) ? '{}' : $oneField->option);
1285 if (in_array($oneField->type, ['multiple_dropdown', 'radio', 'checkbox', 'single_dropdown'])) {
1286 $oneField->field_value = json_decode($oneField->field_value, true);
1287 if (!empty($oneField->value)) {
1288 $oneField->value = explode(',', $oneField->value);
1289 $values = [];
1290 foreach ($oneField->field_value as $oneFieldValue) {
1291 foreach ($oneField->value as $oneValue) {
1292 if ($oneFieldValue['value'] == $oneValue) {
1293 $values[] = $oneFieldValue['title'];
1294 }
1295 }
1296 }
1297 $oneField->value = implode(',', $values);
1298 }
1299 } elseif ($oneField->type === 'file' && !empty($oneField->value)) {
1300 $oneField->value = json_decode($oneField->value);
1301 if (!empty($oneField->value)) {
1302 $oneField->value = $oneField->value[0];
1303 }
1304 } elseif ($oneField->type === 'date') {
1305 if (empty($fieldOptions->format)) {
1306 acym_logError('Date field with ID '.$oneField->id.' without format option');
1307 continue;
1308 }
1309 $formatToDisplay = explode('%', str_replace('y', 'Y', $fieldOptions->format));
1310 unset($formatToDisplay[0]);
1311 if (!empty($oneField->value)) {
1312 $oneField->value = acym_date($oneField->value, implode('-', $formatToDisplay));
1313 }
1314 }
1315 $user->{$oneField->namekey} = empty($oneField->value) ? '' : $oneField->value;
1316 }
1317
1318 return $user;
1319 }
1320
1321 public function getAllSimpleData()
1322 {
1323 return acym_loadObjectList('SELECT email, name FROM #__acym_user');
1324 }
1325
1326 public function synchSaveCmsUser($user, $isnew, $oldUser = null)
1327 {
1328 $source = acym_getVar('string', 'acy_source', '');
1329 if (empty($source)) acym_setVar('acy_source', ACYM_CMS);
1330
1331 if (!$this->config->get('regacy', 0)) return;
1332
1333 $this->checkVisitor = false;
1334 $this->sendConf = false;
1335
1336 $regacyForceConf = $this->config->get('regacy_forceconf', 0);
1337 $cmsUser = new \stdClass();
1338 $cmsUser->email = trim(strip_tags($user['email']));
1339 if (!acym_isValidEmail($cmsUser->email)) return;
1340 if (!empty($user['name'])) $cmsUser->name = trim(strip_tags($user['name']));
1341 if (!$regacyForceConf) $cmsUser->confirmed = 1;
1342 $cmsUser->active = 1 - intval($user['block']);
1343 $cmsUser->cms_id = $user['id'];
1344
1345 if (!$isnew && !empty($oldUser['email']) && $user['email'] != $oldUser['email']) {
1346 $acyUser = $this->getOneByEmail($oldUser['email']);
1347 if (!empty($acyUser)) $cmsUser->id = $acyUser->id;
1348 }
1349
1350 if (empty($cmsUser->id) && !empty($cmsUser->cms_id)) {
1351 $acyUser = $this->getOneByCMSId($cmsUser->cms_id);
1352 if (!empty($acyUser)) $cmsUser->id = $acyUser->id;
1353 }
1354
1355 $acyUser = $this->getOneByEmail($cmsUser->email);
1356 if (!empty($acyUser)) {
1357 if (empty($cmsUser->id)) {
1358 $cmsUser->id = $acyUser->id;
1359 } elseif ($cmsUser->id != $acyUser->id) {
1360 $this->delete($acyUser->id);
1361 }
1362 } else {
1363 $cmsUser->source = $source;
1364 }
1365
1366 $isnew = $isnew || empty($cmsUser->id);
1367
1368 $id = $this->save($cmsUser);
1369
1370 $confirmationRequired = $this->config->get('require_confirmation', 1);
1371 if (!$isnew && !$regacyForceConf && $user['block'] == 0 && !empty($oldUser['block']) && $confirmationRequired == 1) {
1372 $this->confirm($id);
1373 }
1374
1375
1376 $currentSubscription = $this->getSubscriptionStatus($id);
1377
1378 $autoLists = $isnew ? $this->config->get('regacy_autolists', '') : '';
1379 $autoLists = explode(',', $autoLists);
1380 acym_arrayToInteger($autoLists);
1381
1382 $listsClass = new ListClass();
1383 $allLists = $listsClass->getAll();
1384
1385 $visibleLists = acym_getVar('string', 'regacy_visible_lists', '');
1386 $visibleLists = explode(',', $visibleLists);
1387 acym_arrayToInteger($visibleLists);
1388
1389 $visibleListsChecked = acym_getVar('array', 'regacy_visible_lists_checked', []);
1390 acym_arrayToInteger($visibleListsChecked);
1391
1392
1393 if (!$isnew && !empty($visibleLists)) {
1394 $currentlySubscribedLists = [];
1395 foreach ($currentSubscription as $oneSubscription) {
1396 if ($oneSubscription->status == 1) $currentlySubscribedLists[] = $oneSubscription->list_id;
1397 }
1398 $unsubscribeLists = array_intersect($currentlySubscribedLists, array_diff($visibleLists, $visibleListsChecked));
1399 $this->unsubscribe($id, $unsubscribeLists);
1400 }
1401
1402 $listsToSubscribe = [];
1403 foreach ($allLists as $oneList) {
1404 if (!$oneList->active) continue;
1405 if (!empty($currentSubscription[$oneList->id]) && $currentSubscription[$oneList->id]->status == 1) continue;
1406
1407 if (in_array($oneList->id, $visibleListsChecked) || (in_array($oneList->id, $autoLists) && !in_array(
1408 $oneList->id,
1409 $visibleLists
1410 ) && empty($currentSubscription[$oneList->id]))) {
1411 $listsToSubscribe[] = $oneList->id;
1412 }
1413 }
1414
1415 if (!empty($listsToSubscribe)) $this->subscribe($id, $listsToSubscribe);
1416
1417 if ($isnew) $this->sendNotification($id, 'acy_notification_create');
1418
1419 $acymailingUser = $this->getOneById($id);
1420 if (!empty($user['block']) || !empty($acymailingUser->confirmed)) return;
1421
1422 if ($isnew || !empty($oldUser['block'])) {
1423 if ($confirmationRequired && $regacyForceConf) {
1424 $this->forceConf = true;
1425 $this->sendConfirmation($id);
1426 }
1427
1428 if (!$confirmationRequired && !empty($oldUser['email'])) {
1429 $listIDs = acym_loadResultArray('SELECT `list_id` FROM `#__acym_user_has_list` WHERE `status` = 1 AND `user_id` = '.intval($id));
1430 if (empty($listIDs)) return;
1431 $listsClass->sendWelcome($id, $listIDs);
1432 }
1433 }
1434 }
1435
1436 public function synchDeleteCmsUser($userEmail)
1437 {
1438 $acyUser = $this->getOneByEmail($userEmail);
1439
1440 if (empty($acyUser)) return;
1441
1442 if ($this->config->get('regacy', '0') == 1 && $this->config->get('regacy_delete', '0') == 1) {
1443 $this->delete($acyUser->id);
1444 } else {
1445 acym_query('UPDATE #__acym_user SET `cms_id` = 0 WHERE `id` = '.intval($acyUser->id));
1446 }
1447 }
1448
1449 public function getUsersLikeEmail($pattern)
1450 {
1451 $query = 'SELECT id, email FROM #__acym_user WHERE email LIKE '.acym_escapeDB('%'.$pattern.'%');
1452
1453 return acym_loadObjectList($query);
1454 }
1455
1456 public function sendNotification($userId, string $notification, array $params = [])
1457 {
1458 if (empty($userId) || $this->blockNotifications) return;
1459
1460 $notifyUsers = explode(',', $this->config->get($notification));
1461 if (acym_isAdmin() || empty($notifyUsers)) return;
1462
1463 $mailerHelper = new MailerHelper();
1464 $mailerHelper->report = false;
1465 $mailerHelper->autoAddUser = true;
1466
1467 $user = $this->getOneById($userId);
1468 $userField = $this->getAllUserFields($user);
1469 if (!empty($userField)) {
1470 foreach ($userField as $map => $value) {
1471 $mailerHelper->addParam('user:'.$map, $value);
1472 }
1473 }
1474
1475 $rawSubscription = $this->getUserSubscriptionById($userId);
1476 $subscription = [''];
1477 foreach ($rawSubscription as $listId => $listData) {
1478 $currentList = $listData->name.' => ';
1479 if (intval($listData->status) === 1) {
1480 $currentList .= acym_translation('ACYM_SUBSCRIBED').' - '.$listData->subscription_date;
1481 } else {
1482 $currentList .= acym_translation('ACYM_UNSUBSCRIBED').' - '.$listData->unsubscribe_date;
1483 }
1484 $subscription[] = $currentList;
1485 }
1486 $mailerHelper->addParam('user:subscription', implode('<br/>', $subscription));
1487
1488 if (!empty($params)) {
1489 foreach ($params as $name => $value) {
1490 $mailerHelper->addParam($name, $value);
1491 }
1492 }
1493
1494 $mailClass = new MailClass();
1495 foreach ($notifyUsers as $oneUser) {
1496 if (!acym_isValidEmail($oneUser)) {
1497 continue;
1498 }
1499
1500 $notificationEmail = $mailClass->getOneByName($notification);
1501 if (!empty($notificationEmail)) {
1502 $mailerHelper->sendOne($notificationEmail->id, $oneUser);
1503 }
1504 }
1505 }
1506
1507 public function getMailHistory($userId)
1508 {
1509 $query = 'SELECT user_stat.*, mail.id, mail.subject, SUM(url_click.click) as click FROM #__acym_user_stat AS user_stat
1510 JOIN #__acym_mail AS mail ON mail.id = user_stat.mail_id
1511 LEFT JOIN #__acym_url_click AS url_click ON user_stat.mail_id = url_click.mail_id AND url_click.user_id = '.intval($userId).'
1512 WHERE user_stat.user_id = '.intval($userId).' GROUP BY mail_id ORDER BY send_date DESC LIMIT 50';
1513
1514 $mailHistory = acym_loadObjectList($query, 'mail_id');
1515 $mailClass = new MailClass();
1516
1517 return $mailClass->decode($mailHistory);
1518 }
1519
1520 public function deleteHistoryPeriod()
1521 {
1522 if (empty($this->config->get('delete_user_history_enabled', 0))) return;
1523 $deleteOverSecond = $this->config->get('delete_user_history', 0);
1524 if (empty($deleteOverSecond)) return;
1525 $date = time() - $deleteOverSecond;
1526
1527 $query = 'DELETE FROM #__acym_history WHERE date < '.intval($date);
1528
1529 try {
1530 $status = acym_query($query);
1531 $message = empty($status) ? '' : acym_translationSprintf('ACYM_DELETE_X_ROWS_TABLE_X', $status, strtolower(acym_translation('ACYM_USER_HISTORY')));
1532 } catch (\Exception $e) {
1533 $status = false;
1534 $message = $e->getMessage();
1535 }
1536
1537 return ['status' => $status !== false, 'message' => $message];
1538 }
1539
1540 public function resetSubscription($userIds, $lists)
1541 {
1542 if (empty($lists)) return false;
1543
1544 if (!is_array($userIds)) $userIds = [$userIds];
1545 if (!is_array($lists)) $lists = [$lists];
1546
1547 acym_arrayToInteger($userIds);
1548 acym_arrayToInteger($lists);
1549
1550 foreach ($userIds as $userId) {
1551 acym_query(
1552 'DELETE FROM `#__acym_user_has_list`
1553 WHERE user_id = '.$userId.'
1554 AND list_id IN ('.implode(',', $lists).')'
1555 );
1556
1557 acym_query(
1558 'DELETE FROM #__acym_queue WHERE user_id = '.$userId.' AND mail_id IN (
1559 SELECT followup_mail.mail_id FROM #__acym_followup_has_mail AS followup_mail
1560 JOIN #__acym_followup AS followup ON followup.id = followup_mail.followup_id AND followup.list_id IN ('.implode(',', $lists).')
1561 )'
1562 );
1563
1564 $historyClass = new HistoryClass();
1565 $historyData = acym_translationSprintf('ACYM_LISTS_NUMBERS', implode(', ', $lists));
1566 $historyClass->insert($userId, 'reset_subscription', [$historyData]);
1567 }
1568
1569 return true;
1570 }
1571
1572 public function addMissingKeys()
1573 {
1574 $usersMissingKey = acym_loadResultArray('SELECT `id` FROM #__acym_user WHERE `key` IS NULL OR `key` = ""');
1575 foreach ($usersMissingKey as $oneUserId) {
1576 acym_query('UPDATE #__acym_user SET `key` = '.acym_escapeDB(acym_generateKey(14)).' WHERE `id` = '.intval($oneUserId));
1577 }
1578
1579 return count($usersMissingKey);
1580 }
1581
1582 public function hasUserAccess($subscriberId): bool
1583 {
1584 $userId = acym_currentUserId();
1585 if (empty($userId)) {
1586 return false;
1587 }
1588
1589 if (acym_isAdmin()) {
1590 return true;
1591 }
1592
1593 $user = $this->getOneById($subscriberId);
1594 $userEmail = acym_currentUserEmail();
1595 if (!empty($user) && $user->email === $userEmail) {
1596 return true;
1597 }
1598
1599 $listClass = new ListClass();
1600 $allowedListIds = $listClass->getManageableLists();
1601 if (empty($allowedListIds)) {
1602 return false;
1603 }
1604
1605 $query = 'SELECT COUNT(*) FROM #__acym_user AS user
1606 JOIN #__acym_user_has_list AS map ON map.user_id = user.id
1607 WHERE map.list_id IN ('.implode(',', $allowedListIds).')
1608 AND user.id = '.intval($subscriberId);
1609
1610 return acym_loadResult($query) > 0;
1611 }
1612
1613 public function getXUsers(array $options = [])
1614 {
1615 $limit = $options['limit'] ?? 10;
1616 $offset = $options['offset'] ?? 0;
1617 $filters = $options['filters'] ?? [];
1618
1619 $conditions = [];
1620 foreach ($filters as $column => $filter) {
1621 switch ($column) {
1622 case 'id':
1623 case 'cms_id':
1624 case 'active':
1625 case 'confirmed':
1626 case 'tracking':
1627 $conditions[] = acym_secureDBColumn($column).' = '.intval($filter);
1628 break;
1629 default:
1630 $conditions[] = acym_secureDBColumn($column).' LIKE '.acym_escapeDB('%'.$filter.'%');
1631 }
1632 }
1633
1634 if (!empty($options['created_after'])) {
1635 $conditions[] = 'creation_date > '.acym_escapeDB($options['created_after']);
1636 }
1637
1638 if (!empty($options['confirmed_after'])) {
1639 $conditions[] = 'confirmation_date > '.acym_escapeDB($options['confirmed_after']);
1640 }
1641
1642 $query = 'SELECT * FROM #__acym_user';
1643 if (!empty($conditions)) {
1644 $query .= ' WHERE '.implode(' AND ', $conditions);
1645 }
1646
1647 return acym_loadObjectList($query, $this->pkey, $offset, $limit);
1648 }
1649 }
1650