PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 11.1.0
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v11.1.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
← All changes | front/FrontControllers/FrontusersController.php +149 -30 10.10.2 → 11.1.0 View file →
@@ -34,8 +34,9 @@
34 34 $this->publicFrontTasks = [
35 35 'subscribe',
36 36 'unsubscribe',
37 37 'unsubscribeAll',
38 + 'disableTracking',
38 39 'saveSubscriptions',
39 40 'unsubscribePage',
40 41 'confirm',
41 42 'profile',
@@ -59,9 +60,9 @@
59 60 ]
60 61 );
61 62 } else {
62 63 acym_header('Content-type:text/html; charset=utf-8');
63 - echo '<script>alert("'.acym_translation($message, true).'"); window.history.go(-1);</script>';
64 + echo '<script>alert("'.esc_html(acym_translation($message)).'"); window.history.go(-1);</script>';
64 65 }
65 66 exit;
66 67 }
67 68
@@ -68,22 +69,33 @@
68 69 public function subscribe(): void
69 70 {
70 71 acym_checkRobots();
71 72
72 - if (!acym_getVar('string', 'acy_source') && !empty($_GET['user'])) {
73 + $formData = acym_getVar('array', 'user', []);
74 + if (!acym_getVar('string', 'acy_source') && !empty($formData)) {
75 + // Coming from a subscription via url...
73 76 acym_setVar('acy_source', 'url');
74 77 }
75 78
79 + // Do we have to return an ajax response or a web page ?
76 80 $ajax = acym_getVar('int', 'ajax', 0);
77 81 if ($ajax) {
82 + //in case of the page displays some warnings or whatever
78 83 @ob_end_clean();
79 84 acym_header('Content-type:application/json; charset=utf-8');
80 85 }
81 86
87 + //We only allow logged in users and this user is not logged it...
82 88 $currentUserid = acym_currentUserId();
83 89 if ((int)$this->config->get('allow_visitor', 1) != 1 && empty($currentUserid)) {
84 90 if ($ajax) {
85 - echo '{"message":"'.acym_translation('ACYM_ONLY_LOGGED', true).'","type":"error","code":"0"}';
91 + echo json_encode(
92 + [
93 + 'message' => acym_translation('ACYM_ONLY_LOGGED'),
94 + 'type' => 'error',
95 + 'code' => '0',
96 + ]
97 + );
86 98 exit;
87 99 } else {
88 100 acym_askLog(false, 'ACYM_ONLY_LOGGED');
89 101
@@ -97,9 +109,8 @@
97 109 $this->displayMessage('ACYM_WRONG_CAPTCHA', $ajax);
98 110 }
99 111 }
100 112
101 - $formData = acym_getVar('array', 'user', [], '');
102 113 $user = new \stdClass();
103 114 if (!empty($formData['email'])) {
104 115 $user->email = $formData['email'];
105 116 }
@@ -112,12 +123,15 @@
112 123 }
113 124 }
114 125 $user->email = trim($user->email);
115 126
127 + // Check email validity
116 128 if (empty($user->email) || !acym_isValidEmail($user->email, true)) {
117 129 $this->displayMessage('ACYM_VALID_EMAIL', $ajax);
118 130 }
119 131
132 + // E-mail is valid now...
133 + // Check if this user already exists or not...
120 134 $alreadyExists = $userClass->getOneByEmail($user->email);
121 135 if (!empty($alreadyExists->id)) {
122 136 $user->id = $alreadyExists->id;
123 137 }
@@ -132,8 +146,9 @@
132 146 if (!$successfullySaved || empty($user->id)) {
133 147 $this->displayMessage('ACYM_ERROR_SAVE_USER', $ajax);
134 148 }
135 149
150 + //We will now load back the user from the one we saved so that we will have all its infos
136 151 $myuser = $userClass->getOneById($user->id);
137 152 if (empty($myuser->id)) {
138 153 $this->displayMessage('ACYM_ERROR_SAVE_USER', $ajax);
139 154 }
@@ -140,9 +155,9 @@
140 155
141 156 $msgtype = 'success';
142 157 if (empty($myuser->confirmed) && $this->config->get('require_confirmation', 1) == 1) {
143 158 if ($userClass->confirmationSentSuccess || empty($userClass->confirmationSentError)) {
144 - $msg = strip_tags(acym_getVar('string', 'confirmation_message', ''));
159 + $msg = acym_stripTags(acym_getVar('string', 'confirmation_message', ''));
145 160 if (empty($msg)) {
146 161 $msg = 'ACYM_CONFIRMATION_SENT';
147 162 }
148 163 $code = 2;
@@ -152,9 +167,9 @@
152 167 $msgtype = 'error';
153 168 }
154 169 } else {
155 170 if ($userClass->subscribed) {
156 - $msg = strip_tags(acym_getVar('string', 'confirmation_message', ''));
171 + $msg = acym_stripTags(acym_getVar('string', 'confirmation_message', ''));
157 172 if (empty($msg)) {
158 173 $msg = 'ACYM_SUBSCRIPTION_OK';
159 174 }
160 175 $code = 3;
@@ -164,8 +179,9 @@
164 179 $enqueueMsgType = 'info';
165 180 }
166 181 }
167 182
183 + // Replace tags inside the $msg and redirect link
168 184 $replace = [];
169 185 foreach ($myuser as $oneProp => $oneVal) {
170 186 $replace['{user:'.$oneProp.'}'] = $oneVal;
171 187 }
@@ -172,10 +188,17 @@
172 188
173 189 $msg = str_replace(array_keys($replace), $replace, acym_translation($msg));
174 190
175 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
176 193 $msg = str_replace(["\n", "\r", '"', '\\'], [' ', ' ', "'", '\\\\'], $msg);
177 - echo '{"message":"'.$msg.'","type":"'.$msgtype.'","code":"'.$code.'"}';
194 + echo json_encode(
195 + [
196 + 'message' => $msg,
197 + 'type' => $msgtype,
198 + 'code' => $code,
199 + ]
200 + );
178 201 exit;
179 202 } else {
180 203 acym_enqueueMessage($msg, !empty($enqueueMsgType) ? $enqueueMsgType : $msgtype);
181 204 }
@@ -208,15 +231,18 @@
208 231 $visibleSubscription = acym_getVar('array', 'subscription', []);
209 232 $hiddenLists = trim(acym_getVar('string', 'hiddenlists', ''));
210 233 $hiddenSubscription = empty($hiddenLists) ? [] : explode(',', $hiddenLists);
211 234 $unsubscribeLists = array_merge($visibleSubscription, $hiddenSubscription);
235 + acym_arrayToInteger($unsubscribeLists);
212 236
213 237 $mailId = acym_getVar('int', 'mail_id', 0);
214 238
239 + // Protect against bots
215 240 if (!empty($mailId) && !empty($currentUser->id)) {
216 241 $userStatClass = new UserStatClass();
217 242 $userStat = $userStatClass->getOneByMailAndUserId($mailId, $currentUser->id);
218 - if (acym_isRobot() || (!empty($userStat) && acym_getTimeFromUTCDate($userStat->send_date) > time() - 20)) {
243 + $delay = $this->config->get('tracking_delay', 0);
244 + if (acym_isRobot() || (!empty($userStat) && !empty($delay) && acym_getTimeFromUTCDate($userStat->send_date) > time() - $delay)) {
219 245 return;
220 246 }
221 247 }
222 248
@@ -306,8 +332,9 @@
306 332
307 333 $redirectToUnsubPage = $this->config->get('unsubscribe_page', 1);
308 334 $direct = acym_getVar('int', 'direct', 0);
309 335
336 + // Do we have to return an ajax response or a web page ?
310 337 $ajax = acym_getVar('int', 'ajax', 0);
311 338 if ($ajax) {
312 339 @ob_end_clean();
313 340 acym_header('Content-type:application/json; charset=utf-8');
@@ -329,9 +356,9 @@
329 356 $formData = acym_getVar('array', 'user', []);
330 357 $email = '';
331 358
332 359 if (!empty($formData['email'])) {
333 - $email = trim(strip_tags($formData['email']));
360 + $email = trim(acym_stripTags($formData['email']));
334 361 } elseif (empty($user)) {
335 362 return;
336 363 } elseif (!empty($user->email)) {
337 364 $email = $user->email;
@@ -343,8 +370,9 @@
343 370 if (empty($email) && !empty($currentEmail)) {
344 371 $email = $currentEmail;
345 372 }
346 373
374 + // Check email validity
347 375 if (empty($email) || !acym_isValidEmail($email)) {
348 376 $this->displayMessage('ACYM_VALID_EMAIL', $ajax);
349 377 }
350 378
@@ -349,8 +377,9 @@
349 377 }
350 378
351 379 $alreadyExists = $userClass->getOneByEmail($email);
352 380
381 + // User not found
353 382 if (empty($alreadyExists->id)) {
354 383 $this->displayMessage(acym_translationSprintf('ACYM_SUB_NOT_IN_LIST', $email), $ajax);
355 384 }
356 385
@@ -365,8 +394,68 @@
365 394 $this->unsubscribePage($alreadyExists);
366 395 }
367 396 }
368 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 +
369 458 private function getUserFromUnsubPage(): object
370 459 {
371 460 $userID = acym_getVar('int', 'user_id');
372 461 $userClass = new UserClass();
@@ -404,8 +493,9 @@
404 493 }
405 494 }
406 495
407 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
408 498 $userClass->blockNotifications = true;
409 499 $userClass->unsubscribe([$user->id], $lists);
410 500 }
411 501
@@ -452,21 +542,28 @@
452 542 if (empty($userClass->identify(true, 'user_id', 'user_key'))) {
453 543 $this->displayMessage('ACYM_USER_NOT_FOUND', false);
454 544 }
455 545
546 + // Get the user
456 547 $user = $this->getUserFromUnsubPage();
548 + // Get the list the user want to sub
457 549 $listsChecked = acym_getVar('array', 'lists', []);
458 550 $listsChecked = array_filter($listsChecked, function ($status) {
459 551 return intval($status) === 1;
460 552 });
461 553 $listsChecked = array_keys($listsChecked);
554 + // Get the displayed lists
462 555 $displayedCheckedLists = explode(',', acym_getVar('string', 'displayed_checked_lists', ''));
556 + // We get the user subscriptions
463 557 $userSubscriptions = $userClass->getUserSubscriptionById($user->id);
464 558
559 + // We subscribe the user to the checked lists
465 560 $userClass->subscribe([$user->id], $listsChecked);
466 561
562 + // We unsub to the unchecked lists
467 563 $listsToUnsub = [];
468 564 foreach ($userSubscriptions as $subscription) {
565 + // The list wasn't checked && the list is displayed && the user is subscribed to it
469 566 if (
470 567 !in_array($subscription->id, $listsChecked)
471 568 && in_array($subscription->id, $displayedCheckedLists)
472 569 && intval($subscription->status) === 1
@@ -486,14 +583,20 @@
486 583 $mailId = acym_getVar('int', 'mail_id', 0);
487 584 $campaignListOnly = $this->config->get('unsubscribe_campaign_list_only', '0') === '1';
488 585 $displaySurvey = $this->config->get('unsubpage_survey', '0') === '1';
489 586 $surveyAnswers = $this->config->get('unsub_survey', '[]');
490 - $unsubscribeColor = $this->config->get('unsubscribe_color', '#00a4ff');
587 + $unsubscribeColor = $this->config->get('unsubscribe_color');
588 + if (empty($unsubscribeColor)) {
589 + $unsubscribeColor = '#0079d3';
590 + }
491 591 $hoverColor = $this->darkenRGBColor($unsubscribeColor, 20);
492 592
493 593 $surveyAnswers = json_decode($surveyAnswers, true);
494 594
495 - if (ACYM_CMS === 'joomla' && !empty($_GET['language'])) $lang = $_GET['language'];
595 + $languageCode = acym_getVar('string', 'language');
596 + if (ACYM_CMS === 'joomla' && !empty($languageCode)) {
597 + $lang = $languageCode;
598 + }
496 599 acym_setLanguage($lang);
497 600 acym_loadLanguage($lang);
498 601
499 602 $userSubscriptions = $userClass->getUserSubscriptionById($alreadyExists->id, 'id', false, false, true, false, $mailId, $campaignListOnly);
@@ -532,14 +635,10 @@
532 635 $data['surveyAnswers'][] = acym_translation('ACYM_OTHER');
533 636 $data['surveyAnswers'] = array_combine($data['surveyAnswers'], $data['surveyAnswers']);
534 637 }
535 638
536 - if (!empty($this->config->get('unsubscribe_color'))) {
537 - $unsubscribeColor = $this->config->get('unsubscribe_color');
538 - $hoverColor = $this->darkenRGBColor($unsubscribeColor, 20);
539 - $data['unsubscribeColor'] = $unsubscribeColor;
540 - $data['hoverColor'] = $hoverColor;
541 - }
639 + $data['unsubscribeColor'] = $unsubscribeColor;
640 + $data['hoverColor'] = $hoverColor;
542 641 $data['svgImage'] = $this->getSVGImage($unsubscribeColor, $hoverColor);
543 642
544 643 acym_setVar('layout', 'unsubscribepage');
545 644 acym_header('Content-Type: text/html; charset=utf-8');
@@ -595,8 +694,9 @@
595 694 if (acym_isRobot()) {
596 695 return;
597 696 }
598 697
698 + // We identify the user
599 699 $userClass = new UserClass();
600 700 $user = $userClass->identify(true, 'userId', 'userKey');
601 701 if (empty($user)) {
602 702 acym_enqueueMessage(acym_translation('ACYM_USER_NOT_FOUND'), 'error');
@@ -612,20 +712,23 @@
612 712 acym_enqueueMessage(acym_translation('ACYM_SUBSCRIPTION_CONFIRMED'), 'success');
613 713 }
614 714 }
615 715
716 + // Now we can really confirm the user
616 717 if (!$user->confirmed) {
617 718 $userClass->confirm($user->id);
618 719 }
619 720
721 + // Get the redirect url from the configuration page if specified
620 722 $redirectUrl = $this->config->get('confirm_redirect');
621 723 if (!empty($redirectUrl)) {
622 724 $replace = [];
725 + // We replace tags in case of the user added some dynamic information to its url.
623 726 foreach ($user as $key => $val) {
624 727 $replace['{user:'.$key.'}'] = $val;
625 728 }
626 729 $redirectUrl = str_replace(array_keys($replace), $replace, $redirectUrl);
627 - acym_redirect($redirectUrl);
730 + acym_redirect($redirectUrl, '', 'message', false);
628 731
629 732 return;
630 733 }
631 734
@@ -637,8 +740,9 @@
637 740 $userClass = new UserClass();
638 741 $user = $userClass->identify(true);
639 742
640 743 if (empty($user)) {
744 + // Check if the subscription is allowed for non registered users
641 745 $allowvisitor = $this->config->get('allow_visitor', 1);
642 746 if (empty($allowvisitor)) {
643 747 acym_askLog(true, 'ACYM_ONLY_LOGGED', 'message');
644 748
@@ -655,8 +759,9 @@
655 759 acym_addScript(false, ACYM_JS.'libraries/email-misspelled.min.js?v='.filemtime(ACYM_MEDIA.'js'.DS.'libraries'.DS.'email-misspelled.min.js'));
656 760 }
657 761 acym_addScript(false, ACYM_JS.'module.min.js?v='.filemtime(ACYM_MEDIA.'js'.DS.'module.min.js'));
658 762
763 + // Get the page parameters
659 764 $params = new \stdClass();
660 765 $menu = acym_getMenu();
661 766 if (is_object($menu)) {
662 767 $params->source = 'Menu n°'.$menu->id;
@@ -663,8 +768,9 @@
663 768 $menuParameters = method_exists($menu, 'getParams') ? $menu->getParams() : $menu->params;
664 769 $menuparams = new AcymParameter($menuParameters);
665 770
666 771 if (!empty($menuparams)) {
772 + // Joomla specific params
667 773 $params->suffix = $menuparams->get('pageclass_sfx', '');
668 774 $params->page_heading = $menuparams->get('page_heading');
669 775 $params->show_page_heading = $menuparams->get('show_page_heading', 0);
670 776
@@ -677,8 +783,9 @@
677 783 if ($menuparams->get('robots')) {
678 784 acym_addMetadata('robots', $menuparams->get('robots'));
679 785 }
680 786
787 + // Our params
681 788 $params->lists = $menuparams->get('lists', 'none');
682 789 $params->listschecked = $menuparams->get('listschecked', 'none');
683 790 $params->dropdown = $menuparams->get('dropdown');
684 791 $params->hiddenlists = $menuparams->get('hiddenlists', 'None');
@@ -702,8 +809,9 @@
702 809 }
703 810
704 811 public function prepareParams(object $values): array
705 812 {
813 + //TODO problem with the types throughout the entire method
706 814 if (!isset($values->lists)) {
707 815 $values->lists = 'none';
708 816 }
709 817 if (!isset($values->listschecked)) {
@@ -715,13 +823,18 @@
715 823 if (!isset($values->hiddenlists)) {
716 824 $values->hiddenlists = 'None';
717 825 }
718 826 if (empty($values->fields)) {
719 - $values->fields = ['1', '2'];
827 + $values->fields = [2];
828 + } elseif (is_string($values->fields)) {
829 + $values->fields = explode(',', $values->fields);
720 830 }
721 831 if (!in_array('2', $values->fields) && !in_array(2, $values->fields)) {
722 832 $values->fields[] = '2';
723 833 }
834 + if (!isset($values->page_heading)) {
835 + $values->page_heading = '';
836 + }
724 837 if (!isset($values->introtext)) {
725 838 $values->introtext = '';
726 839 }
727 840 if (!isset($values->posttext)) {
@@ -737,8 +850,9 @@
737 850 if ((empty($values->lists) || in_array('None', $values->lists)) && (empty($values->hiddenlists) || strtolower(implode('', $values->hiddenlists)) == 'none')) {
738 851 $values->lists[] = 'All';
739 852 }
740 853
854 + // Get the current user and his subscription
741 855 $userClass = new UserClass();
742 856 $user = $userClass->identify(true);
743 857 if (empty($user)) {
744 858 $listClass = new ListClass();
@@ -819,8 +933,9 @@
819 933 }
820 934 $values->hiddenlists = $hiddenListsArray;
821 935 }
822 936
937 + // Overriding the list with the URL parameters: &listid= for displayed list, &hiddenlist= for hidden lists
823 938 $defaultSubscription = $subscription;
824 939 $forceLists = acym_getVar('string', 'listid', '');
825 940 if (!empty($forceLists)) {
826 941 $subscription = [];
@@ -876,9 +991,9 @@
876 991 }
877 992
878 993 public function savechanges(): void
879 994 {
880 - acym_checkToken();
995 + wp_verify_nonce(acym_getVar('cmd', '_wpnonce'), 'acymnonce') || die('Invalid Token');
881 996 acym_checkRobots();
882 997
883 998 $userClass = new UserClass();
884 999
@@ -904,9 +1019,9 @@
904 1019 }
905 1020
906 1021 public function exportdata(): void
907 1022 {
908 - acym_checkToken();
1023 + wp_verify_nonce(acym_getVar('cmd', '_wpnonce'), 'acymnonce') || die('Invalid Token');
909 1024
910 1025 $userClass = new UserClass();
911 1026 $user = $userClass->identify(true, 'userId', 'userKey');
912 1027
@@ -936,18 +1051,20 @@
936 1051 acym_createArchive($tempFolder.'export_data_user_'.$user->id, $exportFiles);
937 1052
938 1053 $exportHelper = new ExportHelper();
939 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.
940 1056 readfile($tempFolder.'export_data_user_'.$user->id.'.zip');
941 1057
1058 + // Avoid issue when user cancels the download
942 1059 ignore_user_abort(true);
943 - unlink($tempFolder.'export_data_user_'.$user->id.'.zip');
1060 + acym_deleteFile($tempFolder.'export_data_user_'.$user->id.'.zip');
944 1061 exit;
945 1062 }
946 1063
947 1064 public function gdprDelete(): void
948 1065 {
949 - acym_checkToken();
1066 + wp_verify_nonce(acym_getVar('cmd', '_wpnonce'), 'acymnonce') || die('Invalid Token');
950 1067
951 1068 $userClass = new UserClass();
952 1069 $user = $userClass->identify(true, 'userId', 'userKey');
953 1070 if (empty($user->id)) {
@@ -954,29 +1071,31 @@
954 1071 acym_redirect(acym_rootURI());
955 1072 }
956 1073
957 1074 $userClass->delete([$user->id], true);
1075 +
1076 + acym_redirect(acym_rootURI(), 'ACYM_GDPR_DATA_DELETED');
958 1077 }
959 1078
960 1079 public function ajaxGetEnqueuedMessages(): void
961 1080 {
962 - acym_session();
963 -
964 1081 $output = '';
965 1082 $types = ['success', 'info', 'warning', 'error'];
966 1083 foreach ($types as $type) {
967 - if (empty($_SESSION['acymessage'.$type])) continue;
1084 + $messages = acym_getVar('array', 'acymessage'.$type, [], 'SESSION', ACYM_ALLOWHTML);
1085 + if (empty($messages)) {
1086 + continue;
1087 + }
968 1088
969 - $messages = $_SESSION['acymessage'.$type];
970 1089 if (!is_array($messages)) {
971 1090 $messages = [$messages];
972 1091 }
973 1092
974 - $output .= '<div class="acym_callout acym__callout__front__'.$type.'" role="alert">';
1093 + $output .= '<div class="acym_callout acym__callout__front__'.esc_attr($type).'" role="alert">';
975 1094 $output .= '<div>'.implode(' ', $messages).'</div>';
976 - $output .= '<button class="acym_callout_close" aria-label="'.acym_escape(acym_translation('ACYM_CLOSE_NOTIFICATION')).'">x</button></div>';
1095 + $output .= '<button class="acym_callout_close" aria-label="'.esc_attr(acym_translation('ACYM_CLOSE_NOTIFICATION')).'">x</button></div>';
977 1096
978 - unset($_SESSION['acymessage'.$type]);
1097 + acym_setSession('acymessage'.$type, null, true);
979 1098 }
980 1099
981 1100 if (!empty($output)) {
982 1101 $output = '<div id="acym__callout__container">'.$output.'</div>';