| @@ -570,8 +570,17 @@ | ||
| 570 | 570 | : null; |
| 571 | 571 | $scene_yaw = isset( $hotspot['hotspot-scene-yaw'] ) && $hotspot['hotspot-scene-yaw'] !== '' |
| 572 | 572 | ? (float) $hotspot['hotspot-scene-yaw'] |
| 573 | 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 | + | |
| 574 | 583 | $hotspots[] = [ |
| 575 | 584 | 'id' => $hotspot['hotspot-id'] ?? wp_generate_uuid4(), |
| 576 | 585 | 'type' => $hotspot['hotspot-type'] ?? 'info', |
| 577 | 586 | 'pitch' => isset( $hotspot['hotspot-pitch'] ) ? (float) $hotspot['hotspot-pitch'] : 0, |
| @@ -582,8 +591,11 @@ | ||
| 582 | 591 | 'urlOpen' => $hotspot['hotspot-url-open'] ?? 'off', |
| 583 | 592 | 'hover' => $hotspot['hotspot-hover'] ?? '', |
| 584 | 593 | 'targetSceneId' => $hotspot['hotspot-scene'] ?? '', |
| 585 | 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, | |
| 586 | 598 | // Pro styling fields |
| 587 | 599 | 'iconClass' => ( ( $hotspot['hotspot-customclass-pro'] ?? '' ) === 'none' || ( $hotspot['hotspot-customclass-pro'] ?? '' ) === '' ) ? '' : $hotspot['hotspot-customclass-pro'], |
| 588 | 600 | 'iconBgColor' => $hotspot['hotspot-customclass-color-icon-value'] ?? '#00b4ff', |
| 589 | 601 | 'iconColor' => $hotspot['hotspot-custom-icon-color-value'] ?? '#ffffff', |
| @@ -609,11 +621,17 @@ | ||
| 609 | 621 | $is_fluent_form = $hotspot_type === 'fluent_form'; |
| 610 | 622 | $raw_content = (string) ( $hotspot['content'] ?? '' ); |
| 611 | 623 | $raw_hover = (string) ( $hotspot['hover'] ?? '' ); |
| 612 | 624 | |
| 613 | - $content = function_exists( 'sanitize_content_preserve_styles' ) | |
| 614 | - ? sanitize_content_preserve_styles( $raw_content, $is_fluent_form ) | |
| 615 | - : 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 | + } | |
| 616 | 634 | $hover = function_exists( 'sanitize_content_preserve_styles' ) |
| 617 | 635 | ? sanitize_content_preserve_styles( $raw_hover, false ) |
| 618 | 636 | : wp_kses_post( $raw_hover ); |
| 619 | 637 | |
| @@ -631,8 +649,20 @@ | ||
| 631 | 649 | 'hotspot-customclass' => sanitize_text_field( $hotspot['customClass'] ?? '' ), |
| 632 | 650 | 'hotspot-scene-list' => 'none', |
| 633 | 651 | ]; |
| 634 | 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 | + | |
| 635 | 665 | if ( $this->is_pro ) { |
| 636 | 666 | $hs = array_merge( $hs, [ |
| 637 | 667 | // Pro styling fields |
| 638 | 668 | 'hotspot-customclass-pro' => !empty( $hotspot['iconClass'] ) ? $hotspot['iconClass'] : 'none', |
| @@ -643,11 +673,10 @@ | ||
| 643 | 673 | 'hotspot-border' => $hotspot['border'] ?? 'off', |
| 644 | 674 | 'hotspot-border-width' => $hotspot['borderWidth'] ?? '1', |
| 645 | 675 | 'hotspot-border-style' => $hotspot['borderStyle'] ?? 'none', |
| 646 | 676 | 'hotspot-border-color' => $hotspot['borderColor'] ?? '#00b4ff', |
| 647 | - // Pro navigation entry point fields | |
| 648 | - 'hotspot-scene-pitch' => $hotspot['scenePitch'] !== null ? (string) $hotspot['scenePitch'] : '', | |
| 649 | - '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'] : '', | |
| 650 | 679 | 'hotspot-scene-entry-point-mode' => in_array( $hotspot['sceneEntryPointMode'] ?? '', [ 'inherit', 'custom' ], true ) |
| 651 | 680 | ? $hotspot['sceneEntryPointMode'] |
| 652 | 681 | : 'inherit', |
| 653 | 682 | ] ); |