PluginProbe
WPVR – 360 Panorama viewer and Virtual Tour Builder for WordPress / 9.1.3
WPVR – 360 Panorama viewer and Virtual Tour Builder for WordPress v9.1.3
9.1.3 9.1.2 9.1.1 9.1.0 9.0.3 9.0.2 9.0.1 9.0.0 8.5.79 8.5.78 8.5.77 8.5.76 8.5.75 8.5.74 8.5.73 8.5.72 8.5.71 8.5.70 8.5.69 8.5.68 8.5.35 8.5.36 8.5.37 8.5.38 8.5.39 All 222 releases
← All changes | legacy/admin/classes/class-wpvr-ajax.php +94 -27 9.0.19.1.3 View file →
@@ -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 ),