'default', 'layout_icon_bg_color' => '#5a536e', 'layout_icon_color' => '#ffffff', ]; $postdata['draggable'] = true; $postdata['mouseZoom'] = true; $postdata['diskeyboard'] = false; $postdata['keyboardzoom'] = true; $postdata['hfov'] = ''; $postdata['maxHfov'] = ''; $postdata['minHfov'] = ''; $postdata['cpLogoImg'] = ''; $postdata['cpLogoContent'] = ''; if ( ( $postdata['tour-type'] ?? '' ) === 'street-view' || isset( $postdata['streetviewdata'] ) ) { $postdata['tour-type'] = 'image'; $postdata['streetview'] = 'off'; $postdata['streetviewurl'] = ''; unset( $postdata['streetviewdata'] ); } $custom_control = isset( $postdata['customcontrol'] ) && is_array( $postdata['customcontrol'] ) ? $postdata['customcontrol'] : []; foreach ( [ 'panupSwitch', 'panDownSwitch', 'panLeftSwitch', 'panRightSwitch', 'panZoomInSwitch', 'panZoomOutSwitch', 'panFullscreenSwitch', 'gyroscopeSwitch', 'backToHomeSwitch', ] as $key ) { $custom_control[ $key ] = 'off'; } $postdata['customcontrol'] = $custom_control; if ( isset( $postdata['panodata']['scene-list'] ) && is_array( $postdata['panodata']['scene-list'] ) ) { foreach ( $postdata['panodata']['scene-list'] as &$scene ) { if ( ! is_array( $scene ) ) { continue; } if ( ( $scene['scene-type'] ?? 'equirectangular' ) === 'cubemap' ) { $scene['scene-type'] = 'equirectangular'; } foreach ( [ 'ptyscene', 'cvgscene', 'chgscene', 'czscene' ] as $key ) { $scene[ $key ] = 'off'; } foreach ( [ 'scene-author', 'scene-author-url', 'scene-vaov', 'scene-haov', 'scene-vertical-offset', 'scene-pitch', 'scene-yaw', 'scene-maxpitch', 'scene-minpitch', 'scene-maxyaw', 'scene-minyaw', 'scene-zoom', 'scene-maxzoom', 'scene-minzoom', ] as $key ) { $scene[ $key ] = ''; } for ( $face = 0; $face < 6; $face++ ) { $scene[ 'scene-attachment-url-face' . $face ] = ''; } if ( isset( $scene['hotspot-list'] ) && is_array( $scene['hotspot-list'] ) ) { foreach ( $scene['hotspot-list'] as &$hotspot ) { if ( ! is_array( $hotspot ) ) { continue; } $hotspot['hotspot-customclass-pro'] = 'none'; $hotspot['hotspot-blink'] = 'off'; $hotspot['hotspot-shape'] = 'round'; $hotspot['hotspot-border'] = 'off'; $hotspot['hotspot-scene-pitch'] = ''; $hotspot['hotspot-scene-yaw'] = ''; } unset( $hotspot ); } } unset( $scene ); } return $postdata; } } if ( ! function_exists( 'wpvr_tour_uses_pro_features' ) ) { /** * Determine whether a saved tour contains enabled WP VR Pro features. * * @param mixed $postdata Saved tour data. * @return bool */ function wpvr_tour_uses_pro_features( $postdata ) { if ( ! is_array( $postdata ) ) { return false; } $is_on = static function ( $value ) { return in_array( $value, array( true, 1, '1', 'on', 'true' ), true ); }; if ( isset( $postdata['streetviewdata'] ) || ( isset( $postdata['streetview'] ) && $is_on( $postdata['streetview'] ) ) || ! empty( $postdata['streetviewurl'] ) ) { return true; } foreach ( array( 'bg_tour_enabler', 'floorplan-enabled', 'floor_plan_tour_enabler', 'floor_plan_compass', 'floor_plan_direction_indicator', 'gyro', 'deviceorientationcontrol', 'compass', 'vrgallery', 'vrgallery_title', 'vrgallery_icon_size', 'vrgallery_display', 'scene_navigation', 'sceneAnimation', 'bg_music', 'autoplay_bg_music', 'loop_bg_music', 'explainerSwitch', 'cpLogoSwitch', 'genericform', 'calltoaction', 'customcss_enable', ) as $key ) { if ( isset( $postdata[ $key ] ) && $is_on( $postdata[ $key ] ) ) { return true; } } if ( ! empty( $postdata['hfov'] ) || ! empty( $postdata['maxHfov'] ) || ! empty( $postdata['minHfov'] ) ) { return true; } $tour_layout = isset( $postdata['tourLayout'] ) ? $postdata['tourLayout'] : 'default'; $tour_layout = is_array( $tour_layout ) ? ( isset( $tour_layout['layout'] ) ? $tour_layout['layout'] : 'default' ) : $tour_layout; if ( $tour_layout !== '' && $tour_layout !== 'default' ) { return true; } $custom_control = isset( $postdata['customcontrol'] ) && is_array( $postdata['customcontrol'] ) ? $postdata['customcontrol'] : array(); foreach ( array( 'panupSwitch', 'panDownSwitch', 'panLeftSwitch', 'panRightSwitch', 'panZoomInSwitch', 'panZoomOutSwitch', 'panFullscreenSwitch', 'gyroscopeSwitch', 'backToHomeSwitch', ) as $key ) { if ( isset( $custom_control[ $key ] ) && $is_on( $custom_control[ $key ] ) ) { return true; } } $scenes = isset( $postdata['panodata']['scene-list'] ) ? $postdata['panodata']['scene-list'] : array(); if ( ! is_array( $scenes ) ) { return false; } $pro_scene_fields = array( 'scene-vaov', 'scene-haov', 'scene-vertical-offset', 'scene-pitch', 'scene-yaw', 'scene-maxpitch', 'scene-minpitch', 'scene-maxyaw', 'scene-minyaw', 'scene-zoom', 'scene-maxzoom', 'scene-minzoom', ); foreach ( $scenes as $scene ) { if ( ! is_array( $scene ) ) { continue; } if ( isset( $scene['scene-type'] ) && 'cubemap' === $scene['scene-type'] ) { return true; } foreach ( array( 'ptyscene', 'cvgscene', 'chgscene', 'czscene' ) as $key ) { if ( isset( $scene[ $key ] ) && $is_on( $scene[ $key ] ) ) { return true; } } foreach ( $pro_scene_fields as $key ) { if ( isset( $scene[ $key ] ) && '' !== $scene[ $key ] && null !== $scene[ $key ] ) { return true; } } $hotspots = isset( $scene['hotspot-list'] ) ? $scene['hotspot-list'] : array(); if ( ! is_array( $hotspots ) ) { continue; } foreach ( $hotspots as $hotspot ) { if ( ! is_array( $hotspot ) ) { continue; } if ( ( ! empty( $hotspot['hotspot-customclass-pro'] ) && 'none' !== $hotspot['hotspot-customclass-pro'] ) || ( ! empty( $hotspot['hotspot-shape'] ) && 'round' !== $hotspot['hotspot-shape'] ) || ( isset( $hotspot['hotspot-border'] ) && $is_on( $hotspot['hotspot-border'] ) ) || ! empty( $hotspot['hotspot-scene-pitch'] ) || ! empty( $hotspot['hotspot-scene-yaw'] ) || ( isset( $hotspot['hotspot-scene-entry-point-mode'] ) && 'custom' === $hotspot['hotspot-scene-entry-point-mode'] ) ) { return true; } } } return false; } } if ( ! function_exists( 'wpvr_get_pro_feature_tour_ids' ) ) { /** * Return IDs of tours that use at least one enabled Pro feature. * * @return array */ function wpvr_get_pro_feature_tour_ids() { static $tour_ids = null; if ( is_array( $tour_ids ) ) { return $tour_ids; } $tour_ids = array(); $posts = get_posts( array( 'post_type' => 'wpvr_item', 'post_status' => 'any', 'posts_per_page' => -1, 'fields' => 'ids', 'no_found_rows' => true, 'update_post_meta_cache' => true, 'update_post_term_cache' => false, ) ); foreach ( $posts as $tour_id ) { $postdata = get_post_meta( (int) $tour_id, 'panodata', true ); if ( wpvr_tour_uses_pro_features( $postdata ) ) { $tour_ids[] = (int) $tour_id; } } return $tour_ids; } } if ( ! function_exists( 'wpvr_has_pro_feature_tours' ) ) { function wpvr_has_pro_feature_tours() { return ! empty( wpvr_get_pro_feature_tour_ids() ); } } if ( ! function_exists( 'wpvr_enqueue_license_notice_assets' ) ) { /** * Enqueue the global license notice styles while Pro is installed and inactive. * * @return void */ function wpvr_enqueue_license_notice_assets() { if ( ! is_admin() || ! defined( 'WPVR_PRO_VERSION' ) || wpvr_is_pro_active() ) { return; } wp_enqueue_style( 'wpvr-license-notice-fontawesome', WPVR_PLUGIN_DIR_URL . 'admin/lib/fontawesome/css/all.css', array(), WPVR_VERSION ); wp_enqueue_style( 'wpvr-license-notice', WPVR_PLUGIN_DIR_URL . 'admin/css/wpvr-license-notice.css', array( 'wpvr-license-notice-fontawesome' ), WPVR_VERSION ); wp_enqueue_script( 'wpvr-license-notice', WPVR_PLUGIN_DIR_URL . 'admin/js/wpvr-license-notice.js', array(), WPVR_VERSION, true ); } } add_action( 'admin_enqueue_scripts', 'wpvr_enqueue_license_notice_assets', 10 ); if ( ! function_exists( 'wpvr_render_pro_feature_fallback_notice' ) ) { /** * Render the global Pro license notice and explain the saved-tour fallback. * * @return void */ function wpvr_render_pro_feature_fallback_notice() { if ( ! is_admin() || ! current_user_can( 'edit_posts' ) || ! defined( 'WPVR_PRO_VERSION' ) || wpvr_is_pro_active() ) { return; } $status = strtolower( (string) get_option( 'wpvr_edd_license_status', 'no' ) ); $needs_activation = in_array( $status, array( '', 'no' ), true ); if ( ! $needs_activation && ! wpvr_has_pro_feature_tours() ) { return; } if ( $needs_activation ) { $title = __( 'Please Activate your license key to enjoy the pro features for WPVR', 'wpvr' ); $subtitle = ''; $action_label = __( 'Activate', 'wpvr' ); $action_url = admin_url( 'admin.php?page=wpvrpro' ); $action_target = ''; $action_icon = 'fa-key'; } else { $title = 'expired' === $status ? __( 'Your WPVR Pro license has expired.', 'wpvr' ) : __( 'Your WPVR Pro license is not active.', 'wpvr' ); $subtitle = __( 'Pro features are no longer available. Upgrade to Pro now to restore access to all premium features.', 'wpvr' ); $action_label = __( 'Upgrade to Pro', 'wpvr' ); $action_url = 'https://rextheme.com/your-account/#purchase'; $action_target = ' target="_blank" rel="noopener noreferrer"'; $action_icon = 'fa-crown'; } $notice_classes = 'notice notice-error is-dismissible wpvr-license-fallback-notice'; if ( '' === $subtitle ) { $notice_classes .= ' wpvr-license-fallback-notice--single-line'; } echo '
'; echo ''; echo '
'; echo '

' . esc_html( $title ) . '

'; if ( '' !== $subtitle ) { echo '

' . esc_html( $subtitle ) . '

'; } echo '
'; echo ''; echo ''; echo '' . esc_html( $action_label ) . ''; echo ''; echo ''; echo '
'; } } add_action( 'admin_notices', 'wpvr_render_pro_feature_fallback_notice', 10 ); if ( ! function_exists( 'wpvr_add_pro_feature_tour_row_class' ) ) { /** * Add a class to affected tour rows while the Pro entitlement is inactive. * * @param array $classes Existing post classes. * @param array $class Additional classes supplied by WordPress. * @param int $post_id Post ID. * @return array */ function wpvr_add_pro_feature_tour_row_class( $classes, $class = array(), $post_id = 0 ) { if ( ! is_admin() || ! defined( 'WPVR_PRO_VERSION' ) || wpvr_is_pro_active() || 'wpvr_item' !== get_post_type( $post_id ) ) { return $classes; } $postdata = get_post_meta( (int) $post_id, 'panodata', true ); if ( wpvr_tour_uses_pro_features( $postdata ) ) { $classes[] = 'wpvr-pro-feature-tour'; } return $classes; } } add_filter( 'post_class', 'wpvr_add_pro_feature_tour_row_class', 10, 3 ); require_once plugin_dir_path( __FILE__ ) . 'src/Admin/UiModeSwitcher.php'; \RexTheme\WPVR\Admin\UiModeSwitcher::init(); add_action( 'wp_ajax_wpvr_export_tour', 'wpvr_handle_export_tour' ); /** * AJAX handler: return a signed URL for the Pro tour exporter. * * WPVR Pro creates and streams the ZIP through its admin-post endpoint. This * handler authorizes the New UI request and returns that endpoint URL. */ function wpvr_handle_export_tour(): void { check_ajax_referer( 'wpvr_export_tour', 'nonce' ); if ( ! current_user_can( 'edit_posts' ) ) { wp_send_json_error( [ 'message' => 'Insufficient permissions.' ], 403 ); } if ( ! wpvr_is_pro_active() ) { wp_send_json_error( [ 'message' => 'A valid WP VR Pro license is required.' ], 403 ); } $tour_id = isset( $_POST['tour_id'] ) ? absint( $_POST['tour_id'] ) : 0; if ( ! $tour_id ) { wp_send_json_error( [ 'message' => 'Missing tour ID.' ] ); } $post = get_post( $tour_id ); if ( ! $post || $post->post_type !== 'wpvr_item' ) { wp_send_json_error( [ 'message' => 'Tour not found.' ] ); } if ( ! current_user_can( 'edit_post', $tour_id ) ) { wp_send_json_error( [ 'message' => 'You are not allowed to export this tour.' ], 403 ); } $export_url = add_query_arg( [ 'action' => 'wpvr_export_tour', 'post_id' => $tour_id, '_wpnonce' => wp_create_nonce( 'wpvr_export_tour_' . $tour_id ), ], admin_url( 'admin-post.php' ) ); wp_send_json_success( [ 'url' => $export_url ] ); } /** * Ensure legacy Street View keys always exist in panodata. * * WPVR Pro reads these keys directly in classic editor screens. Tours created * through newer flows may miss them, which triggers PHP warnings. */ function wpvr_backfill_legacy_streetview_keys() { if ( ! is_admin() ) { return; } $post_id = isset( $_GET['post'] ) ? absint( $_GET['post'] ) : 0; if ( ! $post_id ) { return; } $post = get_post( $post_id ); if ( ! $post || $post->post_type !== 'wpvr_item' ) { return; } $panodata = get_post_meta( $post_id, 'panodata', true ); if ( ! is_array( $panodata ) ) { return; } $changed = false; if ( ! array_key_exists( 'streetview', $panodata ) ) { $panodata['streetview'] = 'off'; $changed = true; } if ( ! array_key_exists( 'streetviewurl', $panodata ) ) { $panodata['streetviewurl'] = ''; $changed = true; } if ( $changed ) { update_post_meta( $post_id, 'panodata', $panodata ); } } add_action( 'load-post.php', 'wpvr_backfill_legacy_streetview_keys', 1 ); /** * Register a lightweight PSR-4 autoloader for src/ classes. */ function wpvr_register_src_autoloader() { static $loaded = false; if ( $loaded ) { return; } spl_autoload_register( static function ( $class ) { $prefix = 'RexTheme\\WPVR\\'; if ( strpos( $class, $prefix ) !== 0 ) { return; } $relative_class = substr( $class, strlen( $prefix ) ); $file = plugin_dir_path( __FILE__ ) . 'src/' . str_replace( '\\', '/', $relative_class ) . '.php'; if ( file_exists( $file ) ) { require_once $file; } } ); $loaded = true; } // UI/UX Mode Switch: legacy/classic or latest // Option: wpvr_ui_mode (default: legacy) wpvr_register_src_autoloader(); $wpvr_ui_mode = get_option('wpvr_ui_mode', 'legacy'); if ($wpvr_ui_mode === 'latest') { require_once plugin_dir_path(__FILE__) . 'src/Bootstrap.php'; \RexTheme\WPVR\Bootstrap::init(); } else { require_once plugin_dir_path(__FILE__) . 'legacy/wpvr.php'; new \RexTheme\WPVR\Admin\Admin( false ); }