editor_enabled = $editor_enabled; add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_listing_assets' ] ); if ( $this->editor_enabled ) { add_action( 'admin_footer', [ $this, 'render_listing_modal' ] ); } if ( ! $this->editor_enabled ) { return; } add_action( 'admin_menu', [ $this, 'register_editor_page' ] ); add_action( 'load-post.php', [ $this, 'redirect_cpt_edit' ] ); add_action( 'load-post-new.php', [ $this, 'redirect_cpt_new' ] ); add_filter( 'get_edit_post_link', [ $this, 'filter_edit_link' ], 10, 3 ); add_filter( 'post_row_actions', [ $this, 'filter_row_actions' ], 10, 2 ); add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_tour_editor_assets' ] ); add_filter( 'admin_body_class', [ $this, 'add_body_class' ] ); add_filter( 'admin_title', [ $this, 'filter_admin_title' ], 10, 2 ); add_action( 'current_screen', [ $this, 'set_screen_title' ] ); add_filter( 'parent_file', [ $this, 'set_menu_parent' ] ); add_filter( 'submenu_file', [ $this, 'set_menu_submenu' ] ); add_action( 'admin_head', [ $this, 'editor_head_scripts' ] ); add_action( 'admin_footer', [ $this, 'editor_footer_scripts' ] ); } // ------------------------------------------------------------------------- // Step 1 — Register hidden admin page // ------------------------------------------------------------------------- public function register_editor_page(): void { $tour_id = isset( $_GET['tour_id'] ) ? (int) $_GET['tour_id'] : 0; $page_title = $tour_id ? __( 'Edit Tour', 'wpvr' ) : __( 'Add New Tour', 'wpvr' ); add_submenu_page( '', $page_title, $page_title, 'edit_posts', self::PAGE_SLUG, [ $this, 'render_editor_page' ] ); } // ------------------------------------------------------------------------- // Step 2 — Redirect post.php / post-new.php // ------------------------------------------------------------------------- public function redirect_cpt_edit(): void { $action = isset( $_GET['action'] ) ? sanitize_key( $_GET['action'] ) : ''; if ( $action && $action !== 'edit' ) { return; } $post_id = isset( $_GET['post'] ) ? (int) $_GET['post'] : 0; if ( ! $post_id ) { return; } $post = get_post( $post_id ); if ( ! $post || $post->post_type !== self::POST_TYPE ) { return; } wp_redirect( $this->editor_url( $post_id ) ); exit; } public function redirect_cpt_new(): void { $post_type = isset( $_GET['post_type'] ) ? sanitize_key( $_GET['post_type'] ) : ''; if ( $post_type !== self::POST_TYPE ) { return; } wp_redirect( $this->editor_url() ); exit; } // ------------------------------------------------------------------------- // Step 3 — Override edit links // ------------------------------------------------------------------------- public function filter_edit_link( string $url, int $post_id, string $context ): string { $post = get_post( $post_id ); if ( ! $post || $post->post_type !== self::POST_TYPE ) { return $url; } return $this->editor_url( $post_id ); } public function filter_row_actions( array $actions, \WP_Post $post ): array { if ( $post->post_type !== self::POST_TYPE ) { return $actions; } if ( isset( $actions['edit'] ) ) { $actions['edit'] = sprintf( '%s', esc_url( $this->editor_url( $post->ID ) ), __( 'Edit', 'wpvr' ) ); } return $actions; } // ------------------------------------------------------------------------- // Step 4 — Render + enqueue // ------------------------------------------------------------------------- public function render_editor_page(): void { echo '
'; if ( ! wpvr_is_pro_active() ) { require WPVR_PLUGIN_LEGACY_DIR_PATH . 'admin/partials/wpvr-premium-feature-popup.php'; } // Hidden wp_editor() output forces TinyMCE HTML/scripts to load so // wp.editor.initialize() is available for WYSIWYG fields in React. echo ''; } public function enqueue_tour_editor_assets( string $hook ): void { if ( ! $this->is_editor_page( $hook ) ) { return; } $plugin_url = plugin_dir_url( WPVR_FILE ); $plugin_path = plugin_dir_path( WPVR_FILE ); $build_dir = $plugin_path . 'build/tour-editor/'; // Load Pannellum (from legacy bundle). $pano_base = $plugin_url . 'legacy/admin/lib/pannellum/src/'; wp_enqueue_style( 'wpvr-pannellum-css', $pano_base . 'css/pannellum.css', [], WPVR_VERSION ); wp_enqueue_script( 'wpvr-libpannellum', $pano_base . 'js/libpannellum.js', [], WPVR_VERSION, true ); wp_enqueue_script( 'wpvr-pannellum', $pano_base . 'js/pannellum.js', [ 'wpvr-libpannellum' ], WPVR_VERSION, true ); wp_enqueue_style( 'wpvr-fontawesome', $plugin_url . 'legacy/admin/lib/fontawesome/css/all.min.css', [], WPVR_VERSION ); // Tour editor React bundle. $js_file = $build_dir . 'index.js'; $css_file = $build_dir . 'style-index.css'; $asset_file = $build_dir . 'index.asset.php'; if ( ! file_exists( $js_file ) ) { return; } $asset = file_exists( $asset_file ) ? require $asset_file : [ 'dependencies' => [ 'wp-element', 'wp-data', 'wp-api-fetch' ], 'version' => WPVR_VERSION ]; wp_enqueue_script( 'wpvr-tour-editor', $plugin_url . 'build/tour-editor/index.js', array_merge( $asset['dependencies'], [ 'wpvr-pannellum' ] ), $asset['version'], true ); $tour_id_cfg = isset( $_GET['tour_id'] ) ? (int) $_GET['tour_id'] : 0; $tour_status = $tour_id_cfg ? ( get_post_status( $tour_id_cfg ) ?: 'draft' ) : 'draft'; $fa_icons = []; if ( class_exists( 'Wpvr_fontawesome_icons' ) ) { $fa_icons = ( new \Wpvr_fontawesome_icons() )->icon; } wp_localize_script( 'wpvr-tour-editor', 'wpvrTourEditor', [ 'tourId' => $tour_id_cfg ?: null, 'tourStatus' => $tour_status, 'nonce' => wp_create_nonce( 'wp_rest' ), 'apiBase' => rest_url( 'wpvr/v1' ), 'mediaUrl' => rest_url( 'wp/v2/media' ), 'isPro' => wpvr_is_pro_active(), 'pluginUrl' => $plugin_url, 'listUrl' => admin_url( 'edit.php?post_type=' . self::POST_TYPE ), 'postEditUrl' => admin_url( 'post.php' ), 'faIcons' => $fa_icons, 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'exportNonce' => wp_create_nonce( 'wpvr_export_tour' ), 'imageResizeWarning' => [ 'ajaxNonce' => wp_create_nonce( 'wpvr' ), 'canManageSettings' => current_user_can( 'manage_options' ), 'settingEnabled' => get_option( 'high_res_image' ) === 'true', 'heading' => __( 'Keep this panorama in High Resolution', 'wpvr' ), 'scaledMessage' => __( 'By default, a resized version is created while the original image is still available.', 'wpvr' ), 'disableHandler' => __( 'Disable WordPress Large Image Handler on WP VR for future uploads', 'wpvr' ), 'highResolutionNote'=> __( '*Note: Turn this on to use the high-resolution image.', 'wpvr' ), 'close' => __( 'Close', 'wpvr' ), ], ] ); if ( file_exists( $css_file ) ) { wp_enqueue_style( 'wpvr-tour-editor', $plugin_url . 'build/tour-editor/style-index.css', [ 'wpvr-pannellum-css' ], $asset['version'] ); } } public function filter_admin_title( string $admin_title, string $title ): string { if ( ! isset( $_GET['page'] ) || sanitize_key( $_GET['page'] ) !== self::PAGE_SLUG ) { return $admin_title; } $tour_id = isset( $_GET['tour_id'] ) ? (int) $_GET['tour_id'] : 0; $page_title = $tour_id ? __( 'Edit Tour', 'wpvr' ) : __( 'Add New Tour', 'wpvr' ); return $page_title . ' ‹ ' . get_bloginfo( 'name' ) . ' — WordPress'; } public function add_body_class( string $classes ): string { if ( isset( $_GET['page'] ) && sanitize_key( $_GET['page'] ) === self::PAGE_SLUG ) { $classes .= ' wpvr-tour-editor-page'; } return $classes; } public function set_menu_parent( string $parent_file ): string { if ( isset( $_GET['page'] ) && sanitize_key( $_GET['page'] ) === self::PAGE_SLUG ) { return 'edit.php?post_type=' . self::POST_TYPE; } return $parent_file; } public function set_menu_submenu( ?string $submenu_file ): ?string { if ( isset( $_GET['page'] ) && sanitize_key( $_GET['page'] ) === self::PAGE_SLUG ) { return 'post-new.php?post_type=' . self::POST_TYPE; } return $submenu_file; } public function editor_head_scripts(): void { if ( ! isset( $_GET['page'] ) || sanitize_key( $_GET['page'] ) !== self::PAGE_SLUG ) { return; } ?> is_listing_page() && ! $this->is_editor_request() ) { return; } $tour_id = isset( $_GET['tour_id'] ) ? absint( $_GET['tour_id'] ) : 0; $switch_url = $this->get_ui_mode_switch_url( 'legacy', $tour_id ); $button_label = __( 'Switch to Classic UI', 'wpvr' ); ?> get_ui_mode_redirect_url( $mode, $tour_id ) ); exit; } // ------------------------------------------------------------------------- // Step 5 — Set $title global so admin-header.php doesn't get null // ------------------------------------------------------------------------- public function set_screen_title( \WP_Screen $screen ): void { if ( $screen->id !== 'admin_page_' . self::PAGE_SLUG ) { return; } global $title; $tour_id = isset( $_GET['tour_id'] ) ? (int) $_GET['tour_id'] : 0; $title = $tour_id ? __( 'Edit Tour', 'wpvr' ) : __( 'Add New Tour', 'wpvr' ); } // ------------------------------------------------------------------------- // Helpers // ------------------------------------------------------------------------- private function editor_url( int $post_id = 0 ): string { $args = [ 'page' => self::PAGE_SLUG ]; if ( $post_id > 0 ) { $args['tour_id'] = $post_id; } return add_query_arg( $args, admin_url( 'admin.php' ) ); } private function is_editor_page( string $hook ): bool { return $hook === self::HOOK_SUFFIX || ( isset( $_GET['page'] ) && sanitize_key( $_GET['page'] ) === self::PAGE_SLUG ); } private function is_editor_request(): bool { return isset( $_GET['page'] ) && sanitize_key( $_GET['page'] ) === self::PAGE_SLUG; } private function is_listing_page(): bool { $screen = get_current_screen(); return $screen && $screen->id === 'edit-' . self::POST_TYPE; } private function get_ui_mode_switch_url( string $mode, int $tour_id = 0 ): string { $args = [ 'action' => 'wpvr_switch_ui_mode', 'mode' => $mode, ]; if ( $tour_id > 0 ) { $args['tour_id'] = $tour_id; } return wp_nonce_url( add_query_arg( $args, admin_url( 'admin-post.php' ) ), 'wpvr_switch_ui_mode' ); } private function get_ui_mode_redirect_url( string $mode, int $tour_id = 0 ): string { if ( $mode === 'latest' ) { return $this->editor_url( $tour_id ); } if ( $tour_id > 0 ) { return add_query_arg( [ 'post' => $tour_id, 'action' => 'edit', ], admin_url( 'post.php' ) ); } return admin_url( 'edit.php?post_type=' . self::POST_TYPE ); } // ------------------------------------------------------------------------- // Listing page — Create Tour modal // ------------------------------------------------------------------------- public function enqueue_listing_assets( string $hook ): void { if ( ! $this->is_listing_page() ) { return; } $plugin_url = plugin_dir_url( WPVR_FILE ); $style_dependencies = []; $script_dependencies = []; if ( $this->editor_enabled ) { wp_enqueue_style( 'wpvr-create-tour-modal', $plugin_url . 'app/admin/create-tour-modal.css', [], WPVR_VERSION ); wp_enqueue_script( 'wpvr-create-tour-modal', $plugin_url . 'app/admin/create-tour-modal.js', [], WPVR_VERSION, true ); wp_localize_script( 'wpvr-create-tour-modal', 'wpvrListingModal', [ 'nonce' => wp_create_nonce( 'wp_rest' ), 'apiBase' => rest_url( 'wpvr/v1' ), 'editorUrl' => admin_url( 'admin.php?page=' . self::PAGE_SLUG ), 'isPro' => wpvr_is_pro_active(), 'iconsUrl' => $plugin_url . 'app/assets/icons/tour-types/', ] ); $style_dependencies[] = 'wpvr-create-tour-modal'; $script_dependencies[] = 'wpvr-create-tour-modal'; } wp_enqueue_style( 'wpvr-tour-listing', $plugin_url . 'app/admin/tour-listing.css', $style_dependencies, WPVR_VERSION ); wp_enqueue_script( 'wpvr-tour-listing', $plugin_url . 'app/admin/tour-listing.js', $script_dependencies, WPVR_VERSION, true ); } public function render_listing_modal(): void { if ( ! $this->editor_enabled || ! $this->is_listing_page() ) { return; } $is_pro = apply_filters( 'is_wpvr_pro_active', false ) && get_option( 'wpvr_edd_license_status' ) === 'valid'; $icons_url = plugin_dir_url( WPVR_FILE ) . 'app/assets/icons/tour-types/'; ?>