| @@ -103,9 +103,9 @@ | ||
| 103 | 103 | } |
| 104 | 104 | |
| 105 | 105 | $nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : ''; |
| 106 | 106 | |
| 107 | - if (!wp_verify_nonce($nonce, 'wpvr')) { | |
| 107 | + if ( ! wp_verify_nonce( $nonce, 'wpvr_setup_wizard' ) && ! wp_verify_nonce( $nonce, 'wpvr' ) ) { | |
| 108 | 108 | wp_send_json_error(array('message' => 'Permission denied.'), 403); |
| 109 | 109 | } |
| 110 | 110 | |
| 111 | 111 | update_option('high_res_image', 'true'); |
| @@ -178,9 +178,9 @@ | ||
| 178 | 178 | $panovideo = sanitize_text_field( wp_unslash( $_POST['panovideo'] ) ); |
| 179 | 179 | } |
| 180 | 180 | |
| 181 | 181 | $post_type = get_post_type($postid); |
| 182 | - if ($post_type != 'wpvr_item') { | |
| 182 | + if ($post_type != 'wpvr_item' || ! current_user_can('edit_post', $postid)) { | |
| 183 | 183 | die(); |
| 184 | 184 | } |
| 185 | 185 | |
| 186 | 186 | do_action('wpvr_pro_street_view_preview', $postid, $panoid); |
| @@ -247,10 +247,13 @@ | ||
| 247 | 247 | * |
| 248 | 248 | * @return void |
| 249 | 249 | */ |
| 250 | 250 | $post_type = get_post_type( $postid ); |
| 251 | - if ($post_type != 'wpvr_item') { | |
| 252 | - die(); | |
| 251 | + if ($post_type != 'wpvr_item' || ! current_user_can( 'edit_post', $postid )) { | |
| 252 | + wp_send_json([ | |
| 253 | + 'success' => false, | |
| 254 | + 'data' => 'Permission denied.' | |
| 255 | + ]); | |
| 253 | 256 | } |
| 254 | 257 | |
| 255 | 258 | $panoid = 'pano' . $postid; |
| 256 | 259 | |
| @@ -461,9 +464,11 @@ | ||
| 461 | 464 | */ |
| 462 | 465 | public function wpvr_file_import() |
| 463 | 466 | { |
| 464 | 467 | //===Current user capabilities check===// |
| 465 | - if (!current_user_can('edit_posts')) { | |
| 468 | + $post_type_obj = get_post_type_object( 'wpvr_item' ); | |
| 469 | + $edit_cap = $post_type_obj ? $post_type_obj->cap->edit_posts : 'edit_wpvr_tours'; | |
| 470 | + if ( ! current_user_can( $edit_cap ) || ! current_user_can( 'upload_files' ) ) { | |
| 466 | 471 | $response = array( |
| 467 | 472 | 'success' => false, |
| 468 | 473 | 'data' => 'Permission denied.' |
| 469 | 474 | ); |
| @@ -604,9 +609,9 @@ | ||
| 604 | 609 | update_option('wpvr_webp_conversion', $wpvr_webp_conversion); |
| 605 | 610 | update_option('mobile_media_resize', $mobile_media_resize); |
| 606 | 611 | update_option('high_res_image', $high_res_image); |
| 607 | 612 | update_option('dis_on_hover', $dis_on_hover); |
| 608 | - update_option('wpvr_mobile_hotspot_tip', $wpvr_mobile_hotspot_tip ? 'true' : 'false'); | |
| 613 | + update_option('wpvr_mobile_hotspot_tip', 'true' === $wpvr_mobile_hotspot_tip ? 'true' : 'false'); | |
| 609 | 614 | update_option('wpvr_frontend_notice', $wpvr_frontend_notice); |
| 610 | 615 | update_option('wpvr_frontend_notice_area', $wpvr_frontend_notice_area); |
| 611 | 616 | update_option('wpvr_script_control', $wpvr_script_control); |
| 612 | 617 | update_option('wpvr_script_list', $wpvr_script_list); |
| @@ -712,9 +717,9 @@ | ||
| 712 | 717 | return; |
| 713 | 718 | } |
| 714 | 719 | $nonce = filter_input(INPUT_POST, 'security', FILTER_SANITIZE_FULL_SPECIAL_CHARS); |
| 715 | 720 | $nonce = !empty( $nonce ) ? $nonce : null; |
| 716 | - if ( !wp_verify_nonce( $nonce, 'wpvr' ) ) { | |
| 721 | + if ( ! wp_verify_nonce( $nonce, 'wpvr_setup_wizard' ) && ! wp_verify_nonce( $nonce, 'wpvr' ) ) { | |
| 717 | 722 | wp_send_json_error( array( 'message' => 'Invalid nonce' ), 400 ); |
| 718 | 723 | return; |
| 719 | 724 | } |
| 720 | 725 | |
| @@ -790,9 +795,9 @@ | ||
| 790 | 795 | return; |
| 791 | 796 | } |
| 792 | 797 | |
| 793 | 798 | $nonce = isset($_POST['security']) ? sanitize_text_field(wp_unslash( $_POST['security'] )) : ''; |
| 794 | - if ( !wp_verify_nonce( $nonce, 'wpvr' ) ) { | |
| 799 | + if ( ! wp_verify_nonce( $nonce, 'wpvr_setup_wizard' ) && ! wp_verify_nonce( $nonce, 'wpvr' ) ) { | |
| 795 | 800 | wp_send_json_error( array( 'message' => 'Invalid nonce' ), 400 ); |
| 796 | 801 | return; |
| 797 | 802 | } |
| 798 | 803 | |
| @@ -864,9 +869,9 @@ | ||
| 864 | 869 | return; |
| 865 | 870 | } |
| 866 | 871 | |
| 867 | 872 | $nonce = isset($_POST['security']) ? sanitize_text_field(wp_unslash( $_POST['security'] )) : ''; |
| 868 | - if ( !wp_verify_nonce( $nonce, 'wpvr' ) ) { | |
| 873 | + if ( ! wp_verify_nonce( $nonce, 'wpvr_setup_wizard' ) && ! wp_verify_nonce( $nonce, 'wpvr' ) ) { | |
| 869 | 874 | wp_send_json_error( array( 'message' => 'Invalid nonce' ), 400 ); |
| 870 | 875 | return; |
| 871 | 876 | } |
| 872 | 877 | |
| @@ -944,8 +949,14 @@ | ||
| 944 | 949 | 'post_title' => $title, |
| 945 | 950 | 'post_status' => 'publish', |
| 946 | 951 | 'post_type' => 'wpvr_item', |
| 947 | 952 | 'post_author' => get_current_user_id(), |
| 953 | + // meta_input is written before wp_insert_post() fires transition_post_status, | |
| 954 | + // so telemetry listening on that hook sees these flags already set. | |
| 955 | + 'meta_input' => array( | |
| 956 | + 'wpvr_created_from_wizard' => true, | |
| 957 | + 'wpvr_wizard_industry' => $industry, | |
| 958 | + ), | |
| 948 | 959 | ); |
| 949 | 960 | |
| 950 | 961 | $post_id = wp_insert_post( $post_data ); |
| 951 | 962 | if ( is_wp_error( $post_id ) ) { |
| @@ -962,10 +973,8 @@ | ||
| 962 | 973 | } |
| 963 | 974 | $remote_meta['panodata'] = $panodata; |
| 964 | 975 | |
| 965 | 976 | update_post_meta( $post_id, 'panodata', $panodata ); |
| 966 | - update_post_meta( $post_id, 'wpvr_created_from_wizard', true ); | |
| 967 | - update_post_meta( $post_id, 'wpvr_wizard_industry', $industry ); | |
| 968 | 977 | |
| 969 | 978 | if ( ! empty( $remote_meta ) ) { |
| 970 | 979 | foreach ( $remote_meta as $meta_key => $meta_value ) { |
| 971 | 980 | $sanitized_key = sanitize_key( $meta_key ); |
| @@ -1054,9 +1063,18 @@ | ||
| 1054 | 1063 | } |
| 1055 | 1064 | |
| 1056 | 1065 | $attachment_id = attachment_url_to_postid( $source_url ); |
| 1057 | 1066 | if ( ! $attachment_id ) { |
| 1058 | - $attachment_id = media_sideload_image( $source_url, $post_id, null, 'id' ); | |
| 1067 | + if ( ! current_user_can( 'upload_files' ) ) { | |
| 1068 | + continue; | |
| 1069 | + } | |
| 1070 | + | |
| 1071 | + $valid_url = wp_http_validate_url( $source_url ); | |
| 1072 | + if ( ! $valid_url ) { | |
| 1073 | + continue; | |
| 1074 | + } | |
| 1075 | + | |
| 1076 | + $attachment_id = media_sideload_image( $valid_url, $post_id, null, 'id' ); | |
| 1059 | 1077 | if ( is_wp_error( $attachment_id ) ) { |
| 1060 | 1078 | continue; |
| 1061 | 1079 | } |
| 1062 | 1080 | } |
| @@ -1111,9 +1129,9 @@ | ||
| 1111 | 1129 | return; |
| 1112 | 1130 | } |
| 1113 | 1131 | |
| 1114 | 1132 | $nonce = isset($_POST['security']) ? sanitize_text_field(wp_unslash( $_POST['security'] )) : ''; |
| 1115 | - if ( !wp_verify_nonce( $nonce, 'wpvr' ) ) { | |
| 1133 | + if ( ! wp_verify_nonce( $nonce, 'wpvr_setup_wizard' ) && ! wp_verify_nonce( $nonce, 'wpvr' ) ) { | |
| 1116 | 1134 | wp_send_json_error( array( 'message' => 'Invalid nonce' ), 400 ); |
| 1117 | 1135 | return; |
| 1118 | 1136 | } |
| 1119 | 1137 | |
| @@ -1173,15 +1191,17 @@ | ||
| 1173 | 1191 | * |
| 1174 | 1192 | * @since 8.5.48 |
| 1175 | 1193 | */ |
| 1176 | 1194 | public function wpvr_create_tour_from_wizard() { |
| 1177 | - if ( ! current_user_can( 'edit_posts' ) ) { | |
| 1195 | + $post_type_obj = get_post_type_object( 'wpvr_item' ); | |
| 1196 | + $create_cap = $post_type_obj ? $post_type_obj->cap->edit_posts : 'edit_wpvr_tours'; | |
| 1197 | + if ( ! current_user_can( $create_cap ) ) { | |
| 1178 | 1198 | wp_send_json_error( array( 'message' => 'Unauthorized user' ), 403 ); |
| 1179 | 1199 | return; |
| 1180 | 1200 | } |
| 1181 | 1201 | |
| 1182 | 1202 | $nonce = isset($_POST['security']) ? sanitize_text_field(wp_unslash( $_POST['security'] )) : ''; |
| 1183 | - if ( !wp_verify_nonce( $nonce, 'wpvr' ) ) { | |
| 1203 | + if ( ! wp_verify_nonce( $nonce, 'wpvr_setup_wizard' ) && ! wp_verify_nonce( $nonce, 'wpvr' ) ) { | |
| 1184 | 1204 | wp_send_json_error( array( 'message' => 'Invalid nonce' ), 400 ); |
| 1185 | 1205 | return; |
| 1186 | 1206 | } |
| 1187 | 1207 | |
| @@ -1194,8 +1214,30 @@ | ||
| 1194 | 1214 | wp_send_json_error( array( 'message' => 'Panodata is required' ) ); |
| 1195 | 1215 | return; |
| 1196 | 1216 | } |
| 1197 | 1217 | |
| 1218 | + if ( is_array( $panodata ) ) { | |
| 1219 | + if ( isset( $panodata['previewtext'] ) ) { | |
| 1220 | + $panodata['previewtext'] = sanitize_text_field( $panodata['previewtext'] ); | |
| 1221 | + } | |
| 1222 | + if ( isset( $panodata['defaultscene'] ) && is_string( $panodata['defaultscene'] ) ) { | |
| 1223 | + $panodata['defaultscene'] = preg_replace( '/[^0-9a-zA-Z_\-]/', '', $panodata['defaultscene'] ); | |
| 1224 | + } | |
| 1225 | + if ( isset( $panodata['panodata']['firstScene'] ) && is_string( $panodata['panodata']['firstScene'] ) ) { | |
| 1226 | + $panodata['panodata']['firstScene'] = preg_replace( '/[^0-9a-zA-Z_\-]/', '', $panodata['panodata']['firstScene'] ); | |
| 1227 | + } | |
| 1228 | + if ( isset( $panodata['panodata']['scene-list'] ) && is_array( $panodata['panodata']['scene-list'] ) ) { | |
| 1229 | + foreach ( $panodata['panodata']['scene-list'] as $s_idx => $s_val ) { | |
| 1230 | + if ( isset( $s_val['scene-id'] ) && is_string( $s_val['scene-id'] ) ) { | |
| 1231 | + $panodata['panodata']['scene-list'][ $s_idx ]['scene-id'] = preg_replace( '/[^0-9a-zA-Z_\-]/', '', $s_val['scene-id'] ); | |
| 1232 | + } | |
| 1233 | + } | |
| 1234 | + } | |
| 1235 | + } | |
| 1236 | + | |
| 1237 | + $publish_cap = $post_type_obj ? $post_type_obj->cap->publish_posts : 'publish_wpvr_tours'; | |
| 1238 | + $target_status = current_user_can( $publish_cap ) ? 'publish' : 'draft'; | |
| 1239 | + | |
| 1198 | 1240 | if ( $existing_post_id > 0 ) { |
| 1199 | 1241 | $existing_post = get_post( $existing_post_id ); |
| 1200 | 1242 | if ( ! $existing_post || 'wpvr_item' !== $existing_post->post_type || ! current_user_can( 'edit_post', $existing_post_id ) ) { |
| 1201 | 1243 | wp_send_json_error( array( 'message' => 'Invalid existing tour ID' ) ); |
| @@ -1206,9 +1248,14 @@ | ||
| 1206 | 1248 | wp_update_post( |
| 1207 | 1249 | array( |
| 1208 | 1250 | 'ID' => $post_id, |
| 1209 | 1251 | 'post_title' => $title, |
| 1210 | - 'post_status' => 'publish', | |
| 1252 | + 'post_status' => $target_status, | |
| 1253 | + // See note below: meta_input lands before transition_post_status fires. | |
| 1254 | + 'meta_input' => array( | |
| 1255 | + 'wpvr_created_from_wizard' => true, | |
| 1256 | + 'wpvr_wizard_industry' => $industry, | |
| 1257 | + ), | |
| 1211 | 1258 | ) |
| 1212 | 1259 | ); |
| 1213 | 1260 | } else { |
| 1214 | 1261 | // Create new post |
| @@ -1213,11 +1260,17 @@ | ||
| 1213 | 1260 | } else { |
| 1214 | 1261 | // Create new post |
| 1215 | 1262 | $post_data = array( |
| 1216 | 1263 | 'post_title' => $title, |
| 1217 | - 'post_status' => 'publish', | |
| 1264 | + 'post_status' => $target_status, | |
| 1218 | 1265 | 'post_type' => 'wpvr_item', |
| 1219 | 1266 | 'post_author' => get_current_user_id(), |
| 1267 | + // meta_input is written before wp_insert_post() fires transition_post_status, | |
| 1268 | + // so telemetry listening on that hook sees these flags already set. | |
| 1269 | + 'meta_input' => array( | |
| 1270 | + 'wpvr_created_from_wizard' => true, | |
| 1271 | + 'wpvr_wizard_industry' => $industry, | |
| 1272 | + ), | |
| 1220 | 1273 | ); |
| 1221 | 1274 | |
| 1222 | 1275 | $post_id = wp_insert_post( $post_data ); |
| 1223 | 1276 | |
| @@ -1240,22 +1293,34 @@ | ||
| 1240 | 1293 | |
| 1241 | 1294 | // Save panodata as post meta |
| 1242 | 1295 | update_post_meta( $post_id, 'panodata', $panodata ); |
| 1243 | 1296 | |
| 1244 | - // Mark as created from wizard | |
| 1245 | - update_post_meta( $post_id, 'wpvr_created_from_wizard', true ); | |
| 1246 | - update_post_meta( $post_id, 'wpvr_wizard_industry', $industry ); | |
| 1247 | - | |
| 1248 | 1297 | // Save template meta fields if provided (dynamic meta from API) |
| 1249 | 1298 | $template_meta = isset($_POST['templateMeta']) ? json_decode( wp_unslash( $_POST['templateMeta'] ), true ) : array(); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized |
| 1250 | 1299 | if ( ! empty( $template_meta ) && is_array( $template_meta ) ) { |
| 1300 | + $allowed_meta_keys = apply_filters( | |
| 1301 | + 'wpvr_wizard_allowed_template_meta_keys', | |
| 1302 | + array( | |
| 1303 | + 'wpvr_created_from_wizard', | |
| 1304 | + 'wpvr_wizard_industry', | |
| 1305 | + 'wpvr_tour_layout', | |
| 1306 | + 'wpvr_custom_css', | |
| 1307 | + 'wpvr_streetview_settings', | |
| 1308 | + 'wpvr_general_settings', | |
| 1309 | + 'wpvr_controls_settings', | |
| 1310 | + 'wpvr_floor_plan_settings', | |
| 1311 | + 'wpvr_checklist', | |
| 1312 | + ) | |
| 1313 | + ); | |
| 1314 | + | |
| 1251 | 1315 | foreach ( $template_meta as $meta_key => $meta_value ) { |
| 1252 | 1316 | // Sanitize meta key to ensure it's a valid meta key |
| 1253 | 1317 | $sanitized_key = sanitize_key( $meta_key ); |
| 1254 | - if ( ! empty( $sanitized_key ) && 'panodata' !== $sanitized_key ) { | |
| 1318 | + if ( ! empty( $sanitized_key ) && in_array( $sanitized_key, $allowed_meta_keys, true ) ) { | |
| 1255 | 1319 | // Handle different value types |
| 1256 | 1320 | if ( is_array( $meta_value ) ) { |
| 1257 | - update_post_meta( $post_id, $sanitized_key, $meta_value ); | |
| 1321 | + $sanitized_value = map_deep( $meta_value, 'sanitize_text_field' ); | |
| 1322 | + update_post_meta( $post_id, $sanitized_key, $sanitized_value ); | |
| 1258 | 1323 | } else { |
| 1259 | 1324 | update_post_meta( $post_id, $sanitized_key, sanitize_text_field( $meta_value ) ); |
| 1260 | 1325 | } |
| 1261 | 1326 | } |
| @@ -1265,13 +1330,15 @@ | ||
| 1265 | 1330 | // Trigger tour saved action for telemetry |
| 1266 | 1331 | do_action('wpvr_rex_wpvr_tour_saved', $post_id); |
| 1267 | 1332 | do_action( 'wpvr_setup_wizard_completed_event', $industry ); |
| 1268 | 1333 | |
| 1269 | - // Persist industry selection for telemetry (aha event fires later from consent handler). | |
| 1270 | - update_option( 'wpvr_industry_name', sanitize_text_field( $industry ), false ); | |
| 1334 | + if ( current_user_can( 'manage_options' ) ) { | |
| 1335 | + // Persist industry selection for telemetry (aha event fires later from consent handler). | |
| 1336 | + update_option( 'wpvr_industry_name', sanitize_text_field( $industry ), false ); | |
| 1271 | 1337 | |
| 1272 | - // Mark wizard as permanently done so the onboarding notice is suppressed. | |
| 1273 | - update_option( 'wpvr_wizard_onboarding_done', '1', false ); | |
| 1338 | + // Mark wizard as permanently done so the onboarding notice is suppressed. | |
| 1339 | + update_option( 'wpvr_wizard_onboarding_done', '1', false ); | |
| 1340 | + } | |
| 1274 | 1341 | |
| 1275 | 1342 | wp_send_json_success( array( |
| 1276 | 1343 | 'post_id' => $post_id, |
| 1277 | 1344 | 'edit_url' => admin_url( 'post.php?action=edit&post=' . $post_id ), |