*/
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'),
]);
}
}
}