<?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 $plugin; /** * ACF Extended object. * * @since 0.1 * @access public * @var CFAFA_ACFE */ public $acfe; /** * Form Action Name. * * @since 0.1 * @access public * @var string */ public $action_name = 'email_cfafa'; /** * Field Key Prefix. * * @since 0.1 * @access public * @var string */ public $field_key = 'field_cfafa_email_'; /** * Field Name Prefix. * * @since 0.1 * @access public * @var string */ public $field_name = 'cfafa_email_'; /** * 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; $this->acfe = $parent; // 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' ); $form_id = acf_maybe_get( $form, 'ID' ); // 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. $fields = []; // Add Conditional Field. $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() { // Init return. $fields = []; // Add "Mapping" Fields. foreach ( $this->mapped_email_fields as $name => $title ) { $fields[] = $this->mapping_field_get( $name, $title ); } // Add Content Field. $fields[] = [ '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 />' ), 'required' => 0, 'conditional_logic' => 0, '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' => [ 'width' => '', 'class' => '', 'id' => '', ], 'acfe_permissions' => '', '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' => '', 'id' => '', ], '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' ), 'allow_custom' => 1, ], [ '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' => '', 'id' => '', ], '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' => [ 'width' => '', 'class' => '', 'id' => '', ], 'acfe_permissions' => '', '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, 'conditional_logic' => 0, 'wrapper' => [ 'width' => '', 'class' => '', 'id' => '', ], '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 ); // Add Email Content. $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. * * @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 Attachment data. */ public function form_attachments_data( $form, $current_post_id, $action ) { // Init return array. $data = [ 'attachments' => [], 'delete' => [], ]; // 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 = acf_get_field( $file_field_key ); $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'] ); // Sanity checks. 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 ); } // --< return $args; } /** * Checks the Conditional Email Field given data from mapped Fields. * * @since 0.1 * * @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. if ( false === $result ) { return false; } // --< return $args; } /** * Builds an Email item according to the specification. * * For example "First Last <foo@bar.com>". * * @since 0.1 * * @param str $name The name. * @param str $email The email. * @return str $item The built item. */ public function form_email_item_build( $name, $email ) { // Init return. $item = ''; // Parse item from params. if ( ! empty( $name ) && ! empty( $email ) ) { $item = sprintf( '%1$s <%2$s>', $name, $email ); } elseif ( ! empty( $email ) ) { $item = $email; } // --< return $item; } /** * 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 ); } } }