PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.3
Yatra – Travel Booking & Tour Operator Software v3.0.3
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 2.0.11 All 82 releases
yatra / app / Services / InstallerService.php

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

488 lines 19.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\Services;
6
7 /**
8 * Installer Service
9 *
10 * Handles plugin installation and default settings setup
11 * Ensures proper default configuration on fresh installation
12 */
13 class InstallerService
14 {
15 /**
16 * Run installation tasks
17 *
18 * @return void
19 */
20 public static function install(): void
21 {
22 // Create all database tables (one-time action)
23 self::createDatabaseTables();
24
25 // Set all default options for fresh installation
26 self::setDefaultOptions();
27 }
28
29 /**
30 * Create all database tables (centralized table creation)
31 *
32 * @return void
33 */
34 public static function createDatabaseTables(): void
35 {
36 if (class_exists('\Yatra\Core\Database')) {
37 \Yatra\Core\Database::createTables();
38 }
39
40 }
41
42 /**
43 * Set all default options for fresh installation
44 * Only Book Now Pay Later should be enabled by default
45 * Uses SettingsService keys to ensure consistency
46 *
47 * @return void
48 */
49 private static function setDefaultOptions(): void
50 {
51 // Payment Gateway Settings - Only enable Pay Later by default
52 // 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);
58 // Set gateway configs with proper structure - only enable pay_later by default
59 $gateway_configs = [
60 'pay_later' => [
61 'enabled' => true,
62 'title' => 'Book Now, Pay Later',
63 'description' => 'Allow customers to reserve now and pay before the trip',
64 ],
65 // Explicitly disable all other gateways
66 'stripe' => [
67 'enabled' => false,
68 'title' => 'Stripe',
69 'description' => 'Accept credit and debit cards',
70 'api_key' => '',
71 'api_secret' => '',
72 'webhook_secret' => '',
73 ],
74 'paypal' => [
75 'enabled' => false,
76 'title' => 'PayPal',
77 'description' => 'Accept PayPal payments',
78 'api_key' => '',
79 'api_secret' => '',
80 ],
81 'razorpay' => [
82 'enabled' => false,
83 'title' => 'Razorpay',
84 'description' => 'Accept payments via Razorpay',
85 'api_key' => '',
86 'api_secret' => '',
87 ],
88 'square' => [
89 'enabled' => false,
90 'title' => 'Square',
91 'description' => 'Accept payments via Square',
92 'api_key' => '',
93 'api_secret' => '',
94 ],
95 'authorize_net' => [
96 'enabled' => false,
97 'title' => 'Authorize.net',
98 'description' => 'Accept payments via Authorize.net',
99 'api_key' => '',
100 'api_secret' => '',
101 ],
102 'bank_transfer' => [
103 'enabled' => false,
104 'title' => 'Bank Transfer',
105 'description' => 'Accept manual bank transfer payments',
106 'api_key' => '',
107 'api_secret' => '',
108 ]
109 ];
110 update_option('yatra_gateway_configs', $gateway_configs);
111 update_option('yatra_gateway_order', []);
112
113 // 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);
119
120 // 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);
127
128 update_option('yatra_allow_save_payment_methods', false);
129
130 // 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);
136
137 // Customer Settings - Match SettingsService defaults
138 update_option('yatra_enable_customer_accounts', true);
139 update_option('yatra_enable_customer_registration', true);
140
141 // 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);
156
157 // Email identity: canonical keys (REST / EmailService) + legacy keys for older code paths
158 $wpAdminEmail = (string) get_option('admin_email', '');
159 $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);
167
168 // Default transactional template HTML + subjects (Email → Templates / settings API)
169 foreach (EmailTemplateDefaults::settingsOptionDefaults() as $optionKey => $value) {
170 update_option('yatra_' . $optionKey, $value);
171 }
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);
193
194 // Clear any existing Stripe/PayPal settings that might exist
195 delete_option('yatra_stripe_settings');
196 delete_option('yatra_paypal_settings');
197
198 // Set installation tracking (not in SettingsService but needed for tracking)
199 update_option('yatra_installation_date', current_time('mysql'));
200 update_option('yatra_version', defined('YATRA_VERSION') ? YATRA_VERSION : '3.0.3');
201
202
203 }
204
205 /**
206 * Get all required database tables using Table classes
207 *
208 * @return array
209 */
210 public static function getRequiredTables(): array
211 {
212 // Must match \Yatra\Core\Database::createTables() — used for activation, migrations, and targeted checks.
213 $table_classes = [
214 \Yatra\Database\Tables\TripsTable::class,
215 \Yatra\Database\Tables\BookingsTable::class,
216 \Yatra\Database\Tables\BookingPaymentsTable::class,
217 \Yatra\Database\Tables\CustomersTable::class,
218 \Yatra\Database\Tables\BookingTravellersTable::class,
219 \Yatra\Database\Tables\BookingTravellerMetaTable::class,
220 \Yatra\Database\Tables\BookingDeparturesTable::class,
221 \Yatra\Database\Tables\ReviewsTable::class,
222 \Yatra\Database\Tables\DiscountsTable::class,
223 \Yatra\Database\Tables\EnquiriesTable::class,
224 \Yatra\Database\Tables\TripAvailabilityDatesTable::class,
225 \Yatra\Database\Tables\TripAvailabilityRulesTable::class,
226 \Yatra\Database\Tables\TripRevisionsTable::class,
227 \Yatra\Database\Tables\DeparturesTable::class,
228 \Yatra\Database\Tables\TripItineraryDaysTable::class,
229 \Yatra\Database\Tables\TripItineraryDayEntryTable::class,
230 \Yatra\Database\Tables\ClassificationsTable::class,
231 \Yatra\Database\Tables\TripClassificationsTable::class,
232 \Yatra\Database\Tables\TripContentTable::class,
233 ];
234
235 $table_names = [];
236 foreach ($table_classes as $table_class) {
237 if (class_exists($table_class)) {
238 $table_names[] = $table_class::getTableName();
239 }
240 }
241
242 return $table_names;
243 }
244
245 /**
246 * Whether a prefixed table exists. Uses esc_like() because SQL LIKE treats "_" as a wildcard.
247 */
248 public static function databaseTableExists(string $fullTableName): bool
249 {
250 global $wpdb;
251 if ($fullTableName === '') {
252 return false;
253 }
254 $pattern = $wpdb->esc_like($fullTableName);
255 $found = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $pattern));
256
257 return $found === $fullTableName;
258 }
259
260 /**
261 * Check if this is a fresh installation
262 *
263 * @return bool
264 */
265 public static function isFreshInstallation(): bool
266 {
267 // Check if Yatra version exists in database
268 $installed_version = get_option('yatra_version');
269
270 // If no version is set, it's a fresh installation
271 if ($installed_version === false) {
272 return true;
273 }
274
275 // Check installation date
276 $installation_date = get_option('yatra_installation_date');
277 if ($installation_date === false) {
278 return true;
279 }
280
281 // Additional check: if core tables don't exist, it's fresh
282 $required_tables = self::getRequiredTables();
283 if (!empty($required_tables)) {
284 $trips_table = $required_tables[0]; // Use first table (already has prefix)
285 if (!self::databaseTableExists($trips_table)) {
286 return true;
287 }
288 }
289
290 return false;
291 }
292
293 /**
294 * One-time: coupon migration incorrectly stored status "active"; 3.x uses "publish" (admin + checkout).
295 */
296 public static function maybeNormalizeMigratedCouponDiscountStatuses(): void
297 {
298 if (get_option('yatra_discount_active_status_normalized_v1')) {
299 return;
300 }
301
302 if (!class_exists('Yatra\\Database\\Tables\\DiscountsTable')) {
303 return;
304 }
305
306 $table = \Yatra\Database\Tables\DiscountsTable::getTableName();
307 if (!self::databaseTableExists($table)) {
308 return;
309 }
310
311 global $wpdb;
312 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name from schema helper
313 $wpdb->query("UPDATE `{$table}` SET `status` = 'publish' WHERE `status` = 'active'");
314
315 update_option('yatra_discount_active_status_normalized_v1', '1', false);
316 }
317
318 /**
319 * Fill canonical + legacy email identity options when empty (upgrades, partial installs, or empty strings in DB).
320 * Idempotent; safe to run on each admin load via maybeBackfillEmailTemplateDefaults().
321 */
322 public static function maybeBackfillEmailDeliveryIdentity(): void
323 {
324 $wpAdmin = trim((string) get_option('admin_email', ''));
325 $wpName = trim((string) get_bloginfo('name'));
326
327 $isUnsetOrEmpty = static function ($v): bool {
328 return $v === false || $v === null || $v === '' || (is_string($v) && trim($v) === '');
329 };
330
331 if ($wpAdmin !== '') {
332 foreach (['yatra_admin_email', 'yatra_from_email', 'yatra_email_from_address'] as $opt) {
333 if ($isUnsetOrEmpty(get_option($opt, false))) {
334 update_option($opt, $wpAdmin);
335 }
336 }
337 }
338 if ($wpName !== '') {
339 foreach (['yatra_from_name', 'yatra_email_from_name'] as $opt) {
340 if ($isUnsetOrEmpty(get_option($opt, false))) {
341 update_option($opt, $wpName);
342 }
343 }
344 }
345 }
346
347 /**
348 * One-time: persist default HTML subjects/bodies when options exist but are empty (pre-template-defaults installs).
349 */
350 public static function maybeBackfillEmailTemplateDefaults(): void
351 {
352 self::maybeBackfillEmailDeliveryIdentity();
353
354 // Default-on for new option on existing sites (add_option no-ops if already present).
355 add_option('yatra_email_template_admin_new_booking', 1);
356 add_option('yatra_email_template_admin_payment', 1);
357 add_option('yatra_email_template_admin_cancellation', 1);
358
359 self::maybeBackfillCustomerEmailVerificationTemplate();
360 self::maybeBackfillExtendedTransactionalEmailOptionsV2();
361
362 if (!get_option('yatra_email_identity_synced_v1')) {
363 $from = get_option('yatra_from_email', '');
364 if (($from === false || $from === '') && ($legacy = get_option('yatra_email_from_address', '')) && is_string($legacy) && $legacy !== '') {
365 update_option('yatra_from_email', $legacy);
366 }
367 $fname = get_option('yatra_from_name', '');
368 if (($fname === false || $fname === '') && ($legacy = get_option('yatra_email_from_name', '')) && is_string($legacy) && $legacy !== '') {
369 update_option('yatra_from_name', $legacy);
370 }
371 update_option('yatra_email_identity_synced_v1', '1');
372 }
373
374 if (get_option('yatra_email_tpl_defaults_backfill_1')) {
375 return;
376 }
377
378 foreach (EmailTemplateDefaults::settingsOptionDefaults() as $key => $defaultValue) {
379 $name = 'yatra_' . $key;
380 $current = get_option($name, false);
381 $isEmpty = $current === false || $current === '' || (is_string($current) && trim($current) === '');
382 if ($isEmpty) {
383 update_option($name, $defaultValue);
384 }
385 }
386
387 update_option('yatra_email_tpl_defaults_backfill_1', '1');
388 }
389
390 /**
391 * One-time: customer email verification template (Email → Templates) for existing installs.
392 */
393 private static function maybeBackfillCustomerEmailVerificationTemplate(): void
394 {
395 if (get_option('yatra_email_customer_verification_tpl_v1')) {
396 return;
397 }
398
399 add_option('yatra_email_template_customer_verification', true);
400
401 $defaults = EmailTemplateDefaults::settingsOptionDefaults();
402 foreach (['email_tpl_customer_verification_subject', 'email_tpl_customer_verification_body'] as $key) {
403 if (!isset($defaults[$key])) {
404 continue;
405 }
406 $name = 'yatra_' . $key;
407 $current = get_option($name, false);
408 $isEmpty = $current === false || $current === '' || (is_string($current) && trim($current) === '');
409 if ($isEmpty) {
410 update_option($name, $defaults[$key]);
411 }
412 }
413
414 update_option('yatra_email_customer_verification_tpl_v1', '1');
415 }
416
417 /**
418 * One-time: enable flags + default HTML for extended transactional templates (completed, expiry, scheduled, enquiry, review, abandoned).
419 * Only writes options that are still empty so existing customized HTML in the database is preserved on plugin update.
420 */
421 private static function maybeBackfillExtendedTransactionalEmailOptionsV2(): void
422 {
423 if (get_option('yatra_email_tpl_extended_v2')) {
424 return;
425 }
426
427 $boolFlags = [
428 'email_template_booking_completed',
429 'email_template_booking_expired_customer',
430 'email_template_admin_booking_expired',
431 'email_template_scheduled_payment_reminder',
432 'email_template_scheduled_payment_succeeded',
433 'email_template_scheduled_payment_failed',
434 'email_template_admin_scheduled_payment_failed',
435 'email_template_enquiry_received',
436 'email_template_enquiry_admin',
437 'email_template_enquiry_response',
438 'email_template_review_request',
439 'email_template_abandoned_booking_recovery',
440 ];
441 foreach ($boolFlags as $flag) {
442 add_option('yatra_' . $flag, true);
443 }
444
445 $extendedContentKeys = [
446 'email_tpl_booking_completed_subject',
447 'email_tpl_booking_completed_body',
448 'email_tpl_booking_expired_customer_subject',
449 'email_tpl_booking_expired_customer_body',
450 'email_tpl_admin_booking_expired_subject',
451 'email_tpl_admin_booking_expired_body',
452 'email_tpl_scheduled_payment_reminder_subject',
453 'email_tpl_scheduled_payment_reminder_body',
454 'email_tpl_scheduled_payment_succeeded_subject',
455 'email_tpl_scheduled_payment_succeeded_body',
456 'email_tpl_scheduled_payment_failed_subject',
457 'email_tpl_scheduled_payment_failed_body',
458 'email_tpl_admin_scheduled_payment_failed_subject',
459 'email_tpl_admin_scheduled_payment_failed_body',
460 'email_tpl_enquiry_admin_subject',
461 'email_tpl_enquiry_admin_body',
462 'email_tpl_enquiry_received_subject',
463 'email_tpl_enquiry_received_body',
464 'email_tpl_enquiry_response_subject',
465 'email_tpl_enquiry_response_body',
466 'email_tpl_review_request_subject',
467 'email_tpl_review_request_body',
468 'email_tpl_abandoned_booking_recovery_subject',
469 'email_tpl_abandoned_booking_recovery_body',
470 ];
471
472 $defaults = EmailTemplateDefaults::settingsOptionDefaults();
473 foreach ($extendedContentKeys as $key) {
474 if (!isset($defaults[$key])) {
475 continue;
476 }
477 $name = 'yatra_' . $key;
478 $current = get_option($name, false);
479 $isEmpty = $current === false || $current === '' || (is_string($current) && trim($current) === '');
480 if ($isEmpty) {
481 update_option($name, $defaults[$key]);
482 }
483 }
484
485 update_option('yatra_email_tpl_extended_v2', '1');
486 }
487 }
488