PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.3
Yatra – Travel Booking & Tour Operator Software v3.0.3
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 / AdminServiceProvider.php

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

512 lines 15.6 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 use Yatra\Core\ServiceProvider;
8 use Yatra\Core\Container;
9 use Yatra\Core\Modules\ModuleManager;
10
11 /**
12 * Admin Service Provider
13 * Handles admin menu, assets, and pages
14 */
15 class AdminServiceProvider extends ServiceProvider
16 {
17 private const UPGRADE_TO_PRO_URL = 'https://wpyatra.com/pricing';
18
19 /**
20 * Register services
21 */
22 public function register(): void
23 {
24 // Register admin menu
25 add_action('admin_menu', [$this, 'registerAdminMenu']);
26 // After core + other Yatra submenus register; append external “Upgrade” link (avoids add_submenu_page plugin_basename mangling URLs).
27 add_action('admin_menu', [$this, 'registerUpgradeProExternalSubmenu'], 100);
28
29 add_filter('plugin_action_links_' . YATRA_PLUGIN_BASENAME, [$this, 'addPluginUpgradeLink']);
30 add_filter('plugin_row_meta', [$this, 'filterPluginRowMeta'], 10, 4);
31
32 // Enqueue admin assets - use priority 20 to run after WordPress core
33 add_action('admin_enqueue_scripts', [$this, 'enqueueAdminAssets'], 20);
34
35 // Add module type to script tag
36 add_filter('script_loader_tag', [$this, 'addModuleTypeToScript'], 10, 2);
37
38 // Remove admin wrapper for our page
39 add_action('admin_init', [$this, 'removeAdminWrapper']);
40
41 add_action('admin_print_styles', [$this, 'printYatraReactAdminCriticalCss'], 0);
42 add_filter('admin_body_class', [$this, 'filterYatraReactAdminBodyClass']);
43 add_action('admin_head', [$this, 'printUpgradeProAdminStyles']);
44 add_action('admin_head', [$this, 'printYatraAdminMenuIconStyles']);
45 add_action('admin_head', [$this, 'printHideYatraSetupWizardSubmenu']);
46 add_action('admin_footer', [$this, 'upgradeProSubmenuOpenInNewTab']);
47 }
48
49 /**
50 * Whether the current request is the main Yatra React admin screen (admin.php?page=yatra).
51 */
52 private function isYatraMainReactAdminScreen(): bool
53 {
54 if (!is_admin()) {
55 return false;
56 }
57
58 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- screen detection only
59 if (isset($_GET['page']) && $_GET['page'] === 'yatra') {
60 return true;
61 }
62
63 $screen = function_exists('get_current_screen') ? get_current_screen() : null;
64
65 return $screen && $screen->id === 'toplevel_page_yatra';
66 }
67
68 /**
69 * Critical CSS + dark-mode init in &lt;head&gt; (before body) to prevent FOUC / flicker of native wp-admin chrome.
70 *
71 * Previously this lived in templates/admin.php (body), so the menu painted before hide rules applied.
72 */
73 public function printYatraReactAdminCriticalCss(): void
74 {
75 if (!$this->isYatraMainReactAdminScreen()) {
76 return;
77 }
78
79 $app_js = YATRA_PLUGIN_PATH . 'assets/admin/dist/js/app.js';
80 $has_build = file_exists($app_js);
81
82 ?>
83 <script id="yatra-admin-dark-init">
84 (function(){var d=localStorage.getItem('yatra-dark-mode');if(d==='true'){document.documentElement.classList.add('dark');}})();
85 </script>
86 <style id="yatra-react-admin-critical">
87 #wpadminbar,
88 #adminmenumain,
89 #adminmenuback,
90 #adminmenuwrap,
91 #wpfooter,
92 #screen-meta,
93 #screen-meta-links {
94 display: none !important;
95 }
96
97 #wpcontent {
98 margin-left: 0 !important;
99 padding: 0 !important;
100 }
101
102 #wpbody,
103 #wpbody-content {
104 margin: 0 !important;
105 padding: 0 !important;
106 padding-top: 0 !important;
107 }
108
109 .wrap {
110 margin: 0 !important;
111 padding: 0 !important;
112 max-width: 100% !important;
113 }
114
115 html,
116 body.wp-admin {
117 margin: 0 !important;
118 padding: 0 !important;
119 height: 100% !important;
120 overflow: <?php echo $has_build ? 'hidden' : 'auto'; ?> !important;
121 }
122
123 /* Match React Layout shell (gray-50 / gray-900) so the pre-JS phase is not a flat white flash */
124 body.yatra-react-admin-shell {
125 background: #f9fafb !important;
126 }
127
128 html.dark body.yatra-react-admin-shell {
129 background: #111827 !important;
130 }
131
132 #yatra-app-root {
133 width: 100vw;
134 height: 100vh;
135 position: fixed;
136 top: 0;
137 left: 0;
138 z-index: 99999;
139 background: #f9fafb;
140 }
141
142 html.dark #yatra-app-root {
143 background: #111827;
144 }
145
146 /* HTML/CSS-only boot UI (no JS); React replaces #yatra-app-root contents on mount */
147 .yatra-admin-boot-splash {
148 box-sizing: border-box;
149 position: absolute;
150 inset: 0;
151 display: flex;
152 align-items: center;
153 justify-content: center;
154 flex-direction: column;
155 gap: 1rem;
156 z-index: 1;
157 }
158
159 .yatra-admin-boot-spinner {
160 width: 40px;
161 height: 40px;
162 border-radius: 50%;
163 border: 3px solid rgba(79, 70, 229, 0.2);
164 border-top-color: #4f46e5;
165 animation: yatra-boot-spin 0.7s linear infinite;
166 }
167
168 html.dark .yatra-admin-boot-spinner {
169 border-color: rgba(129, 140, 248, 0.25);
170 border-top-color: #a5b4fc;
171 }
172
173 .yatra-admin-boot-text {
174 margin: 0;
175 font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
176 font-size: 14px;
177 color: #6b7280;
178 }
179
180 html.dark .yatra-admin-boot-text {
181 color: #9ca3af;
182 }
183
184 @keyframes yatra-boot-spin {
185 to {
186 transform: rotate(360deg);
187 }
188 }
189
190 .yatra-build-message {
191 padding: 40px;
192 text-align: center;
193 max-width: 800px;
194 margin: 50px auto;
195 background: #fff;
196 border: 1px solid #ddd;
197 border-radius: 8px;
198 box-shadow: 0 2px 4px rgba(0,0,0,0.1);
199 }
200
201 .yatra-build-message h1 {
202 margin-bottom: 20px;
203 color: #333;
204 }
205
206 .yatra-build-message code {
207 display: block;
208 background: #f5f5f5;
209 padding: 15px;
210 margin: 15px 0;
211 border-radius: 4px;
212 text-align: left;
213 border-left: 4px solid #0073aa;
214 }
215 </style>
216 <?php
217 }
218
219 /**
220 * Marker class for the React shell (optional hooks / debugging).
221 *
222 * @param string $classes Space-separated classes.
223 */
224 public function filterYatraReactAdminBodyClass(string $classes): string
225 {
226 if ($this->isYatraMainReactAdminScreen()) {
227 $classes .= ' yatra-react-admin-shell';
228 }
229
230 return $classes;
231 }
232
233 /**
234 * Hide Setup Wizard in the Yatra submenu while keeping it registered (required for direct URL access).
235 */
236 public function printHideYatraSetupWizardSubmenu(): void
237 {
238 if (!apply_filters('yatra_enable_setup_wizard', true)) {
239 return;
240 }
241
242 if (!apply_filters('yatra_hide_setup_wizard_submenu', true)) {
243 return;
244 }
245
246 echo '<style id="yatra-hide-setup-submenu">'
247 . '#adminmenu .toplevel_page_yatra .wp-submenu li:has(> a[href*="page=yatra-setup"]){display:none!important;}'
248 . '</style>';
249 }
250
251 /**
252 * Size the custom Yatra menu icon (PNG) like core dashicons.
253 */
254 public function printYatraAdminMenuIconStyles(): void
255 {
256 if (!function_exists('yatra_get_brand_icon_url') || yatra_get_brand_icon_url() === '') {
257 return;
258 }
259
260 echo '<style id="yatra-admin-menu-brand-icon">#adminmenu .toplevel_page_yatra div.wp-menu-image img{opacity:1!important;width:20px!important;height:20px!important;padding:6px 0!important;object-fit:contain!important;}</style>';
261 }
262
263 /**
264 * “Upgrade to Pro” on the Plugins list (free only).
265 *
266 * @param array<int,string> $links
267 * @return array<int,string>
268 */
269 public function addPluginUpgradeLink(array $links): array
270 {
271 if (apply_filters('yatra_is_pro_active', false)) {
272 return $links;
273 }
274
275 $upgrade = sprintf(
276 '<a href="%s" class="yatra-upgrade-to-pro-link" target="_blank" rel="noopener noreferrer">%s</a>',
277 esc_url(self::UPGRADE_TO_PRO_URL),
278 esc_html__('Upgrade to Pro', 'yatra')
279 );
280
281 array_unshift($links, $upgrade);
282
283 return $links;
284 }
285
286 /**
287 * Plugins list row meta: Version, By MantraBrain, View details, Support, Plugin Homepage, Contact, Rate (5�
288 ).
289 *
290 * @param string[] $plugin_meta
291 * @param array<string, mixed> $plugin_data
292 * @return string[]
293 */
294 public function filterPluginRowMeta(array $plugin_meta, string $plugin_file, array $plugin_data, string $status): array
295 {
296 if ($plugin_file !== YATRA_PLUGIN_BASENAME) {
297 return $plugin_meta;
298 }
299
300 $home = 'https://wpyatra.com/';
301 $org_plugin_page = 'https://wordpress.org/plugins/yatra/';
302 $support_url = 'https://wordpress.org/support/plugin/yatra/reviews/?filter=5';
303 $contact_url = 'https://mantrabrain.com/contact';
304 $rate_url = 'https://wordpress.org/support/plugin/yatra/reviews/?filter=5';
305
306 $row = [];
307
308 if (!empty($plugin_data['Version'])) {
309 $row[] = sprintf(
310 /* translators: %s: plugin version number. */
311 __('Version %s', 'yatra'),
312 $plugin_data['Version']
313 );
314 }
315
316 $row[] = sprintf(
317 /* translators: %s: linked author name (HTML). */
318 __('By %s', 'yatra'),
319 '<a href="' . esc_url($home) . '" target="_blank" rel="noopener noreferrer">MantraBrain</a>'
320 );
321
322 $row[] = sprintf(
323 '<a href="%s" target="_blank" rel="noopener noreferrer">%s</a>',
324 esc_url($org_plugin_page),
325 esc_html__('View details', 'yatra')
326 );
327
328 $row[] = sprintf(
329 '<a href="%s" target="_blank" rel="noopener noreferrer">%s</a>',
330 esc_url($support_url),
331 esc_html__('Support', 'yatra')
332 );
333
334 $row[] = sprintf(
335 '<a href="%s" target="_blank" rel="noopener noreferrer">%s</a>',
336 esc_url($home),
337 esc_html__('Plugin Homepage', 'yatra')
338 );
339
340 $row[] = sprintf(
341 '<a href="%s" target="_blank" rel="noopener noreferrer">%s</a>',
342 esc_url($contact_url),
343 esc_html__('Contact', 'yatra')
344 );
345
346 $row[] = sprintf(
347 '<a href="%s" target="_blank" rel="noopener noreferrer">%s <span aria-hidden="true">&#9733;&#9733;&#9733;&#9733;&#9733;</span></a>',
348 esc_url($rate_url),
349 esc_html__('Rate the plugin', 'yatra')
350 );
351
352 return $row;
353 }
354
355 /**
356 * Amber styling for Upgrade to Pro (admin menu + plugins list).
357 */
358 public function printUpgradeProAdminStyles(): void
359 {
360 if (apply_filters('yatra_is_pro_active', false)) {
361 return;
362 }
363
364 if (!is_admin()) {
365 return;
366 }
367
368 $screen = function_exists('get_current_screen') ? get_current_screen() : null;
369 $onPlugins = $screen && $screen->id === 'plugins';
370
371 $pricingHref = esc_attr(self::UPGRADE_TO_PRO_URL);
372
373 // Plain link emphasis only (same layout as other submenu items / plugin row links).
374 // Bright orange, bold — reads clearly on default admin submenu and Plugins screen (no button chrome).
375 echo '<style id="yatra-upgrade-pro-styles">'
376 . '#adminmenu .toplevel_page_yatra .wp-submenu a[href="' . $pricingHref . '"]{color:#f97316!important;font-weight:700!important;}'
377 . '#adminmenu .toplevel_page_yatra .wp-submenu a[href="' . $pricingHref . '"]:hover{color:#ea580c!important;}'
378 . ($onPlugins
379 ? '.plugins-php .yatra-upgrade-to-pro-link{color:#f97316!important;font-weight:700!important;}'
380 . '.plugins-php .yatra-upgrade-to-pro-link:hover{color:#ea580c!important;}'
381 : '')
382 . '</style>';
383 }
384
385 /**
386 * Open pricing link in a new tab (submenu HTML is generated by core without target="_blank").
387 */
388 public function upgradeProSubmenuOpenInNewTab(): void
389 {
390 if (apply_filters('yatra_is_pro_active', false)) {
391 return;
392 }
393
394 if (!is_admin()) {
395 return;
396 }
397
398 $url = wp_json_encode(self::UPGRADE_TO_PRO_URL, JSON_UNESCAPED_SLASHES);
399 echo '<script>(function(){var u=' . $url . ";document.querySelectorAll('#adminmenu .toplevel_page_yatra .wp-submenu a[href=\"'+u+'\"]').forEach(function(a){a.target='_blank';a.rel='noopener noreferrer';});})();</script>";
400 }
401
402 /**
403 * Direct external submenu link (WordPress prints href from slug when it is not a registered admin page file).
404 *
405 * @global array<string,array<int,array<int,mixed>>> $submenu
406 */
407 public function registerUpgradeProExternalSubmenu(): void
408 {
409 if (apply_filters('yatra_is_pro_active', false)) {
410 return;
411 }
412
413 global $submenu;
414 if (!isset($submenu['yatra']) || !is_array($submenu['yatra'])) {
415 return;
416 }
417
418 $submenu['yatra'][] = [
419 esc_html__('Upgrade to Pro', 'yatra'),
420 'manage_options',
421 self::UPGRADE_TO_PRO_URL,
422 esc_html__('Upgrade to Pro', 'yatra'),
423 ];
424 }
425
426 /**
427 * Add type="module" attribute to yatra-admin script
428 */
429 public function addModuleTypeToScript(string $tag, string $handle): string
430 {
431 if ($handle === 'yatra-admin') {
432 // Check if type="module" is already present
433 if (strpos($tag, 'type="module"') === false && strpos($tag, "type='module'") === false) {
434 // Replace the script tag to add type="module"
435 $tag = str_replace('<script ', '<script type="module" ', $tag);
436 }
437 }
438 return $tag;
439 }
440
441 /**
442 * Remove WordPress admin wrapper for Yatra page
443 */
444 public function removeAdminWrapper(): void
445 {
446 $screen = get_current_screen();
447 if ($screen && $screen->id === 'toplevel_page_yatra') {
448 // Remove admin notices wrapper
449 remove_action('admin_notices', 'wp_admin_notices');
450 }
451 }
452
453 /**
454 * Register admin menu
455 */
456 public function registerAdminMenu(): void
457 {
458 $menu_icon = (function_exists('yatra_get_brand_icon_url') && yatra_get_brand_icon_url() !== '')
459 ? yatra_get_brand_icon_url()
460 : 'dashicons-palmtree';
461
462 add_menu_page(
463 __('Yatra', 'yatra'),
464 __('Yatra', 'yatra'),
465 'manage_options',
466 'yatra',
467 [$this, 'renderAdminPage'],
468 $menu_icon,
469 30
470 );
471
472 // WordPress would otherwise add a first submenu also titled "Yatra" (duplicate of the parent).
473 // A submenu with the same slug as the parent replaces that entry with a distinct label.
474 add_submenu_page(
475 'yatra',
476 __('Yatra Dashboard', 'yatra'),
477 __('Dashboard', 'yatra'),
478 'manage_options',
479 'yatra',
480 [$this, 'renderAdminPage']
481 );
482
483 }
484
485 /**
486 * Enqueue admin assets
487 */
488 public function enqueueAdminAssets(string $hook): void
489 {
490 // Use centralized AdminAssetsProvider
491 $adminAssetsProvider = new \Yatra\Providers\AdminAssetsProvider();
492 $adminAssetsProvider->enqueueAssets($hook);
493 }
494
495
496 /**
497 * Render admin page
498 */
499 public function renderAdminPage(): void
500 {
501 // Load admin template
502 $template = YATRA_PLUGIN_PATH . 'templates/admin.php';
503
504 if (file_exists($template)) {
505 include $template;
506 } else {
507 echo '<div class="wrap"><h1>Yatra Admin</h1><p>Admin template not found.</p></div>';
508 }
509 }
510 }
511
512