*/ private function buildAdminLocalizedData(): array { $current_user = wp_get_current_user(); $capabilities = []; if ($current_user->ID > 0) { $user_caps = $current_user->allcaps; foreach ($user_caps as $cap => $has_cap) { if ($has_cap && strpos((string) $cap, 'yatra_') === 0) { $capabilities[$cap] = true; } } } return apply_filters('yatra_admin_localized_data', [ 'apiUrl' => rest_url('yatra/v1'), 'licenseStatus' => (function () { $all = get_option('yatra_license', []); $status = $all['yatra-pro']['status'] ?? 'inactive'; return (string) $status; })(), 'restUrl' => rest_url(), 'nonce' => wp_create_nonce('wp_rest'), 'currentUser' => $current_user->ID, 'currentUserEmail' => $current_user->user_email, 'currentUserDisplayName' => $current_user->display_name, 'currentUserLogin' => $current_user->user_login, 'currentUserAvatar' => get_avatar($current_user->ID, 96), 'siteUrl' => home_url(), 'adminUrl' => admin_url('admin.php'), 'pluginUrl' => YATRA_PLUGIN_URL, 'brandLogoUrl' => function_exists('yatra_get_brand_icon_url') ? yatra_get_brand_icon_url() : '', 'permalinkStructure' => (get_option('permalink_structure') ?: '') ?: 'plain', 'tripBase' => \Yatra\Services\SettingsService::getTripBase(), 'bookingBase' => \Yatra\Services\SettingsService::getBookingBase(), 'capabilities' => $capabilities, 'roles' => $current_user->roles, 'isPro' => defined('YATRA_PRO_VERSION'), 'version' => defined('YATRA_VERSION') ? YATRA_VERSION : '1.0.0', 'proVersion' => defined('YATRA_PRO_VERSION') ? YATRA_PRO_VERSION : null, 'locale' => get_locale(), 'currency' => \Yatra\Services\SettingsService::getCurrency(), 'date_format' => \Yatra\Services\SettingsService::get('date_format', 'Y-m-d'), 'time_format' => \Yatra\Services\SettingsService::get('time_format', 'H:i'), 'geocodingNonce' => wp_create_nonce('yatra_geocoding_nonce'), 'ajaxUrl' => admin_url('admin-ajax.php'), ]); } /** * Enqueue all admin assets * * @param string $hook Current admin page hook * @return void */ public function enqueueAssets(string $hook): void { // Only load on our admin page if ($hook !== 'toplevel_page_yatra') { return; } // Prevent problematic scripts that cause initialization errors $this->preventProblematicScripts(); // Enqueue WordPress media library dependencies $this->enqueueWordPressMedia(); // Enqueue admin React app assets $this->enqueueAdminReactApp(); // Do not strip styles required by wp_enqueue_media() / wp.media(): stripping `media-views` // (and replacing `forms` with an empty handle) makes the media modal invisible or broken. // See: TripForm gallery / featured image / downloadable file pickers. // Aggressive WordPress Admin CSS removal (keep media modal + its dependency chain intact) $admin_css_handles = [ 'admin-bar', 'admin-menu', 'dashboard', 'list-tables', 'edit', 'revisions', 'themes', 'about', 'nav-menus', 'wp-pointer', 'widgets', 'site-icon', 'l10n', 'wp-auth-check', 'wp-components', 'wp-commands', 'login', 'install', 'wp-reset-editor-styles', 'wp-admin', 'colors', ]; // Dequeue all WordPress admin CSS and register empty placeholders foreach ($admin_css_handles as $handle) { wp_dequeue_style($handle); wp_deregister_style($handle); // Register as empty style to satisfy dependencies wp_register_style($handle, false); } // Final safety dequeue at print time add_action('wp_print_styles', function () use ($admin_css_handles) { foreach ($admin_css_handles as $handle) { wp_dequeue_style($handle); } }, 999); // Add aggressive style loader filter to prevent WordPress admin CSS add_filter('style_loader_src', function($src, $handle) use ($admin_css_handles) { if (in_array($handle, $admin_css_handles)) { return false; // Prevent loading actual CSS files } // Allow load-styles.php but individual handles will be blocked above return $src; }, 999, 2); // Add inline script for media library compatibility $this->addMediaLibraryCompatScript(); } /** * Prevent problematic WordPress scripts * * @return void */ private function preventProblematicScripts(): void { // These scripts try to access wp.media.view before it's initialized wp_dequeue_script('svg-painter'); wp_deregister_script('svg-painter'); wp_dequeue_script('image-edit'); wp_deregister_script('image-edit'); } /** * Enqueue WordPress media library dependencies * * @return void */ private function enqueueWordPressMedia(): void { // Enqueue WordPress media library wp_enqueue_media(); // Ensure wp-mediaelement is loaded wp_enqueue_script('wp-mediaelement'); // Ensure media-audiovideo is loaded wp_enqueue_script('media-audiovideo'); // Keep media-editor loaded - it's required by media-audiovideo // Note: The initialization errors were caused by svg-painter and image-edit, not media-editor // Ensure all required dependencies are loaded wp_enqueue_script('jquery'); wp_enqueue_script('underscore'); wp_enqueue_script('backbone'); } /** * Enqueue admin React app assets * * @return void */ private function enqueueAdminReactApp(): void { // Enqueue compiled React app CSS files $this->enqueueAdminReactCss(); $this->enqueueAdminReactJs(); } /** * Enqueue admin React CSS files * * @return void */ private function enqueueAdminReactCss(): void { $basePath = YATRA_PLUGIN_PATH . 'assets/admin/dist/css/'; // React vendor CSS (contains react-draft-wysiwyg CSS) $reactVendorCss = $basePath . 'react-vendor.css'; if (file_exists($reactVendorCss)) { $cssVersion = YATRA_VERSION . '.' . filemtime($reactVendorCss); wp_enqueue_style( 'yatra-react-vendor', YATRA_PLUGIN_URL . 'assets/admin/dist/css/react-vendor.css', [], $cssVersion ); } // Index CSS (contains main component styles) $indexCss = $basePath . 'index.css'; if (file_exists($indexCss)) { $cssVersion = YATRA_VERSION . '.' . filemtime($indexCss); wp_enqueue_style( 'yatra-index', YATRA_PLUGIN_URL . 'assets/admin/dist/css/index.css', ['yatra-react-vendor'], $cssVersion ); } } /** * Enqueue admin React JS files * * @return void */ private function enqueueAdminReactJs(): void { // Check if we're in development mode and Vite dev server is running $isDevMode = defined('WP_DEBUG') && WP_DEBUG && defined('YATRA_DEV_MODE') && YATRA_DEV_MODE; $viteDevServer = 'http://localhost:5173'; if ($isDevMode && $this->isViteDevServerRunning($viteDevServer)) { // In dev mode, inject localized data and Vite's HMR client add_action('admin_head', function() use ($viteDevServer) { $localized_data = $this->buildAdminLocalizedData(); ?> buildAdminLocalizedData(); // Enqueue our script with media library as dependency wp_enqueue_script( 'yatra-admin', YATRA_PLUGIN_URL . 'assets/admin/dist/js/app.js', [ 'jquery', 'underscore', 'backbone', 'media-models', 'wp-mediaelement', 'media-editor', 'media-audiovideo', 'media-views', 'wp-i18n' ], $jsVersion, true ); // Localize script data wp_localize_script('yatra-admin', 'yatraAdmin', $localized_data); // Start fetching the ES module as early as possible (helps shorten white/splash time before React runs) $app_js_url = YATRA_PLUGIN_URL . 'assets/admin/dist/js/app.js'; add_action('admin_head', static function () use ($app_js_url, $jsVersion): void { $href = esc_url(add_query_arg('ver', rawurlencode((string) $jsVersion), $app_js_url)); echo '' . "\n"; }, 0); } } } /** * Check if Vite dev server is running * * @param string $url * @return bool */ private function isViteDevServerRunning(string $url): bool { // Check the actual asset URL, not the root $assetUrl = $url . '/assets/admin/dist/js/app.js'; $ch = curl_init($assetUrl); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 2); // 2 second timeout curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 1); // 1 second connection timeout curl_setopt($ch, CURLOPT_NOBODY, true); // HEAD request only curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); return $httpCode === 200; } /** * Add inline script for media library compatibility * * @return void */ private function addMediaLibraryCompatScript(): void { // `yatraAdmin` is localized once in enqueueAdminReactJs via buildAdminLocalizedData(). // Load WordPress translation data for the yatra domain $this->loadWordPressTranslations(); } /** * Load WordPress translation data for JavaScript * * @return void */ private function loadWordPressTranslations(): void { // Use WordPress built-in function to load script translations // Specify the path where WordPress should look for JSON translation files if (function_exists('wp_set_script_translations')) { wp_set_script_translations('yatra-admin', 'yatra', YATRA_PLUGIN_FILE . '/i18n/languages'); } } /** * Enqueue setup wizard assets * * @return void */ public function enqueueSetupWizardAssets(): void { // Enqueue setup wizard CSS $cssPath = YATRA_PLUGIN_PATH . 'assets/admin/css/setup-wizard.css'; if (file_exists($cssPath)) { wp_enqueue_style( 'yatra-setup-wizard', YATRA_PLUGIN_URL . 'assets/admin/css/setup-wizard.css', [], YATRA_VERSION ); } // Enqueue setup wizard JS $jsPath = YATRA_PLUGIN_PATH . 'assets/admin/js/setup-wizard.js'; if (file_exists($jsPath)) { wp_enqueue_script( 'yatra-setup-wizard', YATRA_PLUGIN_URL . 'assets/admin/js/setup-wizard.js', ['jquery'], YATRA_VERSION, true ); // Localize setup wizard wp_localize_script('yatra-setup-wizard', 'yatraSetupWizard', [ 'ajaxurl' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('yatra-setup-wizard'), ]); } } }