PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.17
Yatra – Travel Booking & Tour Operator Software v3.0.17
3.0.17 3.0.16 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 All 85 releases
← All changes | app/Services/InstallerService.php +352 -77 3.0.3 → 3.0.17 View file →
@@ -47,15 +47,31 @@
47 47 * @return void
48 48 */
49 49 private static function setDefaultOptions(): void
50 50 {
51 + // Whether this is a brand-new install vs. a reactivation/upgrade.
52 + // Captured before version/date are stamped below so it reflects the
53 + // pre-activation state.
54 + $isFreshInstall = self::isFreshInstallation();
55 +
56 + // Seed a default only when the option is absent. add_option() is a
57 + // no-op when the option already exists (existence is keyed on the
58 + // option NAME, so values legitimately stored as false/0/'' are still
59 + // preserved). This makes activation idempotent: a deactivate ->
60 + // reactivate, or a plugin update, can never overwrite an operator's
61 + // saved settings, while a fresh install (and any newly-introduced
62 + // default on upgrade) is still seeded.
63 + $seed = static function (string $name, $value): void {
64 + add_option($name, $value);
65 + };
66 +
51 67 // Payment Gateway Settings - Only enable Pay Later by default
52 68 // These match SettingsService defaults exactly
53 - update_option('yatra_payment_gateways', ['pay_later']);
54 - update_option('yatra_payment_methods', []);
55 - update_option('yatra_payment_test_mode', true);
56 - update_option('yatra_auto_confirm_pay_later', true);
57 - update_option('yatra_partial_payment', false);
69 + $seed('yatra_payment_gateways', ['pay_later']);
70 + $seed('yatra_payment_methods', []);
71 + $seed('yatra_payment_test_mode', true);
72 + $seed('yatra_auto_confirm_pay_later', true);
73 + $seed('yatra_partial_payment', false);
58 74 // Set gateway configs with proper structure - only enable pay_later by default
59 75 $gateway_configs = [
60 76 'pay_later' => [
61 77 'enabled' => true,
@@ -106,98 +122,109 @@
106 122 'api_key' => '',
107 123 'api_secret' => '',
108 124 ]
109 125 ];
110 - update_option('yatra_gateway_configs', $gateway_configs);
111 - update_option('yatra_gateway_order', []);
126 + $seed('yatra_gateway_configs', $gateway_configs);
127 + $seed('yatra_gateway_order', []);
112 128
113 129 // Currency Settings - Match SettingsService defaults
114 - update_option('yatra_currency', 'USD');
115 - update_option('yatra_currency_position', 'before');
116 - update_option('yatra_thousand_separator', ',');
117 - update_option('yatra_decimal_separator', '.');
118 - update_option('yatra_decimal_places', 2);
130 + $seed('yatra_currency', 'USD');
131 + $seed('yatra_currency_position', 'before');
132 + $seed('yatra_thousand_separator', ',');
133 + $seed('yatra_decimal_separator', '.');
134 + $seed('yatra_decimal_places', 2);
119 135
120 136 // Flexible Payment Settings - Match SettingsService defaults
121 - update_option('yatra_enable_deposit', false);
122 - update_option('yatra_deposit_type', 'percentage');
123 - update_option('yatra_deposit_amount', 20);
124 - update_option('yatra_deposit_required', false);
125 - update_option('yatra_deposit_percentage', 20);
126 - update_option('yatra_partial_payment_percentage', 30);
137 + $seed('yatra_enable_deposit', false);
138 + $seed('yatra_deposit_type', 'percentage');
139 + $seed('yatra_deposit_amount', 20);
140 + $seed('yatra_deposit_required', false);
141 + $seed('yatra_deposit_percentage', 20);
142 + $seed('yatra_partial_payment_percentage', 30);
127 143
128 - update_option('yatra_allow_save_payment_methods', false);
144 + $seed('yatra_allow_save_payment_methods', false);
129 145
130 146 // Trip Settings - Match SettingsService defaults
131 - update_option('yatra_trip_base', 'trip');
132 - update_option('yatra_trips_per_page', 12);
133 - update_option('yatra_enable_wishlist', false);
134 - update_option('yatra_enable_comparison', false);
135 - update_option('yatra_show_sold_out', true);
147 + $seed('yatra_trip_base', 'trip');
148 + $seed('yatra_trips_per_page', 12);
149 + $seed('yatra_enable_wishlist', false);
150 + $seed('yatra_enable_comparison', false);
151 + $seed('yatra_show_sold_out', true);
136 152
137 153 // Customer Settings - Match SettingsService defaults
138 - update_option('yatra_enable_customer_accounts', true);
139 - update_option('yatra_enable_customer_registration', true);
154 + $seed('yatra_enable_customer_accounts', true);
155 + $seed('yatra_enable_customer_registration', true);
140 156
141 157 // Booking Settings - Match SettingsService defaults
142 - update_option('yatra_booking_base', 'book');
143 - update_option('yatra_use_booking_page', false);
144 - update_option('yatra_booking_page_id', 0);
145 - update_option('yatra_enable_guest_booking', true);
146 - update_option('yatra_booking_confirmation', true);
147 - update_option('yatra_auto_confirm_bookings', false);
148 - update_option('yatra_require_login', false);
149 - update_option('yatra_allow_guest_checkout', true);
150 - update_option('yatra_cancellation_policy', 'full_refund');
151 - update_option('yatra_cancellation_days', 7);
152 - update_option('yatra_refund_policy', '');
153 - update_option('yatra_booking_expiry_hours', 24);
154 - update_option('yatra_booking_reminder_days', 3);
155 - update_option('yatra_allow_waitlist', true);
158 + $seed('yatra_booking_base', 'book');
159 + $seed('yatra_use_booking_page', false);
160 + $seed('yatra_booking_page_id', 0);
161 + $seed('yatra_enable_guest_booking', true);
162 + $seed('yatra_booking_confirmation', true);
163 + $seed('yatra_auto_confirm_bookings', false);
164 + $seed('yatra_require_login', false);
165 + $seed('yatra_allow_guest_checkout', true);
166 + // cancellation_policy / cancellation_days / refund_policy
167 + // intentionally not seeded — these are removed settings (see
168 + // SettingsController::$default_settings comment). Existing
169 + // sites that already have orphan values stored will keep
170 + // them in wp_options; new sites won't acquire them.
171 + $seed('yatra_booking_expiry_hours', 24);
172 + $seed('yatra_booking_reminder_days', 3);
173 + $seed('yatra_allow_waitlist', true);
156 174
157 175 // Email identity: canonical keys (REST / EmailService) + legacy keys for older code paths
158 176 $wpAdminEmail = (string) get_option('admin_email', '');
159 177 $blogName = (string) get_bloginfo('name');
160 - update_option('yatra_from_email', $wpAdminEmail);
161 - update_option('yatra_from_name', $blogName);
162 - update_option('yatra_admin_email', $wpAdminEmail);
163 - update_option('yatra_email_from_name', $blogName);
164 - update_option('yatra_email_from_address', $wpAdminEmail);
165 - update_option('yatra_enable_admin_notifications', true);
166 - update_option('yatra_enable_customer_notifications', true);
178 + $seed('yatra_from_email', $wpAdminEmail);
179 + $seed('yatra_from_name', $blogName);
180 + $seed('yatra_admin_email', $wpAdminEmail);
181 + $seed('yatra_email_from_name', $blogName);
182 + $seed('yatra_email_from_address', $wpAdminEmail);
183 + $seed('yatra_enable_admin_notifications', true);
184 + $seed('yatra_enable_customer_notifications', true);
167 185
168 186 // Default transactional template HTML + subjects (Email → Templates / settings API)
169 187 foreach (EmailTemplateDefaults::settingsOptionDefaults() as $optionKey => $value) {
170 - update_option('yatra_' . $optionKey, $value);
188 + $seed('yatra_' . $optionKey, $value);
171 189 }
172 - update_option('yatra_email_template_booking', true);
173 - update_option('yatra_email_template_confirmation', true);
174 - update_option('yatra_email_template_cancellation', true);
175 - update_option('yatra_email_template_reminder', true);
176 - update_option('yatra_email_template_admin_new_booking', true);
177 - update_option('yatra_email_template_admin_payment', true);
178 - update_option('yatra_email_template_admin_cancellation', true);
179 - update_option('yatra_email_template_trip_consent', true);
180 - update_option('yatra_email_template_customer_verification', true);
181 - update_option('yatra_email_template_booking_completed', true);
182 - update_option('yatra_email_template_booking_expired_customer', true);
183 - update_option('yatra_email_template_admin_booking_expired', true);
184 - update_option('yatra_email_template_scheduled_payment_reminder', true);
185 - update_option('yatra_email_template_scheduled_payment_succeeded', true);
186 - update_option('yatra_email_template_scheduled_payment_failed', true);
187 - update_option('yatra_email_template_admin_scheduled_payment_failed', true);
188 - update_option('yatra_email_template_enquiry_received', true);
189 - update_option('yatra_email_template_enquiry_admin', true);
190 - update_option('yatra_email_template_enquiry_response', true);
191 - update_option('yatra_email_template_review_request', true);
192 - update_option('yatra_email_template_abandoned_booking_recovery', true);
190 + $seed('yatra_email_template_booking', true);
191 + $seed('yatra_email_template_confirmation', true);
192 + $seed('yatra_email_template_cancellation', true);
193 + $seed('yatra_email_template_reminder', true);
194 + $seed('yatra_email_template_admin_new_booking', true);
195 + $seed('yatra_email_template_admin_payment', true);
196 + $seed('yatra_email_template_admin_cancellation', true);
197 + $seed('yatra_email_template_trip_consent', true);
198 + $seed('yatra_email_template_customer_verification', true);
199 + $seed('yatra_email_template_guest_verification', true);
200 + $seed('yatra_email_template_booking_completed', true);
201 + $seed('yatra_email_template_booking_expired_customer', true);
202 + $seed('yatra_email_template_admin_booking_expired', true);
203 + $seed('yatra_email_template_scheduled_payment_reminder', true);
204 + $seed('yatra_email_template_scheduled_payment_succeeded', true);
205 + $seed('yatra_email_template_scheduled_payment_failed', true);
206 + $seed('yatra_email_template_admin_scheduled_payment_failed', true);
207 + $seed('yatra_email_template_enquiry_received', true);
208 + $seed('yatra_email_template_enquiry_admin', true);
209 + $seed('yatra_email_template_enquiry_response', true);
210 + // Off on a fresh install — see SettingsService::$defaults for why.
211 + $seed('yatra_email_template_review_request', false);
212 + $seed('yatra_email_template_abandoned_booking_recovery_first', true);
213 + $seed('yatra_email_template_abandoned_booking_recovery_second', true);
214 + $seed('yatra_email_template_abandoned_booking_recovery_final', true);
193 215
194 - // Clear any existing Stripe/PayPal settings that might exist
195 - delete_option('yatra_stripe_settings');
196 - delete_option('yatra_paypal_settings');
216 + // Clear pre-existing legacy Stripe/PayPal settings, but only on a
217 + // brand-new install. On a reactivation these may hold the operator's
218 + // configured gateway data, so deleting them would be destructive.
219 + if ($isFreshInstall) {
220 + delete_option('yatra_stripe_settings');
221 + delete_option('yatra_paypal_settings');
222 + }
197 223
198 - // Set installation tracking (not in SettingsService but needed for tracking)
199 - update_option('yatra_installation_date', current_time('mysql'));
224 + // Installation tracking: stamp the date once (seed); keep the
225 + // version current so upgrade routines can detect version changes.
226 + $seed('yatra_installation_date', current_time('mysql'));
200 227 update_option('yatra_version', defined('YATRA_VERSION') ? YATRA_VERSION : '3.0.3');
201 228
202 229
203 230 }
@@ -315,8 +342,249 @@
315 342 update_option('yatra_discount_active_status_normalized_v1', '1', false);
316 343 }
317 344
318 345 /**
346 + * One-time normalization of recurring availability rules created against the
347 + * legacy schema (only `recurrence_type`, `capacity_value`, `interval`, etc.
348 + * were written) so the new admin React UI — which reads `rule_type`,
349 + * `seats_total`, `interval_days`, `interval_start_date` — can render and
350 + * edit them without showing phantom "1 on All / Active" badges or empty
351 + * "Every " patterns.
352 + *
353 + * What this fixes:
354 + * - Sample-data and pre-3.x rows landed with `rule_type` defaulted to
355 + * 'weekly' regardless of the actual `recurrence_type`, and with
356 + * `seats_total` left NULL (the new capacity column). Daily and monthly
357 + * rules therefore appeared as broken weekly rows in the new UI.
358 + * - The /counts endpoint correctly reported 1 active rule, but the list
359 + * table couldn't render it cleanly, leading users to read the API
360 + * response as "ghost data".
361 + *
362 + * Invariants:
363 + * - Idempotent — every UPDATE filters rows whose new columns are still
364 + * unset, so re-running is a no-op once the data is healed.
365 + * - Read-only on rows already authored by the new UI (`rule_type` already
366 + * matches the recurrence intent), so user edits are never overwritten.
367 + * - No-ops cleanly when the rules table doesn't exist yet (fresh install
368 + * before {@see \Yatra\Core\Database::createTables()} has run).
369 + */
370 + public static function maybeNormalizeAvailabilityRulesLegacyData(): void
371 + {
372 + if (get_option('yatra_availability_rules_legacy_normalized_v1')) {
373 + return;
374 + }
375 +
376 + if (!class_exists('Yatra\\Database\\Tables\\TripAvailabilityRulesTable')) {
377 + return;
378 + }
379 +
380 + $table = \Yatra\Database\Tables\TripAvailabilityRulesTable::getTableName();
381 + if (!self::databaseTableExists($table)) {
382 + return;
383 + }
384 +
385 + global $wpdb;
386 +
387 + // 1. Daily-recurrence rows whose `rule_type` defaulted to 'weekly':
388 + // map to the new "interval" rule type and copy the legacy `interval`
389 + // + `start_date` into the new columns the React form binds to.
390 + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name from schema helper
391 + $wpdb->query("UPDATE `{$table}`
392 + SET `rule_type` = 'interval',
393 + `interval_days` = COALESCE(`interval_days`, NULLIF(`interval`, 0), 1),
394 + `interval_start_date` = COALESCE(`interval_start_date`, `start_date`)
395 + WHERE `recurrence_type` = 'daily'
396 + AND (`rule_type` IS NULL OR `rule_type` = '' OR `rule_type` = 'weekly')");
397 +
398 + // 2. Monthly-recurrence rows whose `rule_type` defaulted to 'weekly':
399 + // relabel to 'monthly'. The new UI uses (week_of_month, day_of_week)
400 + // rather than `day_of_month`, so we leave those NULL for the user
401 + // to set in the form rather than guess from the legacy day_of_month.
402 + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name from schema helper
403 + $wpdb->query("UPDATE `{$table}`
404 + SET `rule_type` = 'monthly'
405 + WHERE `recurrence_type` = 'monthly'
406 + AND (`rule_type` IS NULL OR `rule_type` = '' OR `rule_type` = 'weekly')");
407 +
408 + // 3. Weekly-recurrence rows: ensure `rule_type` is set explicitly
409 + // (most already match the default; this catches any NULL/empty).
410 + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name from schema helper
411 + $wpdb->query("UPDATE `{$table}`
412 + SET `rule_type` = 'weekly'
413 + WHERE `recurrence_type` = 'weekly'
414 + AND (`rule_type` IS NULL OR `rule_type` = '')");
415 +
416 + // 4. seats_total backfill from `capacity_value` for fixed-capacity rows
417 + // so CapacityService and the React table both surface the right
418 + // seat cap without falling through hydrate-time fallbacks.
419 + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name from schema helper
420 + $wpdb->query("UPDATE `{$table}`
421 + SET `seats_total` = `capacity_value`
422 + WHERE `seats_total` IS NULL
423 + AND `capacity_value` IS NOT NULL
424 + AND `capacity_value` > 0
425 + AND (`capacity_type` IS NULL OR `capacity_type` = 'fixed')");
426 +
427 + update_option('yatra_availability_rules_legacy_normalized_v1', '1', false);
428 + }
429 +
430 + /**
431 + * Ensure `wp_yatra_bookings.status` accepts `pending_verification`.
432 + *
433 + * The 3.0.5 guest email-verification feature introduced a new holding
434 + * status (`pending_verification`) but the production ENUM only listed
435 + * the legacy values. Until the column is widened, MySQL non-strict
436 + * mode silently coerces the value to `''` on insert — which makes
437 + * every booking placed with verification enabled appear broken:
438 + *
439 + * 1. The admin booking list renders an empty status badge (the
440 + * React status map has no entry for `''` so the row falls into
441 + * the default branch with an empty label).
442 + * 2. `BookingService::createBooking` sees the in-memory
443 + * `$data['status'] === 'pending_verification'` and defers
444 + * firing `yatra_booking_created`, so the customer never gets
445 + * the booking-confirmation email.
446 + * 3. `verify_email` reads the persisted status (now `''`),
447 + * decides the booking is "already verified", skips the deferred
448 + * fan-out — so neither the status flip nor the booking email
449 + * ever fires.
450 + *
451 + * Doing the widening here (runIdempotentMaintenance — every admin
452 + * pageview) instead of a pure version-gated upgrade step means it
453 + * heals installs whose stored yatra_version was already bumped to
454 + * 3.0.5 by an earlier failed upgrade attempt. Cheap: one
455 + * INFORMATION_SCHEMA query gated by a one-shot option flag, ALTER
456 + * runs at most once per install.
457 + *
458 + * Also backfills any rows whose status was silently coerced to `''`
459 + * by the pre-widening insert path: those bookings *should* have
460 + * landed in `pending_verification`, so we restore them there. The
461 + * original verify-email magic link still works because the HMAC
462 + * token is bound to booking_id + email, not status.
463 + */
464 + public static function maybeAddPendingVerificationBookingStatus(): void
465 + {
466 + if (get_option('yatra_booking_status_pending_verification_v1')) {
467 + return;
468 + }
469 +
470 + if (!class_exists('Yatra\\Database\\Tables\\BookingsTable')) {
471 + return;
472 + }
473 +
474 + $table = \Yatra\Database\Tables\BookingsTable::getTableName();
475 + if (!self::databaseTableExists($table)) {
476 + return;
477 + }
478 +
479 + global $wpdb;
480 +
481 + $columnInfo = $wpdb->get_row(
482 + $wpdb->prepare(
483 + "SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS
484 + WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s AND COLUMN_NAME = %s",
485 + DB_NAME,
486 + $table,
487 + 'status'
488 + )
489 + );
490 +
491 + $columnType = is_object($columnInfo) ? (string) ($columnInfo->COLUMN_TYPE ?? '') : '';
492 + $needsAlter = $columnType !== '' && strpos($columnType, 'pending_verification') === false;
493 +
494 + if ($needsAlter) {
495 + // Match the original column shape exactly minus the new enum
496 + // value — nullable, default 'pending', no NOT NULL.
497 + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name escaped, enum literal is static.
498 + $wpdb->query(
499 + 'ALTER TABLE `' . esc_sql($table) . "` "
500 + . "MODIFY COLUMN `status` "
501 + . "enum('pending','pending_verification','confirmed','processing','completed','cancelled','refunded','failed','on_hold','waitlist') "
502 + . "DEFAULT 'pending'"
503 + );
504 + }
505 +
506 + // Backfill: bookings whose insert hit the old ENUM during a
507 + // verification flow ended up with status='' (silent coerce).
508 + // Now that the enum accepts pending_verification, restore them.
509 + // Filtered to a narrow signal (status='' AND payment_status='pending')
510 + // so we don't accidentally re-stamp unrelated edge cases.
511 + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name from schema helper, literal values only.
512 + $wpdb->query(
513 + "UPDATE `{$table}` SET `status` = 'pending_verification' "
514 + . "WHERE `status` = '' AND `payment_status` = 'pending'"
515 + );
516 +
517 + update_option('yatra_booking_status_pending_verification_v1', '1', false);
518 + }
519 +
520 + /**
521 + * Add the `duration_hours` column to the trips table for hour-based
522 + * (single-day) tours. Purely additive and nullable — existing trips get
523 + * NULL and behave exactly as before (day-based via `duration_days`). Only
524 + * tours that later set a positive `duration_hours` change behaviour.
525 + *
526 + * Idempotent: guarded by a one-shot option AND an INFORMATION_SCHEMA check,
527 + * so it runs its ALTER at most once and is a no-op when the column already
528 + * exists (fresh installs get it from TripsTable::getSchema()).
529 + */
530 + public static function maybeAddTripDurationHoursColumn(): void
531 + {
532 + if (get_option('yatra_trip_duration_hours_v1')) {
533 + return;
534 + }
535 +
536 + if (!class_exists('Yatra\\Database\\Tables\\TripsTable')) {
537 + return;
538 + }
539 +
540 + $table = \Yatra\Database\Tables\TripsTable::getTableName();
541 + if (!self::databaseTableExists($table)) {
542 + return;
543 + }
544 +
545 + global $wpdb;
546 +
547 + $columnExists = $wpdb->get_var(
548 + $wpdb->prepare(
549 + "SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
550 + WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s AND COLUMN_NAME = %s",
551 + DB_NAME,
552 + $table,
553 + 'duration_hours'
554 + )
555 + );
556 +
557 + if (!$columnExists) {
558 + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name escaped, static column definition.
559 + $wpdb->query(
560 + 'ALTER TABLE `' . esc_sql($table) . '` '
561 + . "ADD COLUMN `duration_hours` smallint(5) UNSIGNED DEFAULT NULL "
562 + . "COMMENT 'Duration in hours for hour-based single-day tours, NULL means day-based' "
563 + . 'AFTER `duration_nights`'
564 + );
565 +
566 + // Re-check so we only mark this done when the column really exists.
567 + // If the ALTER failed (e.g. a restrictive host), leave the one-shot
568 + // flag unset so it retries on the next admin load rather than
569 + // disabling the feature permanently.
570 + $columnExists = $wpdb->get_var(
571 + $wpdb->prepare(
572 + "SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
573 + WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s AND COLUMN_NAME = %s",
574 + DB_NAME,
575 + $table,
576 + 'duration_hours'
577 + )
578 + );
579 + }
580 +
581 + if ($columnExists) {
582 + update_option('yatra_trip_duration_hours_v1', '1', false);
583 + }
584 + }
585 +
586 + /**
319 587 * Fill canonical + legacy email identity options when empty (upgrades, partial installs, or empty strings in DB).
320 588 * Idempotent; safe to run on each admin load via maybeBackfillEmailTemplateDefaults().
321 589 */
322 590 public static function maybeBackfillEmailDeliveryIdentity(): void
@@ -396,8 +664,9 @@
396 664 return;
397 665 }
398 666
399 667 add_option('yatra_email_template_customer_verification', true);
668 + add_option('yatra_email_template_guest_verification', true);
400 669
401 670 $defaults = EmailTemplateDefaults::settingsOptionDefaults();
402 671 foreach (['email_tpl_customer_verification_subject', 'email_tpl_customer_verification_body'] as $key) {
403 672 if (!isset($defaults[$key])) {
@@ -435,9 +704,11 @@
435 704 'email_template_enquiry_received',
436 705 'email_template_enquiry_admin',
437 706 'email_template_enquiry_response',
438 707 'email_template_review_request',
439 - 'email_template_abandoned_booking_recovery',
708 + 'email_template_abandoned_booking_recovery_first',
709 + 'email_template_abandoned_booking_recovery_second',
710 + 'email_template_abandoned_booking_recovery_final',
440 711 ];
441 712 foreach ($boolFlags as $flag) {
442 713 add_option('yatra_' . $flag, true);
443 714 }
@@ -464,10 +735,14 @@
464 735 'email_tpl_enquiry_response_subject',
465 736 'email_tpl_enquiry_response_body',
466 737 'email_tpl_review_request_subject',
467 738 'email_tpl_review_request_body',
468 - 'email_tpl_abandoned_booking_recovery_subject',
469 - 'email_tpl_abandoned_booking_recovery_body',
739 + 'email_tpl_abandoned_booking_recovery_first_subject',
740 + 'email_tpl_abandoned_booking_recovery_first_body',
741 + 'email_tpl_abandoned_booking_recovery_second_subject',
742 + 'email_tpl_abandoned_booking_recovery_second_body',
743 + 'email_tpl_abandoned_booking_recovery_final_subject',
744 + 'email_tpl_abandoned_booking_recovery_final_body',
470 745 ];
471 746
472 747 $defaults = EmailTemplateDefaults::settingsOptionDefaults();
473 748 foreach ($extendedContentKeys as $key) {