PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 11.0.0
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v11.0.0
11.1.1 11.1.0 11.0.5 11.0.4 11.0.3 11.0.2 11.0.1 11.0.0 10.11.1 10.11.0 10.10.2 10.10.1 10.10.0 10.9.1 trunk 10.0.0 10.0.1 10.1.0 10.1.1 10.1.2 10.1.3 10.1.4 10.2.0 10.2.1 10.2.2 All 62 releases
acymailing / front / FrontControllers / FrontusersController.php

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

1,104 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', '#00a4ff');
588 $hoverColor = $this->darkenRGBColor($unsubscribeColor, 20);
589
590 $surveyAnswers = json_decode($surveyAnswers, true);
591
592 $languageCode = acym_getVar('string', 'language');
593 if (ACYM_CMS === 'joomla' && !empty($languageCode)) {
594 $lang = $languageCode;
595 }
596 acym_setLanguage($lang);
597 acym_loadLanguage($lang);
598
599 $userSubscriptions = $userClass->getUserSubscriptionById($alreadyExists->id, 'id', false, false, true, false, $mailId, $campaignListOnly);
600
601 $data = [
602 'user' => $alreadyExists,
603 'mail_id' => $mailId,
604 'subscriptions' => $userSubscriptions,
605 'lang' => $lang,
606 ];
607
608 if (acym_isMultilingual()) {
609 $allLanguages = acym_getLanguages(false, true);
610 if ($this->config->get('unsubpage_languages_multi_only', '0') === '1') {
611 $allLanguages = acym_getMultilingualLanguages();
612 }
613
614 $data['languages'] = [];
615 foreach ($allLanguages as $key => $language) {
616 $data['languages'][$key] = $language->name;
617 }
618
619 $surveyAnswersTranslations = $this->config->get('unsub_survey_translation', '');
620
621 if (!empty($surveyAnswersTranslations)) {
622 $surveyAnswersTranslations = json_decode($surveyAnswersTranslations, true);
623 if (isset($surveyAnswersTranslations[$data['lang']])) {
624 $surveyAnswers = $surveyAnswersTranslations[$data['lang']]['unsub_survey'];
625 }
626 }
627 }
628
629 if ($displaySurvey) {
630 $data['surveyAnswers'] = $surveyAnswers;
631 array_unshift($data['surveyAnswers'], acym_translation('ACYM_SELECT_REASON'));
632 $data['surveyAnswers'][] = acym_translation('ACYM_OTHER');
633 $data['surveyAnswers'] = array_combine($data['surveyAnswers'], $data['surveyAnswers']);
634 }
635
636 if (!empty($this->config->get('unsubscribe_color'))) {
637 $unsubscribeColor = $this->config->get('unsubscribe_color');
638 $hoverColor = $this->darkenRGBColor($unsubscribeColor, 20);
639 $data['unsubscribeColor'] = $unsubscribeColor;
640 $data['hoverColor'] = $hoverColor;
641 }
642 $data['svgImage'] = $this->getSVGImage($unsubscribeColor, $hoverColor);
643
644 acym_setVar('layout', 'unsubscribepage');
645 acym_header('Content-Type: text/html; charset=utf-8');
646
647 parent::display($data);
648 }
649
650 private function getSVGImage(string $unsubscribeColor, string $hoverColor): string
651 {
652 if ($this->config->get('display_unsub_image') === '1') {
653 $svgPath = ACYM_IMAGES.'unsubscribe/unsub_image.svg';
654
655 $svgContent = acym_fileGetContent($svgPath);
656
657 return str_replace(
658 ['#B118C8', '#C794CF'],
659 [$unsubscribeColor, $hoverColor],
660 $svgContent
661 );
662 }
663
664 return '';
665 }
666
667 public function hexToRGB(string $hexColor): array
668 {
669 $hexColor = ltrim($hexColor, '#');
670
671 if (strlen($hexColor) === 3) {
672 $hexColor = $hexColor[0].$hexColor[0].$hexColor[1].$hexColor[1].$hexColor[2].$hexColor[2];
673 }
674
675 $r = hexdec(substr($hexColor, 0, 2));
676 $g = hexdec(substr($hexColor, 2, 2));
677 $b = hexdec(substr($hexColor, 4, 2));
678
679 return [$r, $g, $b];
680 }
681
682 public function darkenRGBColor(string $hexColor, int $percent): string
683 {
684 $rgbValues = $this->hexToRGB($hexColor);
685
686 $darkenedRGB = array_map(function ($value) use ($percent) {
687 return max(0, $value - ($value * $percent / 100));
688 }, $rgbValues);
689
690 return sprintf('rgb(%d, %d, %d)', $darkenedRGB[0], $darkenedRGB[1], $darkenedRGB[2]);
691 }
692
693 public function confirm(): void
694 {
695 if (acym_isRobot()) {
696 return;
697 }
698
699 // We identify the user
700 $userClass = new UserClass();
701 $user = $userClass->identify(true, 'userId', 'userKey');
702 if (empty($user)) {
703 acym_enqueueMessage(acym_translation('ACYM_USER_NOT_FOUND'), 'error');
704 acym_redirect(acym_rootURI());
705
706 return;
707 }
708
709 if ($this->config->get('confirmation_message', 1)) {
710 if ($user->confirmed) {
711 acym_enqueueMessage(acym_translation('ACYM_ALREADY_CONFIRMED'), 'info');
712 } else {
713 acym_enqueueMessage(acym_translation('ACYM_SUBSCRIPTION_CONFIRMED'), 'success');
714 }
715 }
716
717 // Now we can really confirm the user
718 if (!$user->confirmed) {
719 $userClass->confirm($user->id);
720 }
721
722 // Get the redirect url from the configuration page if specified
723 $redirectUrl = $this->config->get('confirm_redirect');
724 if (!empty($redirectUrl)) {
725 $replace = [];
726 // We replace tags in case of the user added some dynamic information to its url.
727 foreach ($user as $key => $val) {
728 $replace['{user:'.$key.'}'] = $val;
729 }
730 $redirectUrl = str_replace(array_keys($replace), $replace, $redirectUrl);
731 acym_redirect($redirectUrl, '', 'message', false);
732
733 return;
734 }
735
736 acym_redirect(acym_rootURI());
737 }
738
739 public function profile(): void
740 {
741 $userClass = new UserClass();
742 $user = $userClass->identify(true);
743
744 if (empty($user)) {
745 // Check if the subscription is allowed for non registered users
746 $allowvisitor = $this->config->get('allow_visitor', 1);
747 if (empty($allowvisitor)) {
748 acym_askLog(true, 'ACYM_ONLY_LOGGED', 'message');
749
750 return;
751 }
752 }
753
754 global $acymEmailMisspelledLoaded;
755 $spellChecker = empty($acymEmailMisspelledLoaded) && !empty($this->config->get('email_spellcheck'));
756 if ($spellChecker) $acymEmailMisspelledLoaded = true;
757
758 if ($spellChecker) {
759 acym_addStyle(false, ACYM_CSS.'libraries/email-misspelled.min.css?v='.filemtime(ACYM_MEDIA.'css'.DS.'libraries'.DS.'email-misspelled.min.css'));
760 acym_addScript(false, ACYM_JS.'libraries/email-misspelled.min.js?v='.filemtime(ACYM_MEDIA.'js'.DS.'libraries'.DS.'email-misspelled.min.js'));
761 }
762 acym_addScript(false, ACYM_JS.'module.min.js?v='.filemtime(ACYM_MEDIA.'js'.DS.'module.min.js'));
763
764 // Get the page parameters
765 $params = new \stdClass();
766 $menu = acym_getMenu();
767 if (is_object($menu)) {
768 $params->source = 'Menu n°'.$menu->id;
769 $menuParameters = method_exists($menu, 'getParams') ? $menu->getParams() : $menu->params;
770 $menuparams = new AcymParameter($menuParameters);
771
772 if (!empty($menuparams)) {
773 // Joomla specific params
774 $params->suffix = $menuparams->get('pageclass_sfx', '');
775 $params->page_heading = $menuparams->get('page_heading');
776 $params->show_page_heading = $menuparams->get('show_page_heading', 0);
777
778 if ($menuparams->get('menu-meta_description')) {
779 acym_addMetadata('description', $menuparams->get('menu-meta_description'));
780 }
781 if ($menuparams->get('menu-meta_keywords')) {
782 acym_addMetadata('keywords', $menuparams->get('menu-meta_keywords'));
783 }
784 if ($menuparams->get('robots')) {
785 acym_addMetadata('robots', $menuparams->get('robots'));
786 }
787
788 // Our params
789 $params->lists = $menuparams->get('lists', 'none');
790 $params->listschecked = $menuparams->get('listschecked', 'none');
791 $params->dropdown = $menuparams->get('dropdown');
792 $params->hiddenlists = $menuparams->get('hiddenlists', 'None');
793 $params->fields = $menuparams->get('fields');
794 $params->introtext = $menuparams->get('introtext');
795 $params->posttext = $menuparams->get('posttext');
796 $params->page_title = $menuparams->get('page_title', '');
797 }
798 }
799 $data = $this->prepareParams($params);
800
801 if (empty($data['user']->language)) {
802 $cmsUserLanguage = acym_getCmsUserLanguage();
803 $data['user']->language = empty($cmsUserLanguage) ? acym_getLanguageTag() : $cmsUserLanguage;
804 }
805
806 $data['captchaHelper'] = new CaptchaHelper();
807
808 acym_setVar('layout', 'profile');
809 parent::display($data);
810 }
811
812 public function prepareParams(object $values): array
813 {
814 //TODO problem with the types throughout the entire method
815 if (!isset($values->lists)) {
816 $values->lists = 'none';
817 }
818 if (!isset($values->listschecked)) {
819 $values->listschecked = 'none';
820 }
821 if (!isset($values->dropdown)) {
822 $values->dropdown = 0;
823 }
824 if (!isset($values->hiddenlists)) {
825 $values->hiddenlists = 'None';
826 }
827 if (empty($values->fields)) {
828 $values->fields = [2];
829 } elseif (is_string($values->fields)) {
830 $values->fields = explode(',', $values->fields);
831 }
832 if (!in_array('2', $values->fields) && !in_array(2, $values->fields)) {
833 $values->fields[] = '2';
834 }
835 if (!isset($values->introtext)) {
836 $values->introtext = '';
837 }
838 if (!isset($values->posttext)) {
839 $values->posttext = '';
840 }
841
842 foreach (['lists', 'listschecked', 'hiddenlists', 'fields'] as $option) {
843 if (is_string($values->$option)) {
844 $values->$option = explode(',', $values->$option);
845 }
846 }
847
848 if ((empty($values->lists) || in_array('None', $values->lists)) && (empty($values->hiddenlists) || strtolower(implode('', $values->hiddenlists)) == 'none')) {
849 $values->lists[] = 'All';
850 }
851
852 // Get the current user and his subscription
853 $userClass = new UserClass();
854 $user = $userClass->identify(true);
855 if (empty($user)) {
856 $listClass = new ListClass();
857 $subscription = $listClass->getAll('id');
858
859 if (acym_isMultilingual()) {
860 $subscription = $listClass->getTranslatedNameDescription($subscription);
861 }
862
863 $user = new \stdClass();
864 $user->id = 0;
865 $user->key = 0;
866
867 if (!empty($subscription)) {
868 foreach ($subscription as $id => $onesub) {
869 $subscription[$id]->status = 1;
870 if (strtolower(implode('', $values->listschecked)) != 'all' && !in_array($id, $values->listschecked)) {
871 $subscription[$id]->status = -1;
872 }
873 }
874 }
875
876 acym_addBreadcrumb(acym_translation('ACYM_SUBSCRIPTION'));
877 if (empty($menu)) {
878 acym_setPageTitle(acym_translation('ACYM_SUBSCRIPTION'));
879 }
880 } else {
881 $subscription = $userClass->getAllListsUserSubscriptionById($user->id, 'id', true);
882
883 acym_addBreadcrumb(acym_translation('ACYM_MODIFY_SUBSCRIPTION'));
884 if (empty($menu)) {
885 acym_setPageTitle(acym_translation('ACYM_MODIFY_SUBSCRIPTION'));
886 }
887 }
888
889 if (!empty($values->page_title)) {
890 acym_addBreadcrumb($values->page_title);
891 if (empty($menu)) {
892 acym_setPageTitle($values->page_title);
893 }
894 }
895
896 $allLists = $subscription;
897
898 acym_initModule();
899
900 if (!empty($values->lists) && strtolower(implode('', $values->lists)) != 'all') {
901 if (in_array('None', $values->lists)) {
902 $subscription = [];
903 } else {
904 $newSubscription = [];
905 foreach ($subscription as $id => $onesub) {
906 if (in_array($id, $values->lists)) {
907 $newSubscription[$id] = $onesub;
908 }
909 }
910 $subscription = $newSubscription;
911 }
912 }
913
914 if (!empty($values->hiddenlists)) {
915 $hiddenListsArray = [];
916 if (strtolower(implode('', $values->hiddenlists)) == 'all') {
917 $subscription = [];
918 foreach ($allLists as $oneList) {
919 if (!empty($oneList->active)) {
920 $hiddenListsArray[] = $oneList->id;
921 }
922 }
923 } elseif (strtolower(implode('', $values->hiddenlists)) != 'none') {
924 foreach ($allLists as $oneList) {
925 if (!$oneList->active || !in_array($oneList->id, $values->hiddenlists)) {
926 continue;
927 }
928 $hiddenListsArray[] = $oneList->id;
929 unset($subscription[$oneList->id]);
930 }
931 }
932 $values->hiddenlists = $hiddenListsArray;
933 }
934
935 // Overriding the list with the URL parameters: &listid= for displayed list, &hiddenlist= for hidden lists
936 $defaultSubscription = $subscription;
937 $forceLists = acym_getVar('string', 'listid', '');
938 if (!empty($forceLists)) {
939 $subscription = [];
940 $forceLists = explode(',', $forceLists);
941 foreach ($forceLists as $oneList) {
942 if (!empty($defaultSubscription[$oneList])) {
943 $subscription[$oneList] = $defaultSubscription[$oneList];
944 }
945 }
946 }
947 $forceHiddenLists = acym_getVar('string', 'hiddenlist', '');
948 if (!empty($forceHiddenLists)) {
949 $forceHiddenLists = explode(',', $forceHiddenLists);
950 $tmpList = [];
951 foreach ($forceHiddenLists as $oneList) {
952 if (!empty($defaultSubscription[$oneList]) || in_array($oneList, $values->hiddenlists)) {
953 $tmpList[] = $oneList;
954 }
955 }
956 $values->hiddenlists = $tmpList;
957 }
958
959 $displayLists = false;
960 foreach ($subscription as $oneSub) {
961 if (!empty($oneSub->active) && $oneSub->visible) {
962 $displayLists = true;
963 break;
964 }
965 }
966
967 $fieldClass = new FieldClass();
968 $allfields = $fieldClass->getFieldsByID($values->fields);
969 $fields = [];
970 foreach ($allfields as $field) {
971 if (intval($field->active) === 0) continue;
972 $fields[$field->id] = $field;
973 }
974 $values->fields = $fields;
975
976 $data = [
977 'config' => $this->config,
978 'displayLists' => $displayLists,
979 'user' => $user,
980 'subscription' => $subscription,
981 'fieldClass' => $fieldClass,
982 ];
983
984 foreach ($values as $key => $value) {
985 $data[$key] = $value;
986 }
987
988 return $data;
989 }
990
991 public function savechanges(): void
992 {
993 wp_verify_nonce(acym_getVar('cmd', '_wpnonce'), 'acymnonce') || die('Invalid Token');
994 acym_checkRobots();
995
996 $userClass = new UserClass();
997
998
999 $status = $userClass->saveForm(true);
1000 if ($status) {
1001 if ($userClass->confirmationSentSuccess) {
1002 $this->displayMessage('ACYM_CONFIRMATION_SENT', true, 'success');
1003 } elseif ($userClass->newUser) {
1004 $this->displayMessage('ACYM_SUBSCRIPTION_OK', true, 'success');
1005 } else {
1006 $this->displayMessage('ACYM_SUBSCRIPTION_UPDATED_OK', true, 'success');
1007 }
1008 } elseif (!empty($userClass->requireId)) {
1009 $this->displayMessage('ACYM_IDENTIFICATION_SENT', true, 'success');
1010 } elseif (!empty($userClass->errors)) {
1011 $this->displayMessage(implode('<br/>', $userClass->errors), true);
1012 } else {
1013 $this->displayMessage('ACYM_ERROR_SAVING', true);
1014 }
1015
1016 exit;
1017 }
1018
1019 public function exportdata(): void
1020 {
1021 wp_verify_nonce(acym_getVar('cmd', '_wpnonce'), 'acymnonce') || die('Invalid Token');
1022
1023 $userClass = new UserClass();
1024 $user = $userClass->identify(true, 'userId', 'userKey');
1025
1026 if (empty($user->id)) {
1027 acym_redirect(acym_rootURI());
1028 }
1029
1030 $userHelper = new UserHelper();
1031 $userData = $userHelper->getUserData($user);
1032 $exportData = [];
1033 foreach ($userData as $oneCategory) {
1034 if (!isset($exportData[$oneCategory['group_label']])) {
1035 $exportData[$oneCategory['group_label']] = [];
1036 }
1037
1038 $exportData[$oneCategory['group_label']][] = $oneCategory['data'];
1039 }
1040
1041 $exportFiles = [
1042 [
1043 'name' => 'data.json',
1044 'data' => json_encode($exportData),
1045 ],
1046 ];
1047
1048 $tempFolder = ACYM_MEDIA.'tmp'.DS;
1049 acym_createArchive($tempFolder.'export_data_user_'.$user->id, $exportFiles);
1050
1051 $exportHelper = new ExportHelper();
1052 $exportHelper->setDownloadHeaders('export_data_user_'.$user->id, 'zip');
1053 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_readfile -- Downloading a file, better than WP functions for memory handling.
1054 readfile($tempFolder.'export_data_user_'.$user->id.'.zip');
1055
1056 // Avoid issue when user cancels the download
1057 ignore_user_abort(true);
1058 acym_deleteFile($tempFolder.'export_data_user_'.$user->id.'.zip');
1059 exit;
1060 }
1061
1062 public function gdprDelete(): void
1063 {
1064 wp_verify_nonce(acym_getVar('cmd', '_wpnonce'), 'acymnonce') || die('Invalid Token');
1065
1066 $userClass = new UserClass();
1067 $user = $userClass->identify(true, 'userId', 'userKey');
1068 if (empty($user->id)) {
1069 acym_redirect(acym_rootURI());
1070 }
1071
1072 $userClass->delete([$user->id], true);
1073
1074 acym_redirect(acym_rootURI(), 'ACYM_GDPR_DATA_DELETED');
1075 }
1076
1077 public function ajaxGetEnqueuedMessages(): void
1078 {
1079 $output = '';
1080 $types = ['success', 'info', 'warning', 'error'];
1081 foreach ($types as $type) {
1082 $messages = acym_getVar('array', 'acymessage'.$type, [], 'SESSION');
1083 if (empty($messages)) {
1084 continue;
1085 }
1086
1087 if (!is_array($messages)) {
1088 $messages = [$messages];
1089 }
1090
1091 $output .= '<div class="acym_callout acym__callout__front__'.esc_attr($type).'" role="alert">';
1092 $output .= '<div>'.implode(' ', $messages).'</div>';
1093 $output .= '<button class="acym_callout_close" aria-label="'.esc_attr(acym_translation('ACYM_CLOSE_NOTIFICATION')).'">x</button></div>';
1094
1095 acym_setSession('acymessage'.$type, null, true);
1096 }
1097
1098 if (!empty($output)) {
1099 $output = '<div id="acym__callout__container">'.$output.'</div>';
1100 }
1101 acym_sendAjaxResponse('', ['messages' => $output]);
1102 }
1103 }
1104