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
? ''
: '';
$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 '
|