| @@ -376,9 +376,13 @@ | ||
| 376 | 376 | * @return string |
| 377 | 377 | * @since 9.0.0 |
| 378 | 378 | */ |
| 379 | 379 | function wpvr_render_scene_info_row( $html, $postdata, $id ) { |
| 380 | - $pano_id = 'pano' . absint( $id ); | |
| 380 | + if ( preg_match( '/id=[\'"](pano' . absint( $id ) . '(?:_\d+)?)[\'"]/', $html, $m ) ) { | |
| 381 | + $pano_id = $m[1]; | |
| 382 | + } else { | |
| 383 | + $pano_id = 'pano' . absint( $id ); | |
| 384 | + } | |
| 381 | 385 | $pano_id_json = wp_json_encode( $pano_id ); |
| 382 | 386 | $by_label = wp_json_encode( __( 'By', 'wpvr' ) ); |
| 383 | 387 | $tour_layout = is_array( $postdata['tourLayout'] ?? null ) |
| 384 | 388 | ? ( $postdata['tourLayout']['layout'] ?? 'default' ) |
| @@ -384,9 +388,9 @@ | ||
| 384 | 388 | ? ( $postdata['tourLayout']['layout'] ?? 'default' ) |
| 385 | 389 | : ( $postdata['tourLayout'] ?? 'default' ); |
| 386 | 390 | $is_classic_layout = wp_json_encode( 'layout1' !== $tour_layout ); |
| 387 | 391 | |
| 388 | - $html .= '<style id="wpvr-scene-info-' . esc_attr( $id ) . '"> | |
| 392 | + $html .= '<style id="wpvr-scene-info-' . esc_attr( $pano_id ) . '"> | |
| 389 | 393 | #' . esc_attr( $pano_id ) . ' .pnlm-panorama-info.wpvr-scene-info-row { |
| 390 | 394 | display: flex !important; |
| 391 | 395 | align-items: center !important; |
| 392 | 396 | justify-content: center !important; |
| @@ -482,9 +486,9 @@ | ||
| 482 | 486 | padding-left: 7px !important; |
| 483 | 487 | } |
| 484 | 488 | } |
| 485 | 489 | </style> |
| 486 | - <script id="wpvr-scene-info-script-' . esc_attr( $id ) . '"> | |
| 490 | + <script id="wpvr-scene-info-script-' . esc_attr( $pano_id ) . '"> | |
| 487 | 491 | (function () { |
| 488 | 492 | var pano = document.getElementById(' . $pano_id_json . '); |
| 489 | 493 | var byLabel = ' . $by_label . '; |
| 490 | 494 | |
| @@ -497,9 +501,9 @@ | ||
| 497 | 501 | return; |
| 498 | 502 | } |
| 499 | 503 | |
| 500 | 504 | var firstNode = author.firstChild; |
| 501 | - if (firstNode && firstNode.nodeType === 3) { | |
| 505 | + if (firstNode ? firstNode.nodeType === 3 : false) { | |
| 502 | 506 | var prefix = byLabel + " "; |
| 503 | 507 | if (firstNode.nodeValue.indexOf(prefix) === 0) { |
| 504 | 508 | firstNode.nodeValue = firstNode.nodeValue.slice(prefix.length); |
| 505 | 509 | } |
| @@ -525,10 +529,10 @@ | ||
| 525 | 529 | } |
| 526 | 530 | |
| 527 | 531 | var title = info.querySelector(".pnlm-title-box"); |
| 528 | 532 | var author = info.querySelector(".pnlm-author-box"); |
| 529 | - var hasTitle = !!(title && title.textContent.trim()); | |
| 530 | - var hasAuthor = !!(author && author.textContent.trim()); | |
| 533 | + var hasTitle = Boolean(title ? title.textContent.trim() : false); | |
| 534 | + var hasAuthor = Boolean(author ? author.textContent.trim() : false); | |
| 531 | 535 | |
| 532 | 536 | if (hasTitle) { |
| 533 | 537 | title.setAttribute("title", title.textContent.trim()); |
| 534 | 538 | } else if (title) { |
| @@ -784,12 +788,16 @@ | ||
| 784 | 788 | function sanitize_content_preserve_styles($content, $allow_forms = false) { |
| 785 | 789 | // Decode HTML entities first (in case content was encoded in database) |
| 786 | 790 | $content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5, 'UTF-8'); |
| 787 | 791 | |
| 788 | - // Escape <script> blocks to display as text instead of removing them | |
| 789 | - $content = preg_replace_callback('/<script\b[^>]*>(.*?)<\/script>/si', function($matches) { | |
| 790 | - return esc_html($matches[0]); // Convert to plain text | |
| 791 | - }, $content); | |
| 792 | + // Escape or strip <script> blocks | |
| 793 | + if ($allow_forms) { | |
| 794 | + $content = preg_replace('/<script\b[^>]*>[\s\S]*?<\/script>/i', '', $content); | |
| 795 | + } else { | |
| 796 | + $content = preg_replace_callback('/<script\b[^>]*>(.*?)<\/script>/si', function($matches) { | |
| 797 | + return esc_html($matches[0]); // Convert to plain text | |
| 798 | + }, $content); | |
| 799 | + } | |
| 792 | 800 | |
| 793 | 801 | // Strip dangerous URL-based attributes |
| 794 | 802 | $content = preg_replace('/(href|action|formaction)\s*=\s*["\']?\s*(javascript|vbscript|data|about):/i', '$1=""', $content); |
| 795 | 803 | |
| @@ -832,10 +840,16 @@ | ||
| 832 | 840 | $css = preg_replace('/-moz-binding\s*:/i', '', $css); |
| 833 | 841 | return '<style>' . esc_html($css) . '</style>'; |
| 834 | 842 | }, $content); |
| 835 | 843 | |
| 836 | - // Allow iframes from safe sources only (e.g., YouTube, Vimeo) | |
| 844 | + // Allow iframes and styles from safe sources only | |
| 837 | 845 | $allowed_tags = wp_kses_allowed_html('post'); |
| 846 | + $allowed_tags['style'] = [ | |
| 847 | + 'type' => true, | |
| 848 | + 'id' => true, | |
| 849 | + 'class' => true, | |
| 850 | + 'media' => true, | |
| 851 | + ]; | |
| 838 | 852 | $allowed_tags['iframe'] = [ |
| 839 | 853 | 'src' => true, |
| 840 | 854 | 'width' => true, |
| 841 | 855 | 'height' => true, |
| @@ -865,37 +879,47 @@ | ||
| 865 | 879 | ]; |
| 866 | 880 | |
| 867 | 881 | if ($allow_forms) { |
| 868 | 882 | $form_attributes = [ |
| 869 | - 'id' => true, | |
| 870 | - 'class' => true, | |
| 871 | - 'style' => true, | |
| 872 | - 'name' => true, | |
| 873 | - 'value' => true, | |
| 874 | - 'type' => true, | |
| 875 | - 'placeholder' => true, | |
| 876 | - 'action' => true, | |
| 877 | - 'method' => true, | |
| 878 | - 'target' => true, | |
| 879 | - 'enctype' => true, | |
| 880 | - 'disabled' => true, | |
| 881 | - 'readonly' => true, | |
| 882 | - 'required' => true, | |
| 883 | - 'checked' => true, | |
| 884 | - 'selected' => true, | |
| 885 | - 'multiple' => true, | |
| 886 | - 'size' => true, | |
| 887 | - 'rows' => true, | |
| 888 | - 'cols' => true, | |
| 889 | - 'maxlength' => true, | |
| 890 | - 'minlength' => true, | |
| 891 | - 'min' => true, | |
| 892 | - 'max' => true, | |
| 893 | - 'step' => true, | |
| 894 | - 'pattern' => true, | |
| 895 | - 'autocomplete'=> true, | |
| 896 | - 'autofocus' => true, | |
| 897 | - 'for' => true, | |
| 883 | + 'id' => true, | |
| 884 | + 'class' => true, | |
| 885 | + 'style' => true, | |
| 886 | + 'name' => true, | |
| 887 | + 'value' => true, | |
| 888 | + 'type' => true, | |
| 889 | + 'placeholder' => true, | |
| 890 | + 'action' => true, | |
| 891 | + 'method' => true, | |
| 892 | + 'target' => true, | |
| 893 | + 'enctype' => true, | |
| 894 | + 'disabled' => true, | |
| 895 | + 'readonly' => true, | |
| 896 | + 'required' => true, | |
| 897 | + 'checked' => true, | |
| 898 | + 'selected' => true, | |
| 899 | + 'multiple' => true, | |
| 900 | + 'size' => true, | |
| 901 | + 'rows' => true, | |
| 902 | + 'cols' => true, | |
| 903 | + 'maxlength' => true, | |
| 904 | + 'minlength' => true, | |
| 905 | + 'min' => true, | |
| 906 | + 'max' => true, | |
| 907 | + 'step' => true, | |
| 908 | + 'pattern' => true, | |
| 909 | + 'autocomplete' => true, | |
| 910 | + 'autofocus' => true, | |
| 911 | + 'for' => true, | |
| 912 | + 'data-*' => true, | |
| 913 | + 'data-form_id' => true, | |
| 914 | + 'data-form_instance' => true, | |
| 915 | + 'data-name' => true, | |
| 916 | + 'data-type' => true, | |
| 917 | + 'aria-invalid' => true, | |
| 918 | + 'aria-required' => true, | |
| 919 | + 'aria-label' => true, | |
| 920 | + 'aria-describedby' => true, | |
| 921 | + 'aria-labelledby' => true, | |
| 898 | 922 | ]; |
| 899 | 923 | $allowed_tags['form'] = $form_attributes; |
| 900 | 924 | $allowed_tags['input'] = $form_attributes; |
| 901 | 925 | $allowed_tags['button'] = $form_attributes; |
| @@ -905,8 +929,18 @@ | ||
| 905 | 929 | $allowed_tags['optgroup'] = $form_attributes; |
| 906 | 930 | $allowed_tags['label'] = $form_attributes; |
| 907 | 931 | $allowed_tags['fieldset'] = $form_attributes; |
| 908 | 932 | $allowed_tags['legend'] = $form_attributes; |
| 933 | + if (!isset($allowed_tags['div'])) { | |
| 934 | + $allowed_tags['div'] = []; | |
| 935 | + } | |
| 936 | + $allowed_tags['div']['data-*'] = true; | |
| 937 | + $allowed_tags['div']['data-form_id'] = true; | |
| 938 | + $allowed_tags['div']['data-form_instance'] = true; | |
| 939 | + if (!isset($allowed_tags['span'])) { | |
| 940 | + $allowed_tags['span'] = []; | |
| 941 | + } | |
| 942 | + $allowed_tags['span']['data-*'] = true; | |
| 909 | 943 | } |
| 910 | 944 | |
| 911 | 945 | // Apply wp_kses() to keep only allowed tags/attributes |
| 912 | 946 | $content = wp_kses($content, $allowed_tags); |
| @@ -1023,15 +1057,22 @@ | ||
| 1023 | 1057 | } |
| 1024 | 1058 | |
| 1025 | 1059 | function wpvr_rest_route_permission() |
| 1026 | 1060 | { |
| 1027 | - return true; | |
| 1061 | + $post_type_obj = get_post_type_object( 'wpvr_item' ); | |
| 1062 | + $edit_cap = $post_type_obj ? $post_type_obj->cap->edit_posts : 'edit_wpvr_tours'; | |
| 1063 | + return current_user_can( $edit_cap ) || current_user_can( 'edit_posts' ); | |
| 1028 | 1064 | } |
| 1029 | 1065 | |
| 1030 | 1066 | function wpvr_rest_data_set() |
| 1031 | 1067 | { |
| 1068 | + $post_type_obj = get_post_type_object( 'wpvr_item' ); | |
| 1069 | + $edit_cap = $post_type_obj ? $post_type_obj->cap->edit_posts : 'edit_wpvr_tours'; | |
| 1070 | + $post_status = current_user_can( $edit_cap ) ? array( 'publish', 'draft', 'private' ) : 'publish'; | |
| 1071 | + | |
| 1032 | 1072 | $query = new WP_Query(array( |
| 1033 | - 'post_type' => 'wpvr_item', | |
| 1073 | + 'post_type' => 'wpvr_item', | |
| 1074 | + 'post_status' => $post_status, | |
| 1034 | 1075 | 'posts_per_page' => -1, |
| 1035 | 1076 | )); |
| 1036 | 1077 | |
| 1037 | 1078 | $wpvr_list = array(); |
| @@ -1044,8 +1085,9 @@ | ||
| 1044 | 1085 | $title = $post_id . ' : ' . $title; |
| 1045 | 1086 | $list_ob = array('value' => $post_id, 'label' => $title); |
| 1046 | 1087 | array_push($wpvr_list, $list_ob); |
| 1047 | 1088 | } |
| 1089 | + wp_reset_postdata(); | |
| 1048 | 1090 | |
| 1049 | 1091 | return $wpvr_list; |
| 1050 | 1092 | } |
| 1051 | 1093 | |
| @@ -1354,5 +1396,30 @@ | ||
| 1354 | 1396 | } |
| 1355 | 1397 | } |
| 1356 | 1398 | |
| 1357 | 1399 | return wp_kses( $content, $allowed_tags ); |
| 1400 | +} | |
| 1401 | + | |
| 1402 | +add_filter('fluentform/form_vars_for_JS', 'wpvr_fluent_form_register_inline_vars', 10, 2); | |
| 1403 | +/** | |
| 1404 | + * Register Fluent Forms JS vars safely via WordPress script API. | |
| 1405 | + * Prevents raw script tag concatenation in HTML while ensuring form handlers initialize. | |
| 1406 | + * | |
| 1407 | + * @param array $vars Form configuration variables from Fluent Forms. | |
| 1408 | + * @param object $form Fluent Form database record. | |
| 1409 | + * @return array | |
| 1410 | + */ | |
| 1411 | +function wpvr_fluent_form_register_inline_vars($vars, $form) { | |
| 1412 | + if (!empty($vars['form_instance'])) { | |
| 1413 | + $inline_js = 'window.fluent_form_' . esc_js($vars['form_instance']) . ' = ' . wp_json_encode($vars) . ';'; | |
| 1414 | + if (wp_script_is('fluent-form-submission', 'done') || wp_doing_ajax()) { | |
| 1415 | + if (function_exists('wp_print_inline_script_tag')) { | |
| 1416 | + wp_print_inline_script_tag($inline_js); | |
| 1417 | + } else { | |
| 1418 | + echo '<script type="text/javascript">' . $inline_js . '</script>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped | |
| 1419 | + } | |
| 1420 | + } else { | |
| 1421 | + wp_add_inline_script('fluent-form-submission', $inline_js, 'before'); | |
| 1422 | + } | |
| 1423 | + } | |
| 1424 | + return $vars; | |
| 1358 | 1425 | } |