db = $db; } /** * Seed stub templates (idempotent per key/channel/locale). * * @since 0.1.0 */ public function seed(): void { $now = gmdate( 'Y-m-d H:i:s' ); $table = $this->db->prefix . 'bookline_notification_templates'; foreach ( $this->stubs() as $stub ) { $exists_sql = $this->db->prepare( 'SELECT COUNT(*) FROM %i WHERE `key` = %s AND `channel` = %s AND `locale` = %s', $table, $stub['key'], $stub['channel'], $stub['locale'] ); if ( ! is_string( $exists_sql ) ) { continue; } // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching if ( (int) $this->db->get_var( $exists_sql ) > 0 ) { continue; } // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $this->db->insert( $table, [ 'uuid' => $this->uuid(), 'key' => $stub['key'], 'channel' => $stub['channel'], 'recipient' => $stub['recipient'], 'subject' => $stub['subject'], 'body' => $stub['body'], 'is_enabled' => 1, 'locale' => $stub['locale'], 'created_at' => $now, 'updated_at' => $now, ], [ '%s', '%s', '%s', '%s', '%s', '%s', '%d', '%s', '%s', '%s' ] ); } } /** * Stub template catalogue. * * @return list */ private function stubs(): array { $locale = 'en_GB'; return [ [ 'key' => 'booking.confirmed.customer', 'channel' => 'email', 'recipient' => 'customer', 'subject' => 'Your booking is confirmed', 'body' => '

Hello {{customer.first_name}},

Your booking for {{service.name}} on {{booking.start_date}} at {{booking.start_time}} is confirmed.

Manage: {{links.manage}}

', 'locale' => $locale, ], [ 'key' => 'booking.confirmed.agent', 'channel' => 'email', 'recipient' => 'agent', 'subject' => 'New booking confirmed', 'body' => '

A booking for {{service.name}} with {{customer.full_name}} is confirmed for {{booking.start_date}} at {{booking.start_time}}.

', 'locale' => $locale, ], [ 'key' => 'booking.confirmed.admin', 'channel' => 'email', 'recipient' => 'admin', 'subject' => 'Booking confirmed', 'body' => '

Booking {{booking.uuid}} for {{service.name}} is confirmed.

', 'locale' => $locale, ], [ 'key' => 'booking.pending.customer', 'channel' => 'email', 'recipient' => 'customer', 'subject' => 'We have received your booking request', 'body' => '

Hello {{customer.first_name}},

Your booking for {{service.name}} is awaiting approval.

', 'locale' => $locale, ], [ 'key' => 'booking.approved.customer', 'channel' => 'email', 'recipient' => 'customer', 'subject' => 'Your booking has been approved', 'body' => '

Hello {{customer.first_name}},

Your booking for {{service.name}} on {{booking.start_date}} at {{booking.start_time}} has been approved.

', 'locale' => $locale, ], [ 'key' => 'booking.rescheduled.customer', 'channel' => 'email', 'recipient' => 'customer', 'subject' => 'Your booking has been rescheduled', 'body' => '

Hello {{customer.first_name}},

Your booking is now on {{booking.start_date}} at {{booking.start_time}}.

', 'locale' => $locale, ], [ 'key' => 'booking.rescheduled.agent', 'channel' => 'email', 'recipient' => 'agent', 'subject' => 'Booking rescheduled', 'body' => '

Booking with {{customer.full_name}} moved to {{booking.start_date}} at {{booking.start_time}}.

', 'locale' => $locale, ], [ 'key' => 'booking.cancelled.customer', 'channel' => 'email', 'recipient' => 'customer', 'subject' => 'Your booking has been cancelled', 'body' => '

Hello {{customer.first_name}},

Your booking for {{service.name}} has been cancelled.

', 'locale' => $locale, ], [ 'key' => 'booking.cancelled.agent', 'channel' => 'email', 'recipient' => 'agent', 'subject' => 'Booking cancelled', 'body' => '

Booking with {{customer.full_name}} for {{service.name}} was cancelled.

', 'locale' => $locale, ], [ 'key' => 'booking.cancelled.admin', 'channel' => 'email', 'recipient' => 'admin', 'subject' => 'Booking cancelled', 'body' => '

Booking {{booking.uuid}} was cancelled.

', 'locale' => $locale, ], [ 'key' => 'booking.reminder.customer', 'channel' => 'email', 'recipient' => 'customer', 'subject' => 'Reminder: upcoming booking', 'body' => '

Hello {{customer.first_name}},

This is a reminder that you have {{service.name}} on {{booking.start_date}} at {{booking.start_time}}.

', 'locale' => $locale, ], [ 'key' => 'booking.reminder.agent', 'channel' => 'email', 'recipient' => 'agent', 'subject' => 'Reminder: upcoming booking', 'body' => '

Reminder: {{service.name}} with {{customer.full_name}} on {{booking.start_date}} at {{booking.start_time}}.

', 'locale' => $locale, ], [ 'key' => 'booking.reminder_soon.customer', 'channel' => 'email', 'recipient' => 'customer', 'subject' => 'We are expecting you shortly', 'body' => '

Hello {{customer.first_name}},

Your {{service.name}} appointment is at {{booking.start_time}} today at {{location.name}}.

Need to change it? {{links.manage}}

', 'locale' => $locale, ], [ 'key' => 'booking.prep.customer', 'channel' => 'email', 'recipient' => 'customer', 'subject' => 'Before your first visit', 'body' => '

Hello {{customer.first_name}},

Thank you for booking {{service.name}} on {{booking.start_date}} at {{booking.start_time}}.

Please arrive a few minutes early. If you need to change anything, use {{links.manage}}.

', 'locale' => $locale, ], [ 'key' => 'booking.followup.customer', 'channel' => 'email', 'recipient' => 'customer', 'subject' => 'Thank you for coming in', 'body' => '

Hello {{customer.first_name}},

It was a pleasure seeing you for {{service.name}}. If you have a moment, a short review helps others find us: {{links.review}}

', 'locale' => $locale, ], [ 'key' => 'booking.noshow.customer', 'channel' => 'email', 'recipient' => 'customer', 'subject' => 'Sorry we missed you', 'body' => '

Hello {{customer.first_name}},

We had you down for {{service.name}} and were sorry not to see you. If something came up, you are welcome to book another time: {{links.manage}}

', 'locale' => $locale, ], [ 'key' => 'booking.rebook.customer', 'channel' => 'email', 'recipient' => 'customer', 'subject' => 'Ready for your next visit?', 'body' => '

Hello {{customer.first_name}},

If you would like to book {{service.name}} again, you can choose a time here: {{links.manage}}

', 'locale' => $locale, ], [ 'key' => 'booking.cancelled_rebook.customer', 'channel' => 'email', 'recipient' => 'customer', 'subject' => 'Whenever you are ready', 'body' => '

Hello {{customer.first_name}},

Your {{service.name}} booking was cancelled. If you would like another time, we would be glad to see you: {{links.manage}}

', 'locale' => $locale, ], [ 'key' => 'intent.resume.customer', 'channel' => 'email', 'recipient' => 'customer', 'subject' => 'Finish booking {{service.name}}', 'body' => '

Hello {{customer.first_name}},

You started a booking for {{service.name}} but did not finish. Pick up where you left off: {{links.resume_booking}}

', 'locale' => $locale, ], [ 'key' => 'intent.resume_again.customer', 'channel' => 'email', 'recipient' => 'customer', 'subject' => 'Your appointment slot is still open', 'body' => '

Hello {{customer.first_name}},

Just a second note — you can still complete your {{service.name}} booking here: {{links.resume_booking}}

', 'locale' => $locale, ], [ 'key' => 'payment.failed.customer', 'channel' => 'email', 'recipient' => 'customer', 'subject' => 'We could not take your payment', 'body' => '

Hello {{customer.first_name}},

The payment for your booking did not go through. Please try again from {{links.manage}} so we can keep your appointment.

', 'locale' => $locale, ], [ 'key' => 'customer.welcome', 'channel' => 'email', 'recipient' => 'customer', 'subject' => 'Welcome — we look forward to seeing you', 'body' => '

Hello {{customer.first_name}},

You are on our books. Manage your appointments any time from {{links.manage}}.

', 'locale' => $locale, ], [ 'key' => 'payment.received.customer', 'channel' => 'email', 'recipient' => 'customer', 'subject' => 'Payment received', 'body' => '

Hello {{customer.first_name}},

We have received your payment of {{payment.amount}}.

', 'locale' => $locale, ], [ 'key' => 'payment.refunded.customer', 'channel' => 'email', 'recipient' => 'customer', 'subject' => 'Refund issued', 'body' => '

Hello {{customer.first_name}},

A refund of {{payment.amount}} has been issued.

', 'locale' => $locale, ], [ 'key' => 'payment.balance_due.customer', 'channel' => 'email', 'recipient' => 'customer', 'subject' => 'Balance due', 'body' => '

Hello {{customer.first_name}},

A balance of {{payment.amount}} remains on your booking.

', 'locale' => $locale, ], [ 'key' => 'customer.verify_email', 'channel' => 'email', 'recipient' => 'customer', 'subject' => 'Verify your email address', 'body' => '

Hello {{customer.first_name}},

Please verify your email to manage bookings.

', 'locale' => $locale, ], [ 'key' => 'customer.manage_link', 'channel' => 'email', 'recipient' => 'customer', 'subject' => 'Manage your booking', 'body' => '

Hello {{customer.first_name}},

Use this link to manage your booking: {{links.manage}}

', 'locale' => $locale, ], ]; } /** * Generate a UUID v4 string without external dependencies. */ private function uuid(): string { $data = random_bytes( 16 ); $data[6] = chr( ( ord( $data[6] ) & 0x0f ) | 0x40 ); $data[8] = chr( ( ord( $data[8] ) & 0x3f ) | 0x80 ); $hex = bin2hex( $data ); return sprintf( '%s-%s-%s-%s-%s', substr( $hex, 0, 8 ), substr( $hex, 8, 4 ), substr( $hex, 12, 4 ), substr( $hex, 16, 4 ), substr( $hex, 20, 12 ) ); } }