| @@ -5,11 +5,13 @@ | ||
| 5 | 5 | namespace Yatra\Core; |
| 6 | 6 | |
| 7 | 7 | use Yatra\Core\Routing\Router; |
| 8 | 8 | use Yatra\Core\Routing\PermalinkCanonical; |
| 9 | +use Yatra\Core\Routing\UrlParser; | |
| 10 | +use Yatra\Core\Routing\PageContext; | |
| 11 | +use Yatra\Core\Template\FseTemplates; | |
| 9 | 12 | use Yatra\Services\SettingsService; |
| 10 | 13 | use Yatra\Core\Handlers\BookingConfirmationPageHandler; |
| 11 | -use Yatra\Repositories\BookingRepository; | |
| 12 | 14 | |
| 13 | 15 | /** |
| 14 | 16 | * Template Loader |
| 15 | 17 | * |
| @@ -34,73 +36,156 @@ | ||
| 34 | 36 | // Initialize rewrite rules and query vars first |
| 35 | 37 | add_action('init', [self::class, 'addTripRewriteRules'], 10); |
| 36 | 38 | add_filter('query_vars', [self::class, 'addCustomQueryVars']); |
| 37 | 39 | |
| 38 | - // Early template include for booking confirmation (plain permalinks safety net) | |
| 39 | - add_filter('template_include', [self::class, 'maybeLoadBookingConfirmationTemplate'], 0); | |
| 40 | + // FSE Site Editor integration — virtual block templates + page-content block. | |
| 41 | + // No-ops on classic themes. | |
| 42 | + FseTemplates::init(); | |
| 40 | 43 | |
| 44 | + // Prevent WP::handle_404() from marking Yatra URLs as 404 in the first place. | |
| 45 | + // This is the proper WordPress way — fires before status_header(404) is sent and | |
| 46 | + // before FSE locate_block_template() reads is_404() to pick 404.html. | |
| 47 | + add_filter('pre_handle_404', [self::class, 'preventCore404'], 10, 2); | |
| 48 | + | |
| 49 | + // Strip the `error404` body class for Yatra requests so FSE themes don't apply | |
| 50 | + // 404-specific styling to plugin pages. | |
| 51 | + add_filter('body_class', [self::class, 'filterBodyClass'], 20); | |
| 52 | + | |
| 41 | 53 | add_action('template_redirect', [PermalinkCanonical::class, 'enforce'], 0); |
| 42 | 54 | |
| 43 | - // Initialize the main router for template handling | |
| 55 | + // Initialize the main router for template handling. Handlers configure | |
| 56 | + // $wp_query + a virtual WP_Post here and stash the chosen template in | |
| 57 | + // PageContext; they do NOT include or exit, so the full template-loader | |
| 58 | + // pipeline (template hierarchy resolution, wp_head, wp_footer, plugin | |
| 59 | + // template_include hooks) continues to run normally. | |
| 44 | 60 | add_action('template_redirect', [self::class, 'handleTemplateRedirect'], 1); |
| 61 | + | |
| 62 | + // Hand off to WordPress's template-loader: if a Yatra handler queued a | |
| 63 | + // template via PageContext, swap it in here. Priority 99 ensures we run | |
| 64 | + // after FSE's locate_block_template() (default priority 10) so we win | |
| 65 | + // over the theme's block-template choice without disabling FSE for the | |
| 66 | + // rest of the site. | |
| 67 | + add_filter('template_include', [self::class, 'filterTemplateInclude'], 99); | |
| 45 | 68 | } |
| 46 | 69 | |
| 47 | 70 | /** |
| 48 | - * Early template include for booking confirmation (plain permalinks) | |
| 71 | + * Short-circuit WP::handle_404() for Yatra-owned URLs. | |
| 72 | + * | |
| 73 | + * Returning true tells WordPress core to skip setting is_404 / status_header(404). | |
| 74 | + * This keeps FSE/block themes from resolving to 404.html and pulling the wrong | |
| 75 | + * header template part. The Router still runs on template_redirect to actually | |
| 76 | + * render the page; this filter only prevents the premature 404 marking. | |
| 77 | + * | |
| 78 | + * @param bool $preempt Whether to short-circuit default 404 handling. | |
| 79 | + * @param \WP_Query $wp_query The main WP_Query instance (unused — detection uses globals). | |
| 80 | + * @return bool | |
| 49 | 81 | */ |
| 50 | - public static function maybeLoadBookingConfirmationTemplate(string $template): string | |
| 82 | + public static function preventCore404($preempt, /** @noinspection PhpUnusedParameterInspection */ $wp_query) | |
| 51 | 83 | { |
| 52 | - global $wp_query; | |
| 53 | - if (!empty($wp_query->is_404)) { | |
| 54 | - return $template; | |
| 84 | + if ($preempt) { | |
| 85 | + return $preempt; | |
| 55 | 86 | } |
| 87 | + if (!self::isYatraRequest()) { | |
| 88 | + return $preempt; | |
| 89 | + } | |
| 90 | + // Mirror what WP::handle_404() does on success: do NOT set is_404, but ensure | |
| 91 | + // status header is 200 so downstream caches / CDNs behave correctly. | |
| 92 | + status_header(200); | |
| 93 | + nocache_headers(); | |
| 94 | + return true; | |
| 95 | + } | |
| 56 | 96 | |
| 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; | |
| 97 | + /** | |
| 98 | + * Remove the `error404` body class for Yatra pages so FSE themes render normal page chrome. | |
| 99 | + * | |
| 100 | + * @param array $classes | |
| 101 | + * @return array | |
| 102 | + */ | |
| 103 | + public static function filterBodyClass(array $classes): array | |
| 104 | + { | |
| 105 | + $ctx = PageContext::instance(); | |
| 106 | + $isYatra = self::isYatraRequest() || $ctx->isHandled(); | |
| 107 | + if (!$isYatra) { | |
| 108 | + return $classes; | |
| 61 | 109 | } |
| 62 | 110 | |
| 63 | - if (defined('WP_DEBUG') && WP_DEBUG) { | |
| 111 | + $classes = array_values(array_filter($classes, static function ($c) { | |
| 112 | + return $c !== 'error404' && $c !== 'error-404'; | |
| 113 | + })); | |
| 114 | + | |
| 115 | + if (!in_array('yatra-page', $classes, true)) { | |
| 116 | + $classes[] = 'yatra-page'; | |
| 117 | + } | |
| 118 | + | |
| 119 | + foreach ($ctx->getBodyClasses() as $extra) { | |
| 120 | + if (!in_array($extra, $classes, true)) { | |
| 121 | + $classes[] = $extra; | |
| 64 | 122 | } |
| 123 | + } | |
| 65 | 124 | |
| 66 | - $bookingRepo = new BookingRepository(); | |
| 67 | - $booking = $bookingRepo->findByConfirmationSegment((string) $confirmationId); | |
| 68 | - if (!$booking) { | |
| 69 | - if (defined('WP_DEBUG') && WP_DEBUG) { | |
| 70 | - } | |
| 125 | + return $classes; | |
| 126 | + } | |
| 127 | + | |
| 128 | + /** | |
| 129 | + * Public detection helper — true when the current request belongs to a Yatra route. | |
| 130 | + * Centralises the logic previously in shouldClear404ForYatraRouting() so it can be | |
| 131 | + * reused by pre_handle_404 and body_class filters. | |
| 132 | + */ | |
| 133 | + public static function isYatraRequest(): bool | |
| 134 | + { | |
| 135 | + return self::shouldClear404ForYatraRouting(); | |
| 136 | + } | |
| 137 | + | |
| 138 | + /** | |
| 139 | + * `template_include` filter — choose between PHP template and FSE block template. | |
| 140 | + * | |
| 141 | + * Decision flow when a Yatra handler has queued a template: | |
| 142 | + * 1. If the admin has customised the matching virtual block template in the | |
| 143 | + * Site Editor (wp_template post with source = 'custom'), defer to FSE — | |
| 144 | + * WP renders the customised block template, which embeds Yatra content | |
| 145 | + * via the `yatra/page-content` server block. Their edits take effect. | |
| 146 | + * 2. Otherwise return Yatra's PHP template. It's faster, cache-friendly, | |
| 147 | + * and the path the plugin tested most. | |
| 148 | + * | |
| 149 | + * For non-Yatra requests, return $template unchanged so the theme/FSE keep control. | |
| 150 | + */ | |
| 151 | + public static function filterTemplateInclude(string $template): string | |
| 152 | + { | |
| 153 | + $ctx = PageContext::instance(); | |
| 154 | + if (!$ctx->hasTemplate()) { | |
| 71 | 155 | return $template; |
| 72 | 156 | } |
| 73 | 157 | |
| 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; | |
| 158 | + // If the admin has saved a Site-Editor customisation for this page | |
| 159 | + // type, render via WordPress's block-template canvas with the saved | |
| 160 | + // content. loadCustomisedCanvas() returns null if no customisation | |
| 161 | + // exists (the common case) or returns template-canvas.php with the | |
| 162 | + // canvas globals primed. | |
| 163 | + $pageType = $ctx->getPageType(); | |
| 164 | + if ($pageType !== '') { | |
| 165 | + $canvas = FseTemplates::loadCustomisedCanvas($pageType); | |
| 166 | + if ($canvas !== null) { | |
| 167 | + return $canvas; | |
| 168 | + } | |
| 87 | 169 | } |
| 88 | 170 | |
| 89 | - if (defined('WP_DEBUG') && WP_DEBUG) { | |
| 90 | - } | |
| 91 | - return $template; | |
| 171 | + $selected = $ctx->getTemplate(); | |
| 172 | + return $selected !== null ? $selected : $template; | |
| 92 | 173 | } |
| 93 | 174 | |
| 94 | 175 | /** |
| 95 | - * Handle template redirect using the new routing system | |
| 176 | + * Handle template_redirect: | |
| 177 | + * 1. Clear residual is_404 (paged-home quirk; pre_handle_404 catches the rest). | |
| 178 | + * 2. Run the Router — handlers configure $wp_query and queue a template. | |
| 179 | + * 3. Fall through to WordPress's template-loader. Our template_include | |
| 180 | + * filter at priority 99 swaps in the Yatra template if one was queued. | |
| 181 | + * | |
| 182 | + * No include + exit here — that was the source of the FSE breakage. | |
| 96 | 183 | */ |
| 97 | 184 | public static function handleTemplateRedirect(): void |
| 98 | 185 | { |
| 99 | 186 | global $wp_query; |
| 100 | 187 | |
| 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 | 188 | if (!empty($wp_query->is_404) && self::shouldClear404ForYatraRouting()) { |
| 104 | 189 | $wp_query->is_404 = false; |
| 105 | 190 | status_header(200); |
| 106 | 191 | } |
| @@ -108,86 +193,29 @@ | ||
| 108 | 193 | if (!empty($wp_query->is_404)) { |
| 109 | 194 | return; |
| 110 | 195 | } |
| 111 | 196 | |
| 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 | 197 | if (!self::$router) { |
| 134 | 198 | self::$router = new Router(); |
| 135 | 199 | } |
| 136 | 200 | |
| 137 | - // Let the router handle the request | |
| 138 | 201 | $handled = self::$router->route(); |
| 139 | 202 | |
| 140 | - // If router didn't handle it, let WordPress continue normally | |
| 203 | + // Plain-permalink fallback: ?yatra_booking_confirmation=... still needs | |
| 204 | + // an explicit dispatch because PlainPageMatcher doesn't know about it. | |
| 141 | 205 | 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 | - } | |
| 206 | + $confirmationId = get_query_var('yatra_booking_confirmation') | |
| 207 | + ?: ($_GET['yatra_booking_confirmation'] ?? ($_GET['reference'] ?? ($_GET['booking_id'] ?? ''))); | |
| 208 | + if (!empty($confirmationId)) { | |
| 209 | + $handler = new BookingConfirmationPageHandler(); | |
| 210 | + $handler->handle([ | |
| 211 | + 'confirmation_id' => sanitize_text_field((string) $confirmationId), | |
| 212 | + ]); | |
| 153 | 213 | } |
| 154 | - | |
| 155 | - // Plain permalink fallback: handle ?yatra_login_page= or login requests | |
| 156 | - if (!$handled) { | |
| 157 | - $loginPage = get_query_var('yatra_login_page') ?: ($_GET['yatra_login_page'] ?? ''); | |
| 158 | - if (!empty($loginPage)) { | |
| 159 | - try { | |
| 160 | - // Security: Validate login page request | |
| 161 | - if (self::validateLoginRequest()) { | |
| 162 | - $handler = new \Yatra\Core\Handlers\LoginPageHandler(); | |
| 163 | - $handled = $handler->handle([]); | |
| 164 | - } else { | |
| 165 | - // Log security violation | |
| 166 | - if (defined('WP_DEBUG') && WP_DEBUG) { | |
| 167 | - error_log('Yatra TemplateLoader: Invalid login request detected from IP: ' . self::getClientIp()); | |
| 168 | - } | |
| 169 | - } | |
| 170 | - } catch (Exception $e) { | |
| 171 | - // Log error for debugging | |
| 172 | - if (defined('WP_DEBUG') && WP_DEBUG) { | |
| 173 | - error_log('Yatra TemplateLoader Login Handler Error: ' . $e->getMessage()); | |
| 174 | - } | |
| 175 | - | |
| 176 | - // Fallback to default behavior | |
| 177 | - $handled = false; | |
| 178 | - } | |
| 179 | - } | |
| 180 | - } | |
| 181 | 214 | } |
| 182 | 215 | |
| 183 | - // If still not handled, continue normally | |
| 184 | - if (!$handled) { | |
| 185 | - return; | |
| 186 | - } | |
| 187 | - | |
| 188 | - // If router handled it, exit to prevent further processing | |
| 189 | - exit; | |
| 216 | + // No exit. The selected Yatra template (if any) is in PageContext; | |
| 217 | + // filterTemplateInclude() will return it from the template_include filter. | |
| 190 | 218 | } |
| 191 | 219 | |
| 192 | 220 | /** |
| 193 | 221 | * True when this request should be routed by Yatra even if WP marked it 404 (paged home quirk). |
| @@ -215,11 +243,11 @@ | ||
| 215 | 243 | } |
| 216 | 244 | |
| 217 | 245 | foreach ( |
| 218 | 246 | [ |
| 219 | - SettingsService::getString('destination_base', 'destination'), | |
| 220 | - SettingsService::getString('activity_base', 'activity'), | |
| 221 | - SettingsService::getString('trip_category_base', 'trip-category'), | |
| 247 | + SettingsService::getDestinationBase(), | |
| 248 | + SettingsService::getActivityBase(), | |
| 249 | + SettingsService::getTripCategoryBase(), | |
| 222 | 250 | ] as $base |
| 223 | 251 | ) { |
| 224 | 252 | $bk = preg_replace('/[^a-zA-Z0-9_-]/', '', $base) ?: ''; |
| 225 | 253 | if ($bk === '' || $bk === $tripKey) { |
| @@ -229,9 +257,9 @@ | ||
| 229 | 257 | return true; |
| 230 | 258 | } |
| 231 | 259 | } |
| 232 | 260 | |
| 233 | - foreach (['yatra_trip', 'yatra_trip_slug', 'yatra_destination_slug', 'yatra_activity_slug', 'yatra_category_slug', 'yatra_booking_confirmation'] as $key) { | |
| 261 | + foreach (['yatra_trip', 'yatra_trip_slug', 'yatra_destination_slug', 'yatra_activity_slug', 'yatra_category_slug', 'yatra_booking_confirmation', 'yatra_verify_email'] as $key) { | |
| 234 | 262 | if (!isset($_GET[$key])) { |
| 235 | 263 | continue; |
| 236 | 264 | } |
| 237 | 265 | $v = wp_unslash($_GET[$key]); |
| @@ -239,8 +267,18 @@ | ||
| 239 | 267 | return true; |
| 240 | 268 | } |
| 241 | 269 | } |
| 242 | 270 | |
| 271 | + if ((string) get_query_var('yatra_verify_email') !== '') { | |
| 272 | + return true; | |
| 273 | + } | |
| 274 | + | |
| 275 | + $verifyPath = trim(UrlParser::getCleanRequestPath(), '/'); | |
| 276 | + $verifyPrefix = SettingsService::getPermalinkBases()['email_verification_prefix']; | |
| 277 | + if ($verifyPath !== '' && strpos($verifyPath, $verifyPrefix . '/') === 0) { | |
| 278 | + return true; | |
| 279 | + } | |
| 280 | + | |
| 243 | 281 | return (bool) apply_filters('yatra_clear_404_for_routing', false); |
| 244 | 282 | } |
| 245 | 283 | |
| 246 | 284 | /** |
| @@ -247,24 +285,20 @@ | ||
| 247 | 285 | * Add rewrite rules for trip permalinks and listing pages |
| 248 | 286 | */ |
| 249 | 287 | public static function addTripRewriteRules(): void |
| 250 | 288 | { |
| 251 | - // Use centralized SettingsService for all settings | |
| 252 | - $trip_base = SettingsService::getTripBase(); | |
| 253 | - $booking_base = SettingsService::getBookingBase(); | |
| 254 | - $account_base = SettingsService::getAccountBase(); | |
| 255 | - $account_base = preg_replace('/[^a-z0-9_-]/i', '', $account_base) ?: 'account'; | |
| 289 | + $bases = SettingsService::getPermalinkBases(); | |
| 290 | + $trip_base = $bases['trip_base']; | |
| 291 | + $booking_base = $bases['booking_base']; | |
| 292 | + $account_base = $bases['account_base']; | |
| 293 | + $destination_base = $bases['destination_base']; | |
| 294 | + $activity_base = $bases['activity_base']; | |
| 295 | + $trip_category_base = $bases['trip_category_base']; | |
| 296 | + $bookingConfirmSeg = $bases['booking_flow_confirmation_segment']; | |
| 297 | + $legacyBookingConfirmation = $bases['legacy_booking_confirmation_prefix']; | |
| 298 | + $remainingCheckout = $bases['remaining_checkout_prefix']; | |
| 299 | + $emailVerifyPrefix = $bases['email_verification_prefix']; | |
| 256 | 300 | |
| 257 | - // Get other bases with sanitization | |
| 258 | - $destination_base = SettingsService::getString('destination_base', 'destination'); | |
| 259 | - $destination_base = preg_replace('/[^a-z0-9_-]/i', '', $destination_base) ?: 'destination'; | |
| 260 | - | |
| 261 | - $activity_base = SettingsService::getString('activity_base', 'activity'); | |
| 262 | - $activity_base = preg_replace('/[^a-z0-9_-]/i', '', $activity_base) ?: 'activity'; | |
| 263 | - | |
| 264 | - $trip_category_base = SettingsService::getString('trip_category_base', 'trip-category'); | |
| 265 | - $trip_category_base = preg_replace('/[^a-z0-9_-]/i', '', $trip_category_base) ?: 'trip-category'; | |
| 266 | - | |
| 267 | 301 | // Add query vars first (must be registered before rewrite rules) |
| 268 | 302 | // Single-trip slug query var matches trip URL base (e.g. trip=, tours=) |
| 269 | 303 | add_rewrite_tag('%' . $trip_base . '%', '([^&]+)'); |
| 270 | 304 | add_rewrite_tag('%yatra_booking_confirmation%', '([^&]+)'); |
| @@ -269,9 +303,8 @@ | ||
| 269 | 303 | add_rewrite_tag('%' . $trip_base . '%', '([^&]+)'); |
| 270 | 304 | add_rewrite_tag('%yatra_booking_confirmation%', '([^&]+)'); |
| 271 | 305 | add_rewrite_tag('%yatra_remaining_checkout%', '([^&]+)'); |
| 272 | 306 | add_rewrite_tag('%yatra_verify_email%', '([^&]+)'); |
| 273 | - add_rewrite_tag('%yatra_login_page%', '([^&]+)'); | |
| 274 | 307 | // Single taxonomy page tags |
| 275 | 308 | add_rewrite_tag('%yatra_destination_slug%', '([^&]+)'); |
| 276 | 309 | add_rewrite_tag('%yatra_activity_slug%', '([^&]+)'); |
| 277 | 310 | add_rewrite_tag('%yatra_category_slug%', '([^&]+)'); |
| @@ -277,22 +310,15 @@ | ||
| 277 | 310 | add_rewrite_tag('%yatra_category_slug%', '([^&]+)'); |
| 278 | 311 | add_rewrite_tag('%yatra_page%', '([a-zA-Z0-9_-]+)'); |
| 279 | 312 | add_rewrite_tag('%paged%', '([0-9]+)'); |
| 280 | 313 | |
| 281 | - // Add rewrite rule for email verification: /yatra-verify-email/{token}/ | |
| 314 | + // Add rewrite rule for email verification: /{email_verification_prefix}/{token}/ | |
| 282 | 315 | add_rewrite_rule( |
| 283 | - '^yatra-verify-email/([a-zA-Z0-9_-]+)/?$', | |
| 316 | + '^' . $emailVerifyPrefix . '/([a-zA-Z0-9_-]+)/?$', | |
| 284 | 317 | 'index.php?yatra_verify_email=$matches[1]', |
| 285 | 318 | 'top' |
| 286 | 319 | ); |
| 287 | 320 | |
| 288 | - // Add rewrite rule for login page: /login | |
| 289 | - add_rewrite_rule( | |
| 290 | - '^login/?$', | |
| 291 | - 'index.php?yatra_login_page=1', | |
| 292 | - 'top' | |
| 293 | - ); | |
| 294 | - | |
| 295 | 321 | // Trip listing pagination: {trip_base}/page/{n}/ |
| 296 | 322 | add_rewrite_rule( |
| 297 | 323 | '^' . $trip_base . '/page/([0-9]+)/?$', |
| 298 | 324 | 'index.php?yatra_page=' . $trip_base . '&paged=$matches[1]', |
| @@ -381,11 +407,11 @@ | ||
| 381 | 407 | 'index.php?yatra_page=' . $trip_category_base, |
| 382 | 408 | 'top' |
| 383 | 409 | ); |
| 384 | 410 | |
| 385 | - // Pageless booking confirmation: /{booking_base}/confirmation/{reference}/ (before trip slug rule) | |
| 411 | + // Pageless booking confirmation: /{booking_base}/{confirmation_segment}/{reference}/ (before trip slug rule) | |
| 386 | 412 | add_rewrite_rule( |
| 387 | - '^' . $booking_base . '/confirmation/([a-zA-Z0-9_-]+)/?$', | |
| 413 | + '^' . $booking_base . '/' . $bookingConfirmSeg . '/([a-zA-Z0-9_-]+)/?$', | |
| 388 | 414 | 'index.php?yatra_booking_confirmation=$matches[1]', |
| 389 | 415 | 'top' |
| 390 | 416 | ); |
| 391 | 417 | |
| @@ -402,25 +428,34 @@ | ||
| 402 | 428 | 'index.php?yatra_page=' . $booking_base, |
| 403 | 429 | 'top' |
| 404 | 430 | ); |
| 405 | 431 | |
| 406 | - // Add rewrite rule for booking confirmation page slug: /booking-confirmation/{reference} | |
| 432 | + // Legacy booking confirmation: /{legacy_booking_confirmation_prefix}/{reference} | |
| 407 | 433 | add_rewrite_rule( |
| 408 | - '^booking-confirmation/([a-zA-Z0-9_-]+)/?$', | |
| 434 | + '^' . $legacyBookingConfirmation . '/([a-zA-Z0-9_-]+)/?$', | |
| 409 | 435 | 'index.php?yatra_booking_confirmation=$matches[1]', |
| 410 | 436 | 'top' |
| 411 | 437 | ); |
| 412 | 438 | |
| 413 | - // Add rewrite rule for remaining checkout: /remaining-checkout/{token}/ | |
| 439 | + // Remaining checkout: /{remaining_checkout_prefix}/{token}/ | |
| 414 | 440 | add_rewrite_rule( |
| 415 | - '^remaining-checkout/([a-zA-Z0-9_-]+)/?$', | |
| 441 | + '^' . $remainingCheckout . '/([a-zA-Z0-9_-]+)/?$', | |
| 416 | 442 | 'index.php?yatra_remaining_checkout=$matches[1]', |
| 417 | 443 | 'top' |
| 418 | 444 | ); |
| 445 | + | |
| 446 | + /** | |
| 447 | + * Fires after Yatra registers its core rewrite tags/rules. | |
| 448 | + * | |
| 449 | + * Use {@see \Yatra\Services\SettingsService::getPermalinkBases()} for the same slugs/helpers use. | |
| 450 | + * | |
| 451 | + * @param array<string, string> $bases | |
| 452 | + */ | |
| 453 | + do_action('yatra_register_rewrite_rules', $bases); | |
| 419 | 454 | |
| 420 | 455 | // Check if rewrite rules need flushing (only flush once after plugin update/activation) |
| 421 | 456 | $rewrite_version = get_option('yatra_rewrite_rules_version', '0'); |
| 422 | - $current_version = '1.0.8'; // Increment this when rewrite rules change | |
| 457 | + $current_version = '1.0.9'; // Increment this when rewrite rules change | |
| 423 | 458 | if ($rewrite_version !== $current_version) { |
| 424 | 459 | flush_rewrite_rules(false); |
| 425 | 460 | update_option('yatra_rewrite_rules_version', $current_version); |
| 426 | 461 | } |
| @@ -435,9 +470,8 @@ | ||
| 435 | 470 | 'yatra_trip', // legacy plain/pretty query var for trip slug |
| 436 | 471 | 'yatra_booking_confirmation', |
| 437 | 472 | 'yatra_remaining_checkout', |
| 438 | 473 | 'yatra_verify_email', |
| 439 | - 'yatra_login_page', | |
| 440 | 474 | 'yatra_account_page', |
| 441 | 475 | 'yatra_destination_slug', |
| 442 | 476 | 'yatra_activity_slug', |
| 443 | 477 | 'yatra_category_slug', |
| @@ -445,52 +479,15 @@ | ||
| 445 | 479 | 'paged', |
| 446 | 480 | ]; |
| 447 | 481 | |
| 448 | 482 | // Add dynamic base names for plain permalink support |
| 449 | - $trip_base = SettingsService::getTripBase(); | |
| 450 | - $destination_base = SettingsService::getString('destination_base', 'destination'); | |
| 451 | - $activity_base = SettingsService::getString('activity_base', 'activity'); | |
| 452 | - $category_base = SettingsService::getString('trip_category_base', 'trip-category'); | |
| 483 | + $pb = SettingsService::getPermalinkBases(); | |
| 484 | + $yatra_vars[] = $pb['trip_base']; | |
| 485 | + $yatra_vars[] = $pb['destination_base']; | |
| 486 | + $yatra_vars[] = $pb['activity_base']; | |
| 487 | + $yatra_vars[] = $pb['trip_category_base']; | |
| 453 | 488 | |
| 454 | - $yatra_vars[] = $trip_base; | |
| 455 | - $yatra_vars[] = $destination_base; | |
| 456 | - $yatra_vars[] = $activity_base; | |
| 457 | - $yatra_vars[] = $category_base; | |
| 458 | - | |
| 459 | 489 | return array_merge($vars, $yatra_vars); |
| 460 | - } | |
| 461 | - | |
| 462 | - /** | |
| 463 | - * Validate login page request for security | |
| 464 | - */ | |
| 465 | - private static function validateLoginRequest(): bool | |
| 466 | - { | |
| 467 | - // Check request method | |
| 468 | - if ($_SERVER['REQUEST_METHOD'] !== 'GET') { | |
| 469 | - return false; | |
| 470 | - } | |
| 471 | - | |
| 472 | - // Check for suspicious parameters | |
| 473 | - $suspicious_params = ['exec', 'system', 'eval', 'passthru', 'shell_exec']; | |
| 474 | - foreach ($suspicious_params as $param) { | |
| 475 | - if (isset($_GET[$param]) || isset($_POST[$param])) { | |
| 476 | - return false; | |
| 477 | - } | |
| 478 | - } | |
| 479 | - | |
| 480 | - // Rate limiting check | |
| 481 | - $ip = self::getClientIp(); | |
| 482 | - $transient_key = 'yatra_login_request_limit_' . md5($ip); | |
| 483 | - $requests = get_transient($transient_key) ?: 0; | |
| 484 | - | |
| 485 | - // Allow 50 requests per 10 minutes | |
| 486 | - if ($requests >= 50) { | |
| 487 | - return false; | |
| 488 | - } | |
| 489 | - | |
| 490 | - set_transient($transient_key, $requests + 1, 10 * MINUTE_IN_SECONDS); | |
| 491 | - | |
| 492 | - return true; | |
| 493 | 490 | } |
| 494 | 491 | |
| 495 | 492 | /** |
| 496 | 493 | * Get client IP address |