PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 10.0.1
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v10.0.1
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.0.1, at back/Classes/UserClass.php

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