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 | back/Controllers/Mails/Edition.php +85 -35 10.10.2 → 11.1.0 View file →
@@ -17,9 +17,9 @@
17 17 {
18 18 public function edit(): void
19 19 {
20 20 if (!acym_isAdmin()) {
21 - acym_checkToken();
21 + wp_verify_nonce(acym_getVar('cmd', '_wpnonce'), 'acymnonce') || die('Invalid Token');
22 22 }
23 23
24 24 $tempId = acym_getVar('int', 'id');
25 25 $mailClass = new MailClass();
@@ -62,8 +62,12 @@
62 62 $isAutomationAdmin = false;
63 63 $fromMail = '';
64 64
65 65 if (!empty($fromId)) {
66 + if (!$mailClass->hasUserAccess($fromId)) {
67 + die('Access denied for this email');
68 + }
69 +
66 70 $fromMail = $mailClass->getOneById($fromId);
67 71 }
68 72
69 73 if ($type === 'automation_admin') {
@@ -70,8 +74,9 @@
70 74 $type = MailClass::TYPE_AUTOMATION;
71 75 $isAutomationAdmin = true;
72 76 }
73 77
78 + // new mails
74 79 if (empty($tempId)) {
75 80 if (empty($fromId)) {
76 81 $mail = new \stdClass();
77 82 $mail->name = '';
@@ -131,8 +136,9 @@
131 136
132 137 $breadcrumbTitle = acym_translation($breadcrumbTitle);
133 138 $breadcrumbUrl = 'mails&task=edit&type_editor='.$typeEditor.(!empty($fromId) ? '&from='.$fromId : '').'&type='.$type;
134 139 } else {
140 + // Existing mails and notifications
135 141 if (!$mailClass->hasUserAccess($tempId)) {
136 142 die('Access denied for this email');
137 143 }
138 144
@@ -181,9 +187,10 @@
181 187
182 188 $breadcrumbUrl = 'mails&task=edit&id='.$mail->id;
183 189 } else {
184 190 if (empty($return)) {
185 - $return = empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER'];
191 + $httpReferer = acym_getVar('string', 'HTTP_REFERER', '', 'SERVER');
192 + $return = empty($httpReferer) ? '' : $httpReferer;
186 193 }
187 194
188 195 $notifName = acym_translation('ACYM_NOTIFICATIION_'.strtoupper(substr($mail->name, 4)));
189 196 if (strpos($notifName, 'ACYM_NOTIFICATIION_') !== false) {
@@ -220,9 +227,9 @@
220 227
221 228 if (!empty($return)) {
222 229 $breadcrumbUrl .= '&return='.urlencode(base64_encode($return));
223 230 }
224 - $this->breadcrumb[acym_escape($breadcrumbTitle)] = acym_completeLink($breadcrumbUrl);
231 + $this->breadcrumb[$breadcrumbTitle] = acym_completeLink($breadcrumbUrl);
225 232
226 233 $lists = [];
227 234
228 235 if (in_array($mail->type, [MailClass::TYPE_WELCOME, MailClass::TYPE_UNSUBSCRIBE])) {
@@ -240,9 +247,12 @@
240 247 } elseif (empty($mail->attachments)) {
241 248 $mail->attachments = [];
242 249 }
243 250
251 + $mail->thumbnail = $mail->thumbnail ?? '';
252 +
244 253 $tagClass = new TagClass();
254 + $languages = acym_getLanguages(true, true);
245 255 $data = [
246 256 'mail' => $mail,
247 257 'allTags' => $tagClass->getAllTagsByType(TagClass::TYPE_MAIL),
248 258 'isAutomationAdmin' => $isAutomationAdmin,
@@ -247,9 +257,14 @@
247 257 'allTags' => $tagClass->getAllTagsByType(TagClass::TYPE_MAIL),
248 258 'isAutomationAdmin' => $isAutomationAdmin,
249 259 'social_icons' => $this->config->get('social_icons', '{}'),
250 260 'fromId' => $fromId,
251 - 'langChoice' => acym_languageOption($mail->links_language, 'mail[links_language]'),
261 + 'langChoice' => count($languages) < 2
262 + ? []
263 + : [
264 + 'links' => $mail->links_language,
265 + 'name' => 'mail[links_language]',
266 + ],
252 267 'list_id' => $listIds,
253 268 'lists' => $lists,
254 269 'delay_unit' => $followupClass->getDelayUnits(),
255 270 'default_delay_unit' => FollowupClass::DEFAULT_DELAY_UNIT,
@@ -340,11 +355,17 @@
340 355 $this->loadScripts['edit'][] = 'dtextPicker';
341 356 }
342 357 }
343 358
359 + /**
360 + * Spaghetti code, needs refactoring.
361 + *
362 + * 1 - Save the main mail
363 + * 2 - Save the alternate versions if any
364 + */
344 365 public function store(bool $ajax = false): int
345 366 {
346 - acym_checkToken();
367 + wp_verify_nonce(acym_getVar('cmd', '_wpnonce'), 'acymnonce') || die('Invalid Token');
347 368
348 369 $mailClass = new MailClass();
349 370 $formData = acym_getVar('array', 'mail', []);
350 371 $versions = acym_getVar('array', 'versions', [], 'REQUEST', ACYM_ALLOWRAW);
@@ -349,9 +370,9 @@
349 370 $formData = acym_getVar('array', 'mail', []);
350 371 $versions = acym_getVar('array', 'versions', [], 'REQUEST', ACYM_ALLOWRAW);
351 372 $versionType = acym_getVar('string', 'version_type', '');
352 373 $currentVersion = acym_getVar('string', 'current_version', 'main');
353 - $allowedFields = acym_getColumns('mail');
374 + $allowedFields = array_diff(acym_getColumns('mail'), ['creator_id']);
354 375 $fromId = acym_getVar('int', 'fromId', 0);
355 376 $return = acym_getVar('string', 'return');
356 377
357 378 $fromAutomation = false;
@@ -368,9 +389,9 @@
368 389 $mail->{$name} = $data;
369 390 }
370 391
371 392 if (!empty($mail->id)) {
372 - if (!$mailClass->hasUserAccess($mail->id)) {
393 + if (!$mailClass->hasUserAccess($mail->id, true)) {
373 394 die('Cannot save this mail');
374 395 }
375 396
376 397 $previousMail = $mailClass->getOneById($mail->id);
@@ -455,8 +476,9 @@
455 476
456 477 $mailSettings->mainColors = acym_getVar('string', $inputNameColors, '', 'REQUEST', ACYM_ALLOWRAW);
457 478 $mail->mail_settings = json_encode($mailSettings);
458 479
480 + // Use the thumbnail of the source mail if not modified
459 481 if (!empty($fromId) && empty($mail->thumbnail) && !$fromAutomation) {
460 482 $thumbnail = $this->setThumbnailFrom($fromId);
461 483 if (!empty($thumbnail)) {
462 484 if (!empty($previousMail->thumbnail)) {
@@ -505,8 +527,9 @@
505 527 $listIds = acym_getVar('array', 'list_ids', []);
506 528 $listClass = new ListClass();
507 529 $listClass->setWelcomeUnsubEmail($listIds, (int)$mailID, $mail->type);
508 530 } elseif (!empty($mail->type) && $mail->type === MailClass::TYPE_FOLLOWUP) {
531 + // Pass the new email ID in the return URL to ask user if we should add it to the queue
509 532 acym_setVar('return', acym_getVar('string', 'return').'&newEmailId='.$mailID);
510 533
511 534 $followupData = acym_getVar('array', 'followup', []);
512 535 $followupClass = new FollowupClass();
@@ -614,8 +637,9 @@
614 637 } else {
615 638 $mail->attachments = [];
616 639 }
617 640
641 + // Attachments
618 642 $newAttachments = [];
619 643 $attachments = acym_getVar('array', 'attachments', []);
620 644 if (!empty($attachments)) {
621 645 foreach ($attachments as $filepath) {
@@ -625,13 +649,14 @@
625 649 'filename' => $filepath,
626 650 'size' => filesize(ACYM_ROOT.$filepath),
627 651 ];
628 652
629 - if (preg_match('#\.(php.?|.?htm.?|pl|py|jsp|asp|sh|cgi)#Ui', $attachment['filename'])) {
653 + //We will never allow some files to be uploaded...
654 + if (acym_isForbiddenFileExtension($attachment['filename'])) {
630 655 acym_enqueueMessage(
631 656 acym_translationSprintf(
632 657 'ACYM_ACCEPTED_TYPE',
633 - substr($attachment['filename'], strrpos($attachment['filename'], '.') + 1),
658 + esc_html(substr($attachment['filename'], strrpos($attachment['filename'], '.') + 1)),
634 659 $this->config->get('allowed_files')
635 660 ),
636 661 'notice'
637 662 );
@@ -641,8 +666,9 @@
641 666 if (in_array($attachment, $mail->attachments)) continue;
642 667
643 668 $newAttachments[] = $attachment;
644 669 }
670 + // Add to previous attachments
645 671 if (!empty($mail->attachments)) {
646 672 $newAttachments = array_merge($mail->attachments, $newAttachments);
647 673 }
648 674 $mail->attachments = $newAttachments;
@@ -690,9 +716,15 @@
690 716 public function save(): void
691 717 {
692 718 $mailId = $this->store();
693 719
694 - $return = str_replace('{mailid}', empty($mailId) ? '' : $mailId, acym_getVar('string', 'return', ''));
720 + // When saving notifications, we return to page where we clicked the "Edit email" button
721 + $return = str_replace(
722 + '{mailid}',
723 + empty($mailId) ? '' : $mailId,
724 + acym_getVar('string', 'return', '')
725 + );
726 +
695 727 if (empty($return)) {
696 728 $this->listing();
697 729 } else {
698 730 acym_redirect($return);
@@ -700,8 +732,10 @@
700 732 }
701 733
702 734 public function autoSave(): void
703 735 {
736 + wp_verify_nonce(acym_getVar('cmd', '_wpnonce'), 'acymnonce') || die('Invalid Token');
737 +
704 738 $mailClass = new MailClass();
705 739 $mail = new \stdClass();
706 740
707 741 $language = acym_getVar('string', 'language', 'main');
@@ -707,9 +741,9 @@
707 741 $language = acym_getVar('string', 'language', 'main');
708 742 $mail->id = acym_getVar('int', 'mailId', 0);
709 743 $mail->autosave = base64_decode(acym_getVar('string', 'autoSave', '', 'REQUEST', ACYM_ALLOWRAW));
710 744
711 - if (empty($mail->id) || !$mailClass->hasUserAccess($mail->id) || !$mailClass->autoSave($mail, $language)) {
745 + if (empty($mail->id) || !$mailClass->hasUserAccess($mail->id, true) || !$mailClass->autoSave($mail, $language)) {
712 746 acym_sendAjaxResponse('', [], false);
713 747 } else {
714 748 acym_sendAjaxResponse();
715 749 }
@@ -716,9 +750,9 @@
716 750 }
717 751
718 752 public function ajaxCheckVideoUrl(): void
719 753 {
720 - acym_checkToken();
754 + wp_verify_nonce(acym_getVar('cmd', '_wpnonce'), 'acymnonce') || die('Invalid Token');
721 755 $videoUrl = acym_getVar('string', 'url', '');
722 756
723 757 if (!acym_isValidUrl($videoUrl)) {
724 758 acym_sendAjaxResponse('', [], false);
@@ -788,14 +822,14 @@
788 822 ob_start();
789 823 $status = imagejpeg($output, null, 95);
790 824 $imageContent = ob_get_clean();
791 825 if ($status && acym_writeFile(ACYM_ROOT.ACYM_UPLOAD_FOLDER.$fileName, $imageContent)) {
792 - unlink($tmpFilePath);
826 + acym_deleteFile($tmpFilePath);
793 827
794 828 return ACYM_UPLOADS_URL.$fileName;
795 829 }
796 830
797 - unlink($tmpFilePath);
831 + acym_deleteFile($tmpFilePath);
798 832
799 833 return '';
800 834 }
801 835
@@ -800,9 +834,10 @@
800 834 }
801 835
802 836 public function getTemplateAjax(): void
803 837 {
804 - acym_checkToken();
838 + wp_verify_nonce(acym_getVar('cmd', '_wpnonce'), 'acymnonce') || die('Invalid Token');
839 +
805 840 $pagination = new PaginationHelper();
806 841 $id = acym_getVar('int', 'id');
807 842 $id = empty($id) ? '' : '&id='.$id;
808 843 $searchFilter = acym_getVar('string', 'search', '');
@@ -848,9 +883,9 @@
848 883 if (in_array($type, [MailClass::TYPE_WELCOME, MailClass::TYPE_UNSUBSCRIBE])) {
849 884 $listId = acym_getVar('int', 'list_id', 0);
850 885 }
851 886 foreach ($matchingMails['elements'] as $oneTemplate) {
852 - $return .= '<div class="cell grid-x acym__templates__oneTpl acym__listing__block" id="'.acym_escape($oneTemplate->id).'">
887 + $return .= '<div class="cell grid-x acym__templates__oneTpl acym__listing__block" id="'.esc_attr($oneTemplate->id).'">
853 888 <div class="cell acym__templates__pic text-center">';
854 889
855 890 $url = acym_getVar('cmd', 'ctrl').'&task=edit&step=editEmail&from='.intval($oneTemplate->id).$returnUrl.'&type='.$type.$id.'&'.acym_getFormToken();
856 891 if (!empty($followup_id)) {
@@ -862,20 +897,20 @@
862 897 if (!empty($this->data['campaignInformation'])) {
863 898 $url .= '&id='.intval($this->data['campaignInformation']);
864 899 }
865 900 if (!$automation || !empty($returnUrl)) {
866 - $return .= '<a href="'.acym_completeLink($url, false, false, true).'">';
901 + $return .= '<a href="'.esc_url(acym_completeLink($url, false, false, true)).'">';
867 902 }
868 903
869 - $return .= '<img src="'.acym_escapeUrl(acym_getMailThumbnail($oneTemplate->thumbnail)).'" alt="template thumbnail"/>';
904 + $return .= '<img src="'.esc_url(acym_getMailThumbnail($oneTemplate->thumbnail)).'" alt="template thumbnail"/>';
870 905 if (!$automation || !empty($returnUrl)) {
871 906 $return .= '</a>';
872 907 }
873 908
874 909 if ($oneTemplate->drag_editor) {
875 - $return .= '<div class="acym__templates__choose__ribbon acyeditor">'.acym_translation('ACYM_DD_EDITOR').'</div>';
910 + $return .= '<div class="acym__templates__choose__ribbon acyeditor">'.esc_html(acym_translation('ACYM_DD_EDITOR')).'</div>';
876 911 } else {
877 - $return .= '<div class="acym__templates__choose__ribbon htmleditor">'.acym_translation('ACYM_HTML_EDITOR').'</div>';
912 + $return .= '<div class="acym__templates__choose__ribbon htmleditor">'.esc_html(acym_translation('ACYM_HTML_EDITOR')).'</div>';
878 913 }
879 914
880 915 if (strlen($oneTemplate->name) > 55) {
881 916 $oneTemplate->name = substr($oneTemplate->name, 0, 50).'...';
@@ -880,13 +915,14 @@
880 915 if (strlen($oneTemplate->name) > 55) {
881 916 $oneTemplate->name = substr($oneTemplate->name, 0, 50).'...';
882 917 }
883 918 $return .= '</div>
884 - <div class="cell grid-x acym__templates__footer text-center">
885 - <div class="cell acym__templates__footer__title acym_text_ellipsis" title="'.acym_escape($oneTemplate->name).'">'.acym_escape($oneTemplate->name).'</div>
886 - <div class="cell">'.acym_date($oneTemplate->creation_date, 'ACYM_DATE_FORMAT_LC3').'</div>
887 - </div>
888 - </div>';
919 + <div class="cell grid-x acym__templates__footer text-center">
920 + <div class="cell acym__templates__footer__title acym_text_ellipsis" title="'.esc_attr($oneTemplate->name).'">';
921 + $return .= esc_html($oneTemplate->name).'</div>
922 + <div class="cell">'.esc_html(acym_date($oneTemplate->creation_date, 'ACYM_DATE_FORMAT_LC3')).'</div>
923 + </div>
924 + </div>';
889 925 }
890 926
891 927 $return .= '</div>';
892 928
@@ -891,21 +927,23 @@
891 927 $return .= '</div>';
892 928
893 929 $pagination->setStatus((int)$matchingMails['total']->total, $page, $mailsPerPage);
894 930
895 - $return .= $pagination->displayAjax();
931 + ob_start();
932 + $pagination->display('', '__ajax');
933 + $return .= ob_get_clean();
896 934
897 - echo $return;
935 + acym_sendAjaxResponse('', ['pagination' => $return]);
898 936 exit;
899 937 }
900 938
901 939 public function setNewThumbnail(): void
902 940 {
903 - if (!acym_isAdmin()) {
941 + if (!acym_isAdmin() || (!acym_isAllowed('mails') && !acym_isAllowed('campaigns'))) {
904 942 die('Access denied for thumbnail creation');
905 943 }
906 944
907 - acym_checkToken();
945 + wp_verify_nonce(acym_getVar('cmd', '_wpnonce'), 'acymnonce') || die('Invalid Token');
908 946 $contentThumbnail = acym_getVar('string', 'content', '');
909 947 if (strpos($contentThumbnail, 'data:image/png') !== 0) {
910 948 acym_sendAjaxResponse('This file is not allowed.', [], false);
911 949 }
@@ -937,9 +975,9 @@
937 975 }
938 976
939 977 public function setNewIconShare(): void
940 978 {
941 - acym_checkToken();
979 + wp_verify_nonce(acym_getVar('cmd', '_wpnonce'), 'acymnonce') || die('Invalid Token');
942 980 $socialName = acym_getVar('string', 'social', '');
943 981 $socialMedias = acym_getSocialMedias();
944 982 if (!in_array($socialName, $socialMedias)) {
945 983 acym_sendAjaxResponse(acym_translationSprintf('ACYM_UNKNOWN_SOCIAL', $socialName), [], false);
@@ -944,17 +982,21 @@
944 982 if (!in_array($socialName, $socialMedias)) {
945 983 acym_sendAjaxResponse(acym_translationSprintf('ACYM_UNKNOWN_SOCIAL', $socialName), [], false);
946 984 }
947 985
948 - $extension = pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
986 + $file = acym_getVar('array', 'file', [], 'FILES');
987 + $extension = pathinfo($file['name'], PATHINFO_EXTENSION);
949 988 $newPath = ACYM_UPLOAD_FOLDER.'socials'.DS.$socialName;
950 989 $newPathComplete = $newPath.'.'.$extension;
951 990
952 - $allowedExtensions = acym_getImageFileExtensions();
991 + $allowedExtensions = acym_getImageFileExtensions(true);
953 992 if (!in_array($extension, $allowedExtensions)) {
954 - $errorMessage = acym_translationSprintf('ACYM_ACCEPTED_TYPE', $extension, implode(', ', $allowedExtensions));
955 - } elseif (!acym_uploadFile($_FILES['file']['tmp_name'], ACYM_ROOT.$newPathComplete)) {
993 + $errorMessage = acym_translationSprintf('ACYM_ACCEPTED_TYPE', esc_html($extension), implode(', ', $allowedExtensions));
994 + } elseif (!acym_uploadFile($file['tmp_name'], ACYM_ROOT.$newPathComplete)) {
956 995 $errorMessage = acym_translationSprintf('ACYM_ERROR_UPLOADING_FILE_X', $newPathComplete);
996 + } elseif (strtolower($extension) === 'svg' && !acym_isSvgFileSafe(ACYM_ROOT.$newPathComplete)) {
997 + acym_deleteFile(ACYM_ROOT.$newPathComplete);
998 + $errorMessage = acym_translationSprintf('ACYM_ERROR_UPLOADING_FILE_X', $newPathComplete);
957 999 }
958 1000
959 1001 if (!empty($errorMessage)) {
960 1002 acym_sendAjaxResponse($errorMessage, [], false);
@@ -997,12 +1039,20 @@
997 1039
998 1040 $mailClass = new MailClass();
999 1041 $mail = $mailClass->getOneById($idMail);
1000 1042
1043 + if (empty($mail)) {
1044 + exit;
1045 + }
1046 +
1047 + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Returning content as text/css, need to output raw content.
1001 1048 echo $mailClass->buildCSS($mail->stylesheet);
1002 1049 exit;
1003 1050 }
1004 1051
1052 + /*
1053 + * Method used to send a test when editing a notification email
1054 + */
1005 1055 public function test(): void
1006 1056 {
1007 1057 $mailId = $this->store();
1008 1058 $return = acym_getVar('string', 'return', '');
@@ -1039,9 +1089,9 @@
1039 1089 }
1040 1090
1041 1091 public function sendTest(): void
1042 1092 {
1043 - acym_checkToken();
1093 + wp_verify_nonce(acym_getVar('cmd', '_wpnonce'), 'acymnonce') || die('Invalid Token');
1044 1094 $controller = acym_getVar('string', 'controller', 'mails');
1045 1095 $level = 'info';
1046 1096
1047 1097 $testNote = acym_getVar('string', 'test_note', '');