parse_placeholders( $subject, $case_data ); $body = $this->parse_placeholders( $body, $case_data ); $message = $this->render_email_template( 'default', [ 'content' => $body ] ); // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.wp_mail_wp_mail -- Single transactional email, not bulk sending. return wp_mail( $admin_email, $subject, $message, [ 'Content-Type: text/html; charset=UTF-8', ] ); } /** * Send a case confirmation email to the customer. * * @param array $case_data Case data. * @return bool */ public function send_case_confirmation( array $case_data ): bool { $settings = EmailTemplates::get_default_settings(); $config = $settings['confirmation'] ?? []; if ( empty( $config['enabled'] ) ) { return false; } $subject = $config['subject'] ?? ''; $body = $config['body'] ?? ''; $subject = $this->parse_placeholders( $subject, $case_data ); $body = $this->parse_placeholders( $body, $case_data ); $site_name = get_bloginfo( 'name' ); // Use an address on the site's own domain for the From header so it // stays SPF/DKIM aligned with the sending server. Sending "From" an // arbitrary admin_email (e.g. a gmail.com address) on a server that // isn't authorized to send for that domain looks like spoofing and // gets the message flagged as spam. Put the real reply address in // Reply-To instead, which doesn't affect deliverability checks. $site_domain = wp_parse_url( home_url(), PHP_URL_HOST ); $from_email = 'wordpress@' . $site_domain; $admin_email = get_option( 'admin_email' ); $headers = [ 'Content-Type: text/html; charset=UTF-8', 'From: ' . $site_name . ' <' . $from_email . '>', 'Reply-To: ' . $admin_email, ]; $message = $this->render_email_template( 'case-confirmation', [ 'content' => $body, ] ); // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.wp_mail_wp_mail -- Single transactional email, not bulk sending. return wp_mail( $case_data['customer_email'], $subject, $message, $headers ); } /** * Render an email template to a string. * * @param string $template Template slug (internal, not user input). * @param array $data Variables to expose to the template. * @return string */ public function render_email_template( string $template, array $data = [] ): string { $email_template_path = AARAMBHA_CASE_MANAGEMENT_PATH . 'includes/Mail/Templates/' . $template . '.php'; if ( ! file_exists( $email_template_path ) ) { return ''; } // phpcs:ignore WordPress.PHP.DontExtract.extract_extract -- Template files rely on named variables (e.g. $content); $data is always internally built, never raw user input. extract( $data, EXTR_SKIP ); ob_start(); // phpcs:ignore WordPressVIPMinimum.Files.IncludingFile.UsingVariable -- $template is always a hardcoded internal string (e.g. 'default', 'case-confirmation'), never user input. include $email_template_path; return ob_get_clean(); } /** * Replace placeholder tokens in email content with case values. * * @param string $content Raw content containing placeholder tokens. * @param array $case_data Case data. * @return string */ private function parse_placeholders( string $content, array $case_data ): string { return str_replace( [ '{case_number}', '{customer_name}', '{customer_email}', '{customer_phone}', '{customer_subject}', '{customer_description}', '{status}', '{case_create_date}', '{site_name}', ], [ $case_data['case_number'], $case_data['customer_name'], $case_data['customer_email'], $case_data['phone'], $case_data['subject'], $case_data['description'], ucfirst( $case_data['status'] ), $case_data['created_at'], get_bloginfo( 'name' ), ], $content ); } /** * Send a status-change notification email to the customer. * * @param string $type Settings key for the status type (e.g. 'in_progress', 'resolved', 'closed'). * @param array $case_data Case data. * @return bool */ public function send_status_email( string $type, array $case_data ): bool { $settings = EmailTemplates::get_default_settings(); $config = $settings[ $type ] ?? []; if ( empty( $config['enabled'] ) ) { return false; } $subject = $config['subject'] ?? ''; $body = $config['body'] ?? ''; $subject = $this->parse_placeholders( $subject, $case_data ); $body = $this->parse_placeholders( $body, $case_data ); $message = $this->render_email_template( 'default', [ 'content' => $body ] ); // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.wp_mail_wp_mail -- Single transactional email, not bulk sending. return wp_mail( $case_data['customer_email'], $subject, $message, [ 'Content-Type: text/html; charset=UTF-8', ] ); } /** * Get the list of placeholder tokens available for use in email templates. * * @return array */ public static function get_placeholders(): array { return [ '{customer_name}' => __( 'Customer name', 'aarambha-case-management' ), '{customer_email}' => __( 'Customer email', 'aarambha-case-management' ), '{customer_phone}' => __( 'Customer phone', 'aarambha-case-management' ), '{customer_subject}' => __( 'Case subject', 'aarambha-case-management' ), '{customer_description}' => __( 'Case description', 'aarambha-case-management' ), '{case_number}' => __( 'Case reference number', 'aarambha-case-management' ), '{status}' => __( 'Current case status', 'aarambha-case-management' ), '{site_name}' => __( 'Website name', 'aarambha-case-management' ), ]; } }