PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / trunk
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress vtrunk
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 10.3.0 10.4.0 All 60 releases
acymailing / front / FrontControllers / FrontusersController.php

FrontusersController.php in AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress trunk, at front/FrontControllers/FrontusersController.php

1,106 lines 39.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace AcyMailing\FrontControllers;
4
5 use AcyMailing\Classes\FieldClass;
6 use AcyMailing\Classes\ListClass;
7 use AcyMailing\Classes\MailClass;
8 use AcyMailing\Classes\ScenarioClass;
9 use AcyMailing\Classes\UserClass;
10 use AcyMailing\Classes\UserStatClass;
11 use AcyMailing\Controllers\UsersController;
12 use AcyMailing\Helpers\CaptchaHelper;
13 use AcyMailing\Helpers\EntitySelectHelper;
14 use AcyMailing\Helpers\ExportHelper;
15 use AcyMailing\Helpers\ToolbarHelper;
16 use AcyMailing\Helpers\UserHelper;
17 use AcyMailing\Core\AcymParameter;
18
19 class FrontusersController extends UsersController
20 {
21 public function __construct()
22 {
23 parent::__construct();
24
25 if (ACYM_CMS === 'joomla') {
26 $menu = acym_getMenu();
27 if (is_object($menu)) {
28 $params = method_exists($menu, 'getParams') ? $menu->getParams() : $menu->params;
29 $menuParams = new AcymParameter($params);
30 $this->menuClass = $menuParams->get('pageclass_sfx', '');
31 }
32 }
33
34 $this->publicFrontTasks = [
35 'subscribe',
36 'unsubscribe',
37 'unsubscribeAll',
38 'disableTracking',
39 'saveSubscriptions',
40 'unsubscribePage',
41 'confirm',
42 'profile',
43 'savechanges',
44 'exportdata',
45 'ajaxGetEnqueuedMessages',
46 'gdprDelete',
47 ];
48
49 }
50
51
52 private function displayMessage(string $message, bool $ajax, string $type = 'error'): void
53 {
54 if ($ajax) {
55 echo json_encode(
56 [
57 'message' => acym_translation($message),
58 'type' => $type,
59 'code' => '1',
60 ]
61 );
62 } else {
63 acym_header('Content-type:text/html; charset=utf-8');
64 echo '<script>alert("'.esc_html(acym_translation($message)).'"); window.history.go(-1);</script>';
65 }
66 exit;
67 }
68
69 public function subscribe(): void
70 {
71 acym_checkRobots();
72
73 $formData = acym_getVar('array', 'user', []);
74 if (!acym_getVar('string', 'acy_source') && !empty($formData)) {
75 // Coming from a subscription via url...
76 acym_setVar('acy_source', 'url');
77 }
78
79 // Do we have to return an ajax response or a web page ?
80 $ajax = acym_getVar('int', 'ajax', 0);
81 if ($ajax) {
82 //in case of the page displays some warnings or whatever
83 @ob_end_clean();
84 acym_header('Content-type:application/json; charset=utf-8');
85 }
86
87 //We only allow logged in users and this user is not logged it...
88 $currentUserid = acym_currentUserId();
89 if ((int)$this->config->get('allow_visitor', 1) != 1 && empty($currentUserid)) {
90 if ($ajax) {
91 echo json_encode(
92 [
93 'message' => acym_translation('ACYM_ONLY_LOGGED'),
94 'type' => 'error',
95 'code' => '0',
96 ]
97 );
98 exit;
99 } else {
100 acym_askLog(false, 'ACYM_ONLY_LOGGED');
101
102 return;
103 }
104 }
105
106 if (empty($currentUserid) && $this->config->get('captcha', 'none') !== 'none' && acym_level(ACYM_ESSENTIAL)) {
107 $captchaHelper = new CaptchaHelper();
108 if (!$captchaHelper->check()) {
109 $this->displayMessage('ACYM_WRONG_CAPTCHA', $ajax);
110 }
111 }
112
113 $user = new \stdClass();
114 if (!empty($formData['email'])) {
115 $user->email = $formData['email'];
116 }
117
118 $userClass = new UserClass();
119 if (empty($user->email)) {
120 $connectedUser = $userClass->identify(true, 'userId', 'userKey');
121 if (!empty($connectedUser->email)) {
122 $user->email = $connectedUser->email;
123 }
124 }
125 $user->email = trim($user->email);
126
127 // Check email validity
128 if (empty($user->email) || !acym_isValidEmail($user->email, true)) {
129 $this->displayMessage('ACYM_VALID_EMAIL', $ajax);
130 }
131
132 // E-mail is valid now...
133 // Check if this user already exists or not...
134 $alreadyExists = $userClass->getOneByEmail($user->email);
135 if (!empty($alreadyExists->id)) {
136 $user->id = $alreadyExists->id;
137 }
138
139 $successfullySaved = $userClass->saveForm($ajax);
140 $user->id = acym_getVar('int', 'userId');
141
142 if (!empty($userClass->errors)) {
143 $this->displayMessage(implode('<br /><br />', $userClass->errors), $ajax);
144 }
145
146 if (!$successfullySaved || empty($user->id)) {
147 $this->displayMessage('ACYM_ERROR_SAVE_USER', $ajax);
148 }
149
150 //We will now load back the user from the one we saved so that we will have all its infos
151 $myuser = $userClass->getOneById($user->id);
152 if (empty($myuser->id)) {
153 $this->displayMessage('ACYM_ERROR_SAVE_USER', $ajax);
154 }
155
156 $msgtype = 'success';
157 if (empty($myuser->confirmed) && $this->config->get('require_confirmation', 1) == 1) {
158 if ($userClass->confirmationSentSuccess || empty($userClass->confirmationSentError)) {
159 $msg = acym_stripTags(acym_getVar('string', 'confirmation_message', ''));
160 if (empty($msg)) {
161 $msg = 'ACYM_CONFIRMATION_SENT';
162 }
163 $code = 2;
164 } else {
165 $msg = $userClass->confirmationSentError;
166 $code = 7;
167 $msgtype = 'error';
168 }
169 } else {
170 if ($userClass->subscribed) {
171 $msg = acym_stripTags(acym_getVar('string', 'confirmation_message', ''));
172 if (empty($msg)) {
173 $msg = 'ACYM_SUBSCRIPTION_OK';
174 }
175 $code = 3;
176 } else {
177 $msg = 'ACYM_ALREADY_SUBSCRIBED';
178 $code = 5;
179 $enqueueMsgType = 'info';
180 }
181 }
182
183 // Replace tags inside the $msg and redirect link
184 $replace = [];
185 foreach ($myuser as $oneProp => $oneVal) {
186 $replace['{user:'.$oneProp.'}'] = $oneVal;
187 }
188
189 $msg = str_replace(array_keys($replace), $replace, acym_translation($msg));
190
191 if ($ajax) {
192 //Make sure the message has a valid format for Ajax... so the user can customize it the way he wants without breaking anything
193 $msg = str_replace(["\n", "\r", '"', '\\'], [' ', ' ', "'", '\\\\'], $msg);
194 echo json_encode(
195 [
196 'message' => $msg,
197 'type' => $msgtype,
198 'code' => $code,
199 ]
200 );
201 exit;
202 } else {
203 acym_enqueueMessage($msg, !empty($enqueueMsgType) ? $enqueueMsgType : $msgtype);
204 }
205
206 $redirectUrl = urldecode(acym_getVar('string', 'redirect', ''));
207 $redirectUrl = str_replace(array_keys($replace), $replace, $redirectUrl);
208 if (empty($redirectUrl)) {
209 $redirectUrl = acym_rootURI();
210 }
211
212 acym_redirect($redirectUrl, '', 'message', true);
213 }
214
215 private function unsubscribeDirectly(object $alreadyExists, bool $ajax): void
216 {
217 $userClass = new UserClass();
218 if ($this->config->get('allow_modif', 'data') === 'none') {
219 $currentUser = $userClass->identify(true, 'userId', 'userKey');
220 if (empty($currentUser->email)) {
221 $this->endUnsubscribe('ACYM_LOGIN', $ajax, 'error');
222
223 return;
224 }
225 }
226
227 if (empty($currentUser) || empty($currentUser->id)) {
228 $currentUser = $alreadyExists;
229 }
230
231 $visibleSubscription = acym_getVar('array', 'subscription', []);
232 $hiddenLists = trim(acym_getVar('string', 'hiddenlists', ''));
233 $hiddenSubscription = empty($hiddenLists) ? [] : explode(',', $hiddenLists);
234 $unsubscribeLists = array_merge($visibleSubscription, $hiddenSubscription);
235 acym_arrayToInteger($unsubscribeLists);
236
237 $mailId = acym_getVar('int', 'mail_id', 0);
238
239 // Protect against bots
240 if (!empty($mailId) && !empty($currentUser->id)) {
241 $userStatClass = new UserStatClass();
242 $userStat = $userStatClass->getOneByMailAndUserId($mailId, $currentUser->id);
243 $delay = $this->config->get('tracking_delay', 0);
244 if (acym_isRobot() || (!empty($userStat) && !empty($delay) && acym_getTimeFromUTCDate($userStat->send_date) > time() - $delay)) {
245 return;
246 }
247 }
248
249 if (empty($unsubscribeLists)) {
250 $mailClass = new MailClass();
251 $mailType = $mailClass->getMailType($mailId);
252 if (empty($currentUser) || empty($currentUser->id)) {
253 $currentUser = $userClass->identify(true, 'userId', 'userKey');
254 }
255
256 if ($mailType === $mailClass::TYPE_SCENARIO) {
257 $scenarioClass = new ScenarioClass();
258 $scenarioId = $scenarioClass->getScenarioIdByMailId($mailId);
259 if (!empty($currentUser->id)) {
260 $scenarioClass->markUserUnsubscribeFromScenario($currentUser->id, $scenarioId);
261 }
262 }
263 }
264
265 if (!empty($mailId)) {
266 $mailClass = new MailClass();
267 $unsubscribeLists = array_keys($mailClass->getAllListsByMailId($mailId));
268 }
269
270 if (empty($unsubscribeLists)) {
271 $msg = 'ACYM_NO_SUBSCRIPTION_LINKED_EMAIL';
272 } elseif (false === $userClass->unsubscribe([$alreadyExists->id], $unsubscribeLists)) {
273 $msg = 'ACYM_UNSUBSCRIPTION_NOT_IN_LIST';
274 } else {
275 $msg = 'ACYM_UNSUBSCRIPTION_OK';
276 }
277
278 if (!empty($mailType) && $mailType === $mailClass::TYPE_SCENARIO) {
279 $msg = 'ACYM_UNSUBSCRIPTION_SCENARIO_OK';
280 }
281
282
283 $this->endUnsubscribe($msg, $ajax);
284 }
285
286 private function endUnsubscribe(string $msg, bool $ajax, string $type = 'success'): void
287 {
288 $msg = acym_translation($msg);
289
290 if ($ajax) {
291 echo json_encode(
292 [
293 'message' => $msg,
294 'type' => $type,
295 'code' => '10',
296 ]
297 );
298 exit;
299 }
300 acym_enqueueMessage($msg, $type);
301
302 $redirectUrl = urldecode(acym_getVar('string', 'redirectunsub', ''));
303 if (empty($redirectUrl)) {
304 $redirectUrl = acym_rootURI();
305 }
306
307 acym_redirect($this->normalizeRedirectUrl($redirectUrl), '', 'message', true);
308 }
309
310 private function normalizeRedirectUrl(string $url): string
311 {
312 if ($url === '') {
313 return '';
314 }
315 if (preg_match('~^[a-z][a-z0-9+.\-]*://~i', $url)) {
316 return $url;
317 }
318 if (strpos($url, '/') === 0 || strpos($url, 'index.php') === 0) {
319 return $url;
320 }
321 if (preg_match('~^[^/?#]+\.[a-z]{2,}(?:[/?#]|$)~i', $url)) {
322 return 'https://'.$url;
323 }
324
325 return $url;
326 }
327
328 public function unsubscribe(): void
329 {
330 acym_checkRobots();
331 $userClass = new UserClass();
332
333 $redirectToUnsubPage = $this->config->get('unsubscribe_page', 1);
334 $direct = acym_getVar('int', 'direct', 0);
335
336 // Do we have to return an ajax response or a web page ?
337 $ajax = acym_getVar('int', 'ajax', 0);
338 if ($ajax) {
339 @ob_end_clean();
340 acym_header('Content-type:application/json; charset=utf-8');
341 }
342
343 $currentUserid = acym_currentUserId();
344 $user = $userClass->identify(true, 'userId', 'userKey');
345 if (empty($user)) {
346 $user = $userClass->identify(true, 'user_id', 'user_key');
347 $direct = 1;
348 }
349 if (empty($user) && empty($currentUserid) && $this->config->get('captcha', 'none') !== 'none' && acym_level(ACYM_ESSENTIAL)) {
350 $captchaClass = new CaptchaHelper();
351 if (!$captchaClass->check()) {
352 $this->displayMessage('ACYM_WRONG_CAPTCHA', $ajax);
353 }
354 }
355
356 $formData = acym_getVar('array', 'user', []);
357 $email = '';
358
359 if (!empty($formData['email'])) {
360 $email = trim(acym_stripTags($formData['email']));
361 } elseif (empty($user)) {
362 return;
363 } elseif (!empty($user->email)) {
364 $email = $user->email;
365 } elseif (!empty(acym_currentUserEmail())) {
366 $email = acym_currentUserEmail();
367 }
368
369 $currentEmail = acym_currentUserEmail();
370 if (empty($email) && !empty($currentEmail)) {
371 $email = $currentEmail;
372 }
373
374 // Check email validity
375 if (empty($email) || !acym_isValidEmail($email)) {
376 $this->displayMessage('ACYM_VALID_EMAIL', $ajax);
377 }
378
379 $alreadyExists = $userClass->getOneByEmail($email);
380
381 // User not found
382 if (empty($alreadyExists->id)) {
383 $this->displayMessage(acym_translationSprintf('ACYM_SUB_NOT_IN_LIST', $email), $ajax);
384 }
385
386 $fromModuleOrWidget = acym_getVar('string', 'acysubmode', '');
387 if ((!empty($fromModuleOrWidget) && in_array($fromModuleOrWidget, ['form_acym', 'mod_acym', 'widget_acym'])) || $ajax || !$redirectToUnsubPage || !empty($direct)) {
388 $this->unsubscribeDirectly($alreadyExists, $ajax);
389 } elseif ($redirectToUnsubPage && !$ajax && !$direct) {
390 $identifiedEmail = !empty($user->email) ? $user->email : $currentEmail;
391 if (empty($identifiedEmail) || strtolower($alreadyExists->email) !== strtolower($identifiedEmail)) {
392 $this->displayMessage('ACYM_NOT_ALLOWED_MODIFY_USER', $ajax);
393 }
394 $this->unsubscribePage($alreadyExists);
395 }
396 }
397
398 public function disableTracking(): void
399 {
400 acym_checkRobots();
401
402 // Do we have to return an ajax response or a web page ?
403 $ajax = acym_getVar('int', 'ajax', 0);
404 if ($ajax) {
405 @ob_end_clean();
406 acym_header('Content-type:application/json; charset=utf-8');
407 }
408
409 $userClass = new UserClass();
410 $user = $userClass->identify(true, 'userId', 'userKey');
411
412 if (empty($user->id)) {
413 $this->displayMessage('ACYM_USER_NOT_FOUND', $ajax);
414 }
415
416 // Protect against bots opening the link right after the email was sent
417 $mailId = acym_getVar('int', 'mail_id', 0);
418 if (!empty($mailId)) {
419 $userStatClass = new UserStatClass();
420 $userStat = $userStatClass->getOneByMailAndUserId($mailId, $user->id);
421 $delay = $this->config->get('tracking_delay', 0);
422 if (acym_isRobot() || (!empty($userStat) && !empty($delay) && acym_getTimeFromUTCDate($userStat->send_date) > time() - $delay)) {
423 return;
424 }
425 }
426
427 if ((int)$user->tracking !== 0) {
428 $userClass->disableTracking($user, $mailId);
429 }
430
431 $this->endDisableTracking('ACYM_TRACKING_DISABLED_OK', $ajax);
432 }
433
434 private function endDisableTracking(string $msg, bool $ajax, string $type = 'success'): void
435 {
436 $msg = acym_translation($msg);
437
438 if ($ajax) {
439 echo json_encode(
440 [
441 'message' => $msg,
442 'type' => $type,
443 'code' => '10',
444 ]
445 );
446 exit;
447 }
448 acym_enqueueMessage($msg, $type);
449
450 $redirectUrl = urldecode(acym_getVar('string', 'redirectunsub', ''));
451 if (empty($redirectUrl)) {
452 $redirectUrl = acym_rootURI();
453 }
454
455 acym_redirect($this->normalizeRedirectUrl($redirectUrl), '', 'message', true);
456 }
457
458 private function getUserFromUnsubPage(): object
459 {
460 $userID = acym_getVar('int', 'user_id');
461 $userClass = new UserClass();
462
463 $user = $userClass->getOneById($userID);
464
465 if (empty($user)) {
466 acym_enqueueMessage(acym_translation('ACYM_USER_NOT_FOUND'), 'error');
467
468 acym_redirect(acym_rootURI());
469 }
470
471 return $user;
472 }
473
474 private function unsubscribeAllInner(bool $update = false): void
475 {
476 $userClass = new UserClass();
477 $user = $this->getUserFromUnsubPage();
478 $redirectUrl = urldecode(acym_getVar('string', 'redirectunsub', ''));
479 if (empty($redirectUrl)) {
480 $redirectUrl = $this->config->get('unsub_redirect_url', '');
481 }
482
483 $allLists = $userClass->getUserSubscriptionById($user->id);
484 if (empty($allLists)) {
485 acym_enqueueMessage(acym_translation('ACYM_NOT_SUBSCRIBED_ANY_LIST'), 'info');
486 acym_redirect($this->normalizeRedirectUrl(!empty($redirectUrl) ? $redirectUrl : acym_rootURI()), '', 'message', true);
487 }
488
489 $lists = [];
490 foreach ($allLists as $list) {
491 if (!$update || $list->visible == 1) {
492 $lists[] = $list->id;
493 }
494 }
495
496 $userClass->sendNotification($user->id, 'acy_notification_unsuball');
497 // A notification is sent when a user unsubscribes from a list, be we already sent a notification for this
498 $userClass->blockNotifications = true;
499 $userClass->unsubscribe([$user->id], $lists);
500 }
501
502 private function redirectUnsubWorked(): void
503 {
504 acym_enqueueMessage(acym_translation('ACYM_SUBSCRIPTION_UPDATED_OK'));
505 $redirectUrl = urldecode(acym_getVar('string', 'redirectunsub', ''));
506 if (empty($redirectUrl)) {
507 $redirectUrl = $this->config->get('unsub_redirect_url', '');
508 }
509 if (!empty($redirectUrl)) {
510 acym_redirect($this->normalizeRedirectUrl($redirectUrl), '', 'message', true);
511 } else {
512 acym_redirect(acym_rootURI());
513 }
514 }
515
516 public function unsubscribeAll(): void
517 {
518 $userClass = new UserClass();
519 if (empty($userClass->identify(true, 'user_id', 'user_key'))) {
520 acym_enqueueMessage(acym_translation('ACYM_USER_NOT_FOUND'), 'error');
521
522 acym_redirect(acym_rootURI());
523 }
524
525 $displayCheckedLists = acym_getVar('string', 'displayed_checked_lists', '');
526 if (!empty($displayCheckedLists)) {
527 $user = $this->getUserFromUnsubPage();
528 $userClass->unsubscribe([$user->id], explode(',', $displayCheckedLists));
529 } else {
530 $this->unsubscribeAllInner();
531 }
532
533 $ajax = acym_getVar('int', 'ajax', 0);
534 if (!$ajax) {
535 $this->redirectUnsubWorked();
536 }
537 }
538
539 public function saveSubscriptions(): void
540 {
541 $userClass = new UserClass();
542 if (empty($userClass->identify(true, 'user_id', 'user_key'))) {
543 $this->displayMessage('ACYM_USER_NOT_FOUND', false);
544 }
545
546 // Get the user
547 $user = $this->getUserFromUnsubPage();
548 // Get the list the user want to sub
549 $listsChecked = acym_getVar('array', 'lists', []);
550 $listsChecked = array_filter($listsChecked, function ($status) {
551 return intval($status) === 1;
552 });
553 $listsChecked = array_keys($listsChecked);
554 // Get the displayed lists
555 $displayedCheckedLists = explode(',', acym_getVar('string', 'displayed_checked_lists', ''));
556 // We get the user subscriptions
557 $userSubscriptions = $userClass->getUserSubscriptionById($user->id);
558
559 // We subscribe the user to the checked lists
560 $userClass->subscribe([$user->id], $listsChecked);
561
562 // We unsub to the unchecked lists
563 $listsToUnsub = [];
564 foreach ($userSubscriptions as $subscription) {
565 // The list wasn't checked && the list is displayed && the user is subscribed to it
566 if (
567 !in_array($subscription->id, $listsChecked)
568 && in_array($subscription->id, $displayedCheckedLists)
569 && intval($subscription->status) === 1
570 ) {
571 $listsToUnsub[] = $subscription->id;
572 }
573 }
574
575 $userClass->unsubscribe([$user->id], $listsToUnsub);
576 $this->redirectUnsubWorked();
577 }
578
579 public function unsubscribePage(object $alreadyExists): void
580 {
581 $userClass = new UserClass();
582 $lang = acym_getVar('string', 'language', acym_getLanguageTag());
583 $mailId = acym_getVar('int', 'mail_id', 0);
584 $campaignListOnly = $this->config->get('unsubscribe_campaign_list_only', '0') === '1';
585 $displaySurvey = $this->config->get('unsubpage_survey', '0') === '1';
586 $surveyAnswers = $this->config->get('unsub_survey', '[]');
587 $unsubscribeColor = $this->config->get('unsubscribe_color');
588 if (empty($unsubscribeColor)) {
589 $unsubscribeColor = '#0079d3';
590 }
591 $hoverColor = $this->darkenRGBColor($unsubscribeColor, 20);
592
593 $surveyAnswers = json_decode($surveyAnswers, true);
594
595 $languageCode = acym_getVar('string', 'language');
596 if (ACYM_CMS === 'joomla' && !empty($languageCode)) {
597 $lang = $languageCode;
598 }
599 acym_setLanguage($lang);
600 acym_loadLanguage($lang);
601
602 $userSubscriptions = $userClass->getUserSubscriptionById($alreadyExists->id, 'id', false, false, true, false, $mailId, $campaignListOnly);
603
604 $data = [
605 'user' => $alreadyExists,
606 'mail_id' => $mailId,
607 'subscriptions' => $userSubscriptions,
608 'lang' => $lang,
609 ];
610
611 if (acym_isMultilingual()) {
612 $allLanguages = acym_getLanguages(false, true);
613 if ($this->config->get('unsubpage_languages_multi_only', '0') === '1') {
614 $allLanguages = acym_getMultilingualLanguages();
615 }
616
617 $data['languages'] = [];
618 foreach ($allLanguages as $key => $language) {
619 $data['languages'][$key] = $language->name;
620 }
621
622 $surveyAnswersTranslations = $this->config->get('unsub_survey_translation', '');
623
624 if (!empty($surveyAnswersTranslations)) {
625 $surveyAnswersTranslations = json_decode($surveyAnswersTranslations, true);
626 if (isset($surveyAnswersTranslations[$data['lang']])) {
627 $surveyAnswers = $surveyAnswersTranslations[$data['lang']]['unsub_survey'];
628 }
629 }
630 }
631
632 if ($displaySurvey) {
633 $data['surveyAnswers'] = $surveyAnswers;
634 array_unshift($data['surveyAnswers'], acym_translation('ACYM_SELECT_REASON'));
635 $data['surveyAnswers'][] = acym_translation('ACYM_OTHER');
636 $data['surveyAnswers'] = array_combine($data['surveyAnswers'], $data['surveyAnswers']);
637 }
638
639 $data['unsubscribeColor'] = $unsubscribeColor;
640 $data['hoverColor'] = $hoverColor;
641 $data['svgImage'] = $this->getSVGImage($unsubscribeColor, $hoverColor);
642
643 acym_setVar('layout', 'unsubscribepage');
644 acym_header('Content-Type: text/html; charset=utf-8');
645
646 parent::display($data);
647 }
648
649 private function getSVGImage(string $unsubscribeColor, string $hoverColor): string
650 {
651 if ($this->config->get('display_unsub_image') === '1') {
652 $svgPath = ACYM_IMAGES.'unsubscribe/unsub_image.svg';
653
654 $svgContent = acym_fileGetContent($svgPath);
655
656 return str_replace(
657 ['#B118C8', '#C794CF'],
658 [$unsubscribeColor, $hoverColor],
659 $svgContent
660 );
661 }
662
663 return '';
664 }
665
666 public function hexToRGB(string $hexColor): array
667 {
668 $hexColor = ltrim($hexColor, '#');
669
670 if (strlen($hexColor) === 3) {
671 $hexColor = $hexColor[0].$hexColor[0].$hexColor[1].$hexColor[1].$hexColor[2].$hexColor[2];
672 }
673
674 $r = hexdec(substr($hexColor, 0, 2));
675 $g = hexdec(substr($hexColor, 2, 2));
676 $b = hexdec(substr($hexColor, 4, 2));
677
678 return [$r, $g, $b];
679 }
680
681 public function darkenRGBColor(string $hexColor, int $percent): string
682 {
683 $rgbValues = $this->hexToRGB($hexColor);
684
685 $darkenedRGB = array_map(function ($value) use ($percent) {
686 return max(0, $value - ($value * $percent / 100));
687 }, $rgbValues);
688
689 return sprintf('rgb(%d, %d, %d)', $darkenedRGB[0], $darkenedRGB[1], $darkenedRGB[2]);
690 }
691
692 public function confirm(): void
693 {
694 if (acym_isRobot()) {
695 return;
696 }
697
698 // We identify the user
699 $userClass = new UserClass();
700 $user = $userClass->identify(true, 'userId', 'userKey');
701 if (empty($user)) {
702 acym_enqueueMessage(acym_translation('ACYM_USER_NOT_FOUND'), 'error');
703 acym_redirect(acym_rootURI());
704
705 return;
706 }
707
708 if ($this->config->get('confirmation_message', 1)) {
709 if ($user->confirmed) {
710 acym_enqueueMessage(acym_translation('ACYM_ALREADY_CONFIRMED'), 'info');
711 } else {
712 acym_enqueueMessage(acym_translation('ACYM_SUBSCRIPTION_CONFIRMED'), 'success');
713 }
714 }
715
716 // Now we can really confirm the user
717 if (!$user->confirmed) {
718 $userClass->confirm($user->id);
719 }
720
721 // Get the redirect url from the configuration page if specified
722 $redirectUrl = $this->config->get('confirm_redirect');
723 if (!empty($redirectUrl)) {
724 $replace = [];
725 // We replace tags in case of the user added some dynamic information to its url.
726 foreach ($user as $key => $val) {
727 $replace['{user:'.$key.'}'] = $val;
728 }
729 $redirectUrl = str_replace(array_keys($replace), $replace, $redirectUrl);
730 acym_redirect($redirectUrl, '', 'message', false);
731
732 return;
733 }
734
735 acym_redirect(acym_rootURI());
736 }
737
738 public function profile(): void
739 {
740 $userClass = new UserClass();
741 $user = $userClass->identify(true);
742
743 if (empty($user)) {
744 // Check if the subscription is allowed for non registered users
745 $allowvisitor = $this->config->get('allow_visitor', 1);
746 if (empty($allowvisitor)) {
747 acym_askLog(true, 'ACYM_ONLY_LOGGED', 'message');
748
749 return;
750 }
751 }
752
753 global $acymEmailMisspelledLoaded;
754 $spellChecker = empty($acymEmailMisspelledLoaded) && !empty($this->config->get('email_spellcheck'));
755 if ($spellChecker) $acymEmailMisspelledLoaded = true;
756
757 if ($spellChecker) {
758 acym_addStyle(false, ACYM_CSS.'libraries/email-misspelled.min.css?v='.filemtime(ACYM_MEDIA.'css'.DS.'libraries'.DS.'email-misspelled.min.css'));
759 acym_addScript(false, ACYM_JS.'libraries/email-misspelled.min.js?v='.filemtime(ACYM_MEDIA.'js'.DS.'libraries'.DS.'email-misspelled.min.js'));
760 }
761 acym_addScript(false, ACYM_JS.'module.min.js?v='.filemtime(ACYM_MEDIA.'js'.DS.'module.min.js'));
762
763 // Get the page parameters
764 $params = new \stdClass();
765 $menu = acym_getMenu();
766 if (is_object($menu)) {
767 $params->source = 'Menu n°'.$menu->id;
768 $menuParameters = method_exists($menu, 'getParams') ? $menu->getParams() : $menu->params;
769 $menuparams = new AcymParameter($menuParameters);
770
771 if (!empty($menuparams)) {
772 // Joomla specific params
773 $params->suffix = $menuparams->get('pageclass_sfx', '');
774 $params->page_heading = $menuparams->get('page_heading');
775 $params->show_page_heading = $menuparams->get('show_page_heading', 0);
776
777 if ($menuparams->get('menu-meta_description')) {
778 acym_addMetadata('description', $menuparams->get('menu-meta_description'));
779 }
780 if ($menuparams->get('menu-meta_keywords')) {
781 acym_addMetadata('keywords', $menuparams->get('menu-meta_keywords'));
782 }
783 if ($menuparams->get('robots')) {
784 acym_addMetadata('robots', $menuparams->get('robots'));
785 }
786
787 // Our params
788 $params->lists = $menuparams->get('lists', 'none');
789 $params->listschecked = $menuparams->get('listschecked', 'none');
790 $params->dropdown = $menuparams->get('dropdown');
791 $params->hiddenlists = $menuparams->get('hiddenlists', 'None');
792 $params->fields = $menuparams->get('fields');
793 $params->introtext = $menuparams->get('introtext');
794 $params->posttext = $menuparams->get('posttext');
795 $params->page_title = $menuparams->get('page_title', '');
796 }
797 }
798 $data = $this->prepareParams($params);
799
800 if (empty($data['user']->language)) {
801 $cmsUserLanguage = acym_getCmsUserLanguage();
802 $data['user']->language = empty($cmsUserLanguage) ? acym_getLanguageTag() : $cmsUserLanguage;
803 }
804
805 $data['captchaHelper'] = new CaptchaHelper();
806
807 acym_setVar('layout', 'profile');
808 parent::display($data);
809 }
810
811 public function prepareParams(object $values): array
812 {
813 //TODO problem with the types throughout the entire method
814 if (!isset($values->lists)) {
815 $values->lists = 'none';
816 }
817 if (!isset($values->listschecked)) {
818 $values->listschecked = 'none';
819 }
820 if (!isset($values->dropdown)) {
821 $values->dropdown = 0;
822 }
823 if (!isset($values->hiddenlists)) {
824 $values->hiddenlists = 'None';
825 }
826 if (empty($values->fields)) {
827 $values->fields = [2];
828 } elseif (is_string($values->fields)) {
829 $values->fields = explode(',', $values->fields);
830 }
831 if (!in_array('2', $values->fields) && !in_array(2, $values->fields)) {
832 $values->fields[] = '2';
833 }
834 if (!isset($values->page_heading)) {
835 $values->page_heading = '';
836 }
837 if (!isset($values->introtext)) {
838 $values->introtext = '';
839 }
840 if (!isset($values->posttext)) {
841 $values->posttext = '';
842 }
843
844 foreach (['lists', 'listschecked', 'hiddenlists', 'fields'] as $option) {
845 if (is_string($values->$option)) {
846 $values->$option = explode(',', $values->$option);
847 }
848 }
849
850 if ((empty($values->lists) || in_array('None', $values->lists)) && (empty($values->hiddenlists) || strtolower(implode('', $values->hiddenlists)) == 'none')) {
851 $values->lists[] = 'All';
852 }
853
854 // Get the current user and his subscription
855 $userClass = new UserClass();
856 $user = $userClass->identify(true);
857 if (empty($user)) {
858 $listClass = new ListClass();
859 $subscription = $listClass->getAll('id');
860
861 if (acym_isMultilingual()) {
862 $subscription = $listClass->getTranslatedNameDescription($subscription);
863 }
864
865 $user = new \stdClass();
866 $user->id = 0;
867 $user->key = 0;
868
869 if (!empty($subscription)) {
870 foreach ($subscription as $id => $onesub) {
871 $subscription[$id]->status = 1;
872 if (strtolower(implode('', $values->listschecked)) != 'all' && !in_array($id, $values->listschecked)) {
873 $subscription[$id]->status = -1;
874 }
875 }
876 }
877
878 acym_addBreadcrumb(acym_translation('ACYM_SUBSCRIPTION'));
879 if (empty($menu)) {
880 acym_setPageTitle(acym_translation('ACYM_SUBSCRIPTION'));
881 }
882 } else {
883 $subscription = $userClass->getAllListsUserSubscriptionById($user->id, 'id', true);
884
885 acym_addBreadcrumb(acym_translation('ACYM_MODIFY_SUBSCRIPTION'));
886 if (empty($menu)) {
887 acym_setPageTitle(acym_translation('ACYM_MODIFY_SUBSCRIPTION'));
888 }
889 }
890
891 if (!empty($values->page_title)) {
892 acym_addBreadcrumb($values->page_title);
893 if (empty($menu)) {
894 acym_setPageTitle($values->page_title);
895 }
896 }
897
898 $allLists = $subscription;
899
900 acym_initModule();
901
902 if (!empty($values->lists) && strtolower(implode('', $values->lists)) != 'all') {
903 if (in_array('None', $values->lists)) {
904 $subscription = [];
905 } else {
906 $newSubscription = [];
907 foreach ($subscription as $id => $onesub) {
908 if (in_array($id, $values->lists)) {
909 $newSubscription[$id] = $onesub;
910 }
911 }
912 $subscription = $newSubscription;
913 }
914 }
915
916 if (!empty($values->hiddenlists)) {
917 $hiddenListsArray = [];
918 if (strtolower(implode('', $values->hiddenlists)) == 'all') {
919 $subscription = [];
920 foreach ($allLists as $oneList) {
921 if (!empty($oneList->active)) {
922 $hiddenListsArray[] = $oneList->id;
923 }
924 }
925 } elseif (strtolower(implode('', $values->hiddenlists)) != 'none') {
926 foreach ($allLists as $oneList) {
927 if (!$oneList->active || !in_array($oneList->id, $values->hiddenlists)) {
928 continue;
929 }
930 $hiddenListsArray[] = $oneList->id;
931 unset($subscription[$oneList->id]);
932 }
933 }
934 $values->hiddenlists = $hiddenListsArray;
935 }
936
937 // Overriding the list with the URL parameters: &listid= for displayed list, &hiddenlist= for hidden lists
938 $defaultSubscription = $subscription;
939 $forceLists = acym_getVar('string', 'listid', '');
940 if (!empty($forceLists)) {
941 $subscription = [];
942 $forceLists = explode(',', $forceLists);
943 foreach ($forceLists as $oneList) {
944 if (!empty($defaultSubscription[$oneList])) {
945 $subscription[$oneList] = $defaultSubscription[$oneList];
946 }
947 }
948 }
949 $forceHiddenLists = acym_getVar('string', 'hiddenlist', '');
950 if (!empty($forceHiddenLists)) {
951 $forceHiddenLists = explode(',', $forceHiddenLists);
952 $tmpList = [];
953 foreach ($forceHiddenLists as $oneList) {
954 if (!empty($defaultSubscription[$oneList]) || in_array($oneList, $values->hiddenlists)) {
955 $tmpList[] = $oneList;
956 }
957 }
958 $values->hiddenlists = $tmpList;
959 }
960
961 $displayLists = false;
962 foreach ($subscription as $oneSub) {
963 if (!empty($oneSub->active) && $oneSub->visible) {
964 $displayLists = true;
965 break;
966 }
967 }
968
969 $fieldClass = new FieldClass();
970 $allfields = $fieldClass->getFieldsByID($values->fields);
971 $fields = [];
972 foreach ($allfields as $field) {
973 if (intval($field->active) === 0) continue;
974 $fields[$field->id] = $field;
975 }
976 $values->fields = $fields;
977
978 $data = [
979 'config' => $this->config,
980 'displayLists' => $displayLists,
981 'user' => $user,
982 'subscription' => $subscription,
983 'fieldClass' => $fieldClass,
984 ];
985
986 foreach ($values as $key => $value) {
987 $data[$key] = $value;
988 }
989
990 return $data;
991 }
992
993 public function savechanges(): void
994 {
995 wp_verify_nonce(acym_getVar('cmd', '_wpnonce'), 'acymnonce') || die('Invalid Token');
996 acym_checkRobots();
997
998 $userClass = new UserClass();
999
1000
1001 $status = $userClass->saveForm(true);
1002 if ($status) {
1003 if ($userClass->confirmationSentSuccess) {
1004 $this->displayMessage('ACYM_CONFIRMATION_SENT', true, 'success');
1005 } elseif ($userClass->newUser) {
1006 $this->displayMessage('ACYM_SUBSCRIPTION_OK', true, 'success');
1007 } else {
1008 $this->displayMessage('ACYM_SUBSCRIPTION_UPDATED_OK', true, 'success');
1009 }
1010 } elseif (!empty($userClass->requireId)) {
1011 $this->displayMessage('ACYM_IDENTIFICATION_SENT', true, 'success');
1012 } elseif (!empty($userClass->errors)) {
1013 $this->displayMessage(implode('<br/>', $userClass->errors), true);
1014 } else {
1015 $this->displayMessage('ACYM_ERROR_SAVING', true);
1016 }
1017
1018 exit;
1019 }
1020
1021 public function exportdata(): void
1022 {
1023 wp_verify_nonce(acym_getVar('cmd', '_wpnonce'), 'acymnonce') || die('Invalid Token');
1024
1025 $userClass = new UserClass();
1026 $user = $userClass->identify(true, 'userId', 'userKey');
1027
1028 if (empty($user->id)) {
1029 acym_redirect(acym_rootURI());
1030 }
1031
1032 $userHelper = new UserHelper();
1033 $userData = $userHelper->getUserData($user);
1034 $exportData = [];
1035 foreach ($userData as $oneCategory) {
1036 if (!isset($exportData[$oneCategory['group_label']])) {
1037 $exportData[$oneCategory['group_label']] = [];
1038 }
1039
1040 $exportData[$oneCategory['group_label']][] = $oneCategory['data'];
1041 }
1042
1043 $exportFiles = [
1044 [
1045 'name' => 'data.json',
1046 'data' => json_encode($exportData),
1047 ],
1048 ];
1049
1050 $tempFolder = ACYM_MEDIA.'tmp'.DS;
1051 acym_createArchive($tempFolder.'export_data_user_'.$user->id, $exportFiles);
1052
1053 $exportHelper = new ExportHelper();
1054 $exportHelper->setDownloadHeaders('export_data_user_'.$user->id, 'zip');
1055 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_readfile -- Downloading a file, better than WP functions for memory handling.
1056 readfile($tempFolder.'export_data_user_'.$user->id.'.zip');
1057
1058 // Avoid issue when user cancels the download
1059 ignore_user_abort(true);
1060 acym_deleteFile($tempFolder.'export_data_user_'.$user->id.'.zip');
1061 exit;
1062 }
1063
1064 public function gdprDelete(): void
1065 {
1066 wp_verify_nonce(acym_getVar('cmd', '_wpnonce'), 'acymnonce') || die('Invalid Token');
1067
1068 $userClass = new UserClass();
1069 $user = $userClass->identify(true, 'userId', 'userKey');
1070 if (empty($user->id)) {
1071 acym_redirect(acym_rootURI());
1072 }
1073
1074 $userClass->delete([$user->id], true);
1075
1076 acym_redirect(acym_rootURI(), 'ACYM_GDPR_DATA_DELETED');
1077 }
1078
1079 public function ajaxGetEnqueuedMessages(): void
1080 {
1081 $output = '';
1082 $types = ['success', 'info', 'warning', 'error'];
1083 foreach ($types as $type) {
1084 $messages = acym_getVar('array', 'acymessage'.$type, [], 'SESSION', ACYM_ALLOWHTML);
1085 if (empty($messages)) {
1086 continue;
1087 }
1088
1089 if (!is_array($messages)) {
1090 $messages = [$messages];
1091 }
1092
1093 $output .= '<div class="acym_callout acym__callout__front__'.esc_attr($type).'" role="alert">';
1094 $output .= '<div>'.implode(' ', $messages).'</div>';
1095 $output .= '<button class="acym_callout_close" aria-label="'.esc_attr(acym_translation('ACYM_CLOSE_NOTIFICATION')).'">x</button></div>';
1096
1097 acym_setSession('acymessage'.$type, null, true);
1098 }
1099
1100 if (!empty($output)) {
1101 $output = '<div id="acym__callout__container">'.$output.'</div>';
1102 }
1103 acym_sendAjaxResponse('', ['messages' => $output]);
1104 }
1105 }
1106