id; } /** * Admin-facing email title. * * @return string */ public function get_title(): string { return $this->title; } /** * Admin-facing email description. * * @return string */ public function get_description(): string { return $this->description; } /** * Template path relative to the emails directory. * * @return string */ public function get_template(): string { $template = $this->apply_email_filters('template', $this->template); return is_string($template) ? $template : $this->template; } /** * Whether this email type is enabled. * * Connector: emails are always on (no admin status toggle). * * @return bool */ public function is_enabled(): bool { return (bool) apply_filters('bizdex_uplisting_email_enabled', true, $this); } /** * Resolved subject line with placeholders replaced. * * @param array $data Template data for placeholders. * @return string */ public function get_subject(array $data = []): string { $subject = $this->replace_placeholders($this->get_setting('subject', $this->subject), $data); $subject = $this->apply_email_filters('subject', $subject, $data); return is_string($subject) ? $subject : ''; } /** * Resolved heading with placeholders replaced. * * @param array $data Template data for placeholders. * @return string */ public function get_heading(array $data = []): string { $heading = $this->replace_placeholders($this->get_setting('heading', $this->heading), $data); $heading = $this->apply_email_filters('heading', $heading, $data); return is_string($heading) ? $heading : ''; } /** * Resolved additional body content with placeholders replaced. * * @param array $data Template data for placeholders. * @return string */ public function get_additional_content(array $data = []): string { $content = $this->replace_placeholders($this->get_setting('additional_content', ''), $data); $content = $this->apply_email_filters('additional_content', $content, $data); return is_string($content) ? $content : ''; } /** * Default subject before admin overrides. * * @return string */ public function get_default_subject(): string { return $this->subject; } /** * Default heading before admin overrides. * * @return string */ public function get_default_heading(): string { return $this->heading; } /** * Resolve the primary recipient address for a booking. * * @param BookingDetails $booking Booking model. * @return string */ abstract public function get_recipient(BookingDetails $booking): string; /** * Recipient address after email-specific filters. * * @param BookingDetails $booking Booking model. * @return string */ public function get_recipient_filtered(BookingDetails $booking): string { $recipient = $this->apply_email_filters('recipient', $this->get_recipient($booking), $booking); return is_string($recipient) ? $recipient : ''; } /** * Template variables passed to email partials. * * @param BookingDetails $booking Booking model. * @return array */ public function get_template_args(BookingDetails $booking): array { $data = function_exists('bizdex_uplisting_get_booking_data') ? bizdex_uplisting_get_booking_data($booking->get_id()) : []; if (!is_array($data)) { $data = []; } $data['email_heading'] = $this->get_heading($data); $data['additional_content'] = $this->get_additional_content($data); $data['email_id'] = $this->id; $data = $this->apply_email_filters('template_args', $data, $booking); return is_array($data) ? $data : []; } /** * Render full HTML email body (header, template, footer). * * @param array $args Template variables. * @return string */ public function get_content(array $args = []): string { $args['email_heading'] = $args['email_heading'] ?? $this->get_heading($args); $args['additional_content'] = $args['additional_content'] ?? $this->get_additional_content($args); do_action('bizdex_uplisting_before_email_content', $args, $this); $content = $this->get_template_html('layouts/header', $args) . $this->get_template_html($this->get_template(), $args) . $this->get_template_html('layouts/footer', $args); $content = $this->apply_email_filters('content', $content, $args); do_action('bizdex_uplisting_after_email_content', $content, $args, $this); return is_string($content) ? $content : ''; } /** * Send the email for a booking when enabled and recipient is valid. * * @param BookingDetails $booking Booking model. * @return bool True when wp_mail accepts the message. */ public function send(BookingDetails $booking): bool { if (!$this->is_enabled()) { return false; } if (!(bool) apply_filters('bizdex_uplisting_email_should_send', true, $booking, $this)) { return false; } $to = $this->get_recipient_filtered($booking); if ($to === '' || !is_email($to)) { do_action('bizdex_uplisting_email_invalid_recipient', $booking, $this); return false; } $args = $this->get_template_args($booking); $subject = $this->get_subject($args); $message = $this->get_content($args); $headers = apply_filters('bizdex_uplisting_email_headers', [], $this->id, $booking->get_id(), $this); $headers = apply_filters('bizdex_uplisting_email_headers_' . $this->id, is_array($headers) ? $headers : [], $booking->get_id(), $this); if (!is_array($headers)) { $headers = []; } $attachments = $this->apply_email_filters('attachments', [], $booking); if (!is_array($attachments)) { $attachments = []; } do_action('bizdex_uplisting_before_email_send', $to, $subject, $message, $headers, $attachments, $booking, $this); $sent = $this->mail($to, $subject, $message, $headers, $attachments); do_action('bizdex_uplisting_after_email_send', $sent, $to, $subject, $message, $headers, $attachments, $booking, $this); if ($sent) { do_action('bizdex_uplisting_email_sent', $to, $booking, $this); do_action('bizdex_uplisting_email_sent_' . $this->id, $to, $booking, $this); } else { do_action('bizdex_uplisting_email_failed', $to, $booking, $this); do_action('bizdex_uplisting_email_failed_' . $this->id, $to, $booking, $this); } return $sent; } /** * Deliver HTML mail via wp_mail with default From headers. * * @param string $to Recipient address. * @param string $subject Email subject. * @param string $message HTML body. * @param array $headers Extra headers. * @param array $attachments Attachment paths. * @return bool */ public function mail(string $to, string $subject, string $message, array $headers = [], array $attachments = []): bool { if ($to === '' || !is_email($to)) { return false; } $from_name = (string) get_option('bizdex_uplisting_booking_from_name', get_bloginfo('name')); $from_email = (string) get_option('bizdex_uplisting_booking_from_email', get_option('admin_email')); if ($from_name === '') { $from_name = (string) get_bloginfo('name'); } if ($from_email === '' || !is_email($from_email)) { $from_email = (string) get_option('admin_email'); } $from_name = (string) apply_filters('bizdex_uplisting_email_from_name', $from_name, $this); $from_email = (string) apply_filters('bizdex_uplisting_email_from_address', $from_email, $this); $default_headers = [ 'Content-Type: text/html; charset=UTF-8', 'From: ' . $from_name . ' <' . $from_email . '>', ]; $headers = array_values(array_filter(array_merge($default_headers, $headers))); $headers = apply_filters('bizdex_uplisting_email_mail_headers', $headers, $to, $subject, $this); if (!is_array($headers)) { $headers = $default_headers; } $message = (string) apply_filters('bizdex_uplisting_email_mail_message', $message, $to, $subject, $this); return (bool) wp_mail($to, $subject, $message, $headers, $attachments); } /** * Render a single email template partial to HTML. * * @param string $template Partial path without extension. * @param array $args Variables for the partial. * @return string */ protected function get_template_html(string $template, array $args = []): string { $args = apply_filters('bizdex_uplisting_email_partial_args', $args, $template, $this); if (!is_array($args)) { $args = []; } $path = $this->locate_template($template); if ($path === '') { return ''; } $view_args = $args; ob_start(); do_action('bizdex_uplisting_before_email_template', $template, $path, $args, $this); include $path; do_action('bizdex_uplisting_after_email_template', $template, $path, $args, $this); $html = (string) ob_get_clean(); return (string) apply_filters('bizdex_uplisting_email_partial_html', $html, $template, $args, $this); } /** * Resolve template file path (theme override or plugin default). * * @param string $template Partial path without extension. * @return string Absolute path, or empty when not found. */ protected function locate_template(string $template): string { $template = ltrim(str_replace(['..', '\\'], ['', '/'], $template), '/'); $relative = 'emails/' . $template . '.php'; $theme = (string) locate_template([ 'bizdex-uplisting/' . $relative, 'wp-uplisting-connector/' . $relative, ]); if ($theme !== '' && is_readable($theme)) { $path = $theme; } else { $plugin = BIZDEX_UPLISTING_PLUGIN_DIR . 'templates/' . $relative; $path = is_readable($plugin) ? $plugin : ''; } $path = apply_filters('bizdex_uplisting_email_locate_template', $path, $template, $this); return is_string($path) ? $path : ''; } /** * WordPress option key for an email setting field. * * @param string $field Setting field name (e.g. subject, enabled). * @return string */ public function option_key(string $field): string { return 'bizdex_uplisting_email_' . $this->id . '_' . $field; } /** * Stored setting value, falling back to default when missing or blank. * * @param string $field Setting field name. * @param string $default Fallback when unset or blank. * @return string */ public function get_setting(string $field, string $default = ''): string { $value = get_option($this->option_key($field), null); if (!is_string($value) || trim($value) === '') { return $default; } return $value; } /** * Apply global then per-email-id filters: `bizdex_uplisting_email_{$name}` and `_{$name}_{$id}`. * * @param mixed $value * @param mixed ...$args Extra args between value and $this. * @return mixed */ protected function apply_email_filters(string $name, $value, ...$args) { $value = apply_filters("bizdex_uplisting_email_{$name}", $value, ...[...$args, $this]); $value = apply_filters("bizdex_uplisting_email_{$name}_{$this->id}", $value, ...[...$args, $this]); return $value; } /** * Replace `{key}` placeholders in text with scalar values from data. * * @param string $text Source text. * @param array $data Replacement values. * @return string */ protected function replace_placeholders(string $text, array $data): string { if ($text === '' || $data === []) { return $text; } $replacements = []; foreach ($data as $key => $value) { if (!is_scalar($value) && $value !== null) { continue; } $replacements['{' . $key . '}'] = (string) $value; } $replacements = apply_filters('bizdex_uplisting_email_placeholders', $replacements, $data, $this); if (!is_array($replacements)) { return $text; } return str_replace(array_keys($replacements), array_values($replacements), $text); } }