| @@ -53,8 +53,9 @@ | ||
| 53 | 53 | 'previewImage' => $raw['preview'] ?? '', |
| 54 | 54 | 'sceneFadeDuration' => isset( $raw['scenefadeduration'] ) ? (int) $raw['scenefadeduration'] : 0, |
| 55 | 55 | 'showSceneInfo' => ( $raw['scene-info-enabled'] ?? 'on' ) !== 'off', |
| 56 | 56 | 'autoRotate' => $auto_rotate, |
| 57 | + 'socialShare' => ( $raw['wpvr_social_share'] ?? 'off' ) === 'on', | |
| 57 | 58 | ], |
| 58 | 59 | 'floorPlan' => $this->floor_plan_to_api( $raw ), |
| 59 | 60 | 'backgroundTour' => [ |
| 60 | 61 | 'enabled' => ( $raw['bg_tour_enabler'] ?? 'off' ) === 'on', |
| @@ -163,9 +164,8 @@ | ||
| 163 | 164 | 'video-autoplay' => ! empty( $video_data['autoplay'] ) ? 'on' : 'off', |
| 164 | 165 | 'video-loop' => ! empty( $video_data['loop'] ) ? 'on' : 'off', |
| 165 | 166 | 'autoplay' => ! empty( $video_data['autoplay'] ) ? 'on' : 'off', |
| 166 | 167 | 'loop' => ! empty( $video_data['loop'] ) ? 'on' : 'off', |
| 167 | - 'vidid' => $tour_type === 'video' && ! empty( $video_data['url'] ) ? ( 'vid' . ( $data['tourId'] ?? $data['id'] ?? wp_rand( 1000, 99999 ) ) ) : '', | |
| 168 | 168 | 'streetviewurl' => esc_url_raw( $street_view_data['embedUrl'] ?? '' ), |
| 169 | 169 | 'streetview' => ! empty( $street_view_data['embedUrl'] ) ? 'on' : 'off', |
| 170 | 170 | 'panodata' => [ |
| 171 | 171 | 'scene-list' => $this->scenes_from_api( |
| @@ -173,10 +173,15 @@ | ||
| 173 | 173 | $default_scene_id, |
| 174 | 174 | ( $settings['showSceneInfo'] ?? true ) !== false |
| 175 | 175 | ), |
| 176 | 176 | ], |
| 177 | + 'wpvr_social_share' => ! empty( $settings['socialShare'] ) ? 'on' : 'off', | |
| 177 | 178 | ]; |
| 178 | 179 | |
| 180 | + if ( $tour_type === 'video' && ! empty( $video_data['url'] ) ) { | |
| 181 | + $raw['vidid'] = 'vid' . ( $data['tourId'] ?? $data['id'] ?? wp_rand( 1000, 99999 ) ); | |
| 182 | + } | |
| 183 | + | |
| 179 | 184 | return $raw; |
| 180 | 185 | } |
| 181 | 186 | |
| 182 | 187 | // ------------------------------------------------------------------------- |
| @@ -269,9 +274,9 @@ | ||
| 269 | 274 | } |
| 270 | 275 | if ( isset( $raw['streetviewdata'] ) || ! empty( $raw['streetviewurl'] ) ) { |
| 271 | 276 | return 'street-view'; |
| 272 | 277 | } |
| 273 | - if ( isset( $raw['vidid'] ) || ! empty( $raw['vidurl'] ) ) { | |
| 278 | + if ( ! empty( $raw['vidid'] ) || ! empty( $raw['vidurl'] ) ) { | |
| 274 | 279 | return 'video'; |
| 275 | 280 | } |
| 276 | 281 | return 'image'; |
| 277 | 282 | } |
| @@ -565,8 +570,17 @@ | ||
| 565 | 570 | : null; |
| 566 | 571 | $scene_yaw = isset( $hotspot['hotspot-scene-yaw'] ) && $hotspot['hotspot-scene-yaw'] !== '' |
| 567 | 572 | ? (float) $hotspot['hotspot-scene-yaw'] |
| 568 | 573 | : null; |
| 574 | + $product_id = isset( $hotspot['hotspot-product-id'] ) ? (string) $hotspot['hotspot-product-id'] : ''; | |
| 575 | + $product_name = ''; | |
| 576 | + if ( ! empty( $product_id ) && function_exists( 'wc_get_product' ) ) { | |
| 577 | + $product_obj = wc_get_product( $product_id ); | |
| 578 | + if ( is_object( $product_obj ) ) { | |
| 579 | + $product_name = $product_obj->get_formatted_name(); | |
| 580 | + } | |
| 581 | + } | |
| 582 | + | |
| 569 | 583 | $hotspots[] = [ |
| 570 | 584 | 'id' => $hotspot['hotspot-id'] ?? wp_generate_uuid4(), |
| 571 | 585 | 'type' => $hotspot['hotspot-type'] ?? 'info', |
| 572 | 586 | 'pitch' => isset( $hotspot['hotspot-pitch'] ) ? (float) $hotspot['hotspot-pitch'] : 0, |
| @@ -577,8 +591,11 @@ | ||
| 577 | 591 | 'urlOpen' => $hotspot['hotspot-url-open'] ?? 'off', |
| 578 | 592 | 'hover' => $hotspot['hotspot-hover'] ?? '', |
| 579 | 593 | 'targetSceneId' => $hotspot['hotspot-scene'] ?? '', |
| 580 | 594 | 'customClass' => $hotspot['hotspot-customclass'] ?? '', |
| 595 | + 'fluentFormId' => isset( $hotspot['fluent-form-id'] ) ? (string) $hotspot['fluent-form-id'] : '', | |
| 596 | + 'productId' => $product_id, | |
| 597 | + 'productName' => $product_name, | |
| 581 | 598 | // Pro styling fields |
| 582 | 599 | 'iconClass' => ( ( $hotspot['hotspot-customclass-pro'] ?? '' ) === 'none' || ( $hotspot['hotspot-customclass-pro'] ?? '' ) === '' ) ? '' : $hotspot['hotspot-customclass-pro'], |
| 583 | 600 | 'iconBgColor' => $hotspot['hotspot-customclass-color-icon-value'] ?? '#00b4ff', |
| 584 | 601 | 'iconColor' => $hotspot['hotspot-custom-icon-color-value'] ?? '#ffffff', |
| @@ -604,11 +621,17 @@ | ||
| 604 | 621 | $is_fluent_form = $hotspot_type === 'fluent_form'; |
| 605 | 622 | $raw_content = (string) ( $hotspot['content'] ?? '' ); |
| 606 | 623 | $raw_hover = (string) ( $hotspot['hover'] ?? '' ); |
| 607 | 624 | |
| 608 | - $content = function_exists( 'sanitize_content_preserve_styles' ) | |
| 609 | - ? sanitize_content_preserve_styles( $raw_content, $is_fluent_form ) | |
| 610 | - : wp_kses_post( $raw_content ); | |
| 625 | + // For fluent_form hotspots, content is dynamically rendered server-side from fluent-form-id. | |
| 626 | + // User-supplied content is never used and must not be saved, completely eliminating stored XSS. | |
| 627 | + if ( $is_fluent_form ) { | |
| 628 | + $content = ''; | |
| 629 | + } else { | |
| 630 | + $content = function_exists( 'sanitize_content_preserve_styles' ) | |
| 631 | + ? sanitize_content_preserve_styles( $raw_content, false ) | |
| 632 | + : wp_kses_post( $raw_content ); | |
| 633 | + } | |
| 611 | 634 | $hover = function_exists( 'sanitize_content_preserve_styles' ) |
| 612 | 635 | ? sanitize_content_preserve_styles( $raw_hover, false ) |
| 613 | 636 | : wp_kses_post( $raw_hover ); |
| 614 | 637 | |
| @@ -626,8 +649,20 @@ | ||
| 626 | 649 | 'hotspot-customclass' => sanitize_text_field( $hotspot['customClass'] ?? '' ), |
| 627 | 650 | 'hotspot-scene-list' => 'none', |
| 628 | 651 | ]; |
| 629 | 652 | |
| 653 | + if ( isset( $hotspot['fluentFormId'] ) ) { | |
| 654 | + $hs['fluent-form-id'] = (string) absint( $hotspot['fluentFormId'] ); | |
| 655 | + } elseif ( isset( $hotspot['fluent-form-id'] ) ) { | |
| 656 | + $hs['fluent-form-id'] = (string) absint( $hotspot['fluent-form-id'] ); | |
| 657 | + } | |
| 658 | + | |
| 659 | + if ( isset( $hotspot['productId'] ) ) { | |
| 660 | + $hs['hotspot-product-id'] = sanitize_text_field( $hotspot['productId'] ); | |
| 661 | + } elseif ( isset( $hotspot['hotspot-product-id'] ) ) { | |
| 662 | + $hs['hotspot-product-id'] = sanitize_text_field( $hotspot['hotspot-product-id'] ); | |
| 663 | + } | |
| 664 | + | |
| 630 | 665 | if ( $this->is_pro ) { |
| 631 | 666 | $hs = array_merge( $hs, [ |
| 632 | 667 | // Pro styling fields |
| 633 | 668 | 'hotspot-customclass-pro' => !empty( $hotspot['iconClass'] ) ? $hotspot['iconClass'] : 'none', |
| @@ -638,11 +673,10 @@ | ||
| 638 | 673 | 'hotspot-border' => $hotspot['border'] ?? 'off', |
| 639 | 674 | 'hotspot-border-width' => $hotspot['borderWidth'] ?? '1', |
| 640 | 675 | 'hotspot-border-style' => $hotspot['borderStyle'] ?? 'none', |
| 641 | 676 | 'hotspot-border-color' => $hotspot['borderColor'] ?? '#00b4ff', |
| 642 | - // Pro navigation entry point fields | |
| 643 | - 'hotspot-scene-pitch' => $hotspot['scenePitch'] !== null ? (string) $hotspot['scenePitch'] : '', | |
| 644 | - 'hotspot-scene-yaw' => $hotspot['sceneYaw'] !== null ? (string) $hotspot['sceneYaw'] : '', | |
| 677 | + 'hotspot-scene-pitch' => ( isset( $hotspot['scenePitch'] ) && $hotspot['scenePitch'] !== null ) ? (string) $hotspot['scenePitch'] : '', | |
| 678 | + 'hotspot-scene-yaw' => ( isset( $hotspot['sceneYaw'] ) && $hotspot['sceneYaw'] !== null ) ? (string) $hotspot['sceneYaw'] : '', | |
| 645 | 679 | 'hotspot-scene-entry-point-mode' => in_array( $hotspot['sceneEntryPointMode'] ?? '', [ 'inherit', 'custom' ], true ) |
| 646 | 680 | ? $hotspot['sceneEntryPointMode'] |
| 647 | 681 | : 'inherit', |
| 648 | 682 | ] ); |