| @@ -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', |
| @@ -62,10 +63,10 @@ | ||
| 62 | 63 | 'subtitle' => $raw['bg_tour_subtitle'] ?? '', |
| 63 | 64 | ], |
| 64 | 65 | 'videoData' => [ |
| 65 | 66 | 'url' => $raw['vidurl'] ?? '', |
| 66 | - 'autoplay' => ( $raw['video-autoplay'] ?? 'off' ) === 'on', | |
| 67 | - 'loop' => ( $raw['video-loop'] ?? 'off' ) === 'on', | |
| 67 | + 'autoplay' => ( $raw['video-autoplay'] ?? $raw['autoplay'] ?? 'off' ) === 'on', | |
| 68 | + 'loop' => ( $raw['video-loop'] ?? $raw['loop'] ?? 'off' ) === 'on', | |
| 68 | 69 | ], |
| 69 | 70 | 'streetViewData' => [ |
| 70 | 71 | 'embedUrl' => $raw['streetviewurl'] ?? '', |
| 71 | 72 | ], |
| @@ -113,9 +114,9 @@ | ||
| 113 | 114 | 'autoLoad' => ! empty( $settings['autoLoad'] ) && $settings['autoLoad'] !== 'off' && $settings['autoLoad'] !== 'false', |
| 114 | 115 | 'showControls' => ! empty( $settings['showControls'] ), |
| 115 | 116 | 'draggable' => ( isset( $advanced_control['draggable'] ) && ( $advanced_control['draggable'] === 'off' || $advanced_control['draggable'] === false ) ) ? 'off' : 'on', |
| 116 | 117 | 'mouseZoom' => ( isset( $advanced_control['mouseZoom'] ) && ( $advanced_control['mouseZoom'] === 'off' || $advanced_control['mouseZoom'] === false ) ) ? 'off' : 'on', |
| 117 | - 'diskeyboard' => ( isset( $advanced_control['diskeyboard'] ) && ( $advanced_control['diskeyboard'] === 'off' || $advanced_control['diskeyboard'] === false ) ) ? 'off' : 'on', | |
| 118 | + 'diskeyboard' => ( isset( $advanced_control['diskeyboard'] ) && ( $advanced_control['diskeyboard'] === 'off' || $advanced_control['diskeyboard'] === false ) ) ? 'on' : 'off', | |
| 118 | 119 | 'keyboardzoom' => ( isset( $advanced_control['keyboardzoom'] ) && ( $advanced_control['keyboardzoom'] === 'off' || $advanced_control['keyboardzoom'] === false ) ) ? false : true, |
| 119 | 120 | 'previewtext' => $settings['previewText'] ?? '', |
| 120 | 121 | 'scenefadeduration' => isset( $settings['sceneFadeDuration'] ) ? (string) $settings['sceneFadeDuration'] : '0', |
| 121 | 122 | 'scene-info-enabled' => array_key_exists( 'showSceneInfo', $settings ) && ! $settings['showSceneInfo'] ? 'off' : 'on', |
| @@ -161,8 +162,10 @@ | ||
| 161 | 162 | 'tour-type' => $tour_type, |
| 162 | 163 | 'vidurl' => esc_url_raw( $video_data['url'] ?? '' ), |
| 163 | 164 | 'video-autoplay' => ! empty( $video_data['autoplay'] ) ? 'on' : 'off', |
| 164 | 165 | 'video-loop' => ! empty( $video_data['loop'] ) ? 'on' : 'off', |
| 166 | + 'autoplay' => ! empty( $video_data['autoplay'] ) ? 'on' : 'off', | |
| 167 | + 'loop' => ! empty( $video_data['loop'] ) ? 'on' : 'off', | |
| 165 | 168 | 'streetviewurl' => esc_url_raw( $street_view_data['embedUrl'] ?? '' ), |
| 166 | 169 | 'streetview' => ! empty( $street_view_data['embedUrl'] ) ? 'on' : 'off', |
| 167 | 170 | 'panodata' => [ |
| 168 | 171 | 'scene-list' => $this->scenes_from_api( |
| @@ -170,10 +173,15 @@ | ||
| 170 | 173 | $default_scene_id, |
| 171 | 174 | ( $settings['showSceneInfo'] ?? true ) !== false |
| 172 | 175 | ), |
| 173 | 176 | ], |
| 177 | + 'wpvr_social_share' => ! empty( $settings['socialShare'] ) ? 'on' : 'off', | |
| 174 | 178 | ]; |
| 175 | 179 | |
| 180 | + if ( $tour_type === 'video' && ! empty( $video_data['url'] ) ) { | |
| 181 | + $raw['vidid'] = 'vid' . ( $data['tourId'] ?? $data['id'] ?? wp_rand( 1000, 99999 ) ); | |
| 182 | + } | |
| 183 | + | |
| 176 | 184 | return $raw; |
| 177 | 185 | } |
| 178 | 186 | |
| 179 | 187 | // ------------------------------------------------------------------------- |
| @@ -266,9 +274,9 @@ | ||
| 266 | 274 | } |
| 267 | 275 | if ( isset( $raw['streetviewdata'] ) || ! empty( $raw['streetviewurl'] ) ) { |
| 268 | 276 | return 'street-view'; |
| 269 | 277 | } |
| 270 | - if ( isset( $raw['vidid'] ) || ! empty( $raw['vidurl'] ) ) { | |
| 278 | + if ( ! empty( $raw['vidid'] ) || ! empty( $raw['vidurl'] ) ) { | |
| 271 | 279 | return 'video'; |
| 272 | 280 | } |
| 273 | 281 | return 'image'; |
| 274 | 282 | } |
| @@ -294,9 +302,9 @@ | ||
| 294 | 302 | return [ |
| 295 | 303 | 'tourLayout' => $tour_layout, |
| 296 | 304 | 'layout_icon_bg_color' => (string) ( $raw['layout_icon_bg_color'] ?? '#5a536e' ), |
| 297 | 305 | 'layout_icon_color' => (string) ( $raw['layout_icon_color'] ?? '#ffffff' ), |
| 298 | - 'diskeyboard' => $b( $raw['diskeyboard'] ?? null, true ), | |
| 306 | + 'diskeyboard' => ! in_array( $raw['diskeyboard'] ?? 'off', [ 'on', 'true', true ], true ), | |
| 299 | 307 | 'keyboardzoom' => $b( $raw['keyboardzoom'] ?? null, true ), |
| 300 | 308 | 'draggable' => $b( $raw['draggable'] ?? null, true ), |
| 301 | 309 | 'mouseZoom' => $b( $raw['mouseZoom'] ?? null, true ), |
| 302 | 310 | 'gyro' => $b( $raw['gyro'] ?? null, false ), |
| @@ -562,8 +570,17 @@ | ||
| 562 | 570 | : null; |
| 563 | 571 | $scene_yaw = isset( $hotspot['hotspot-scene-yaw'] ) && $hotspot['hotspot-scene-yaw'] !== '' |
| 564 | 572 | ? (float) $hotspot['hotspot-scene-yaw'] |
| 565 | 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 | + | |
| 566 | 583 | $hotspots[] = [ |
| 567 | 584 | 'id' => $hotspot['hotspot-id'] ?? wp_generate_uuid4(), |
| 568 | 585 | 'type' => $hotspot['hotspot-type'] ?? 'info', |
| 569 | 586 | 'pitch' => isset( $hotspot['hotspot-pitch'] ) ? (float) $hotspot['hotspot-pitch'] : 0, |
| @@ -574,8 +591,11 @@ | ||
| 574 | 591 | 'urlOpen' => $hotspot['hotspot-url-open'] ?? 'off', |
| 575 | 592 | 'hover' => $hotspot['hotspot-hover'] ?? '', |
| 576 | 593 | 'targetSceneId' => $hotspot['hotspot-scene'] ?? '', |
| 577 | 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, | |
| 578 | 598 | // Pro styling fields |
| 579 | 599 | 'iconClass' => ( ( $hotspot['hotspot-customclass-pro'] ?? '' ) === 'none' || ( $hotspot['hotspot-customclass-pro'] ?? '' ) === '' ) ? '' : $hotspot['hotspot-customclass-pro'], |
| 580 | 600 | 'iconBgColor' => $hotspot['hotspot-customclass-color-icon-value'] ?? '#00b4ff', |
| 581 | 601 | 'iconColor' => $hotspot['hotspot-custom-icon-color-value'] ?? '#ffffff', |
| @@ -596,23 +616,53 @@ | ||
| 596 | 616 | |
| 597 | 617 | protected function hotspots_from_api( array $hotspots ): array { |
| 598 | 618 | $hotspot_list = []; |
| 599 | 619 | foreach ( $hotspots as $hotspot ) { |
| 620 | + $hotspot_type = sanitize_key( $hotspot['type'] ?? 'info' ); | |
| 621 | + $is_fluent_form = $hotspot_type === 'fluent_form'; | |
| 622 | + $raw_content = (string) ( $hotspot['content'] ?? '' ); | |
| 623 | + $raw_hover = (string) ( $hotspot['hover'] ?? '' ); | |
| 624 | + | |
| 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 | + } | |
| 634 | + $hover = function_exists( 'sanitize_content_preserve_styles' ) | |
| 635 | + ? sanitize_content_preserve_styles( $raw_hover, false ) | |
| 636 | + : wp_kses_post( $raw_hover ); | |
| 637 | + | |
| 600 | 638 | $hs = [ |
| 601 | 639 | 'hotspot-id' => $hotspot['id'] ?? wp_generate_uuid4(), |
| 602 | - 'hotspot-type' => $hotspot['type'] ?? 'info', | |
| 640 | + 'hotspot-type' => $hotspot_type, | |
| 603 | 641 | 'hotspot-pitch' => (string) ( $hotspot['pitch'] ?? 0 ), |
| 604 | 642 | 'hotspot-yaw' => (string) ( $hotspot['yaw'] ?? 0 ), |
| 605 | - 'hotspot-title' => $hotspot['text'] ?? '', | |
| 606 | - 'hotspot-content' => $hotspot['content'] ?? '', | |
| 607 | - 'hotspot-url' => $hotspot['url'] ?? '', | |
| 608 | - 'hotspot-url-open' => $hotspot['urlOpen'] ?? 'off', | |
| 609 | - 'hotspot-hover' => $hotspot['hover'] ?? '', | |
| 610 | - 'hotspot-scene' => $hotspot['targetSceneId'] ?? '', | |
| 611 | - 'hotspot-customclass' => $hotspot['customClass'] ?? '', | |
| 643 | + 'hotspot-title' => sanitize_text_field( $hotspot['text'] ?? '' ), | |
| 644 | + 'hotspot-content' => $content, | |
| 645 | + 'hotspot-url' => sanitize_text_field( $hotspot['url'] ?? '' ), | |
| 646 | + 'hotspot-url-open' => ( $hotspot['urlOpen'] ?? 'off' ) === 'on' ? 'on' : 'off', | |
| 647 | + 'hotspot-hover' => $hover, | |
| 648 | + 'hotspot-scene' => sanitize_text_field( $hotspot['targetSceneId'] ?? '' ), | |
| 649 | + 'hotspot-customclass' => sanitize_text_field( $hotspot['customClass'] ?? '' ), | |
| 612 | 650 | 'hotspot-scene-list' => 'none', |
| 613 | 651 | ]; |
| 614 | 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 | + | |
| 615 | 665 | if ( $this->is_pro ) { |
| 616 | 666 | $hs = array_merge( $hs, [ |
| 617 | 667 | // Pro styling fields |
| 618 | 668 | 'hotspot-customclass-pro' => !empty( $hotspot['iconClass'] ) ? $hotspot['iconClass'] : 'none', |
| @@ -623,11 +673,10 @@ | ||
| 623 | 673 | 'hotspot-border' => $hotspot['border'] ?? 'off', |
| 624 | 674 | 'hotspot-border-width' => $hotspot['borderWidth'] ?? '1', |
| 625 | 675 | 'hotspot-border-style' => $hotspot['borderStyle'] ?? 'none', |
| 626 | 676 | 'hotspot-border-color' => $hotspot['borderColor'] ?? '#00b4ff', |
| 627 | - // Pro navigation entry point fields | |
| 628 | - 'hotspot-scene-pitch' => $hotspot['scenePitch'] !== null ? (string) $hotspot['scenePitch'] : '', | |
| 629 | - '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'] : '', | |
| 630 | 679 | 'hotspot-scene-entry-point-mode' => in_array( $hotspot['sceneEntryPointMode'] ?? '', [ 'inherit', 'custom' ], true ) |
| 631 | 680 | ? $hotspot['sceneEntryPointMode'] |
| 632 | 681 | : 'inherit', |
| 633 | 682 | ] ); |