$details) { $templates[$context] = self::template($trigger, $context); } return $templates; } /** * @return mixed[] */ public static function blankTemplates(string $trigger): array { $templates = []; foreach (Registry::contexts($trigger) as $context => $details) { $templates[$context] = self::blankTemplate($trigger, $context); } return $templates; } /** * @return mixed[] */ public static function template(string $trigger, string $context, ?bool $include_portal_link = null): array { return self::baseTemplate([ 'active' => in_array($context, [Registry::CONTEXT_ADMIN, Registry::CONTEXT_PRIMARY_BOOKER], true) ? 1 : 0, 'subject' => self::subject($trigger, $context), 'content' => self::content($trigger, $context, $include_portal_link), ]); } /** * @return mixed[] */ public static function blankTemplate(string $trigger, string $context): array { return self::baseTemplate(); } /** * @return string[] */ public static function collectionTemplates(): array { return [ 'participant_list' => '

{participant_name}
{participant_email}
{participant_fields}

', 'option_list' => '
  • {option_title} x {option_quantity}
  • ', 'session_list' => '
  • {session_item}
  • ', ]; } /** * @param mixed[] $overrides * @return mixed[] */ private static function baseTemplate(array $overrides = []): array { return array_replace([ 'active' => 0, 'subject' => '', 'content' => '', 'to' => self::emptyPicker(), 'cc_enabled' => 1, 'cc' => self::emptyPicker(), 'bcc_enabled' => 1, 'bcc' => self::emptyPicker(), 'attach_ical' => 0, 'attachments' => [], 'collections' => self::collectionTemplates(), ], $overrides); } /** * @return mixed[] */ public static function emptyPicker(): array { return [ 'users' => [], 'roles' => [], 'emails' => '', ]; } public static function subject(string $trigger, string $context): string { if ($trigger === Registry::TRIGGER_CUSTOMER_PORTAL_VERIFICATION_CODE) { return esc_html__('[{site_name}] Your booking verification code', 'daytidy'); } $title = self::triggerTitle($trigger); if ($context === Registry::CONTEXT_ADMIN || $context === Registry::CONTEXT_OFFERING_HOST) { // translators: Placeholder values. return sprintf(esc_html__('[{site_name}] %s: {offering_title}', 'daytidy'), $title); } // translators: Placeholder values. return sprintf(esc_html__('%s for {offering_title}', 'daytidy'), $title); } public static function content(string $trigger, string $context, ?bool $include_portal_link = null): string { if ($trigger === Registry::TRIGGER_CUSTOMER_PORTAL_VERIFICATION_CODE) { return self::renderContent([ self::paragraph( __( 'Your DayTidy verification code is {booking_verification_code}. It expires in {booking_verification_code_expiry_minutes} minutes.', 'daytidy' ) ), ]); } $blocks = self::bookerContent( $trigger, $context === Registry::CONTEXT_PRIMARY_BOOKER ? $include_portal_link : false ); if ($context === Registry::CONTEXT_ADMIN || $context === Registry::CONTEXT_OFFERING_HOST) { $blocks = self::adminContent($trigger); } return self::renderContent($blocks); } public static function triggerTitle(string $trigger): string { $triggers = Registry::triggers(); return $triggers[$trigger]['label'] ?? esc_html__('Booking Notification', 'daytidy'); } /** * @return mixed[] */ private static function adminContent(string $trigger): array { return [ self::paragraph(self::intro($trigger, true)), self::field(__('Booking Ref', 'daytidy'), '{booking_ref}'), self::field(__('Booker', 'daytidy'), '{booking_first_name} {booking_last_name} <{booking_email}>'), self::field(__('Offering', 'daytidy'), '{offering_title}'), self::section(__('Sessions', 'daytidy'), '{session_list}', 'ul'), self::section(__('Options', 'daytidy'), '{option_list}', 'ul'), self::section(__('Participants', 'daytidy'), '{participant_list}'), self::field(__('Total', 'daytidy'), '{booking_total}'), ]; } /** * @return mixed[] */ private static function bookerContent(string $trigger, ?bool $include_portal_link = null): array { $blocks = [ self::paragraph(self::intro($trigger, false)), self::field(__('Booking Ref', 'daytidy'), '{booking_ref}'), self::field(__('Offering', 'daytidy'), '{offering_title}'), self::section(__('Sessions', 'daytidy'), '{session_list}', 'ul'), self::section(__('Options', 'daytidy'), '{option_list}', 'ul'), self::field(__('Total', 'daytidy'), '{booking_total}'), ]; if ($include_portal_link ?? self::hasPortalPage()) $blocks[] = self::field(__('Manage Booking', 'daytidy'), '{booking_portal_url}'); return $blocks; } private static function hasPortalPage(): bool { $page_id = PortalSettings::pageId(); return $page_id > 0 && PageSetup::isUsablePortalPage($page_id); } private static function intro(string $trigger, bool $admin): string { $message = [ Registry::TRIGGER_BOOKING_CREATED => $admin ? __('A new booking has been created.', 'daytidy') : __('Your booking has been received.', 'daytidy'), Registry::TRIGGER_BOOKING_PAID => $admin ? __('A booking has been marked as paid.', 'daytidy') : __('Your booking payment has been received.', 'daytidy'), Registry::TRIGGER_BOOKING_REFUNDED => $admin ? __('A booking has been refunded.', 'daytidy') : __('Your booking has been refunded.', 'daytidy'), Registry::TRIGGER_BOOKING_RESCHEDULED => $admin ? __('A booking has been rescheduled.', 'daytidy') : __('Your booking has been rescheduled.', 'daytidy'), Registry::TRIGGER_BOOKING_REMINDER_BEFORE_START => $admin ? __('A booking session is coming up soon.', 'daytidy') : __('Your booking session is coming up soon.', 'daytidy'), Registry::TRIGGER_BOOKING_CONFIRMED => $admin ? __('A booking has been confirmed.', 'daytidy') : __('Your booking has been confirmed.', 'daytidy'), Registry::TRIGGER_BOOKING_DECLINED => $admin ? __('A booking has been declined.', 'daytidy') : __('Your booking has been declined.', 'daytidy'), Registry::TRIGGER_BOOKING_CANCELLED => $admin ? __('A booking has been cancelled.', 'daytidy') : __('Your booking has been cancelled.', 'daytidy'), Registry::TRIGGER_BOOKING_COMPLETED => $admin ? __('A booking has been completed.', 'daytidy') : __('Your booking has been completed.', 'daytidy'), Registry::TRIGGER_BOOKING_PAYMENT_FAILED => $admin ? __('A booking payment has failed.', 'daytidy') : __('Your booking payment could not be completed.', 'daytidy'), Registry::TRIGGER_CUSTOMER_PORTAL_VERIFICATION_CODE => __('Your booking verification code is ready.', 'daytidy'), ]; return $message[$trigger] ?? __('A booking notification has been triggered.', 'daytidy'); } /** * @param mixed[] $blocks */ private static function renderContent(array $blocks): string { $html = []; foreach ($blocks as $block) { if (!is_array($block)) continue; $rendered = self::renderBlock($block); if ($rendered) $html[] = $rendered; } return implode('
    ', $html); } /** * @param mixed[] $block */ private static function renderBlock(array $block): string { $type = (string) ($block['type'] ?? ''); if ($type === 'paragraph') { return '

    ' . esc_html((string) ($block['text'] ?? '')) . '

    '; } if ($type === 'field') { return sprintf( '

    %s: %s

    ', esc_html((string) ($block['label'] ?? '')), (string) ($block['value'] ?? '') ); } if ($type === 'section') { $content = (string) ($block['content'] ?? ''); $wrapper = (string) ($block['wrapper'] ?? ''); if ($wrapper) $content = sprintf('<%1$s>%2$s', esc_attr($wrapper), $content); return sprintf( '

    %s:

    %s', esc_html((string) ($block['label'] ?? '')), $content ); } return ''; } /** * @return mixed[] */ private static function paragraph(string $text): array { return [ 'type' => 'paragraph', 'text' => $text, ]; } /** * @return mixed[] */ private static function field(string $label, string $value): array { return [ 'type' => 'field', 'label' => $label, 'value' => $value, ]; } /** * @return mixed[] */ private static function section(string $label, string $content, string $wrapper = ''): array { return [ 'type' => 'section', 'label' => $label, 'content' => $content, 'wrapper' => in_array($wrapper, ['ol', 'ul'], true) ? $wrapper : '', ]; } }