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