|string}> */ public static function definitions(): array { $previewTok = defined('YATRA_EMAIL_VERIFICATION_PREVIEW_TOKEN') ? (string) YATRA_EMAIL_VERIFICATION_PREVIEW_TOKEN : 'preview-verify-token'; $verificationSampleLink = function_exists('yatra_get_email_verification_url') ? yatra_get_email_verification_url($previewTok) : home_url('/?yatra_verify_email=' . rawurlencode($previewTok)); $dateFormat = function_exists('get_option') ? (string) get_option('date_format') : 'Y-m-d'; $sampleTravelDate = function_exists('date_i18n') ? date_i18n($dateFormat, strtotime('+30 days') ?: time()) : date('Y-m-d', strtotime('+30 days') ?: time()); $sampleDueDate = function_exists('date_i18n') ? date_i18n($dateFormat, strtotime('+14 days') ?: time()) : date('Y-m-d', strtotime('+14 days') ?: time()); $homeUrl = function_exists('home_url') ? home_url('/') : 'https://example.test/'; $adminUrl = function_exists('admin_url') ? admin_url('admin.php?page=yatra') : $homeUrl . 'wp-admin/admin.php?page=yatra'; $siteName = function_exists('get_bloginfo') ? (string) get_bloginfo('name') : 'Your Site'; $adminEmail = function_exists('get_option') ? (string) get_option('admin_email') : 'admin@example.test'; $bookingContextEvents = self::BOOKING_CONTEXT_EVENTS; $enquiryContextEvents = self::ENQUIRY_CONTEXT_EVENTS; $scheduledPaymentEvents = self::SCHEDULED_PAYMENT_EVENTS; $catalog = [ // --------------------------------------------------------- // General — available to every event because parseTemplate // merges getDefaultVariables() over the caller's $variables. // --------------------------------------------------------- 'site_name' => [ 'key' => 'site_name', 'label' => 'Site Name', 'description' => 'Your website name (from WordPress Site Title).', 'category' => self::CATEGORY_GENERAL, 'sample' => $siteName !== '' ? $siteName : 'Your Site', 'events' => '*', ], 'site_url' => [ 'key' => 'site_url', 'label' => 'Site URL', 'description' => 'Your website home URL.', 'category' => self::CATEGORY_GENERAL, 'sample' => $homeUrl, 'events' => '*', ], 'admin_email' => [ 'key' => 'admin_email', 'label' => 'Admin Email', 'description' => 'Site administrator email address.', 'category' => self::CATEGORY_GENERAL, 'sample' => $adminEmail !== '' ? $adminEmail : 'admin@example.test', 'events' => '*', ], 'admin_url' => [ 'key' => 'admin_url', 'label' => 'Admin URL', 'description' => 'Link to the Yatra admin dashboard.', 'category' => self::CATEGORY_GENERAL, 'sample' => $adminUrl, 'events' => '*', ], 'current_date' => [ 'key' => 'current_date', 'label' => 'Current Date', 'description' => "Today's date formatted per the site's date format.", 'category' => self::CATEGORY_GENERAL, 'sample' => function_exists('date_i18n') ? date_i18n($dateFormat) : date($dateFormat), 'events' => '*', ], 'current_year' => [ 'key' => 'current_year', 'label' => 'Current Year', 'description' => 'Current four-digit year.', 'category' => self::CATEGORY_GENERAL, 'sample' => date('Y'), 'events' => '*', ], // --------------------------------------------------------- // Customer — produced by variablesFromBooking + variablesFromEnquiry // + the verification + consent + recovery senders. // --------------------------------------------------------- 'customer_name' => [ 'key' => 'customer_name', 'label' => 'Customer Name', 'description' => 'Full name (first + last) of the customer / enquirer.', 'category' => self::CATEGORY_CUSTOMER, 'sample' => 'John Doe', 'events' => array_merge( $bookingContextEvents, $enquiryContextEvents, [ self::EVENT_ACCOUNT_EMAIL_VERIFICATION, self::EVENT_BOOKING_ABANDONED_RECOVERY, ] ), ], 'customer_first_name' => [ 'key' => 'customer_first_name', 'label' => 'First Name', 'description' => 'Customer first name only.', 'category' => self::CATEGORY_CUSTOMER, 'sample' => 'John', 'events' => array_merge( $bookingContextEvents, [self::EVENT_ACCOUNT_EMAIL_VERIFICATION] ), ], 'customer_last_name' => [ 'key' => 'customer_last_name', 'label' => 'Last Name', 'description' => 'Customer last name only.', 'category' => self::CATEGORY_CUSTOMER, 'sample' => 'Doe', 'events' => $bookingContextEvents, ], 'customer_email' => [ 'key' => 'customer_email', 'label' => 'Customer Email', 'description' => 'Customer email address.', 'category' => self::CATEGORY_CUSTOMER, 'sample' => 'john.doe@example.com', 'events' => array_merge( $bookingContextEvents, $enquiryContextEvents, [ self::EVENT_ACCOUNT_EMAIL_VERIFICATION, self::EVENT_BOOKING_ABANDONED_RECOVERY, ] ), ], 'customer_phone' => [ 'key' => 'customer_phone', 'label' => 'Customer Phone', 'description' => 'Customer phone number.', 'category' => self::CATEGORY_CUSTOMER, 'sample' => '+1 234 567 8900', 'events' => array_merge($bookingContextEvents, $enquiryContextEvents), ], // --------------------------------------------------------- // Booking — core fields from variablesFromBooking + the rich // tags appended by BookingEmailRichMergeTags::forBooking. // --------------------------------------------------------- 'booking_reference' => [ 'key' => 'booking_reference', 'label' => 'Booking Reference', 'description' => 'Customer-visible booking code (e.g. YTR-12345).', 'category' => self::CATEGORY_BOOKING, 'sample' => 'YTR-2024-001234', 'events' => array_merge( $bookingContextEvents, [ self::EVENT_CONSENT_REQUESTED, self::EVENT_BOOKING_ABANDONED_RECOVERY, ] ), ], 'booking_id' => [ 'key' => 'booking_id', 'label' => 'Booking ID', 'description' => 'Internal numeric booking identifier.', 'category' => self::CATEGORY_BOOKING, 'sample' => '1234', 'events' => $bookingContextEvents, ], 'booking_url' => [ 'key' => 'booking_url', 'label' => 'Booking URL', 'description' => 'Link to view the booking in My Account.', 'category' => self::CATEGORY_BOOKING, 'sample' => $homeUrl . 'my-account/bookings/1234', 'events' => $bookingContextEvents, ], 'booking_status' => [ 'key' => 'booking_status', 'label' => 'Booking Status', 'description' => 'pending / confirmed / cancelled / completed.', 'category' => self::CATEGORY_BOOKING, 'sample' => 'confirmed', 'events' => $bookingContextEvents, ], 'payment_status' => [ 'key' => 'payment_status', 'label' => 'Payment Status', 'description' => 'unpaid / partial / paid / refunded.', 'category' => self::CATEGORY_BOOKING, 'sample' => 'paid', 'events' => $bookingContextEvents, ], 'trip_name' => [ 'key' => 'trip_name', 'label' => 'Trip Name', 'description' => 'Title of the trip the booking / enquiry is for.', 'category' => self::CATEGORY_BOOKING, 'sample' => 'Amazing Mountain Adventure', 'events' => array_merge( $bookingContextEvents, $enquiryContextEvents, [ self::EVENT_CONSENT_REQUESTED, self::EVENT_BOOKING_ABANDONED_RECOVERY, ] ), ], 'trip_url' => [ 'key' => 'trip_url', 'label' => 'Trip URL', 'description' => 'Public link to the trip detail page.', 'category' => self::CATEGORY_BOOKING, 'sample' => $homeUrl . 'trips/amazing-mountain-adventure', 'events' => array_merge($bookingContextEvents, $enquiryContextEvents), ], 'travel_date' => [ 'key' => 'travel_date', 'label' => 'Travel Date', 'description' => 'Departure date formatted per site settings.', 'category' => self::CATEGORY_BOOKING, 'sample' => $sampleTravelDate, 'events' => array_merge($bookingContextEvents, [self::EVENT_CONSENT_REQUESTED]), ], 'travelers_count' => [ 'key' => 'travelers_count', 'label' => 'Travelers Count', 'description' => 'Number of travelers on the booking.', 'category' => self::CATEGORY_BOOKING, 'sample' => '4', 'events' => $bookingContextEvents, ], 'travelers_list' => [ 'key' => 'travelers_list', 'label' => 'Travelers List (plain)', 'description' => 'Plain-text list of traveler names.', 'category' => self::CATEGORY_BOOKING, 'sample' => "John Doe\nJane Doe", 'events' => $bookingContextEvents, ], 'travelers_list_html' => [ 'key' => 'travelers_list_html', 'label' => 'Travelers List (HTML)', 'description' => 'HTML-formatted list of traveler names.', 'category' => self::CATEGORY_BOOKING, 'sample' => '
We were unable to process your scheduled payment.
', 'events' => [self::EVENT_SCHEDULED_PAYMENT_FAILED], ], 'failure_followup_html' => [ 'key' => 'failure_followup_html', 'label' => 'Failure Follow-up (HTML)', 'description' => 'Closing block prompting the customer to update payment.', 'category' => self::CATEGORY_SCHEDULED_PAYMENT, 'sample' => 'Please update your payment method to avoid cancellation.
', 'events' => [self::EVENT_SCHEDULED_PAYMENT_FAILED], ], // --------------------------------------------------------- // Reminder (trip & booking reminders) // --------------------------------------------------------- 'days_until_trip' => [ 'key' => 'days_until_trip', 'label' => 'Days Until Trip', 'description' => 'Days remaining until departure.', 'category' => self::CATEGORY_REMINDER, 'sample' => '30', 'events' => [self::EVENT_REMINDER_TRIP], ], 'reminder_days' => [ 'key' => 'reminder_days', 'label' => 'Reminder Days', 'description' => 'Configured number of days before the trip when the reminder fires.', 'category' => self::CATEGORY_REMINDER, 'sample' => '3', 'events' => [self::EVENT_REMINDER_TRIP], ], 'reminder_extra_html' => [ 'key' => 'reminder_extra_html', 'label' => 'Reminder Extra (HTML)', 'description' => 'Optional extra block appended to reminder emails (packing list, etc.).', 'category' => self::CATEGORY_REMINDER, 'sample' => 'Don\'t forget your passport and travel insurance.
', 'events' => [self::EVENT_REMINDER_TRIP], ], 'review_url' => [ 'key' => 'review_url', 'label' => 'Review URL', 'description' => 'Public link the customer opens to leave a review.', 'category' => self::CATEGORY_REMINDER, 'sample' => $homeUrl . 'trips/amazing-mountain-adventure#reviews', 'events' => [self::EVENT_REVIEW_REQUEST], ], // --------------------------------------------------------- // Enquiry // --------------------------------------------------------- 'enquiry_id' => [ 'key' => 'enquiry_id', 'label' => 'Enquiry ID', 'description' => 'Internal numeric enquiry identifier.', 'category' => self::CATEGORY_ENQUIRY, 'sample' => '4567', 'events' => $enquiryContextEvents, ], 'enquiry_date' => [ 'key' => 'enquiry_date', 'label' => 'Enquiry Date', 'description' => 'When the enquiry was submitted.', 'category' => self::CATEGORY_ENQUIRY, 'sample' => function_exists('date_i18n') ? date_i18n($dateFormat) : date($dateFormat), 'events' => $enquiryContextEvents, ], 'subject' => [ 'key' => 'subject', 'label' => 'Subject', 'description' => 'Subject line the customer provided.', 'category' => self::CATEGORY_ENQUIRY, 'sample' => 'Question about Amazing Mountain Adventure', 'events' => $enquiryContextEvents, ], 'message' => [ 'key' => 'message', 'label' => 'Message', 'description' => 'Customer message body (sanitised, line breaks preserved).', 'category' => self::CATEGORY_ENQUIRY, 'sample' => 'I would like to know more about this trip. What is included in the package?', 'events' => $enquiryContextEvents, ], 'original_message' => [ 'key' => 'original_message', 'label' => 'Original Message', 'description' => 'First message in the enquiry thread (no line-break escaping).', 'category' => self::CATEGORY_ENQUIRY, 'sample' => 'I would like to know more about this trip.', 'events' => $enquiryContextEvents, ], 'response' => [ 'key' => 'response', 'label' => 'Response', 'description' => "Operator's typed reply (alias of response_message).", 'category' => self::CATEGORY_ENQUIRY, 'sample' => 'Thank you for your interest! The package includes accommodation, meals, and guided tours.', 'events' => [self::EVENT_ENQUIRY_RESPONDED], ], 'response_message' => [ 'key' => 'response_message', 'label' => 'Response Message', 'description' => "Operator's typed reply.", 'category' => self::CATEGORY_ENQUIRY, 'sample' => 'Thank you for your interest! The package includes accommodation, meals, and guided tours.', 'events' => [self::EVENT_ENQUIRY_RESPONDED], ], 'response_date' => [ 'key' => 'response_date', 'label' => 'Response Date', 'description' => 'When the reply was sent.', 'category' => self::CATEGORY_ENQUIRY, 'sample' => function_exists('date_i18n') ? date_i18n($dateFormat) : date($dateFormat), 'events' => [self::EVENT_ENQUIRY_RESPONDED], ], // --------------------------------------------------------- // Trip consent (Pro) // --------------------------------------------------------- 'recipient_name' => [ 'key' => 'recipient_name', 'label' => 'Recipient Name', 'description' => 'Traveler receiving the consent email.', 'category' => self::CATEGORY_TRIP_CONSENT, 'sample' => 'Alex Traveler', 'events' => [self::EVENT_CONSENT_REQUESTED], ], 'form_name' => [ 'key' => 'form_name', 'label' => 'Consent Form Name', 'description' => 'Title of the consent form.', 'category' => self::CATEGORY_TRIP_CONSENT, 'sample' => 'Trip liability & release', 'events' => [self::EVENT_CONSENT_REQUESTED], ], 'consent_link' => [ 'key' => 'consent_link', 'label' => 'Consent Link', 'description' => 'URL to open and sign the form.', 'category' => self::CATEGORY_TRIP_CONSENT, 'sample' => $homeUrl . 'trip-consent/preview-token/', 'events' => [self::EVENT_CONSENT_REQUESTED], ], 'consent_test_notice_html' => [ 'key' => 'consent_test_notice_html', 'label' => 'Test Notice (HTML)', 'description' => 'Shown only on admin test sends.', 'category' => self::CATEGORY_TRIP_CONSENT, 'sample' => '', 'events' => [self::EVENT_CONSENT_REQUESTED], ], // --------------------------------------------------------- // Account verification // --------------------------------------------------------- 'verification_link' => [ 'key' => 'verification_link', 'label' => 'Verification Link', 'description' => 'Magic link the customer opens to verify their email.', 'category' => self::CATEGORY_ACCOUNT, 'sample' => $verificationSampleLink, 'events' => [self::EVENT_ACCOUNT_EMAIL_VERIFICATION], ], 'intro_paragraph' => [ 'key' => 'intro_paragraph', 'label' => 'Intro Paragraph', 'description' => 'Opening sentence (registration / resend variant).', 'category' => self::CATEGORY_ACCOUNT, 'sample' => 'Thank you for registering. Click the button in this email to verify your address.', 'events' => [self::EVENT_ACCOUNT_EMAIL_VERIFICATION], ], 'footer_note' => [ 'key' => 'footer_note', 'label' => 'Footer Note', 'description' => 'Disclaimer for unintended recipients.', 'category' => self::CATEGORY_ACCOUNT, 'sample' => 'If you did not create an account, you can ignore this email.', 'events' => [self::EVENT_ACCOUNT_EMAIL_VERIFICATION], ], 'expiry_notice_html' => [ 'key' => 'expiry_notice_html', 'label' => 'Expiry Notice (HTML)', 'description' => 'Link-expiry messaging block (consent / verification emails).', 'category' => self::CATEGORY_ACCOUNT, 'sample' => 'Security note: This link expires in 24 hours.', 'events' => [ self::EVENT_ACCOUNT_EMAIL_VERIFICATION, self::EVENT_CONSENT_REQUESTED, ], ], // --------------------------------------------------------- // Abandoned booking recovery (Pro) // --------------------------------------------------------- 'recovery_link' => [ 'key' => 'recovery_link', 'label' => 'Recovery Link', 'description' => 'Resume the abandoned checkout from the customer email.', 'category' => self::CATEGORY_ABANDONED_RECOVERY, 'sample' => $homeUrl . 'checkout/recover/sample-token', 'events' => [self::EVENT_BOOKING_ABANDONED_RECOVERY], ], 'recovery_reminder_label' => [ 'key' => 'recovery_reminder_label', 'label' => 'Reminder Label', 'description' => 'Sequence-stage label (First, Second, Final).', 'category' => self::CATEGORY_ABANDONED_RECOVERY, 'sample' => 'First reminder', 'events' => [self::EVENT_BOOKING_ABANDONED_RECOVERY], ], 'recovery_intro_html' => [ 'key' => 'recovery_intro_html', 'label' => 'Intro Paragraph (HTML)', 'description' => 'Lead paragraph specific to each recovery email.', 'category' => self::CATEGORY_ABANDONED_RECOVERY, 'sample' => 'You\'re just one step away from booking your dream trip.
', 'events' => [self::EVENT_BOOKING_ABANDONED_RECOVERY], ], ]; // Dynamically expose every enabled Contact/Emergency booking-form field — // including custom fields an operator adds — so they're discoverable and // usable as email variables. Values are resolved at send time by // BookingEmailRichMergeTags (contact_/emergency_ prefixes). $catalog = array_merge($catalog, self::bookingFormFieldDefinitions($bookingContextEvents)); /** * Filter the email merge-tag catalogue so integrations (Channel * Manager, WhatsApp, custom modules) can append their own tags. * * @param array $catalog The tag definitions keyed by tag key. */ return function_exists('apply_filters') ? (array) apply_filters('yatra_email_merge_tag_definitions', $catalog) : $catalog; } /** * Build merge-tag definitions from the live booking-form config so dynamic * (and custom) Contact/Emergency fields surface in the email editor. Only * enabled fields in enabled sections are included; existing canonical tags * are never overwritten. * * @param array