PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.2.9
Yatra – Travel Booking & Tour Operator Software v3.0.2.9
3.0.15 3.0.14 3.0.14.1 3.0.14.2 3.0.12 3.0.13 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.5.1 3.0.4 3.0.3 3.0.2.9 3.0.2.7 3.0.2.8 3.0.2.6 trunk 1.0.0 2.0.0 2.0.1 2.0.10 All 83 releases
yatra / app / Providers / AdminAssetsProvider.php

AdminAssetsProvider.php in Yatra – Travel Booking & Tour Operator Software 3.0.2.9, at app/Providers/AdminAssetsProvider.php

400 lines 14.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 namespace Yatra\Providers;
6
7 /**
8 * Admin Assets Provider
9 *
10 * Handles enqueuing of all admin-related CSS and JavaScript assets
11 * Centralizes admin asset management for better organization and maintainability
12 *
13 * @package Yatra\Providers
14 * @since 3.0.0
15 */
16 class AdminAssetsProvider
17 {
18 /**
19 * Full `window.yatraAdmin` payload — must match what addMediaLibraryCompatScript used to merge
20 * (permalinkStructure, tripBase, locale, etc.) so View links and REST helpers work in all modes.
21 *
22 * @return array<string, mixed>
23 */
24 private function buildAdminLocalizedData(): array
25 {
26 $current_user = wp_get_current_user();
27 $capabilities = [];
28 if ($current_user->ID > 0) {
29 $user_caps = $current_user->allcaps;
30 foreach ($user_caps as $cap => $has_cap) {
31 if ($has_cap && strpos((string) $cap, 'yatra_') === 0) {
32 $capabilities[$cap] = true;
33 }
34 }
35 }
36
37 return apply_filters('yatra_admin_localized_data', [
38 'apiUrl' => rest_url('yatra/v1'),
39 'licenseStatus' => (function () {
40 $all = get_option('yatra_license', []);
41 $status = $all['yatra-pro']['status'] ?? 'inactive';
42 return (string) $status;
43 })(),
44 'restUrl' => rest_url(),
45 'nonce' => wp_create_nonce('wp_rest'),
46 'currentUser' => $current_user->ID,
47 'currentUserEmail' => $current_user->user_email,
48 'currentUserDisplayName' => $current_user->display_name,
49 'currentUserLogin' => $current_user->user_login,
50 'currentUserAvatar' => get_avatar($current_user->ID, 96),
51 'siteUrl' => home_url(),
52 'adminUrl' => admin_url('admin.php'),
53 'pluginUrl' => YATRA_PLUGIN_URL,
54 'brandLogoUrl' => function_exists('yatra_get_brand_icon_url') ? yatra_get_brand_icon_url() : '',
55 'permalinkStructure' => (get_option('permalink_structure') ?: '') ?: 'plain',
56 'tripBase' => \Yatra\Services\SettingsService::getTripBase(),
57 'bookingBase' => \Yatra\Services\SettingsService::getBookingBase(),
58 'capabilities' => $capabilities,
59 'roles' => $current_user->roles,
60 'isPro' => defined('YATRA_PRO_VERSION'),
61 'version' => defined('YATRA_VERSION') ? YATRA_VERSION : '1.0.0',
62 'proVersion' => defined('YATRA_PRO_VERSION') ? YATRA_PRO_VERSION : null,
63
64 'locale' => get_locale(),
65 'currency' => \Yatra\Services\SettingsService::getCurrency(),
66 'date_format' => \Yatra\Services\SettingsService::get('date_format', 'Y-m-d'),
67 'time_format' => \Yatra\Services\SettingsService::get('time_format', 'H:i'),
68 'geocodingNonce' => wp_create_nonce('yatra_geocoding_nonce'),
69 'ajaxUrl' => admin_url('admin-ajax.php'),
70 ]);
71 }
72
73 /**
74 * Enqueue all admin assets
75 *
76 * @param string $hook Current admin page hook
77 * @return void
78 */
79 public function enqueueAssets(string $hook): void
80 {
81 // Only load on our admin page
82 if ($hook !== 'toplevel_page_yatra') {
83 return;
84 }
85
86 // Prevent problematic scripts that cause initialization errors
87 $this->preventProblematicScripts();
88
89 // Enqueue WordPress media library dependencies
90 $this->enqueueWordPressMedia();
91
92 // Enqueue admin React app assets
93 $this->enqueueAdminReactApp();
94
95 // Do not strip styles required by wp_enqueue_media() / wp.media(): stripping `media-views`
96 // (and replacing `forms` with an empty handle) makes the media modal invisible or broken.
97 // See: TripForm gallery / featured image / downloadable file pickers.
98
99 // Aggressive WordPress Admin CSS removal (keep media modal + its dependency chain intact)
100 $admin_css_handles = [
101 'admin-bar',
102 'admin-menu',
103 'dashboard',
104 'list-tables',
105 'edit',
106 'revisions',
107 'themes',
108 'about',
109 'nav-menus',
110 'wp-pointer',
111 'widgets',
112 'site-icon',
113 'l10n',
114 'wp-auth-check',
115 'wp-components',
116 'wp-commands',
117 'login',
118 'install',
119 'wp-reset-editor-styles',
120 'wp-admin',
121 'colors',
122 ];
123
124 // Dequeue all WordPress admin CSS and register empty placeholders
125 foreach ($admin_css_handles as $handle) {
126 wp_dequeue_style($handle);
127 wp_deregister_style($handle);
128 // Register as empty style to satisfy dependencies
129 wp_register_style($handle, false);
130 }
131
132 // Final safety dequeue at print time
133 add_action('wp_print_styles', function () use ($admin_css_handles) {
134 foreach ($admin_css_handles as $handle) {
135 wp_dequeue_style($handle);
136 }
137 }, 999);
138
139 // Add aggressive style loader filter to prevent WordPress admin CSS
140 add_filter('style_loader_src', function($src, $handle) use ($admin_css_handles) {
141 if (in_array($handle, $admin_css_handles)) {
142 return false; // Prevent loading actual CSS files
143 }
144 // Allow load-styles.php but individual handles will be blocked above
145 return $src;
146 }, 999, 2);
147
148 // Add inline script for media library compatibility
149 $this->addMediaLibraryCompatScript();
150 }
151
152 /**
153 * Prevent problematic WordPress scripts
154 *
155 * @return void
156 */
157 private function preventProblematicScripts(): void
158 {
159 // These scripts try to access wp.media.view before it's initialized
160 wp_dequeue_script('svg-painter');
161 wp_deregister_script('svg-painter');
162 wp_dequeue_script('image-edit');
163 wp_deregister_script('image-edit');
164 }
165
166 /**
167 * Enqueue WordPress media library dependencies
168 *
169 * @return void
170 */
171 private function enqueueWordPressMedia(): void
172 {
173 // Enqueue WordPress media library
174 wp_enqueue_media();
175
176 // Ensure wp-mediaelement is loaded
177 wp_enqueue_script('wp-mediaelement');
178
179 // Ensure media-audiovideo is loaded
180 wp_enqueue_script('media-audiovideo');
181
182 // Keep media-editor loaded - it's required by media-audiovideo
183 // Note: The initialization errors were caused by svg-painter and image-edit, not media-editor
184
185 // Ensure all required dependencies are loaded
186 wp_enqueue_script('jquery');
187 wp_enqueue_script('underscore');
188 wp_enqueue_script('backbone');
189 }
190
191 /**
192 * Enqueue admin React app assets
193 *
194 * @return void
195 */
196 private function enqueueAdminReactApp(): void
197 {
198 // Enqueue compiled React app CSS files
199 $this->enqueueAdminReactCss();
200 $this->enqueueAdminReactJs();
201 }
202
203 /**
204 * Enqueue admin React CSS files
205 *
206 * @return void
207 */
208 private function enqueueAdminReactCss(): void
209 {
210 $basePath = YATRA_PLUGIN_PATH . 'assets/admin/dist/css/';
211
212 // React vendor CSS (contains react-draft-wysiwyg CSS)
213 $reactVendorCss = $basePath . 'react-vendor.css';
214 if (file_exists($reactVendorCss)) {
215 $cssVersion = YATRA_VERSION . '.' . filemtime($reactVendorCss);
216 wp_enqueue_style(
217 'yatra-react-vendor',
218 YATRA_PLUGIN_URL . 'assets/admin/dist/css/react-vendor.css',
219 [],
220 $cssVersion
221 );
222 }
223
224 // Index CSS (contains main component styles)
225 $indexCss = $basePath . 'index.css';
226 if (file_exists($indexCss)) {
227 $cssVersion = YATRA_VERSION . '.' . filemtime($indexCss);
228 wp_enqueue_style(
229 'yatra-index',
230 YATRA_PLUGIN_URL . 'assets/admin/dist/css/index.css',
231 ['yatra-react-vendor'],
232 $cssVersion
233 );
234 }
235 }
236
237 /**
238 * Enqueue admin React JS files
239 *
240 * @return void
241 */
242 private function enqueueAdminReactJs(): void
243 {
244 // Check if we're in development mode and Vite dev server is running
245 $isDevMode = defined('WP_DEBUG') && WP_DEBUG && defined('YATRA_DEV_MODE') && YATRA_DEV_MODE;
246 $viteDevServer = 'http://localhost:5173';
247
248 if ($isDevMode && $this->isViteDevServerRunning($viteDevServer)) {
249 // In dev mode, inject localized data and Vite's HMR client
250 add_action('admin_head', function() use ($viteDevServer) {
251 $localized_data = $this->buildAdminLocalizedData();
252
253 ?>
254 <script>
255 window.yatraAdmin = <?php echo wp_json_encode($localized_data); ?>;
256 </script>
257 <script type="module">
258 import { injectIntoGlobalHook } from "<?php echo $viteDevServer; ?>/@react-refresh";
259 injectIntoGlobalHook(window);
260 window.$RefreshReg$ = () => {};
261 window.$RefreshSig$ = () => (type) => type;
262 </script>
263 <script type="module" src="<?php echo $viteDevServer; ?>/@vite/client"></script>
264 <?php
265 }, 1);
266
267 // Load the entry point as ES module
268 add_action('admin_footer', function() use ($viteDevServer) {
269 ?>
270 <script type="module" src="<?php echo $viteDevServer; ?>/resources/js/main.tsx"></script>
271 <?php
272 }, 1);
273
274 } else {
275 // Use built assets in production
276 $appJs = YATRA_PLUGIN_PATH . 'assets/admin/dist/js/app.js';
277
278 if (file_exists($appJs)) {
279 $jsVersion = YATRA_VERSION . '.' . filemtime($appJs) . '.view-icon-fix.' . time() . '.' . microtime(true);
280
281 $localized_data = $this->buildAdminLocalizedData();
282
283 // Enqueue our script with media library as dependency
284 wp_enqueue_script(
285 'yatra-admin',
286 YATRA_PLUGIN_URL . 'assets/admin/dist/js/app.js',
287 [
288 'jquery',
289 'underscore',
290 'backbone',
291 'media-models',
292 'wp-mediaelement',
293 'media-editor',
294 'media-audiovideo',
295 'media-views',
296 'wp-i18n'
297 ],
298 $jsVersion,
299 true
300 );
301
302 // Localize script data
303 wp_localize_script('yatra-admin', 'yatraAdmin', $localized_data);
304
305 // Start fetching the ES module as early as possible (helps shorten white/splash time before React runs)
306 $app_js_url = YATRA_PLUGIN_URL . 'assets/admin/dist/js/app.js';
307 add_action('admin_head', static function () use ($app_js_url, $jsVersion): void {
308 $href = esc_url(add_query_arg('ver', rawurlencode((string) $jsVersion), $app_js_url));
309 echo '<link rel="modulepreload" href="' . $href . '" />' . "\n";
310 }, 0);
311 }
312 }
313 }
314
315 /**
316 * Check if Vite dev server is running
317 *
318 * @param string $url
319 * @return bool
320 */
321 private function isViteDevServerRunning(string $url): bool
322 {
323 // Check the actual asset URL, not the root
324 $assetUrl = $url . '/assets/admin/dist/js/app.js';
325 $ch = curl_init($assetUrl);
326 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
327 curl_setopt($ch, CURLOPT_TIMEOUT, 2); // 2 second timeout
328 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 1); // 1 second connection timeout
329 curl_setopt($ch, CURLOPT_NOBODY, true); // HEAD request only
330 curl_exec($ch);
331 $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
332 curl_close($ch);
333
334 return $httpCode === 200;
335 }
336
337 /**
338 * Add inline script for media library compatibility
339 *
340 * @return void
341 */
342 private function addMediaLibraryCompatScript(): void
343 {
344 // `yatraAdmin` is localized once in enqueueAdminReactJs via buildAdminLocalizedData().
345 // Load WordPress translation data for the yatra domain
346 $this->loadWordPressTranslations();
347 }
348
349 /**
350 * Load WordPress translation data for JavaScript
351 *
352 * @return void
353 */
354 private function loadWordPressTranslations(): void
355 {
356 // Use WordPress built-in function to load script translations
357 // Specify the path where WordPress should look for JSON translation files
358 if (function_exists('wp_set_script_translations')) {
359 wp_set_script_translations('yatra-admin', 'yatra', YATRA_PLUGIN_FILE . '/i18n/languages');
360 }
361 }
362
363 /**
364 * Enqueue setup wizard assets
365 *
366 * @return void
367 */
368 public function enqueueSetupWizardAssets(): void
369 {
370 // Enqueue setup wizard CSS
371 $cssPath = YATRA_PLUGIN_PATH . 'assets/admin/css/setup-wizard.css';
372 if (file_exists($cssPath)) {
373 wp_enqueue_style(
374 'yatra-setup-wizard',
375 YATRA_PLUGIN_URL . 'assets/admin/css/setup-wizard.css',
376 [],
377 YATRA_VERSION
378 );
379 }
380
381 // Enqueue setup wizard JS
382 $jsPath = YATRA_PLUGIN_PATH . 'assets/admin/js/setup-wizard.js';
383 if (file_exists($jsPath)) {
384 wp_enqueue_script(
385 'yatra-setup-wizard',
386 YATRA_PLUGIN_URL . 'assets/admin/js/setup-wizard.js',
387 ['jquery'],
388 YATRA_VERSION,
389 true
390 );
391
392 // Localize setup wizard
393 wp_localize_script('yatra-setup-wizard', 'yatraSetupWizard', [
394 'ajaxurl' => admin_url('admin-ajax.php'),
395 'nonce' => wp_create_nonce('yatra-setup-wizard'),
396 ]);
397 }
398 }
399 }
400