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/Classes/FieldClass.php +81 -22 11.0.3 → 11.1.0 View file →
@@ -142,8 +142,9 @@
142 142 }
143 143
144 144 public function store(int $userID, array $fields, bool $ajax = false): void
145 145 {
146 + $uploadedFileFields = [];
146 147 $customField = acym_getVar('array', 'customField', [], 'FILES');
147 148 if (!empty($customField['tmp_name'])) {
148 149 $uploadFolder = trim(acym_cleanPath(html_entity_decode(acym_getFilesFolder(true))), DS.' ').DS;
149 150 $uploadPath = acym_cleanPath(ACYM_ROOT.$uploadFolder.'userfiles'.DS.$userID.DS);
@@ -160,10 +161,11 @@
160 161 $fileName = $customField['name'][$key];
161 162 while (is_array($fileName) && isset($fileName[0])) {
162 163 $fileName = $fileName[0];
163 164 }
165 + $fileName = basename($fileName);
164 166
165 - if (!preg_match('#\.('.implode('|', $allowedExtensions).')$#Ui', $fileName)) {
167 + if (!acym_fileNameValid($fileName) || !preg_match('#\.('.implode('|', array_map('preg_quote', $allowedExtensions)).')$#i', $fileName)) {
166 168 $ext = substr($fileName, strrpos($fileName, '.') + 1);
167 169 if ($ajax) {
168 170 $this->errors[] = acym_translationSprintf(
169 171 'ACYM_ACCEPTED_TYPE',
@@ -184,9 +186,9 @@
184 186
185 187 continue;
186 188 }
187 189
188 - if (!acym_uploadFile($value, $uploadPath.$fileName)) {
190 + if (!acym_isPathWithinDirectory($uploadPath, $uploadPath.$fileName) || !acym_uploadFile($value, $uploadPath.$fileName)) {
189 191 if ($ajax) {
190 192 $this->errors[] = acym_translation('ACYM_ERROR_SAVING');
191 193 } else {
192 194 acym_enqueueMessage(acym_translation('ACYM_ERROR_SAVING'), 'error');
@@ -193,9 +195,10 @@
193 195 }
194 196
195 197 continue;
196 198 }
197 - $fields[$key] = $customField['name'][$key];
199 + $fields[$key] = $fileName;
200 + $uploadedFileFields[$key] = true;
198 201 }
199 202 }
200 203
201 204 if (empty($fields)) return;
@@ -248,8 +251,13 @@
248 251 if (in_array($field->type, ['text', 'textarea']) && !empty($fieldOptions->max_characters)) {
249 252 $value = substr($value, 0, $fieldOptions->max_characters);
250 253 }
251 254
255 + // A file field value can only be set through an actual upload, never through a plain posted string
256 + if ($field->type === 'file' && strlen($value) !== 0 && empty($uploadedFileFields[$id])) {
257 + continue;
258 + }
259 +
252 260 // If deleting a file field, also delete the physical file
253 261 if ($field->type === 'file' && strlen($value) === 0) {
254 262 $oldValue = acym_loadResult(
255 263 'SELECT `value` FROM #__acym_user_has_field WHERE `user_id` = '.intval($userID).' AND `field_id` = '.intval($id)
@@ -258,15 +266,21 @@
258 266 $fileName = @json_decode($oldValue, true);
259 267 if (is_array($fileName)) $fileName = $fileName[0];
260 268 if (empty($fileName)) $fileName = $oldValue;
261 269
262 - $uploadFolder = trim(acym_cleanPath(html_entity_decode(acym_getFilesFolder(true))), DS.' ').DS;
263 - // Try new path (with user folder) first, then legacy path (without user folder)
264 - $filePath = acym_cleanPath(ACYM_ROOT.$uploadFolder.'userfiles'.DS.$userID.DS.$fileName);
265 - if (!file_exists($filePath)) {
266 - $filePath = acym_cleanPath(ACYM_ROOT.$uploadFolder.'userfiles'.DS.$fileName);
270 + $fileName = basename($fileName);
271 + if (acym_fileNameValid($fileName)) {
272 + $uploadFolder = trim(acym_cleanPath(html_entity_decode(acym_getFilesFolder(true))), DS.' ').DS;
273 + $userfilesBase = acym_cleanPath(ACYM_ROOT.$uploadFolder.'userfiles'.DS);
274 + // Try new path (with user folder) first, then legacy path (without user folder)
275 + $filePath = acym_cleanPath($userfilesBase.$userID.DS.$fileName);
276 + if (!file_exists($filePath)) {
277 + $filePath = acym_cleanPath($userfilesBase.$fileName);
278 + }
279 + if (acym_isPathWithinDirectory($userfilesBase, $filePath)) {
280 + acym_deleteFile($filePath);
281 + }
267 282 }
268 - acym_deleteFile($filePath);
269 283 }
270 284 }
271 285
272 286 // The user removed a value, don't add an empty line and remove any previous value from the bd
@@ -561,8 +575,19 @@
561 575 : json_encode($defaultValue)
562 576 );
563 577 }
564 578
579 + $allowedFieldNameTags = array_merge(SecurityHelper::ALLOWED_HTML_FIELD_NAME, [
580 + 'a' => [
581 + 'href' => true,
582 + 'target' => true,
583 + 'title' => true,
584 + 'class' => true,
585 + 'rel' => true,
586 + 'aria-label' => true,
587 + ],
588 + ]);
589 +
565 590 $labelTypes = ['text', 'textarea', 'single_dropdown', 'multiple_dropdown', 'custom_text', 'language'];
566 591 $titleTypes = ['date', 'radio', 'checkbox', 'file'];
567 592
568 593 $useLabel = $displayOutside && (in_array($field->id, [1, 2]) || in_array($field->type, $labelTypes));
@@ -568,12 +593,27 @@
568 593 $useLabel = $displayOutside && (in_array($field->id, [1, 2]) || in_array($field->type, $labelTypes));
569 594 $useTitle = $displayOutside && in_array($field->type, $titleTypes);
570 595
571 596 if ($useLabel) {
572 - echo '<label class="cell margin-top-1"><span class="acym__users__creation__fields__title">'.esc_html($field->name).'</span>';
597 + echo '<label class="cell margin-top-1"><span class="acym__users__creation__fields__title">'.wp_kses(
598 + $field->name,
599 + SecurityHelper::ALLOWED_HTML_FIELD_NAME
600 + ).'</span>';
573 601 }
574 602 if ($useTitle) {
575 - echo '<div class="cell margin-top-1"><div class="acym__users__creation__fields__title">'.esc_html($field->name).'</div>';
603 + if (in_array($field->type, ['date', 'radio', 'checkbox'])) {
604 + $groupLabelId = 'acym__field__label__'.intval($field->id).'_'.acym_rand(1000, 9999);
605 + echo '<div class="cell margin-top-1" role="group" aria-labelledby="'.esc_attr($groupLabelId).'"'.($field->required ? ' aria-required="true"' : '').'>';
606 + echo '<div id="'.esc_attr($groupLabelId).'" class="acym__users__creation__fields__title">'.wp_kses(
607 + $field->name,
608 + $allowedFieldNameTags
609 + ).'</div>';
610 + } else {
611 + echo '<div class="cell margin-top-1"><div class="acym__users__creation__fields__title">'.wp_kses(
612 + $field->name,
613 + $allowedFieldNameTags
614 + ).'</div>';
615 + }
576 616 }
577 617
578 618 $messageRequired = empty($field->option->error_message) ? '' : acym_translation($field->option->error_message);
579 619 $requiredJson = json_encode(['type' => $field->type, 'message' => $messageRequired]);
@@ -579,10 +619,12 @@
579 619 $requiredJson = json_encode(['type' => $field->type, 'message' => $messageRequired]);
580 620 $required = '';
581 621 if ($field->required) {
582 622 $data['data-required'] = $requiredJson;
623 + $data['aria-required'] = 'true';
583 624 $required = ' data-required="'.esc_attr($requiredJson).'"';
584 625 $attributesSelectField['data-required'] = $requiredJson;
626 + $attributesSelectField['aria-required'] = 'true';
585 627 }
586 628
587 629 $readonly = '';
588 630 if (!$showField && acym_isAdmin() && acym_level(ACYM_ENTERPRISE) && $isCoreField) {
@@ -681,8 +723,11 @@
681 723 echo '<textarea name="'.esc_attr($name).'"';
682 724 if (!empty($data['data-required'])) {
683 725 echo ' data-required="'.esc_attr($data['data-required']).'"';
684 726 }
727 + if (!empty($data['aria-required'])) {
728 + echo ' aria-required="true"';
729 + }
685 730 if (isset($data['maxCharacters'])) {
686 731 echo ' maxlength="'.esc_attr($data['maxCharacters']).'"';
687 732 }
688 733 echo ' rows="'.intval($field->option->rows).'" cols="'.intval($field->option->columns).'">';
@@ -727,8 +772,9 @@
727 772 'style' => empty($field->option->size) ? '' : 'width:'.$field->option->size.'px',
728 773 ];
729 774 if ($field->required) {
730 775 $attributes['data-required'] = $displayFront ? esc_attr($requiredJson) : $requiredJson;
776 + $attributes['aria-required'] = 'true';
731 777 }
732 778
733 779 acym_selectMultiple(
734 780 $valuesArray,
@@ -760,8 +806,9 @@
760 806 [
761 807 'select' => [
762 808 'data-required' => true,
763 809 'acym-field-type' => true,
810 + 'aria-label' => true,
764 811 ],
765 812 'div' => [
766 813 'class' => true,
767 814 ],
@@ -777,9 +824,10 @@
777 824 } else {
778 825 $downloadUrl = '';
779 826 if (!empty($defaultValue) && !empty($user->id)) {
780 827 $fileName = is_array($defaultValue) ? $defaultValue[0] : $defaultValue;
781 - if (!empty($fileName)) {
828 + $fileName = basename($fileName);
829 + if (!empty($fileName) && acym_fileNameValid($fileName)) {
782 830 $uploadFolder = trim(acym_cleanPath(html_entity_decode(acym_getFilesFolder(true))), DS.' ').DS;
783 831 $userFilePath = $uploadFolder.'userfiles'.DS.$user->id.DS.$fileName;
784 832 $legacyFilePath = $uploadFolder.'userfiles'.DS.$fileName;
785 833
@@ -800,9 +848,14 @@
800 848 $indicator = !empty($defaultValue[0]) ? $defaultValue[0] : '';
801 849 $number = !empty($defaultValue[1]) ? $defaultValue[1] : '';
802 850
803 851 if ($displayOutside) {
804 - echo '<div class="cell margin-top-1 grid-x"><div class="acym__users__creation__fields__title cell">'.esc_html($field->name).'</div>';
852 + $phoneLabelId = 'acym__field__label__'.intval($field->id).'_'.acym_rand(1000, 9999);
853 + echo '<div class="cell margin-top-1 grid-x" role="group" aria-labelledby="'.esc_attr($phoneLabelId).'"'.($field->required ? ' aria-required="true"' : '').'>';
854 + echo '<div id="'.esc_attr($phoneLabelId).'" class="acym__users__creation__fields__title cell">'.wp_kses(
855 + $field->name,
856 + SecurityHelper::ALLOWED_HTML_FIELD_NAME
857 + ).'</div>';
805 858 }
806 859 echo '<div class="cell large-5 medium-4 padding-next-1">';
807 860 acym_generateCountryNumber($name.'[code]', $indicator);
808 861 echo '</div>';
@@ -812,8 +865,11 @@
812 865 $data['name'] = $name.'[phone]';
813 866 $data['autocomplete'] = 'tel-national';
814 867 $data['class'] = 'cell large-7 medium-8';
815 868 $data['value'] = $number;
869 + if ($displayOutside) {
870 + $data['aria-label'] = acym_translation('ACYM_PHONE_NUMBER');
871 + }
816 872 include acym_getPartial('fields', 'input');
817 873
818 874 if ($displayOutside) {
819 875 echo '</div>';
@@ -829,9 +885,9 @@
829 885 if ($useTitle) {
830 886 echo '</div>';
831 887 }
832 888
833 - echo '<div class="acym__field__error__block" data-acym-field-id="'.intval($field->id).'"></div>';
889 + echo '<div class="acym__field__error__block" data-acym-field-id="'.intval($field->id).'" aria-live="assertive"></div>';
834 890 if ($displayFront && $extraErrors && !acym_isAdmin()) {
835 891 echo '<div class="acym__message__invalid__field acym__color__error" style="display: none;">';
836 892 echo '<i class="acymicon-times-circle acym__cross__invalid acym__color__error"></i>'.esc_html(acym_translation('ACYM_THANKS_TO_FILL_IN_THIS_FIELD')).'</div>';
837 893 }
@@ -983,16 +1039,19 @@
983 1039 echo '<span class="acym__users__creation__fields__title">'.esc_html(acym_translation('ACYM_EMAIL_CONFIRMATION')).'</span>';
984 1040 }
985 1041
986 1042 $placeholder = !$displayOutside ? acym_translation('ACYM_EMAIL_CONFIRMATION') : '';
987 - echo '<input id="'.esc_attr($uniqueId).'"
988 - '.(empty($field->option->size) ? '' : ' style="width:'.intval($field->option->size).'px"').'
989 - required
990 - type="email"
991 - class="cell acym__user__edit__email"
992 - name="user[email_confirmation]"
993 - placeholder="'.esc_attr($placeholder).'" />';
994 - echo '<span class="acym__field__error__block"></span>';
1043 + echo '<input id="'.esc_attr($uniqueId).'"
1044 + '.(empty($field->option->size) ? '' : ' style="width:'.intval($field->option->size).'px"').'
1045 + required
1046 + type="email"
1047 + class="cell acym__user__edit__email"
1048 + name="user[email_confirmation]"';
1049 + if (!$displayOutside) {
1050 + echo ' aria-label="'.esc_attr(acym_translation('ACYM_EMAIL_CONFIRMATION')).'"';
1051 + }
1052 + echo ' placeholder="'.esc_attr($placeholder).'" />';
1053 + echo '<span class="acym__field__error__block" aria-live="assertive"></span>';
995 1054
996 1055 $extraErrors = $this->config->get('extra_errors', '0');
997 1056 if ($extraErrors && !acym_isAdmin()) {
998 1057 echo '<span class="acym__message__invalid__field acym__color__error" style="display: none;">';