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 / Core / TemplateLoader.php

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

448 lines 16.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\Core;
6
7 use Yatra\Core\Routing\Router;
8 use Yatra\Core\Routing\PermalinkCanonical;
9 use Yatra\Services\SettingsService;
10 use Yatra\Core\Handlers\BookingConfirmationPageHandler;
11 use Yatra\Repositories\BookingRepository;
12
13 /**
14 * Template Loader
15 *
16 * Handles all frontend template loading and routing for Yatra pages.
17 * Uses a modern, modular architecture with dedicated routing and asset management.
18 *
19 * @package Yatra\Core
20 * @since 3.0.0
21 */
22 class TemplateLoader
23 {
24 /**
25 * Router instance
26 */
27 private static ?Router $router = null;
28
29 /**
30 * Initialize template loading hooks
31 */
32 public static function init(): void
33 {
34 // Initialize rewrite rules and query vars first
35 add_action('init', [self::class, 'addTripRewriteRules'], 10);
36 add_filter('query_vars', [self::class, 'addCustomQueryVars']);
37
38 // Early template include for booking confirmation (plain permalinks safety net)
39 add_filter('template_include', [self::class, 'maybeLoadBookingConfirmationTemplate'], 0);
40
41 add_action('template_redirect', [PermalinkCanonical::class, 'enforce'], 0);
42
43 // Initialize the main router for template handling
44 add_action('template_redirect', [self::class, 'handleTemplateRedirect'], 1);
45 }
46
47 /**
48 * Early template include for booking confirmation (plain permalinks)
49 */
50 public static function maybeLoadBookingConfirmationTemplate(string $template): string
51 {
52 global $wp_query;
53 if (!empty($wp_query->is_404)) {
54 return $template;
55 }
56
57 $confirmationId = get_query_var('yatra_booking_confirmation')
58 ?: ($_GET['yatra_booking_confirmation'] ?? ($_GET['reference'] ?? ($_GET['booking_id'] ?? '')));
59 if (empty($confirmationId)) {
60 return $template;
61 }
62
63 if (defined('WP_DEBUG') && WP_DEBUG) {
64 }
65
66 $bookingRepo = new BookingRepository();
67 $booking = $bookingRepo->findByConfirmationSegment((string) $confirmationId);
68 if (!$booking) {
69 if (defined('WP_DEBUG') && WP_DEBUG) {
70 }
71 return $template;
72 }
73
74 // Prevent 404 and set globals
75 global $wp_query;
76 $wp_query->is_404 = false;
77 status_header(200);
78 $GLOBALS['yatra_booking'] = $booking;
79 $wp_query->set('yatra_booking_confirmation', $confirmationId);
80 $wp_query->set('yatra_booking', $booking);
81
82 $template_path = YATRA_PLUGIN_PATH . 'templates/booking-confirmation.php';
83 if (file_exists($template_path)) {
84 if (defined('WP_DEBUG') && WP_DEBUG) {
85 }
86 return $template_path;
87 }
88
89 if (defined('WP_DEBUG') && WP_DEBUG) {
90 }
91 return $template;
92 }
93
94 /**
95 * Handle template redirect using the new routing system
96 */
97 public static function handleTemplateRedirect(): void
98 {
99 global $wp_query;
100
101 // WordPress often sets 404 for ?paged=N on the front page when the main blog query has no Nth page.
102 // Yatra listings use the same query vars (?yatra_page=…&paged=2); clear 404 so routing can run.
103 if (!empty($wp_query->is_404) && self::shouldClear404ForYatraRouting()) {
104 $wp_query->is_404 = false;
105 status_header(200);
106 }
107
108 if (!empty($wp_query->is_404)) {
109 return;
110 }
111
112 // Early plain-permalink handling for booking confirmation via query var
113 $confirmationId = get_query_var('yatra_booking_confirmation')
114 ?: ($_GET['yatra_booking_confirmation'] ?? ($_GET['reference'] ?? ($_GET['booking_id'] ?? '')));
115 if (!empty($confirmationId)) {
116 $bookingRepo = new BookingRepository();
117 $booking = $bookingRepo->findByConfirmationSegment((string) $confirmationId);
118 if ($booking) {
119 global $wp_query;
120 $wp_query->is_404 = false;
121 status_header(200);
122 $GLOBALS['yatra_booking'] = $booking;
123 $wp_query->set('yatra_booking_confirmation', $confirmationId);
124 $wp_query->set('yatra_booking', $booking);
125 $template_path = YATRA_PLUGIN_PATH . 'templates/booking-confirmation.php';
126 if (file_exists($template_path)) {
127 include $template_path;
128 exit;
129 }
130 }
131 }
132
133 if (!self::$router) {
134 self::$router = new Router();
135 }
136
137 // Let the router handle the request
138 $handled = self::$router->route();
139
140 // If router didn't handle it, let WordPress continue normally
141 if (!$handled) {
142 // Plain permalinks: routing uses ?yatra_page={base from settings} (see PlainPageMatcher).
143
144 // Plain permalink fallback: handle ?yatra_booking_confirmation=
145 if (!$handled) {
146 $confirmationId = get_query_var('yatra_booking_confirmation') ?: ($_GET['yatra_booking_confirmation'] ?? '');
147 if (!empty($confirmationId)) {
148 $handler = new BookingConfirmationPageHandler();
149 $handled = $handler->handle([
150 'confirmation_id' => sanitize_text_field($confirmationId),
151 ]);
152 }
153 }
154
155 // Plain permalink fallback: login endpoint removed (use [yatra_login] shortcode instead)
156 }
157
158 // If still not handled, continue normally
159 if (!$handled) {
160 return;
161 }
162
163 // If router handled it, exit to prevent further processing
164 exit;
165 }
166
167 /**
168 * True when this request should be routed by Yatra even if WP marked it 404 (paged home quirk).
169 */
170 private static function shouldClear404ForYatraRouting(): bool
171 {
172 if (is_admin() || wp_doing_ajax() || wp_doing_cron()) {
173 return false;
174 }
175
176 $yatraPage = isset($_GET['yatra_page']) ? trim((string) wp_unslash($_GET['yatra_page'])) : '';
177 if ($yatraPage !== '') {
178 return true;
179 }
180
181 $qv = (string) get_query_var('yatra_page');
182 if ($qv !== '') {
183 return true;
184 }
185
186 // Plain trip / taxonomy slug keys (same idea as {@see PermalinkCanonical::requestHasPlainYatraRoutingQuery})
187 $tripKey = preg_replace('/[^a-zA-Z0-9_-]/', '', (string) SettingsService::getTripBase()) ?: 'trip';
188 if (isset($_GET[$tripKey]) && is_string($_GET[$tripKey]) && trim(wp_unslash($_GET[$tripKey])) !== '') {
189 return true;
190 }
191
192 foreach (
193 [
194 SettingsService::getString('destination_base', 'destination'),
195 SettingsService::getString('activity_base', 'activity'),
196 SettingsService::getString('trip_category_base', 'trip-category'),
197 ] as $base
198 ) {
199 $bk = preg_replace('/[^a-zA-Z0-9_-]/', '', $base) ?: '';
200 if ($bk === '' || $bk === $tripKey) {
201 continue;
202 }
203 if (isset($_GET[$bk]) && is_string($_GET[$bk]) && trim(wp_unslash($_GET[$bk])) !== '') {
204 return true;
205 }
206 }
207
208 foreach (['yatra_trip', 'yatra_trip_slug', 'yatra_destination_slug', 'yatra_activity_slug', 'yatra_category_slug', 'yatra_booking_confirmation'] as $key) {
209 if (!isset($_GET[$key])) {
210 continue;
211 }
212 $v = wp_unslash($_GET[$key]);
213 if (is_string($v) && trim($v) !== '') {
214 return true;
215 }
216 }
217
218 return (bool) apply_filters('yatra_clear_404_for_routing', false);
219 }
220
221 /**
222 * Add rewrite rules for trip permalinks and listing pages
223 */
224 public static function addTripRewriteRules(): void
225 {
226 // Use centralized SettingsService for all settings
227 $trip_base = SettingsService::getTripBase();
228 $booking_base = SettingsService::getBookingBase();
229 $account_base = SettingsService::getAccountBase();
230 $account_base = preg_replace('/[^a-z0-9_-]/i', '', $account_base) ?: 'account';
231
232 // Get other bases with sanitization
233 $destination_base = SettingsService::getString('destination_base', 'destination');
234 $destination_base = preg_replace('/[^a-z0-9_-]/i', '', $destination_base) ?: 'destination';
235
236 $activity_base = SettingsService::getString('activity_base', 'activity');
237 $activity_base = preg_replace('/[^a-z0-9_-]/i', '', $activity_base) ?: 'activity';
238
239 $trip_category_base = SettingsService::getString('trip_category_base', 'trip-category');
240 $trip_category_base = preg_replace('/[^a-z0-9_-]/i', '', $trip_category_base) ?: 'trip-category';
241
242 // Add query vars first (must be registered before rewrite rules)
243 // Single-trip slug query var matches trip URL base (e.g. trip=, tours=)
244 add_rewrite_tag('%' . $trip_base . '%', '([^&]+)');
245 add_rewrite_tag('%yatra_booking_confirmation%', '([^&]+)');
246 add_rewrite_tag('%yatra_remaining_checkout%', '([^&]+)');
247 add_rewrite_tag('%yatra_verify_email%', '([^&]+)');
248 // Single taxonomy page tags
249 add_rewrite_tag('%yatra_destination_slug%', '([^&]+)');
250 add_rewrite_tag('%yatra_activity_slug%', '([^&]+)');
251 add_rewrite_tag('%yatra_category_slug%', '([^&]+)');
252 add_rewrite_tag('%yatra_page%', '([a-zA-Z0-9_-]+)');
253 add_rewrite_tag('%paged%', '([0-9]+)');
254
255 // Add rewrite rule for email verification: /yatra-verify-email/{token}/
256 add_rewrite_rule(
257 '^yatra-verify-email/([a-zA-Z0-9_-]+)/?$',
258 'index.php?yatra_verify_email=$matches[1]',
259 'top'
260 );
261
262 // Trip listing pagination: {trip_base}/page/{n}/
263 add_rewrite_rule(
264 '^' . $trip_base . '/page/([0-9]+)/?$',
265 'index.php?yatra_page=' . $trip_base . '&paged=$matches[1]',
266 'top'
267 );
268
269 // Trip listing (page 1): {trip_base}/
270 add_rewrite_rule(
271 '^' . $trip_base . '/?$',
272 'index.php?yatra_page=' . $trip_base,
273 'top'
274 );
275
276 // Customer account (pageless): /{account_base}/ and /{account_base}/{tab}/
277 // Must be real rewrite rules so WordPress doesn't 404 before Yatra Router runs.
278 add_rewrite_rule(
279 '^' . $account_base . '/?$',
280 'index.php?yatra_page=' . $account_base . '&yatra_account_page=dashboard',
281 'top'
282 );
283
284 add_rewrite_rule(
285 '^' . $account_base . '/([^/]+)/?$',
286 'index.php?yatra_page=' . $account_base . '&yatra_account_page=$matches[1]',
287 'top'
288 );
289
290 // Single trip: {trip_base}/{trip_slug}/
291 add_rewrite_rule(
292 '^' . $trip_base . '/([^/]+)/?$',
293 'index.php?yatra_page=' . $trip_base . '&' . $trip_base . '=$matches[1]',
294 'top'
295 );
296
297 // Taxonomy: single + pagination (yatra_page = base from settings; paged = WP pagination)
298 add_rewrite_rule(
299 '^' . $destination_base . '/([^/]+)/page/([0-9]+)/?$',
300 'index.php?yatra_page=' . $destination_base . '&yatra_destination_slug=$matches[1]&paged=$matches[2]',
301 'top'
302 );
303
304 add_rewrite_rule(
305 '^' . $destination_base . '/([^/]+)/?$',
306 'index.php?yatra_page=' . $destination_base . '&yatra_destination_slug=$matches[1]',
307 'top'
308 );
309
310 add_rewrite_rule(
311 '^' . $destination_base . '/?$',
312 'index.php?yatra_page=' . $destination_base,
313 'top'
314 );
315
316 add_rewrite_rule(
317 '^' . $activity_base . '/([^/]+)/page/([0-9]+)/?$',
318 'index.php?yatra_page=' . $activity_base . '&yatra_activity_slug=$matches[1]&paged=$matches[2]',
319 'top'
320 );
321
322 add_rewrite_rule(
323 '^' . $activity_base . '/([^/]+)/?$',
324 'index.php?yatra_page=' . $activity_base . '&yatra_activity_slug=$matches[1]',
325 'top'
326 );
327
328 add_rewrite_rule(
329 '^' . $activity_base . '/?$',
330 'index.php?yatra_page=' . $activity_base,
331 'top'
332 );
333
334 add_rewrite_rule(
335 '^' . $trip_category_base . '/([^/]+)/page/([0-9]+)/?$',
336 'index.php?yatra_page=' . $trip_category_base . '&yatra_category_slug=$matches[1]&paged=$matches[2]',
337 'top'
338 );
339
340 add_rewrite_rule(
341 '^' . $trip_category_base . '/([^/]+)/?$',
342 'index.php?yatra_page=' . $trip_category_base . '&yatra_category_slug=$matches[1]',
343 'top'
344 );
345
346 add_rewrite_rule(
347 '^' . $trip_category_base . '/?$',
348 'index.php?yatra_page=' . $trip_category_base,
349 'top'
350 );
351
352 // Pageless booking confirmation: /{booking_base}/confirmation/{reference}/ (before trip slug rule)
353 add_rewrite_rule(
354 '^' . $booking_base . '/confirmation/([a-zA-Z0-9_-]+)/?$',
355 'index.php?yatra_booking_confirmation=$matches[1]',
356 'top'
357 );
358
359 // Booking with trip slug: /{booking_base}/{trip}/
360 add_rewrite_rule(
361 '^' . $booking_base . '/([^/]+)/?$',
362 'index.php?yatra_page=' . $booking_base . '&trip=$matches[1]',
363 'top'
364 );
365
366 // Booking hub (Settings → booking base, e.g. /book/)
367 add_rewrite_rule(
368 '^' . $booking_base . '/?$',
369 'index.php?yatra_page=' . $booking_base,
370 'top'
371 );
372
373 // Add rewrite rule for booking confirmation page slug: /booking-confirmation/{reference}
374 add_rewrite_rule(
375 '^booking-confirmation/([a-zA-Z0-9_-]+)/?$',
376 'index.php?yatra_booking_confirmation=$matches[1]',
377 'top'
378 );
379
380 // Add rewrite rule for remaining checkout: /remaining-checkout/{token}/
381 add_rewrite_rule(
382 '^remaining-checkout/([a-zA-Z0-9_-]+)/?$',
383 'index.php?yatra_remaining_checkout=$matches[1]',
384 'top'
385 );
386
387 // Check if rewrite rules need flushing (only flush once after plugin update/activation)
388 $rewrite_version = get_option('yatra_rewrite_rules_version', '0');
389 $current_version = '1.0.8'; // Increment this when rewrite rules change
390 if ($rewrite_version !== $current_version) {
391 flush_rewrite_rules(false);
392 update_option('yatra_rewrite_rules_version', $current_version);
393 }
394 }
395
396 /**
397 * Add custom query vars for Yatra functionality
398 */
399 public static function addCustomQueryVars(array $vars): array
400 {
401 $yatra_vars = [
402 'yatra_trip', // legacy plain/pretty query var for trip slug
403 'yatra_booking_confirmation',
404 'yatra_remaining_checkout',
405 'yatra_verify_email',
406 'yatra_account_page',
407 'yatra_destination_slug',
408 'yatra_activity_slug',
409 'yatra_category_slug',
410 'yatra_page',
411 'paged',
412 ];
413
414 // Add dynamic base names for plain permalink support
415 $trip_base = SettingsService::getTripBase();
416 $destination_base = SettingsService::getString('destination_base', 'destination');
417 $activity_base = SettingsService::getString('activity_base', 'activity');
418 $category_base = SettingsService::getString('trip_category_base', 'trip-category');
419
420 $yatra_vars[] = $trip_base;
421 $yatra_vars[] = $destination_base;
422 $yatra_vars[] = $activity_base;
423 $yatra_vars[] = $category_base;
424
425 return array_merge($vars, $yatra_vars);
426 }
427
428 /**
429 * Get client IP address
430 */
431 private static function getClientIp(): string
432 {
433 $ip_keys = ['HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP', 'HTTP_CLIENT_IP', 'REMOTE_ADDR'];
434
435 foreach ($ip_keys as $key) {
436 if (!empty($_SERVER[$key])) {
437 $ips = explode(',', $_SERVER[$key]);
438 $ip = trim($ips[0]);
439 if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
440 return $ip;
441 }
442 }
443 }
444
445 return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
446 }
447 }
448