1, 'template' => '...' ). * When 'enabled' is truthy and 'template' is non-empty, * this form-specific HTML is used instead of the global * default template. * @param bool $is_standalone Optional. True when this submission has no file * attached — uses the standalone-forms global default * instead of the file-download global default, when * there's no form-specific override. * @return string Ready-to-send HTML */ public static function render( array $context, array $form_settings = array(), $is_standalone = false ) { $template = self::resolve_template( $form_settings, $is_standalone ); // ── Step 1: Inject design tokens ({{double_braces}}) ────────────── $html = self::apply_design_tokens( $template ); // ── Step 2: Inject runtime lead data ({single_braces}) ───────────── $html = self::process_lead_tags( $html, $context ); return $html; } /** * Substitute the {{double_brace}} design tokens (logo, button color/ * text, footer text, link expiry, current year) sourced from the * global Email Settings page. Extracted out of render() so any other * email-sending code in the plugin not just the free-download * flow's own template can apply the exact same design tokens * without duplicating this logic. Safe to call on any HTML string; * tokens that don't appear in the input are simply no-ops. * * @param string $html * @return string */ public static function apply_design_tokens( $html ) { $header_color = sanitize_hex_color( get_option( 'acquis_email_header_color', '#1a73e8' ) ) ?: '#1a73e8'; $button_color = sanitize_hex_color( get_option( 'acquis_email_button_color', '#0f9d58' ) ) ?: '#0f9d58'; $button_text = sanitize_text_field( get_option( 'acquis_email_button_text', 'Download My File' ) ); $footer_text = wp_kses_post( get_option( 'acquis_email_footer_text', 'You received this because you requested a file from {site_name}.' ) ); $expiry_minutes = absint( get_option( 'acquis_link_expiry_minutes', 30 ) ); $logo_url = esc_url( get_option( 'acquis_email_logo_url', '' ) ); $logo_block = $logo_url ? '' . esc_attr( get_bloginfo( 'name' ) ) . '' : ''; $design_tokens = array( '{{header_color}}' => esc_attr( $header_color ), '{{button_color}}' => esc_attr( $button_color ), '{{button_text}}' => esc_html( $button_text ), '{{footer_text}}' => $footer_text, '{{expiry_minutes}}' => $expiry_minutes, '{{logo_block}}' => $logo_block, '{{current_year}}' => gmdate( 'Y' ), ); return str_replace( array_keys( $design_tokens ), array_values( $design_tokens ), $html ); } /** * Decide which raw template string to use: a form's own override if it * has one enabled and non-empty, otherwise the global default. * * @param array $form_settings A form's `settings['email']` array. * @param bool $is_standalone True when this submission has no file attached. * @return string Raw, un-rendered template HTML. */ private static function resolve_template( array $form_settings = array(), $is_standalone = false ) { $use_custom = ! empty( $form_settings['enabled'] ); $custom_markup = isset( $form_settings['template'] ) ? trim( $form_settings['template'] ) : ''; if ( $use_custom && '' !== $custom_markup ) { return $form_settings['template']; } if ( $is_standalone ) { $template = get_option( 'acquis_standalone_email_template', '' ); return '' !== trim( $template ) ? $template : self::fallback_standalone_template(); } $template = get_option( 'acquis_email_template', '' ); if ( empty( trim( $template ) ) ) { // Regenerate default if somehow empty ACQUIS_Activator::create_tables(); $template = get_option( 'acquis_email_template', self::fallback_template() ); } return $template; } /** * Render the plain-text fallback version. * * @param array $context * @param bool $is_standalone True when this submission has no file attached. */ public static function render_plain_text( array $context, $is_standalone = false ) { $full_name = sanitize_text_field( $context['full_name'] ?? '' ); $site_name = sanitize_text_field( $context['site_name'] ?? get_bloginfo( 'name' ) ); if ( $is_standalone ) { return "Hi {$full_name},\n\n" . "Thanks for your submission we'll be in touch shortly.\n\n" . " {$site_name}"; } $button_text = sanitize_text_field( get_option( 'acquis_email_button_text', 'Download My File' ) ); $expiry_minutes = absint( get_option( 'acquis_link_expiry_minutes', 30 ) ); $file_name = sanitize_text_field( $context['file_name'] ?? 'your file' ); $download_link = esc_url_raw( $context['download_link'] ?? '#' ); return "Hi {$full_name},\n\n" . "Your file \"{$file_name}\" is ready to download.\n\n" . "{$button_text}:\n{$download_link}\n\n" . "This link expires in {$expiry_minutes} minutes.\n\n" . " {$site_name}"; } /** * Replace all recognised {lead_tags} with escaped runtime values. * * Beyond the 5 fixed tags (full_name, email, file_name, download_link, * site_name), this also substitutes any custom form-field tag present * in $context under a 'custom_fields' key e.g. {phone}, {company} * so a form's own fields can be referenced in its custom email * template. Reserved tag names always win: a custom field can never * override full_name/email/file_name/download_link/site_name even if * a form happens to have a field with one of those keys. * * @param string $html * @param array $context May include 'custom_fields' => array( field_key => value, ... ) * @return string */ public static function process_lead_tags( $html, array $context ) { $site_name = sanitize_text_field( get_bloginfo( 'name' ) ); $download_link = esc_url( $context['download_link'] ?? '#' ); $reserved = array( 'full_name', 'email', 'file_name', 'download_link', 'site_name' ); $tags = array( '{full_name}' => esc_html( $context['full_name'] ?? '' ), '{email}' => esc_html( $context['email'] ?? '' ), '{file_name}' => esc_html( $context['file_name'] ?? '' ), '{download_link}' => $download_link, '{site_name}' => esc_html( $context['site_name'] ?? $site_name ), ); // Custom form-field tags only ever sourced from $context, never // allowed to collide with a reserved tag name above. if ( ! empty( $context['custom_fields'] ) && is_array( $context['custom_fields'] ) ) { foreach ( $context['custom_fields'] as $field_key => $value ) { $field_key = sanitize_key( $field_key ); if ( '' === $field_key || in_array( $field_key, $reserved, true ) ) { continue; } $tags[ '{' . $field_key . '}' ] = esc_html( is_scalar( $value ) ? $value : '' ); } } return str_replace( array_keys( $tags ), array_values( $tags ), $html ); } /** * Render the email subject line (supports {lead_tags}). * * @param array $context * @param array $form_settings Optional. A form's `settings['email']` array. * When 'enabled' is truthy and 'subject' is non-empty, * this form-specific subject is used instead of the * global default. * @param bool $is_standalone Optional. True when this submission has no file * attached — uses the standalone-forms global default * subject instead of the file-download one. */ public static function render_subject( array $context, array $form_settings = array(), $is_standalone = false ) { $use_custom = ! empty( $form_settings['enabled'] ); $custom_subject = isset( $form_settings['subject'] ) ? trim( $form_settings['subject'] ) : ''; if ( $use_custom && '' !== $custom_subject ) { $subject = $form_settings['subject']; } elseif ( $is_standalone ) { $subject = get_option( 'acquis_standalone_email_subject', 'Thanks for your submission, {full_name}!' ); } else { $subject = get_option( 'acquis_email_subject', 'Your download is ready, {full_name}!' ); } return self::process_lead_tags( $subject, $context ); } /** * Return the list of available dynamic tags for the admin UI. */ public static function available_tags() { return array( '{full_name}' => esc_attr__( 'Lead\'s full name', 'acquisitionsuite' ), '{email}' => esc_attr__( 'Lead\'s email address', 'acquisitionsuite' ), '{file_name}' => esc_attr__( 'Name of the requested file', 'acquisitionsuite' ), '{download_link}' => esc_attr__( 'Signed, expiring download URL', 'acquisitionsuite' ), '{site_name}' => esc_attr__( 'Your WordPress site name', 'acquisitionsuite' ), ); } /** * The file-download default template. Also usable by the Form * Builder's "Load Default Template" button. * * @return string */ public static function get_default_template() { $template = get_option( 'acquis_email_template', '' ); return '' !== trim( $template ) ? $template : self::fallback_template(); } /** * Minimal fallback template (used if DB option is corrupted/empty). */ private static function fallback_template() { return '

Hi {full_name},

Your file {file_name} is ready: Download Now

{site_name}

'; } /** * The standalone-forms default template — same branded HTML structure * (logo, colored header, footer, design tokens) as the file-download * default, just without the file/download-link copy, since a * standalone submission has nothing to download. Used both as the * resolve_template() fallback below AND by the Form Builder's "Load * Default Template" button (ACQUIS_P3_Admin), so there is exactly one * place this content is defined. * * @return string */ public static function get_default_standalone_template() { return '
{{logo_block}}

Thanks for your submission!

Hi {full_name},

We\'ve received your submission and will be in touch shortly.

{{footer_text}}

© {{current_year}} {site_name}. All rights reserved.

'; } /** * @deprecated Use get_default_standalone_template() — kept as a thin * alias so any external code calling the old private-by-convention * name still resolves to the same content. */ private static function fallback_standalone_template() { return self::get_default_standalone_template(); } }