Newer
Older
<?php
/**
* "Conditional Email" ACFE Form Action Class.
*
* Handles the "Conditional Email" ACFE Form Action.
*
* @package Conditional_Form_Actions_For_ACFE
*/
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit;
/**
* "Conditional Email" ACFE Form Action Class.
*
* A class that handles the "Conditional Email" ACFE Form Action.
*
* @since 0.1
*/
class CFAFA_Form_Action_Email extends CFAFA_Form_Action_Base {
/**
* Plugin object.
*
* @since 0.1
* @access public
* @var Conditional_Form_Actions_For_ACFE
*/
public $acfe;
/**
* Form Action Name.
*
* @since 0.1
* @access public
*/
public $action_name = 'email_cfafa';
/**
* Field Key Prefix.
*
* @since 0.1
* @access public
*/
public $field_key = 'field_cfafa_email_';
/**
* Field Name Prefix.
*
* @since 0.1
* @access public
/**
* Array of translatable mapped Email Fields.
*
* @since 0.1
* @access public
* @var array
*/
public $mapped_email_fields = [];
/**
* Constructor.
*
* @since 0.1
*
* @param object $parent The parent object reference.
*/
public function __construct( $parent ) {
// Store references to objects.
$this->plugin = $parent->plugin;
// Label this Form Action.
$this->action_label = __( 'Conditional Email action', 'conditional-form-actions-for-acfe' );
// Alias Placeholder for this Form Action.
$this->alias_placeholder = __( 'Conditional Email', 'conditional-form-actions-for-acfe' );
// Init parent.
parent::__construct();
}
/**
* Configure this object.
*
* @since 0.1
*/
public function configure() {
// Declare the mapped Email Fields with translatable titles.
$this->mapped_email_fields = [
'from_name' => __( 'From Name', 'conditional-form-actions-for-acfe' ),
'from_email' => __( 'From Email', 'conditional-form-actions-for-acfe' ),
'reply_to_name' => __( 'Reply To Name', 'conditional-form-actions-for-acfe' ),
'reply_to_email' => __( 'Reply To Email', 'conditional-form-actions-for-acfe' ),
'to_name' => __( 'To Name', 'conditional-form-actions-for-acfe' ),
'to_email' => __( 'To Email', 'conditional-form-actions-for-acfe' ),
'cc' => __( 'Cc', 'conditional-form-actions-for-acfe' ),
'bcc' => __( 'Bcc', 'conditional-form-actions-for-acfe' ),
'subject' => __( 'Subject', 'conditional-form-actions-for-acfe' ),
];
// Populate mapping Fields.
foreach ( $this->mapped_email_fields as $name => $title ) {
$this->mapping_field_filters_add( $name );
}
// Email Conditional Field.
$this->mapping_field_filters_add( 'email_conditional' );
// Map "File" Field.
$this->mapping_field_filter_deep( $this->field_name . 'file' );
}
/**
* Performs validation when the Form the Action is attached to is submitted.
*
* @since 0.1
*
* @param array $form The array of Form data.
* @param integer $current_post_id The ID of the Post from which the Form has been submitted.
* @param string $action The customised name of the action.
*/
public function validation( $form, $current_post_id, $action ) {
/*
// Get some Form details.
$form_name = acf_maybe_get( $form, 'name' );
$form_id = acf_maybe_get( $form, 'ID' );
//acfe_add_validation_error( $selector, $message );
*/
}
/**
* Performs the action when the Form the Action is attached to is submitted.
*
* @since 0.1
*
* @param array $form The array of Form data.
* @param integer $current_post_id The ID of the Post from which the Form has been submitted.
* @param string $action The customised name of the action.
*/
public function make( $form, $current_post_id, $action ) {
// Bail if a filter has overridden the action.
if ( false === $this->make_skip( $form, $current_post_id, $action ) ) {
return;
}
// Get some Form details.
$form_name = acf_maybe_get( $form, 'name' );
// Populate Email data array.
$email = $this->form_email_data( $form, $current_post_id, $action );
// Populate Attachments data array.
$attachments = $this->form_attachments_data( $form, $current_post_id, $action );
// Check Conditional.
if ( $this->form_conditional_check( $email ) ) {
// Build the arguments.
$args = $this->form_build_args( $email, $attachments, $form, $current_post_id, $action );
// Skip when the args have been overridden.
if ( ! empty( $args ) ) {
// Send the Email with the built arguments.
$email = $this->form_email_send( $args, $email );
// Maybe remove Dynamic Attachments.
$this->form_attachments_delete( $attachments );
}
}
// Save the results of this Action for later use.
$this->make_action_save( $action, $email );
}
/**
* Defines additional Fields for the "Action" Tab.
*
* @since 0.1
*
* @return array $fields The array of Fields for this section.
*/
public function tab_action_append() {
// Init Fields.
$code = 'email_conditional';
$label = __( 'Conditional On', 'conditional-form-actions-for-acfe' );
$conditional = $this->mapping_field_get( $code, $label );
$conditional['placeholder'] = __( 'Always send', 'conditional-form-actions-for-acfe' );
$conditional['wrapper']['data-instruction-placement'] = 'field';
$conditional['instructions'] = __( 'To send the Email only when a Form Field is populated (e.g. "First Name") link this to the Form Field. To send the Email only when more complex conditions are met, link this to a Hidden Field with value "1" where the conditional logic of that Field shows it when the conditions are met.', 'conditional-form-actions-for-acfe' );
$fields[] = $conditional;
// --<
return $fields;
}
/**
* Defines the "Mapping" Tab.
*
* @since 0.1
*
* @return array $fields The array of Fields for this section.
*/
public function tab_mapping_add() {
// Get Tab Header.
$label = __( 'Email', 'conditional-form-actions-for-acfe' );
$mapping_tab_header = $this->tab_mapping_header( $label );
// Build Email Details Accordion.
$mapping_email_accordion = $this->tab_mapping_accordion_email_add();
// Combine Sub-Fields.
$fields = array_merge(
$mapping_tab_header,
$mapping_email_accordion
);
// --<
return $fields;
}
/**
* Defines the Fields in the "Email Fields" Accordion.
*
* @since 0.1
*
* @return array $fields The array of Fields for this section.
*/
public function tab_mapping_accordion_email_add() {
// Add "Mapping" Fields.
foreach ( $this->mapped_email_fields as $name => $title ) {
$fields[] = $this->mapping_field_get( $name, $title );
'key' => $this->field_key . 'content',
'label' => __( 'Content', 'conditional-form-actions-for-acfe' ),
'name' => $this->field_name . 'content',
'type' => 'wysiwyg',
'instructions' => sprintf(
/* translators: 1: Opening code tag, 2: Closing code tag, 3: Line break tag. */
__( 'Fields values may be included using %1$s{field:field_key}%2$s %1$s{field:title}%2$s. All fields may be included using %1$s{fields}%2$s.%3$sSee "Cheatsheet" tab for advanced usage.', 'conditional-form-actions-for-acfe' ),
'<code>',
'</code>',
'<br />'
),
'wrapper' => [
'width' => '',
'class' => '',
'id' => '',
'data-instruction-placement' => 'field',
'acfe_permissions' => '',
'default_value' => '',
'tabs' => 'all',
'toolbar' => 'full',
'media_upload' => 1,
'delay' => 0,
'endpoint' => 1,
];
// --<
return $fields;
}
/**
* Defines the "Attachments" Tab.
*
* @since 0.1
*
* @return array $fields The array of Fields for this section.
*/
public function tab_attachments_add() {
// Init tab array.
$attachments_tab = [];
// Add "Dynamic Files" Repeater.
$attachments_tab[] = [
'key' => $this->field_key . 'files',
'label' => __( 'Dynamic Files', 'conditional-form-actions-for-acfe' ),
'name' => $this->field_name . 'files',
'type' => 'repeater',
'instructions' => '',
'required' => 0,
'conditional_logic' => 0,
'wrapper' => [
'acfe_repeater_stylised_button' => 0,
'collapsed' => '',
'min' => 0,
'max' => 0,
'layout' => 'table',
'button_label' => __( 'Add File', 'conditional-form-actions-for-acfe' ),
'sub_fields' => [
'key' => $this->field_key . 'file',
'label' => __( 'File', 'conditional-form-actions-for-acfe' ),
'name' => $this->field_name . 'file',
'type' => 'select',
'instructions' => '',
'required' => 0,
'conditional_logic' => 0,
'wrapper' => [
'width' => '',
'class' => '',
'acfe_permissions' => '',
'choices' => [],
'default_value' => [],
'allow_null' => 0,
'multiple' => 0,
'ui' => 1,
'return_format' => 'value',
'ajax' => 0,
'placeholder' => '',
'search_placeholder' => __( 'Enter a custom value or template tag. (See "Cheatsheet" tab)', 'conditional-form-actions-for-acfe' ),
'key' => $this->field_key . 'file_delete',
'label' => __( 'Delete File', 'conditional-form-actions-for-acfe' ),
'name' => $this->field_name . 'file_delete',
'type' => 'true_false',
'instructions' => '',
'required' => 0,
'wrapper' => [
'width' => '',
'class' => '',
],
'acfe_permissions' => '',
'message' => __( 'Delete once submitted', 'conditional-form-actions-for-acfe' ),
'default_value' => 0,
'ui' => 1,
'ui_on_text' => '',
'ui_off_text' => '',
// Add "Static Files" Repeater.
$attachments_tab[] = [
'key' => $this->field_key . 'files_static',
'label' => __( 'Static Files', 'conditional-form-actions-for-acfe' ),
'name' => $this->field_name . 'files_static',
'type' => 'repeater',
'instructions' => '',
'required' => 0,
'conditional_logic' => 0,
'wrapper' => [
'acfe_repeater_stylised_button' => 0,
'collapsed' => '',
'min' => 0,
'max' => 0,
'layout' => 'table',
'button_label' => __( 'Add File', 'conditional-form-actions-for-acfe' ),
'sub_fields' => [
'key' => $this->field_key . 'file_static',
'label' => __( 'File', 'conditional-form-actions-for-acfe' ),
'name' => $this->field_name . 'file_static',
'type' => 'file',
'instructions' => '',
'required' => 0,
'width' => '',
'class' => '',
'acfe_permissions' => '',
'return_format' => 'id',
// Get Tab Header.
$attachments_tab_header = $this->tab_attachments_header();
// Combine Sub-Fields.
$fields = array_merge(
$attachments_tab_header,
$attachments_tab
);
// --<
return $fields;
}
/**
* Builds Email data array from mapped Fields.
*
* @since 0.1
*
* @param array $form The array of Form data.
* @param integer $current_post_id The ID of the Post from which the Form has been submitted.
* @param string $action The customised name of the action.
* @return array $data The array of Email data.
*/
public function form_email_data( $form, $current_post_id, $action ) {
// Build Fields array.
$fields = [];
foreach ( $this->mapped_email_fields as $name => $title ) {
$fields[ $name ] = get_sub_field( $this->field_key . 'map_' . $name );
}
// Populate data array with values of mapped Fields.
$data = acfe_form_map_vs_fields( $fields, $fields, $current_post_id, $form );
$content = get_sub_field( $this->field_key . 'content' );
$data['content'] = acfe_form_map_field_value( $content, $current_post_id, $form );
// Get Email Conditional Reference.
$data['email_conditional_ref'] = get_sub_field( $this->field_key . 'map_email_conditional' );
$conditionals = [ $data['email_conditional_ref'] ];
// Populate array with mapped Conditional Field values.
$conditionals = acfe_form_map_vs_fields( $conditionals, $conditionals, $current_post_id, $form );
// Save Email Conditional.
$data['email_conditional'] = array_pop( $conditionals );
// --<
return $data;
}
/**
* Builds Attachment data array from mapped Fields.
* @param array $form The array of Form data.
* @param integer $current_post_id The ID of the Post from which the Form has been submitted.
* @param string $action The customised name of the action.
* @return array $data The array of Attachment data.
public function form_attachments_data( $form, $current_post_id, $action ) {
// Init return array.
$data = [
'attachments' => [],
];
// Process Dynamic Attachments.
if ( have_rows( $this->field_name . 'files' ) ) {
while ( have_rows( $this->field_name . 'files' ) ) {
the_row();
// Find the ID of the File.
$file_field_key = get_sub_field( $this->field_name . 'file' );
$file_id = acfe_form_map_field_value( $file_field_key, $current_post_id, $form );
// Configure the File Field to return an array.
$field['return_format'] = 'array';
// Build an array of data for the Dynamic Attachments.
$files = acf_format_value( $file_id, 0, $field );
$files = acf_get_array( $files );
if ( acf_maybe_get( $files, 'ID' ) ) {
$files = [ $files ];
}
$file_delete = get_sub_field( $this->field_name . 'file_delete' );
foreach ( $files as $file ) {
// Skip if there's no File ID to process.
if ( ! acf_maybe_get( $file, 'ID' ) ) {
continue;
}
// Add to Attachments array.
$data['attachments'][] = get_attached_file( $file['ID'] );
// Maybe add to delete array.
if ( $file_delete ) {
$data['delete'][] = $file['ID'];
}
}
// Process Static Attachments.
if ( have_rows( $this->field_name . 'files_static' ) ) {
while ( have_rows( $this->field_name . 'files_static' ) ) {
the_row();
// The File reference is the Sub-field content.
$file = get_sub_field( $this->field_name . 'file_static' );
// Just add to Attachments array.
$data['attachments'][] = get_attached_file( $file );
}
// --<
return $data;
}
/**
* Builds the arguments array.
*
* @since 0.1
*
* @param array $email_data The array of Email data.
* @param array $attachments_data The array of Attachments data.
* @param array $form The array of Form data.
* @param integer $current_post_id The ID of the Post from which the Form has been submitted.
* @param string $action The customised name of the action.
* @return array|bool $args The arguments array, or false on failure.
*/
public function form_build_args( $email_data, $attachments_data, $form, $current_post_id, $action ) {
// Build "From" and "Reply To" params.
$from = $this->form_email_item_build( $email_data['from_name'], $email_data['from_email'] );
$reply_to = $this->form_email_item_build( $email_data['reply_to_name'], $email_data['reply_to_email'] );
// Maybe use "From" when "Reply To" is empty.
if ( empty( $reply_to ) ) {
$reply_to = $from;
// Build "To" param.
$to = $this->form_email_item_build( $email_data['to_name'], $email_data['to_email'] );
if ( empty( $from ) || empty( $to ) ) {
return false;
// Build Email headers.
$headers[] = 'From: ' . $from;
if ( ! empty( $reply_to ) ) {
$headers[] = 'Reply-To: ' . $reply_to;
}
if ( ! empty( $email_data['cc'] ) ) {
$headers[] = 'Cc: ' . $email_data['cc'];
}
if ( ! empty( $email_data['bcc'] ) ) {
$headers[] = 'Bcc: ' . $email_data['bcc'];
}
$headers[] = 'Content-Type: text/html';
$headers[] = 'charset=UTF-8';
// Finally, build args array.
$args = [
'from' => $from,
'to' => $to,
'reply_to' => $reply_to,
'cc' => $email_data['cc'],
'bcc' => $email_data['bcc'],
'subject' => $email_data['subject'],
'content' => $email_data['content'],
'headers' => $headers,
'attachments' => $attachments_data['attachments'],
];
// Get the Form name.
$form_name = acf_maybe_get( $form, 'name' );
/**
* Allow others to filter the Email arguments.
*
* Returning false for any of these filters will skip sending the Email.
*
* @since 0.1
*
* @param bool $args The array of arguments.
* @param array $form The array of Form data.
* @param integer $current_post_id The ID of the Post from which the Form has been submitted.
* @param string $action The customised name of the Form Action.
*/
$filter = 'acfe/form/submit/' . $this->action_name . '/email_args';
$args = apply_filters( $filter, $args, $form, $current_post_id, $action );
$args = apply_filters( $filter . '/form=' . $form_name, $args, $form, $current_post_id, $action );
if ( ! empty( $action ) ) {
$args = apply_filters( $filter . '/action=' . $action, $args, $form, $current_post_id, $action );
* Checks the Conditional Email Field given data from mapped Fields.
* @param array $email_data The array of Email data.
* @return bool $continue True if the Action should continue or false to skip.
public function form_conditional_check( $email_data ) {
// Approve by default.
$continue = true;
// Skip if the Email Conditional Reference Field has a value.
if ( ! empty( $email_data['email_conditional_ref'] ) ) {
// And the Email Conditional Field has no value.
if ( empty( $email_data['email_conditional'] ) ) {
return false;
}
// --<
return $continue;
}
/**
* Sends the Email given a set of arguments.
*
* @since 0.1
*
* @param array $args The array of Email arguments.
* @param array $email_data The array of Email data.
* @return array|bool $args The array of Email arguments, or false on failure.
*/
public function form_email_send( $args, $email_data ) {
// Define rules for checking Email headers.
$rules = [
[
'args_key' => 'from',
'value_old' => $this->form_email_item_build( $email_data['from_name'], $email_data['from_email'] ),
'header_key' => 'From:',
],
[
'args_key' => 'reply_to',
'value_old' => $this->form_email_item_build( $email_data['reply_to_name'], $email_data['reply_to_email'] ),
'header_key' => 'Reply-To:',
],
[
'args_key' => 'cc',
'value_old' => $email_data['cc'],
'header_key' => 'Cc:',
],
[
'args_key' => 'bcc',
'value_old' => $email_data['bcc'],
'header_key' => 'Bcc:',
],
];
// Check if the Email headers have been changed.
foreach ( $rules as $rule ) {
$check = acf_maybe_get( $args, $rule['args_key'] );
if ( ! empty( $check ) && $check !== $rule['value_old'] ) {
foreach ( $args['headers'] as &$header ) {
if ( stripos( $header, $rule['header_key'] ) !== 0 ) {
continue;
}
// Repair Email header.
$header = $rule['header_key'] . ' ' . $check;
break;
}
}
// Send the Email.
$result = wp_mail( $args['to'], $args['subject'], $args['content'], $args['headers'], $args['attachments'] );
// Bail on failure.
* Builds an Email item according to the specification.
*
* For example "First Last <foo@bar.com>".
* @param str $name The name.
* @param str $email The email.
* @return str $item The built item.
public function form_email_item_build( $name, $email ) {
// Parse item from params.
if ( ! empty( $name ) && ! empty( $email ) ) {
$item = sprintf( '%1$s <%2$s>', $name, $email );
} elseif ( ! empty( $email ) ) {
$item = $email;
/**
* Deletes the Dynamic Attachments.
*
* @since 0.1
*
* @param array $attachments_data The array of Attachments data.
*/
public function form_attachments_delete( $attachments_data ) {
// Bail if there are none.
if ( ! empty( $attachments_data['delete'] ) ) {
return;
}
// Delete all the Dynamic Attachments.
foreach ( $attachments_data['delete'] as $file_id ) {
wp_delete_attachment( $file_id, true );
}