PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 11.0.2
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v11.0.2
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.2, at front/FrontControllers/FrontusersController.php

1,107 lines 39.3 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->page_heading)) {
836 $values->page_heading = '';
837 }
838 if (!isset($values->introtext)) {
839 $values->introtext = '';
840 }
841 if (!isset($values->posttext)) {
842 $values->posttext = '';
843 }
844
845 foreach (['lists', 'listschecked', 'hiddenlists', 'fields'] as $option) {
846 if (is_string($values->$option)) {
847 $values->$option = explode(',', $values->$option);
848 }
849 }
850
851 if ((empty($values->lists) || in_array('None', $values->lists)) && (empty($values->hiddenlists) || strtolower(implode('', $values->hiddenlists)) == 'none')) {
852 $values->lists[] = 'All';
853 }
854
855 // Get the current user and his subscription
856 $userClass = new UserClass();
857 $user = $userClass->identify(true);
858 if (empty($user)) {
859 $listClass = new ListClass();
860 $subscription = $listClass->getAll('id');
861
862 if (acym_isMultilingual()) {
863 $subscription = $listClass->getTranslatedNameDescription($subscription);
864 }
865
866 $user = new \stdClass();
867 $user->id = 0;
868 $user->key = 0;
869
870 if (!empty($subscription)) {
871 foreach ($subscription as $id => $onesub) {
872 $subscription[$id]->status = 1;
873 if (strtolower(implode('', $values->listschecked)) != 'all' && !in_array($id, $values->listschecked)) {
874 $subscription[$id]->status = -1;
875 }
876 }
877 }
878
879 acym_addBreadcrumb(acym_translation('ACYM_SUBSCRIPTION'));
880 if (empty($menu)) {
881 acym_setPageTitle(acym_translation('ACYM_SUBSCRIPTION'));
882 }
883 } else {
884 $subscription = $userClass->getAllListsUserSubscriptionById($user->id, 'id', true);
885
886 acym_addBreadcrumb(acym_translation('ACYM_MODIFY_SUBSCRIPTION'));
887 if (empty($menu)) {
888 acym_setPageTitle(acym_translation('ACYM_MODIFY_SUBSCRIPTION'));
889 }
890 }
891
892 if (!empty($values->page_title)) {
893 acym_addBreadcrumb($values->page_title);
894 if (empty($menu)) {
895 acym_setPageTitle($values->page_title);
896 }
897 }
898
899 $allLists = $subscription;
900
901 acym_initModule();
902
903 if (!empty($values->lists) && strtolower(implode('', $values->lists)) != 'all') {
904 if (in_array('None', $values->lists)) {
905 $subscription = [];
906 } else {
907 $newSubscription = [];
908 foreach ($subscription as $id => $onesub) {
909 if (in_array($id, $values->lists)) {
910 $newSubscription[$id] = $onesub;
911 }
912 }
913 $subscription = $newSubscription;
914 }
915 }
916
917 if (!empty($values->hiddenlists)) {
918 $hiddenListsArray = [];
919 if (strtolower(implode('', $values->hiddenlists)) == 'all') {
920 $subscription = [];
921 foreach ($allLists as $oneList) {
922 if (!empty($oneList->active)) {
923 $hiddenListsArray[] = $oneList->id;
924 }
925 }
926 } elseif (strtolower(implode('', $values->hiddenlists)) != 'none') {
927 foreach ($allLists as $oneList) {
928 if (!$oneList->active || !in_array($oneList->id, $values->hiddenlists)) {
929 continue;
930 }
931 $hiddenListsArray[] = $oneList->id;
932 unset($subscription[$oneList->id]);
933 }
934 }
935 $values->hiddenlists = $hiddenListsArray;
936 }
937
938 // Overriding the list with the URL parameters: &listid= for displayed list, &hiddenlist= for hidden lists
939 $defaultSubscription = $subscription;
940 $forceLists = acym_getVar('string', 'listid', '');
941 if (!empty($forceLists)) {
942 $subscription = [];
943 $forceLists = explode(',', $forceLists);
944 foreach ($forceLists as $oneList) {
945 if (!empty($defaultSubscription[$oneList])) {
946 $subscription[$oneList] = $defaultSubscription[$oneList];
947 }
948 }
949 }
950 $forceHiddenLists = acym_getVar('string', 'hiddenlist', '');
951 if (!empty($forceHiddenLists)) {
952 $forceHiddenLists = explode(',', $forceHiddenLists);
953 $tmpList = [];
954 foreach ($forceHiddenLists as $oneList) {
955 if (!empty($defaultSubscription[$oneList]) || in_array($oneList, $values->hiddenlists)) {
956 $tmpList[] = $oneList;
957 }
958 }
959 $values->hiddenlists = $tmpList;
960 }
961
962 $displayLists = false;
963 foreach ($subscription as $oneSub) {
964 if (!empty($oneSub->active) && $oneSub->visible) {
965 $displayLists = true;
966 break;
967 }
968 }
969
970 $fieldClass = new FieldClass();
971 $allfields = $fieldClass->getFieldsByID($values->fields);
972 $fields = [];
973 foreach ($allfields as $field) {
974 if (intval($field->active) === 0) continue;
975 $fields[$field->id] = $field;
976 }
977 $values->fields = $fields;
978
979 $data = [
980 'config' => $this->config,
981 'displayLists' => $displayLists,
982 'user' => $user,
983 'subscription' => $subscription,
984 'fieldClass' => $fieldClass,
985 ];
986
987 foreach ($values as $key => $value) {
988 $data[$key] = $value;
989 }
990
991 return $data;
992 }
993
994 public function savechanges(): void
995 {
996 wp_verify_nonce(acym_getVar('cmd', '_wpnonce'), 'acymnonce') || die('Invalid Token');
997 acym_checkRobots();
998
999 $userClass = new UserClass();
1000
1001
1002 $status = $userClass->saveForm(true);
1003 if ($status) {
1004 if ($userClass->confirmationSentSuccess) {
1005 $this->displayMessage('ACYM_CONFIRMATION_SENT', true, 'success');
1006 } elseif ($userClass->newUser) {
1007 $this->displayMessage('ACYM_SUBSCRIPTION_OK', true, 'success');
1008 } else {
1009 $this->displayMessage('ACYM_SUBSCRIPTION_UPDATED_OK', true, 'success');
1010 }
1011 } elseif (!empty($userClass->requireId)) {
1012 $this->displayMessage('ACYM_IDENTIFICATION_SENT', true, 'success');
1013 } elseif (!empty($userClass->errors)) {
1014 $this->displayMessage(implode('<br/>', $userClass->errors), true);
1015 } else {
1016 $this->displayMessage('ACYM_ERROR_SAVING', true);
1017 }
1018
1019 exit;
1020 }
1021
1022 public function exportdata(): void
1023 {
1024 wp_verify_nonce(acym_getVar('cmd', '_wpnonce'), 'acymnonce') || die('Invalid Token');
1025
1026 $userClass = new UserClass();
1027 $user = $userClass->identify(true, 'userId', 'userKey');
1028
1029 if (empty($user->id)) {
1030 acym_redirect(acym_rootURI());
1031 }
1032
1033 $userHelper = new UserHelper();
1034 $userData = $userHelper->getUserData($user);
1035 $exportData = [];
1036 foreach ($userData as $oneCategory) {
1037 if (!isset($exportData[$oneCategory['group_label']])) {
1038 $exportData[$oneCategory['group_label']] = [];
1039 }
1040
1041 $exportData[$oneCategory['group_label']][] = $oneCategory['data'];
1042 }
1043
1044 $exportFiles = [
1045 [
1046 'name' => 'data.json',
1047 'data' => json_encode($exportData),
1048 ],
1049 ];
1050
1051 $tempFolder = ACYM_MEDIA.'tmp'.DS;
1052 acym_createArchive($tempFolder.'export_data_user_'.$user->id, $exportFiles);
1053
1054 $exportHelper = new ExportHelper();
1055 $exportHelper->setDownloadHeaders('export_data_user_'.$user->id, 'zip');
1056 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_readfile -- Downloading a file, better than WP functions for memory handling.
1057 readfile($tempFolder.'export_data_user_'.$user->id.'.zip');
1058
1059 // Avoid issue when user cancels the download
1060 ignore_user_abort(true);
1061 acym_deleteFile($tempFolder.'export_data_user_'.$user->id.'.zip');
1062 exit;
1063 }
1064
1065 public function gdprDelete(): void
1066 {
1067 wp_verify_nonce(acym_getVar('cmd', '_wpnonce'), 'acymnonce') || die('Invalid Token');
1068
1069 $userClass = new UserClass();
1070 $user = $userClass->identify(true, 'userId', 'userKey');
1071 if (empty($user->id)) {
1072 acym_redirect(acym_rootURI());
1073 }
1074
1075 $userClass->delete([$user->id], true);
1076
1077 acym_redirect(acym_rootURI(), 'ACYM_GDPR_DATA_DELETED');
1078 }
1079
1080 public function ajaxGetEnqueuedMessages(): void
1081 {
1082 $output = '';
1083 $types = ['success', 'info', 'warning', 'error'];
1084 foreach ($types as $type) {
1085 $messages = acym_getVar('array', 'acymessage'.$type, [], 'SESSION');
1086 if (empty($messages)) {
1087 continue;
1088 }
1089
1090 if (!is_array($messages)) {
1091 $messages = [$messages];
1092 }
1093
1094 $output .= '<div class="acym_callout acym__callout__front__'.esc_attr($type).'" role="alert">';
1095 $output .= '<div>'.implode(' ', $messages).'</div>';
1096 $output .= '<button class="acym_callout_close" aria-label="'.esc_attr(acym_translation('ACYM_CLOSE_NOTIFICATION')).'">x</button></div>';
1097
1098 acym_setSession('acymessage'.$type, null, true);
1099 }
1100
1101 if (!empty($output)) {
1102 $output = '<div id="acym__callout__container">'.$output.'</div>';
1103 }
1104 acym_sendAjaxResponse('', ['messages' => $output]);
1105 }
1106 }
1107