FetchInfoBlocksTask.php 0000644 00000004111 15171646462 0011114 0 ustar 00 init();
}
/**
* Initialize the task with all the proper checks.
*
* @since 1.6.4
*/
public function init() {
$this->hooks();
$tasks = wpforms()->obj( 'tasks' );
// Add new if none exists.
if ( $tasks->is_scheduled( self::ACTION ) !== false ) {
return;
}
$this->recurring( $this->generate_start_date(), WEEK_IN_SECONDS )->register();
}
/**
* Add hooks.
*
* @since 1.7.3
*/
private function hooks() {
// Register the action handler.
add_action( self::ACTION, [ $this, 'process' ] );
}
/**
* Randomly pick a timestamp which is not more than 1 week in the future
* starting before Email Summaries dispatch happens.
*
* @since 1.6.4
*
* @return int
*/
private function generate_start_date() {
$tracking = [];
$tracking['days'] = wp_rand( 0, 6 ) * DAY_IN_SECONDS;
$tracking['hours'] = wp_rand( 0, 23 ) * HOUR_IN_SECONDS;
$tracking['minutes'] = wp_rand( 0, 59 ) * MINUTE_IN_SECONDS;
$tracking['seconds'] = wp_rand( 0, 59 );
return strtotime( 'previous monday 1pm' ) + array_sum( $tracking );
}
/**
* Process the task.
*
* @since 1.6.4
*/
public function process() {
$last_run = get_option( self::LAST_RUN );
// Make sure we do not run it more than once a day.
if (
$last_run !== false &&
( time() - $last_run ) < DAY_IN_SECONDS
) {
return;
}
( new InfoBlocks() )->cache_all();
// Update the last run option to the current timestamp.
update_option( self::LAST_RUN, time() );
}
}
Helpers.php 0000644 00000031145 15171646462 0006677 0 ustar 00 esc_html__( 'Legacy', 'wpforms-lite' ),
];
}
// Iterate through templates and build $choices array.
foreach ( $templates as $template_key => $template ) {
// Skip if the template name is empty.
if ( empty( $template['name'] ) ) {
continue;
}
$choices[ $template_key ] = $template;
}
return $choices;
}
/**
* Retrieves the current email template name.
* If the current template is not found, the default template will be returned.
*
* This method respects backward compatibility and will return the old "Legacy" template if it is set.
* If a template name is provided, the function will attempt to validate and return it. If validation fails,
* it will default to the email template name "Classic."
*
* @since 1.8.5
*
* @param string $template_name Optional. The name of the email template to evaluate.
*
* @return string
*/
public static function get_current_template_name( $template_name = '' ) {
// If a template name is provided, sanitize it. Otherwise, use the default template name from settings.
$settings_template = wpforms_setting( 'email-template', Notifications::DEFAULT_TEMPLATE );
$template = ! empty( $template_name ) ? trim( sanitize_text_field( $template_name ) ) : $settings_template;
// If the user has set the legacy template, return it.
if ( $template === Notifications::LEGACY_TEMPLATE && self::is_legacy_html_template() ) {
return Notifications::LEGACY_TEMPLATE;
}
// In case the user has changed the general settings template,
// but the form submitted still uses the “Legacy” template,
// we need to revert to the general settings template.
if ( $template === Notifications::LEGACY_TEMPLATE && ! self::is_legacy_html_template() ) {
$template = wpforms_setting( 'email-template', Notifications::DEFAULT_TEMPLATE );
}
// Check if the given template name is valid by looking into available templates.
$current_template = Notifications::get_available_templates( $template );
// If the current template is not found or its corresponding class does not exist, return the default template.
if ( ! isset( $current_template['path'] ) || ! class_exists( $current_template['path'] ) ) {
// Last resort, check if the template defined in the settings can be used.
// This would be helpful when user downgrades from Pro to Lite version and the template is not available anymore.
if ( isset( $current_template[ $settings_template ] ) ) {
return $settings_template;
}
return Notifications::DEFAULT_TEMPLATE;
}
// The provided template is valid, so return it.
return $template;
}
/**
* Get the current email template class path.
*
* @since 1.8.5
*
* @param string $template_name Optional. The name of the email template to evaluate.
* @param string $fallback_class Optional. The class to use if the template is not found.
* This argument most likely will be used for backward compatibility and supporting the "Legacy" template.
*
* @return string
*/
public static function get_current_template_class( $template_name = '', $fallback_class = '' ) {
$template_name = self::get_current_template_name( $template_name );
// If the user has set the legacy template, return the "General" template.
if ( $template_name === Notifications::LEGACY_TEMPLATE ) {
return ! empty( $fallback_class ) && class_exists( $fallback_class ) ? $fallback_class : __NAMESPACE__ . '\Templates\General';
}
// Check if the given template name is valid by looking into available templates.
$current_template = Notifications::get_available_templates( $template_name );
// If the current template is not found or its corresponding class does not exist, return the "Classic" template.
if ( ! isset( $current_template['path'] ) || ! class_exists( $current_template['path'] ) ) {
return Notifications::get_available_templates( Notifications::DEFAULT_TEMPLATE )['path'];
}
// The provided template is valid, so return it.
return $current_template['path'];
}
/**
* Get the style overrides for the current email template.
*
* This function retrieves the style overrides for the email template, including background color,
* body color, text color, link color, and typography. It provides default values and handles
* different settings for both the free and Pro versions of the plugin.
*
* @since 1.8.5
*
* @return array
*/
public static function get_current_template_style_overrides() {
// Get the header image size for the current template.
list( $header_image_size, $header_image_size_dark ) = self::get_template_header_image_size();
// Get the typography for the current template.
list( $email_typography, $email_typography_dark ) = self::get_template_typography();
// Default style overrides.
$defaults = [
'email_background_color' => '#e9eaec',
'email_body_color' => '#ffffff',
'email_text_color' => '#333333',
'email_links_color' => '#e27730',
'email_background_color_dark' => '#2d2f31',
'email_body_color_dark' => '#1f1f1f',
'email_text_color_dark' => '#dddddd',
'email_links_color_dark' => '#e27730',
'email_typography' => $email_typography,
'email_typography_dark' => $email_typography_dark,
'header_image_max_width' => $header_image_size['width'],
'header_image_max_height' => $header_image_size['height'],
'header_image_max_width_dark' => $header_image_size_dark['width'],
'header_image_max_height_dark' => $header_image_size_dark['height'],
];
// Retrieve old background colors setting from the Lite version.
$lite_background_color = wpforms_setting( 'email-background-color', $defaults['email_background_color'] );
$lite_background_color_dark = wpforms_setting( 'email-background-color-dark', $defaults['email_background_color_dark'] );
// Leave early if the user has the Lite version.
if ( ! wpforms()->is_pro() ) {
// Override the background colors with the old setting.
$defaults['email_background_color'] = $lite_background_color;
$defaults['email_background_color_dark'] = $lite_background_color_dark;
/**
* Filter the style overrides for the current email template.
*
* @since 1.8.6
*
* @param array $overrides The current email template style overrides.
*/
return (array) apply_filters( 'wpforms_emails_helpers_style_overrides_args', $defaults );
}
// Get the color scheme from the settings.
$color_scheme = wpforms_setting( 'email-color-scheme', [] );
// If the user has the Pro version, but the light mode background color is the old setting, override it.
if ( empty( $color_scheme['email_background_color'] ) && ! empty( $lite_background_color ) ) {
$color_scheme['email_background_color'] = $lite_background_color;
}
// Get the dark mode color scheme from the settings.
$color_scheme_dark = wpforms_setting( 'email-color-scheme-dark', [] );
// If the user has the Pro version, but the dark mode background color is the old setting, override it.
if ( empty( $color_scheme_dark['email_background_color_dark'] ) && ! empty( $lite_background_color_dark ) ) {
$color_scheme_dark['email_background_color_dark'] = $lite_background_color_dark;
}
// Merge the color schemes with the defaults.
$overrides = wp_parse_args( $color_scheme + $color_scheme_dark, $defaults );
/**
* Filter the style overrides for the current email template.
*
* @since 1.8.6
*
* @param array $overrides The current email template style overrides.
*/
return (array) apply_filters( 'wpforms_emails_helpers_style_overrides_args', $overrides );
}
/**
* Check if the current email template is plain text.
*
* @since 1.8.5
*
* @param string $template_name Optional. The name of the email template to compare.
*
* @return bool
*/
public static function is_plain_text_template( $template_name = '' ) {
// Leave early in case the given template name is not empty, and we can resolve it early.
if ( ! empty( $template_name ) ) {
return $template_name === Notifications::PLAIN_TEMPLATE;
}
return wpforms_setting( 'email-template', Notifications::DEFAULT_TEMPLATE ) === Notifications::PLAIN_TEMPLATE;
}
/**
* Check if the current template is legacy.
* Legacy template is the one that its value is 'default'.
*
* @since 1.8.5
*
* @return bool
*/
public static function is_legacy_html_template() {
return wpforms_setting( 'email-template', Notifications::DEFAULT_TEMPLATE ) === Notifications::LEGACY_TEMPLATE;
}
/**
* Get the current template's typography.
*
* This function retrieves the typography setting for email templates and returns the corresponding font family.
*
* If the user has the Pro version, the font-family is determined based on the current template.
* For free users, the font-family defaults to "Sans Serif" because the available templates
* ("Classic" and "Compact") use this font-family in their design.
*
* @since 1.8.5
* @since 1.8.6 Added $typography argument.
*
* @param string $typography Optional. The typography setting to evaluate.
*
* @return array|string
*/
public static function get_template_typography( $typography = '' ) {
// Predefined font families for light and dark modes.
$font_families = [
'sans_serif' => '-apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, helvetica, Cantarell, Ubuntu, roboto, noto, arial, sans-serif',
'serif' => 'Iowan Old Style, Apple Garamond, Baskerville, Times New Roman, Droid Serif, Times, Source Serif Pro, serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol',
];
// If the user is not using the Pro version, return "Sans Serif" font-family.
if ( ! wpforms()->is_pro() ) {
return [ $font_families['sans_serif'], $font_families['sans_serif'] ];
}
// Leave early if a specific typography is requested.
if ( ! empty( $typography ) ) {
// Validate the input and return the corresponding font family.
return $font_families[ $typography ] ?? $font_families['sans_serif'];
}
// Get typography settings from email settings.
$setting_typography = [
// Light mode.
wpforms_setting( 'email-typography', 'sans-serif' ),
// Dark mode.
wpforms_setting( 'email-typography-dark', 'sans-serif' ),
];
// Map setting values to predefined font families, default to 'sans_serif' if not found.
return array_map(
static function ( $item ) use ( $font_families ) {
return $font_families[ $item ] ?? $font_families['sans_serif'];
},
$setting_typography
);
}
/**
* Get the header image size based on the specified size or 'medium' by default.
*
* Note that when given a size input, this function will only validate the input and return the corresponding size.
* Otherwise, it will return the header image size for the current template in both light and dark modes.
*
* @since 1.8.5
* @since 1.8.6 Added $size argument.
*
* @param string $size Optional. The desired image size ('small', 'medium', or 'large').
*
* @return array
*/
public static function get_template_header_image_size( $size = '' ) {
// Predefined image sizes.
$sizes = [
'small' => [
'width' => '240',
'height' => '120',
],
'medium' => [
'width' => '350',
'height' => '180',
],
'large' => [
'width' => '500',
'height' => '240',
],
];
// Leave early if a specific size is requested.
if ( ! empty( $size ) ) {
// Validate the input and return the corresponding size.
return $sizes[ $size ] ?? $sizes['medium'];
}
// Get header image sizes from settings.
$setting_size = [
// Light mode.
wpforms_setting( 'email-header-image-size', 'medium' ),
// Dark mode.
wpforms_setting( 'email-header-image-size-dark', 'medium' ),
];
// Map setting values to predefined sizes, default to 'medium' if not found.
return array_map(
static function ( $item ) use ( $sizes ) {
return $sizes[ $item ] ?? $sizes['medium'];
},
$setting_size
);
}
}
InfoBlocks.php 0000644 00000012132 15171646462 0007321 0 ustar 00 get_cache_file_path();
if ( empty( $cache_file ) || ! is_readable( $cache_file ) ) {
return $this->fetch_all();
}
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
$contents = file_get_contents( $cache_file );
$contents = json_decode( $contents, true );
return $this->verify_fetched( $contents );
}
/**
* Fetch info blocks info from remote.
*
* @since 1.5.4
*
* @return array
*/
public function fetch_all() {
$info = [];
$res = wp_remote_get(
self::SOURCE_URL,
[
'timeout' => 10,
'user-agent' => wpforms_get_default_user_agent(),
]
);
if ( is_wp_error( $res ) ) {
return $info;
}
$body = wp_remote_retrieve_body( $res );
if ( empty( $body ) ) {
return $info;
}
$body = json_decode( $body, true );
return $this->verify_fetched( $body );
}
/**
* Verify fetched blocks data.
*
* @since 1.5.4
*
* @param array $fetched Fetched blocks data.
*
* @return array
*/
protected function verify_fetched( $fetched ) {
$info = [];
if ( ! \is_array( $fetched ) ) {
return $info;
}
foreach ( $fetched as $item ) {
if ( empty( $item['id'] ) ) {
continue;
}
$id = \absint( $item['id'] );
if ( empty( $id ) ) {
continue;
}
$info[ $id ] = $item;
}
return $info;
}
/**
* Get info blocks relevant to customer's licence.
*
* @since 1.5.4
*
* @return array
*/
protected function get_by_license() {
$data = $this->get_all();
$filtered = [];
if ( empty( $data ) || ! \is_array( $data ) ) {
return $filtered;
}
// When there is no license, we assume it's a Lite version.
// This is needed to show blocks for Lite users, as they don't have a license type.
$license_type = wpforms_setting( 'type', 'lite', 'wpforms_license' );
foreach ( $data as $key => $item ) {
if ( ! isset( $item['type'] ) || ! \is_array( $item['type'] ) ) {
continue;
}
if ( ! \in_array( $license_type, $item['type'], true ) ) {
continue;
}
$filtered[ $key ] = $item;
}
return $filtered;
}
/**
* Get the first block with a valid id.
* Needed to ignore blocks with invalid/missing ids.
*
* @since 1.5.4
*
* @param array $data Blocks array.
*
* @return array
*/
protected function get_first_with_id( $data ) {
if ( empty( $data ) || ! \is_array( $data ) ) {
return [];
}
foreach ( $data as $item ) {
$item_id = \absint( $item['id'] );
if ( ! empty( $item_id ) ) {
return $item;
}
}
return [];
}
/**
* Get next info block that wasn't sent yet.
*
* @since 1.5.4
*
* @return array
*/
public function get_next() {
$data = $this->get_by_license();
$block = [];
if ( empty( $data ) || ! \is_array( $data ) ) {
return $block;
}
$blocks_sent = \get_option( 'wpforms_emails_infoblocks_sent' );
if ( empty( $blocks_sent ) || ! \is_array( $blocks_sent ) ) {
$block = $this->get_first_with_id( $data );
}
if ( empty( $block ) ) {
$data = \array_diff_key( $data, \array_flip( $blocks_sent ) );
$block = $this->get_first_with_id( $data );
}
return $block;
}
/**
* Register a block as sent.
*
* @since 1.5.4
*
* @param array $info_block Info block.
*/
public function register_sent( $info_block ) {
$block_id = isset( $info_block['id'] ) ? absint( $info_block['id'] ) : false;
if ( empty( $block_id ) ) {
return;
}
$option_name = 'wpforms_email_summaries_info_blocks_sent';
$blocks = get_option( $option_name );
if ( empty( $blocks ) || ! is_array( $blocks ) ) {
update_option( $option_name, [ $block_id ] );
return;
}
if ( in_array( $block_id, $blocks, true ) ) {
return;
}
$blocks[] = $block_id;
update_option( $option_name, $blocks );
}
/**
* Get a path of the blocks cache file.
*
* @since 1.6.4
*
* @return string
*/
public function get_cache_file_path() {
$upload_dir = wpforms_upload_dir();
if ( ! isset( $upload_dir['path'] ) ) {
return '';
}
$cache_dir = trailingslashit( $upload_dir['path'] ) . 'cache';
return wp_normalize_path( trailingslashit( $cache_dir ) . 'email-summaries.json' );
}
/**
* Fetch and cache blocks in a file.
*
* @since 1.6.4
*/
public function cache_all() {
$file_path = $this->get_cache_file_path();
if ( empty( $file_path ) ) {
return;
}
$dir = dirname( $file_path );
if ( ! wp_mkdir_p( $dir ) ) {
return;
}
wpforms_create_index_html_file( $dir );
wpforms_create_upload_dir_htaccess_file();
$info_blocks = $this->fetch_all();
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
file_put_contents( $file_path, wp_json_encode( $info_blocks ) );
}
}
Mailer.php 0000644 00000030431 15171646462 0006503 0 ustar 00 $key = $value;
}
/**
* Get a property.
*
* @since 1.5.4
*
* @param string $key Property name.
*
* @return string
*/
public function __get( $key ) {
return $this->$key;
}
/**
* Check if a property exists.
*
* @since 1.5.4
*
* @param string $key Property name.
*
* @return bool
*/
public function __isset( $key ) {
return isset( $this->key );
}
/**
* Unset a property.
*
* @since 1.5.4
*
* @param string $key Property name.
*/
public function __unset( $key ) {
unset( $this->key );
}
/**
* Email kill switch if needed.
*
* @since 1.5.4
*
* @return bool
*/
public function is_email_disabled() {
// phpcs:ignore WPForms.Comments.PHPDocHooks.RequiredHookDocumentation
return (bool) apply_filters( 'wpforms_emails_mailer_is_email_disabled', false, $this );
}
/**
* Sanitize the string.
*
* @since 1.5.4
* @since 1.6.0 Deprecated param: $linebreaks. This is handled by wpforms_decode_string().
*
* @param string $input String that may contain tags.
*
* @return string
* @uses wpforms_decode_string()
*/
public function sanitize( $input = '' ) {
return wpforms_decode_string( $input );
}
/**
* Get the email from name.
*
* @since 1.5.4
*
* @return string
*/
public function get_from_name() {
$this->from_name = $this->from_name ? $this->sanitize( $this->from_name ) : get_bloginfo( 'name' );
// phpcs:ignore WPForms.Comments.PHPDocHooks.RequiredHookDocumentation
return apply_filters( 'wpforms_emails_mailer_get_from_name', $this->from_name, $this );
}
/**
* Get the email from address.
*
* @since 1.5.4
*
* @return string
*/
public function get_from_address() {
$this->from_address = $this->from_address ? $this->sanitize( $this->from_address ) : get_option( 'admin_email' );
// phpcs:ignore WPForms.Comments.PHPDocHooks.RequiredHookDocumentation
return apply_filters( 'wpforms_emails_mailer_get_from_address', $this->from_address, $this );
}
/**
* Get the email reply to address.
*
* @since 1.5.4
*
* @return string
*/
public function get_reply_to_address() {
if ( empty( $this->reply_to ) || ! is_email( $this->reply_to ) ) {
$this->reply_to = $this->from_address;
}
$this->reply_to = $this->sanitize( $this->reply_to );
if ( empty( $this->reply_to ) || ! is_email( $this->reply_to ) ) {
$this->reply_to = get_option( 'admin_email' );
}
// phpcs:ignore WPForms.Comments.PHPDocHooks.RequiredHookDocumentation
return apply_filters( 'wpforms_emails_mailer_get_reply_to_address', $this->reply_to, $this );
}
/**
* Get the email carbon copy addresses.
*
* @since 1.5.4
* @since 1.8.9 Allow using CC field as an array.
*
* @return string The email carbon copy addresses.
*/
public function get_cc_address() {
if ( is_array( $this->cc ) ) {
$this->cc = implode( ',', $this->cc );
}
if ( empty( $this->cc ) ) {
/**
* Filters the email carbon copy addresses.
*
* @since 1.5.4
*
* @param string $cc Carbon copy addresses.
* @param Mailer $this Mailer instance.
*/
return apply_filters( 'wpforms_emails_mailer_get_cc_address', $this->cc, $this );
}
$this->cc = $this->sanitize( $this->cc );
$addresses = array_filter( array_map( 'sanitize_email', explode( ',', $this->cc ) ) );
$this->cc = implode( ',', $addresses );
/** This filter is documented in src/Emails/Mailer.php. */
return apply_filters( 'wpforms_emails_mailer_get_cc_address', $this->cc, $this );
}
/**
* Get the email content type.
*
* @since 1.5.4
*
* @return string The email content type.
*/
public function get_content_type() {
$is_html = ! Helpers::is_plain_text_template();
if ( ! $this->content_type && $is_html ) {
// phpcs:ignore WPForms.Comments.PHPDocHooks.RequiredHookDocumentation
$this->content_type = apply_filters( 'wpforms_emails_mailer_get_content_type_default', 'text/html', $this );
} elseif ( ! $is_html ) {
$this->content_type = 'text/plain';
}
// phpcs:ignore WPForms.Comments.PHPDocHooks.RequiredHookDocumentation
return apply_filters( 'wpforms_emails_mailer_get_content_type', $this->content_type, $this );
}
/**
* Get the email subject.
*
* @since 1.8.9
*
* @return string The email subject.
*/
private function get_subject() {
if ( empty( $this->subject ) ) {
$this->subject = __( 'New Email Submit', 'wpforms-lite' );
}
/**
* Filters the email subject.
*
* @since 1.8.9
*
* @param string $subject Email subject.
* @param Mailer $this Mailer instance.
*/
return apply_filters( 'wpforms_emails_mailer_get_subject', $this->subject, $this );
}
/**
* Get the email message.
*
* @since 1.5.4
*
* @return string The email message.
*/
public function get_message() {
if ( empty( $this->message ) && ! empty( $this->template ) ) {
$this->message = $this->template->get();
}
return \apply_filters( 'wpforms_emails_mailer_get_message', $this->message, $this );
}
/**
* Get the email headers.
*
* @since 1.5.4
*
* @return string The email headers.
*/
public function get_headers() {
if ( $this->headers ) {
// phpcs:ignore WPForms.Comments.PHPDocHooks.RequiredHookDocumentation
return apply_filters( 'wpforms_emails_mailer_get_headers', $this->headers, $this );
}
$this->headers = "From: {$this->get_from_name()} <{$this->get_from_address()}>\r\n";
if ( $this->get_reply_to_address() ) {
$this->headers .= "Reply-To: {$this->get_reply_to_address()}\r\n";
}
if ( $this->get_cc_address() ) {
$this->headers .= "Cc: {$this->get_cc_address()}\r\n";
}
$this->headers .= "Content-Type: {$this->get_content_type()}; charset=utf-8\r\n";
return \apply_filters( 'wpforms_emails_mailer_get_headers', $this->headers, $this );
}
/**
* Get the email attachments.
*
* @since 1.5.4
*
* @return string|string[]
*/
public function get_attachments() {
if ( $this->attachments === null ) {
$this->attachments = [];
}
/**
* Filters the email attachments.
*
* @since 1.5.4
*
* @param string|string[] $attachments Array or string with attachment paths.
* @param Mailer $this Mailer instance.
*/
return apply_filters( 'wpforms_emails_mailer_get_attachments', $this->attachments, $this );
}
/**
* Set email address to send to.
*
* @since 1.5.4
*
* @param string|string[] $email Array or comma-separated list of email addresses to send a message.
*
* @return Mailer
*/
public function to_email( $email ) {
if ( is_string( $email ) ) {
$email = explode( ',', $email );
}
// phpcs:ignore WPForms.Comments.PHPDocHooks.RequiredHookDocumentation
$this->to_email = apply_filters( 'wpforms_emails_mailer_to_email', $email, $this );
return $this;
}
/**
* Set email subject.
*
* @since 1.5.4
*
* @param string $subject Email subject.
*
* @return Mailer
*/
public function subject( $subject ) {
$subject = $this->sanitize( $subject );
// phpcs:ignore WPForms.Comments.PHPDocHooks.RequiredHookDocumentation
$this->subject = apply_filters( 'wpforms_emails_mailer_subject', $subject, $this );
return $this;
}
/**
* Set email message (body).
*
* @since 1.5.4
*
* @param string $message Email message.
*
* @return Mailer
*/
public function message( $message ) {
// phpcs:ignore WPForms.Comments.PHPDocHooks.RequiredHookDocumentation
$this->message = apply_filters( 'wpforms_emails_mailer_message', $message, $this );
return $this;
}
/**
* Set email template.
*
* @since 1.5.4
*
* @param General $template Email template.
*
* @return Mailer
*/
public function template( General $template ) {
// phpcs:ignore WPForms.Comments.PHPDocHooks.RequiredHookDocumentation
$this->template = apply_filters( 'wpforms_emails_mailer_template', $template, $this );
return $this;
}
/**
* Get email errors.
*
* @since 1.5.4
*
* @return array
*/
protected function get_errors() {
$errors = [];
foreach ( (array) $this->to_email as $email ) {
if ( ! is_email( $email ) ) {
$errors[] = sprintf( /* translators: %1$s - namespaced class name, %2$s - invalid email. */
esc_html__( '%1$s Invalid email address %2$s.', 'wpforms-lite' ),
'[WPForms\Emails\Mailer]',
$email
);
}
}
if ( empty( $this->get_subject() ) ) {
$errors[] = sprintf( /* translators: %s - namespaced class name. */
esc_html__( '%s Empty subject line.', 'wpforms-lite' ),
'[WPForms\Emails\Mailer]'
);
}
if ( empty( $this->get_message() ) ) {
$errors[] = sprintf( /* translators: %s - namespaced class name. */
esc_html__( '%s Empty message.', 'wpforms-lite' ),
'[WPForms\Emails\Mailer]'
);
}
return $errors;
}
/**
* Log given email errors.
*
* @since 1.5.4
*
* @param array $errors Errors to log.
*/
protected function log_errors( $errors ) {
if ( empty( $errors ) || ! is_array( $errors ) ) {
return;
}
foreach ( $errors as $error ) {
wpforms_log(
$error,
[
'to_email' => $this->to_email,
'subject' => $this->subject,
'message' => wp_trim_words( $this->get_message() ),
],
[
'type' => 'error',
]
);
}
}
/**
* Send the email.
*
* @since 1.5.4
*
* @return bool
*/
public function send() {
if ( ! did_action( 'init' ) && ! did_action( 'admin_init' ) ) {
_doing_it_wrong( __FUNCTION__, esc_html__( 'You cannot send emails with WPForms\Emails\Mailer until init/admin_init has been reached.', 'wpforms-lite' ), null );
return false;
}
// Don't send anything if emails have been disabled.
if ( $this->is_email_disabled() ) {
return false;
}
$errors = $this->get_errors();
if ( $errors ) {
$this->log_errors( $errors );
return false;
}
$this->send_before();
$sent = wp_mail(
$this->to_email,
$this->get_subject(),
$this->get_message(),
$this->get_headers(),
$this->get_attachments()
);
$this->send_after();
return $sent;
}
/**
* Add filters / actions before the email is sent.
*
* @since 1.5.4
*/
public function send_before() { // phpcs:ignore WPForms.PHP.HooksMethod.InvalidPlaceForAddingHooks
// phpcs:ignore WPForms.Comments.PHPDocHooks.RequiredHookDocumentation
do_action( 'wpforms_emails_mailer_send_before', $this );
add_filter( 'wp_mail_from', [ $this, 'get_from_address' ] );
add_filter( 'wp_mail_from_name', [ $this, 'get_from_name' ] );
add_filter( 'wp_mail_content_type', [ $this, 'get_content_type' ] );
}
/**
* Remove filters / actions after the email is sent.
*
* @since 1.5.4
*/
public function send_after() { // phpcs:ignore WPForms.PHP.HooksMethod.InvalidPlaceForAddingHooks
// phpcs:ignore WPForms.Comments.PHPDocHooks.RequiredHookDocumentation
do_action( 'wpforms_emails_mailer_send_after', $this );
remove_filter( 'wp_mail_from', [ $this, 'get_from_address' ] );
remove_filter( 'wp_mail_from_name', [ $this, 'get_from_name' ] );
remove_filter( 'wp_mail_content_type', [ $this, 'get_content_type' ] );
}
}
NotificationBlocks.php 0000644 00000011202 15171646462 0011051 0 ustar 00 notifications = wpforms()->obj( 'notifications' );
}
/**
* Retrieves the notification block from the feed, considering shown notifications and license type.
*
* @since 1.8.8
*
* @return array
*/
public function get_block(): array {
// Check if the user has access to notifications.
// If the user has disabled announcements, return an empty array.
if ( ! $this->notifications || ! $this->notifications->has_access() ) {
return [];
}
// Get the response array from the notifications.
$notifications = $this->notifications->get_option();
// Check if 'feed' key is present and non-empty.
if ( empty( $notifications['feed'] ) || ! is_array( $notifications['feed'] ) ) {
return [];
}
// Remove items from $feed where their id index is in `shown_notifications` option value.
$feed = $this->filter_feed( $notifications['feed'] );
// Sort the array of items using usort and the custom comparison function.
$feed = $this->sort_feed( $feed );
// Get the very first item from the $feed.
$block = reset( $feed );
// Check if $block is empty.
if ( empty( $block ) ) {
return [];
}
// Return the notification block.
return $this->prepare_and_sanitize_content( $block );
}
/**
* Save the shown notification block if it's not empty.
*
* @since 1.8.8
*
* @param array $notification The notification to be saved.
*/
public function maybe_remember_shown_block( array $notification ) {
// Check if the notification or its ID is empty.
if ( empty( $notification ) || empty( $notification['id'] ) ) {
// If the notification or its ID is empty, return early.
return;
}
// Get shown notifications from options.
$shown_notifications = (array) get_option( 'wpforms_email_summaries_shown_notifications', [] );
// Add the notification id to the $shown_notifications array.
$shown_notifications[] = (int) $notification['id'];
// Update the shown notifications in the options.
// Avoid autoloading the option, as it's not needed.
update_option( 'wpforms_email_summaries_shown_notifications', $shown_notifications, false );
}
/**
* Filter the feed to remove shown notifications.
*
* @since 1.8.8
*
* @param array $feed The feed to filter.
*
* @return array
*/
private function filter_feed( array $feed ): array {
$shown_notifications = (array) get_option( 'wpforms_email_summaries_shown_notifications', [] );
return array_filter(
$feed,
static function ( $item ) use ( $shown_notifications ) {
return ! in_array( $item['id'], $shown_notifications, true );
}
);
}
/**
* Sort the feed in descending order by start date.
*
* @since 1.8.8
*
* @param array $feed The feed to sort.
*
* @return array
*/
private function sort_feed( array $feed ): array {
usort(
$feed,
static function ( $a, $b ) {
return strtotime( $b['start'] ) - strtotime( $a['start'] );
}
);
return $feed;
}
/**
* Prepare and sanitize content for display.
*
* @since 1.8.8
*
* @param string|array $content The content to be prepared and sanitized.
*
* @return string|array
*/
private function prepare_and_sanitize_content( $content ) {
// If the content is empty, return as is.
if ( empty( $content ) ) {
return $content;
}
// If the content is already a string, sanitize and return it.
if ( is_string( $content ) ) {
// Define allowed HTML tags and attributes.
$content_allowed_tags = $this->notifications->get_allowed_tags();
// For design consistency, remove the 'p' tag from the allowed tags.
unset( $content_allowed_tags['p'] );
// Apply wp_kses() for sanitization.
return wp_kses( $content, $content_allowed_tags );
}
// If the content is an array with the 'content' index, modify and sanitize it.
if ( is_array( $content ) && isset( $content['content'] ) ) {
// Sanitize the content of the array.
$content['content'] = $this->prepare_and_sanitize_content( $content['content'] );
// Return the modified array.
return $content;
}
// If the content is not a string or an array with 'content' index, return the content as is.
return $content;
}
}
Notifications.php 0000644 00000104202 15171646462 0010101 0 ustar 00 hooks();
// Assign the current template.
$this->current_template = Helpers::get_current_template_name( $template );
// If the old class doesn't exist, return the current class.
// The old class might be removed in the future.
if ( ! class_exists( 'WPForms_WP_Emails' ) ) {
return $this;
}
// In case user is still using the old "Legacy" default template, use the old class.
// Use the old class if the current template is "Legacy".
if ( $this->current_template === self::LEGACY_TEMPLATE ) {
return new WPForms_WP_Emails();
}
// Plain text and other HTML templates will use the current class.
return $this;
}
/**
* Add hooks.
*
* @since 1.9.0
*/
private function hooks() {
add_filter( 'wpforms_smart_tags_formatted_field_value', [ $this, 'get_multi_field_formatted_value' ], 10, 4 );
}
/**
* Maybe send an email right away or schedule it.
*
* @since 1.8.5
*
* @return bool Whether the email was sent successfully.
*/
public function send() {
// Leave the method if the arguments are empty.
// We will be looking for 3 arguments: $to, $subject, $message.
// The primary reason for this method not to take any direct arguments is to make it compatible with the parent class.
if ( empty( func_get_args() ) || count( func_get_args() ) < 3 ) {
return false;
}
// Don't send anything if emails have been disabled.
if ( $this->is_email_disabled() ) {
return false;
}
// Set the arguments.
[ $to, $subject, $message ] = func_get_args();
// Don't send it if the email address is invalid.
if ( ! is_email( $to ) ) {
return false;
}
/**
* Fires before the email is sent.
*
* The filter has been ported from "class-emails.php" to maintain backward compatibility
* and avoid unintended breaking changes where these hooks may have been used.
*
* @since 1.8.5.2
*
* @param Notifications $this An instance of the "Notifications" class.
*/
do_action( 'wpforms_email_send_before', $this ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
// Set the attachments to an empty array.
// We will set the attachments later in the filter.
$attachments = [];
/**
* Filter the email data before sending.
*
* The filter has been ported from "class-emails.php" to maintain backward compatibility
* and avoid unintended breaking changes where these hooks may have been used.
*
* @since 1.8.5
*
* @param array $data Email data.
* @param Notifications $this An instance of the "Notifications" class.
*/
$data = (array) apply_filters( // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
'wpforms_emails_send_email_data',
[
'to' => $to,
'subject' => $subject,
'message' => $message,
'headers' => $this->get_headers(),
'attachments' => $attachments,
],
$this
);
// Set the recipient email address.
$this->to_email( $data['to'] );
// Set the email subject.
$this->subject( $this->process_subject( $data['subject'] ) );
// Process the email template.
$this->process_email_template( $data['message'] );
// Set the attachments to the email.
$this->__set( 'attachments', $data['attachments'] );
/**
* Filter whether to send the email in the same process.
*
* The filter has been ported from "class-emails.php" to maintain backward compatibility
* and avoid unintended breaking changes where these hooks may have been used.
*
* @since 1.8.5
*
* @param bool $send_same_process Whether to send the email in the same process.
* @param array $fields List of submitted fields.
* @param array $entry Entry data.
* @param array $form_data Form data.
* @param int $entry_id Entry ID.
* @param string $type Email type.
*/
$send_same_process = (bool) apply_filters( // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName, WPForms.Comments.ParamTagHooks.InvalidParamTagsQuantity
'wpforms_tasks_entry_emails_trigger_send_same_process',
false,
$this->fields,
! empty( wpforms()->obj( 'entry' ) ) ? wpforms()->obj( 'entry' )->get( $this->entry_id ) : [],
$this->form_data,
$this->entry_id,
'entry'
);
// Send the email immediately.
if ( $send_same_process || ! empty( $this->form_data['settings']['disable_entries'] ) ) {
$results = parent::send();
} else {
$results = (bool) ( new EntryEmailsTask() )
->params(
$this->__get( 'to_email' ),
$this->__get( 'subject' ),
$this->get_message(),
$this->get_headers(),
$this->get_attachments()
)
->register();
}
/**
* Fires after the email has been sent.
*
* The filter has been ported from "class-emails.php" to maintain backward compatibility
* and avoid unintended breaking changes where these hooks may have been used.
*
* @since 1.8.5.2
*
* @param Notifications $this An instance of the "Notifications" class.
*/
do_action( 'wpforms_email_send_after', $this ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
return $results;
}
/**
* Process the email template.
*
* @since 1.8.5
*
* @param string $message Email message.
*/
private function process_email_template( $message ) {
$template = self::get_available_templates( $this->current_template );
// Return if the template is not set.
// This can happen if the template is not found or if the template class doesn't exist.
if ( ! isset( $template['path'] ) || ! class_exists( $template['path'] ) ) {
return;
}
// Set the email template, i.e., WPForms\Emails\Templates\Classic.
$this->template( new $template['path']( '', false, $this->current_template ) );
/**
* Email template.
*
* @var General $email_template
*/
$email_template = $this->__get( 'template' );
if (
! method_exists( $email_template, 'get_field_template' ) ||
! method_exists( $email_template, 'set_field' )
) {
return;
}
// Set the field template.
$this->field_template = $email_template->get_field_template();
// Set the email template fields.
$email_template->set_field( $this->process_message( $message ) );
$content = $email_template->get();
// Return if the template is empty.
if ( ! $content ) {
return;
}
$this->message( $content );
}
/**
* Format and process the email subject.
*
* @since 1.8.5
*
* @param string $subject Email subject.
*
* @return string
*/
private function process_subject( $subject ) {
$subject = $this->process_tag( $subject );
$subject = trim( str_replace( [ "\r\n", "\r", "\n" ], ' ', $subject ) );
return wpforms_decode_string( $subject );
}
/**
* Process the email message.
*
* @since 1.8.5
*
* @param string $message Email message.
*
* @return string
*/
private function process_message( $message ) {
// Check if the placeholder '{all_fields}' is not present in the message.
if ( strpos( $message, '{all_fields}' ) === false ) {
// Wrap the message with a table row after processing tags.
$message = $this->wrap_content_with_table_row( $message );
} else {
// If {all_fields} is present, extract content before and after into separate variables.
[ $before, $after ] = array_map( 'trim', explode( '{all_fields}', $message, 2 ) );
// Wrap before and after content with
tags if they are not empty to maintain styling.
// Note that whatever comes after the {all_fields} should be wrapped in a table row to avoid content misplacement.
$before_tr = ! empty( $before ) ? $this->wrap_content_with_table_row( $before ) : '';
$after_tr = ! empty( $after ) ? $this->wrap_content_with_table_row( $after ) : '';
// Replace {all_fields} with $this->process_field_values() output.
$message = $before_tr . $this->process_field_values() . $after_tr;
}
/**
* Filter and modify the email message content before sending.
* This filter allows customizing the email message content for notifications.
*
* @since 1.8.5
*
* @param string $message The email message to be sent out.
* @param string $template The email template name.
* @param Notifications $this The instance of the "Notifications" class.
*/
$message = apply_filters( 'wpforms_emails_notifications_message', $message, $this->current_template, $this );
// Leave early if the template is set to plain text.
if ( Helpers::is_plain_text_template( $this->current_template ) ) {
return $message;
}
return make_clickable( str_replace( "\r\n", '
', $message ) );
}
/**
* Process the field values.
*
* @since 1.8.5
*
* @return string
*/
private function process_field_values() {
// If fields are empty, return an empty message.
if ( empty( $this->fields ) ) {
return '';
}
// If no message was generated, create an empty message.
$default_message = esc_html__( 'An empty form was submitted.', 'wpforms-lite' );
/**
* Filter whether to display empty fields in the email.
*
* @since 1.8.5
* @deprecated 1.8.5.2
*
* @param bool $show_empty_fields Whether to display empty fields in the email.
*/
$show_empty_fields = apply_filters_deprecated( // phpcs:disable WPForms.Comments.ParamTagHooks.InvalidParamTagsQuantity
'wpforms_emails_notifications_display_empty_fields',
[ false ],
'1.8.5.2 of the WPForms plugin',
'wpforms_email_display_empty_fields'
);
/** This filter is documented in /includes/emails/class-emails.php */
$show_empty_fields = apply_filters( // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
'wpforms_email_display_empty_fields',
false
);
// Process either plain text or HTML message based on the template type.
if ( Helpers::is_plain_text_template( $this->current_template ) ) {
$message = $this->process_plain_message( $show_empty_fields );
} else {
$message = $this->process_html_message( $show_empty_fields );
}
return empty( $message ) ? $default_message : $message;
}
/**
* Process the plain text email message.
*
* @since 1.8.5
*
* @param bool $show_empty_fields Whether to display empty fields in the email.
*
* @return string
*/
private function process_plain_message( bool $show_empty_fields = false ): string {
/**
* Filter the form data before it is used to generate the email message.
*
* @since 1.8.9
*
* @param array $form_data Form data.
* @param array $fields List of submitted fields.
*/
$this->form_data = apply_filters( 'wpforms_emails_notifications_form_data', $this->form_data, $this->fields );
$message = '';
foreach ( $this->form_data['fields'] as $field ) {
/**
* Filter whether to ignore the field in the email.
*
* @since 1.9.0
*
* @param bool $ignore Whether to ignore the field in the email.
* @param array $field Field data.
* @param array $form_data Form data.
*/
if ( apply_filters( 'wpforms_emails_notifications_field_ignored', false, $field, $this->form_data ) ) {
continue;
}
$field_message = $this->get_field_plain( $field, $show_empty_fields );
/**
* Filter the field message before it is added to the email message.
*
* @since 1.8.9
* @since 1.8.9.3 The $notifications parameter was added.
*
* @param string $field_message Field message.
* @param array $field Field data.
* @param bool $show_empty_fields Whether to display empty fields in the email.
* @param array $form_data Form data.
* @param array $fields List of submitted fields.
* @param Notifications $notifications Notifications instance.
*/
$message .= apply_filters( 'wpforms_emails_notifications_field_message_plain', $field_message, $field, $show_empty_fields, $this->form_data, $this->fields, $this );
}
// Trim the message and return.
return rtrim( $message, "\r\n" );
}
/**
* Get a single field plain text markup.
*
* @since 1.8.9
*
* @param array $field Field data.
* @param bool $show_empty_fields Whether to display empty fields in the email.
*
* @return string
*/
public function get_field_plain( array $field, bool $show_empty_fields ): string { // phpcs:ignore Generic.Metrics.CyclomaticComplexity
$field_id = $field['id'] ?? '';
$field = $this->fields[ $field_id ] ?? $field;
$message = '';
if ( ! $show_empty_fields && ( ! isset( $field['value'] ) || (string) $field['value'] === '' ) ) {
return $message;
}
if ( $this->is_calculated_field_hidden( $field_id ) ) {
return $message;
}
$field_name = $field['name'] ?? '';
$field_val = empty( $field['value'] ) && ! is_numeric( $field['value'] ) ? esc_html__( '(empty)', 'wpforms-lite' ) : $field['value'];
// Add quantity for the field.
if ( wpforms_payment_has_quantity( $field, $this->form_data ) ) {
$field_val = wpforms_payment_format_quantity( $field );
}
// Set a default field name if empty.
if ( empty( $field_name ) && $field_name !== null ) {
$field_name = $this->get_default_field_name( $field['id'] );
}
$message .= '--- ' . $field_name . " ---\r\n\r\n";
$field_value = wpforms_decode_string( $field_val ) . "\r\n\r\n";
/**
* Filter the field value before it is added to the email message.
*
* @since 1.8.5
* @deprecated 1.8.7
*
* @param string $field_value Field value.
* @param array $field Field data.
* @param array $form_data Form data.
*/
$field_value = apply_filters_deprecated( // phpcs:disable WPForms.Comments.ParamTagHooks.InvalidParamTagsQuantity
'wpforms_emails_notifications_plaintext_field_value',
[ $field_value, $field, $this->form_data ],
'1.8.7 of the WPForms plugin',
'wpforms_plaintext_field_value'
);
/** This filter is documented in /includes/emails/class-emails.php */
$field_value = apply_filters( // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
'wpforms_plaintext_field_value',
$field_value,
$field,
$this->form_data
);
// Append the filtered field value to the message.
$message .= $field_value;
return $message;
}
/**
* Process the HTML email message.
*
* @since 1.8.5
*
* @param bool $show_empty_fields Whether to display empty fields in the email.
*
* @return string
*/
private function process_html_message( $show_empty_fields = false ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity
$message = '';
/**
* Filter the list of field types to display in the email.
*
* @since 1.8.5
* @deprecated 1.8.5.2
*
* @param array $other_fields List of field types.
* @param array $form_data Form data.
*/
$other_fields = apply_filters_deprecated( // phpcs:disable WPForms.Comments.ParamTagHooks.InvalidParamTagsQuantity
'wpforms_emails_notifications_display_other_fields',
[ [], $this->form_data ],
'1.8.5.2 of the WPForms plugin',
'wpforms_email_display_other_fields'
);
/** This filter is documented in /includes/emails/class-emails.php */
$other_fields = (array) apply_filters( // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
'wpforms_email_display_other_fields',
[],
$this
);
/**
* Filter the form data before it is used to generate the email message.
*
* @since 1.8.8
* @since 1.8.9 The $fields parameter was added.
*
* @param array $form_data Form data.
* @param array $fields List of submitted fields.
*/
$this->form_data = apply_filters( 'wpforms_emails_notifications_form_data', $this->form_data, $this->fields );
foreach ( $this->form_data['fields'] as $field ) {
/**
* Filter whether to ignore the field in the email.
*
* @since 1.9.0
*
* @param bool $ignore Whether to ignore the field in the email.
* @param array $field Field data.
* @param array $form_data Form data.
*/
if ( apply_filters( 'wpforms_emails_notifications_field_ignored', false, $field, $this->form_data ) ) {
continue;
}
$field_message = $this->get_field_html( $field, $show_empty_fields, $other_fields );
/**
* Filter the field message before it is added to the email message.
*
* @since 1.8.9
* @since 1.8.9.3 The $notifications parameter was added.
*
* @param string $field_message Field message.
* @param array $field Field data.
* @param bool $show_empty_fields Whether to display empty fields in the email.
* @param array $other_fields List of field types.
* @param array $form_data Form data.
* @param array $fields List of submitted fields.
* @param Notifications $notifications Notifications instance.
*/
$message .= apply_filters( 'wpforms_emails_notifications_field_message_html', $field_message, $field, $show_empty_fields, $other_fields, $this->form_data, $this->fields, $this );
}
return $message;
}
/**
* Get a single field HTML markup.
*
* @since 1.8.9
*
* @param array $field Field data.
* @param bool $show_empty_fields Whether to display empty fields in the email.
* @param array $other_fields List of field types.
*
* @return string
*/
public function get_field_html( array $field, bool $show_empty_fields, array $other_fields ): string { // phpcs:ignore Generic.Metrics.CyclomaticComplexity
$field_type = ! empty( $field['type'] ) ? $field['type'] : '';
$field_id = $field['id'] ?? '';
// Check if the field is empty in $this->fields.
if ( empty( $this->fields[ $field_id ] ) ) {
// Check if the field type is in $other_fields, otherwise skip.
// Skip if the field is conditionally hidden.
if (
empty( $other_fields ) ||
! in_array( $field_type, $other_fields, true ) ||
(
wpforms()->is_pro() &&
wpforms_conditional_logic_fields()->field_is_hidden( $this->form_data, $field_id )
)
) {
return '';
}
// Handle specific field types.
[ $field_name, $field_val ] = $this->process_special_field_values( $field );
} else {
// Handle fields that are not empty in $this->fields.
if ( ! $show_empty_fields && ( ! isset( $this->fields[ $field_id ]['value'] ) || (string) $this->fields[ $field_id ]['value'] === '' ) ) {
return '';
}
if ( $this->is_calculated_field_hidden( $field_id ) ) {
return '';
}
$field_name = $this->fields[ $field_id ]['name'] ?? '';
$field_val = empty( $this->fields[ $field_id ]['value'] ) && ! is_numeric( $this->fields[ $field_id ]['value'] ) ? '' . esc_html__( '(empty)', 'wpforms-lite' ) . '' : $this->fields[ $field_id ]['value'];
}
// Set a default field name if empty.
if ( empty( $field_name ) && $field_name !== null ) {
$field_name = $this->get_default_field_name( $field_id );
}
/**
* Filter the field name before it is added to the email message.
*
* @since 1.9.1
*
* @param string $field_name Field name.
* @param array $field Field data.
* @param array $form_data Form data.
* @param string $context Context of the field name.
*/
$field_name = (string) apply_filters( // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
'wpforms_html_field_name',
$field_name,
$this->fields[ $field_id ] ?? $field,
$this->form_data,
'email-html'
);
/** This filter is documented in src/SmartTags/SmartTag/FieldHtmlId.php.*/
$field_val = (string) apply_filters( // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
'wpforms_html_field_value',
$field_val,
$this->fields[ $field_id ] ?? $field,
$this->form_data,
'email-html'
);
$field_val = str_replace( [ "\r\n", "\r", "\n" ], '
', $field_val );
// Replace the payment total value if an order summary is enabled.
// Ideally, it could be done through the `wpforms_html_field_value` filter,
// but needed data is missed there, e.g., entry data ($this->fields).
if ( $field_type === 'payment-total' && ! empty( $field['summary'] ) ) {
$field_val = $this->get_payment_total_value( $field_val );
}
// Append the field item to the message.
return str_replace(
[ '{field_type}', '{field_name}', '{field_value}' ],
[ $field_type, $field_name, $field_val ],
$this->field_template
);
}
/**
* Get payment total value.
*
* @since 1.9.3
*
* @param string $value Field value.
*
* @return string
*/
private function get_payment_total_value( string $value ): string {
return $this->process_tag( '{order_summary}' ) . '' . $value . '';
}
/**
* Check if a calculated field is hidden.
*
* @since 1.8.9.5
*
* @param int $field_id Field ID.
*
* @return bool
*/
private function is_calculated_field_hidden( $field_id ): bool {
return ! empty( $this->form_data['fields'][ $field_id ]['calculation_is_enabled'] ) &&
! empty( $this->form_data['fields'][ $field_id ]['calculation_code_php'] ) &&
isset( $this->fields[ $field_id ]['visible'] )
&& ! $this->fields[ $field_id ]['visible'];
}
/**
* Process a smart tag.
*
* @since 1.8.5
*
* @param string $input Smart tag.
*
* @return string
*/
private function process_tag( $input = '' ) {
return wpforms_process_smart_tags( $input, $this->form_data, $this->fields, $this->entry_id, 'notification' );
}
/**
* Process special field types.
* This is used for fields such as Page Break, HTML, Content, etc.
*
* @since 1.8.5
*
* @param array $field Field data.
*
* @return array
*/
private function process_special_field_values( $field ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity
$field_name = null;
$field_val = null;
// Use a switch-case statement to handle specific field types.
switch ( $field['type'] ) {
case 'divider':
$field_name = ! empty( $field['label'] ) ? str_repeat( '—', 3 ) . ' ' . $field['label'] . ' ' . str_repeat( '—', 3 ) : null;
$field_val = ! empty( $field['description'] ) ? $field['description'] : '';
break;
case 'pagebreak':
// Skip if position is 'bottom'.
if ( ! empty( $field['position'] ) && $field['position'] === 'bottom' ) {
break;
}
$title = ! empty( $field['title'] ) ? $field['title'] : esc_html__( 'Page Break', 'wpforms-lite' );
$field_name = str_repeat( '—', 6 ) . ' ' . $title . ' ' . str_repeat( '—', 6 );
break;
case 'html':
$field_name = ! empty( $field['name'] ) ? $field['name'] : esc_html__( 'HTML / Code Block', 'wpforms-lite' );
$field_val = $field['code'];
break;
case 'content':
$field_name = esc_html__( 'Content', 'wpforms-lite' );
$field_val = wpforms_esc_richtext_field( $field['content'] );
break;
default:
$field_name = '';
$field_val = '';
break;
}
return [ $field_name, $field_val ];
}
/**
* Get the email reply to address.
* This method has been overridden to add support for the Reply-to Name.
*
* @since 1.8.5
*
* @return string
*/
public function get_reply_to_address() {
$reply_to = $this->__get( 'reply_to' );
$reply_to_name = false;
if ( ! empty( $reply_to ) ) {
// Optional custom format with a Reply-to Name specified: John Doe
// - starts with anything,
// - followed by space,
// - ends with (expected to be an email, validated later).
$regex = '/^(.+) (<.+>)$/';
$matches = [];
if ( preg_match( $regex, $reply_to, $matches ) ) {
$reply_to_name = $this->sanitize( $matches[1] );
$reply_to = trim( $matches[2], '<> ' );
}
$reply_to = $this->process_tag( $reply_to );
if ( ! is_email( $reply_to ) ) {
$reply_to = false;
$reply_to_name = false;
}
}
if ( $reply_to_name ) {
$reply_to = "$reply_to_name <{$reply_to}>";
}
/**
* Filter the email reply-to address.
*
* @since 1.8.5
*
* @param string $reply_to Email reply-to address.
* @param object $this Instance of the Notifications class.
*/
return apply_filters( 'wpforms_emails_notifications_get_reply_to_address', $reply_to, $this );
}
/**
* Sanitize the string.
* This method has been overridden to add support for processing smart tags.
*
* @since 1.8.5
*
* @param string $input String to sanitize and process for smart tags.
*
* @return string
*/
public function sanitize( $input = '' ) {
return wpforms_decode_string( $this->process_tag( $input ) );
}
/**
* Get the email content type.
* This method has been overridden to better declare email template assigned to each notification.
*
* @since 1.8.5.2
*
* @return string
*/
public function get_content_type() {
$content_type = 'text/html';
if ( Helpers::is_plain_text_template( $this->current_template ) ) {
$content_type = 'text/plain';
}
/**
* Filter the email content type.
*
* @since 1.8.5.2
*
* @param string $content_type The email content type.
* @param Notifications $this An instance of the "Notifications" class.
*/
$content_type = apply_filters( 'wpforms_emails_notifications_get_content_type', $content_type, $this );
// Set the content type.
$this->__set( 'content_type', $content_type );
// Return the content type.
return $content_type;
}
/**
* Check if all emails are disabled.
*
* @since 1.8.5
*
* @return bool
*/
public function is_email_disabled() {
/**
* Filter to control email disabling.
*
* The "Notifications" class is designed to mirror the properties and methods
* provided by the "WPForms_WP_Emails" class for backward compatibility.
*
* @since 1.8.5
*
* @param bool $is_disabled Whether to disable all emails.
* @param Notifications $this An instance of the "Notifications" class.
*/
return (bool) apply_filters( // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
'wpforms_disable_all_emails',
false,
$this
);
}
/**
* Get the default field name as a fallback.
*
* @since 1.8.5
*
* @param int $field_id Field ID.
*
* @return string
*/
private function get_default_field_name( $field_id ) {
return sprintf( /* translators: %1$d - field ID. */
esc_html__( 'Field ID #%1$s', 'wpforms-lite' ),
wpforms_validate_field_id( $field_id )
);
}
/**
* Wrap the given content with a table row.
* This method has been added for styling purposes.
*
* @since 1.8.6
*
* @param string $content Processed smart tag content.
*
* @return string
*/
private function wrap_content_with_table_row( $content ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
// If the content is empty, return it as is.
if ( empty( $content ) ) {
return $content;
}
// Process the smart tags in the content.
$processed_content = $this->process_tag( $content );
// If the content doesn't contain any smart tags, wrap it in a table row, and return early.
// Don't go beyond this point if the content doesn't contain any smart tags.
if ( ! preg_match( '/{\w+}/', $processed_content ) ) {
return '| ' . $processed_content . ' |
';
}
// Split the content into lines and remove empty lines.
$lines = array_filter( explode( "\n", $content ), 'strlen' );
// Initialize an empty string to store the modified content.
$modified_content = '';
// Iterate through each line.
foreach ( $lines as $line ) {
// Trim the line.
$trimmed_line = $this->process_tag( trim( $line ) );
// Extract tags at the beginning of the line.
preg_match( '/^(?:\{[^}]+}\s*)+/i', $trimmed_line, $before_line_tags );
if ( ! empty( $before_line_tags[0] ) ) {
// Include the extracted tags at the beginning to the modified content.
$modified_content .= trim( $before_line_tags[0] );
// Remove the extracted tags from the trimmed line.
$trimmed_line = trim( substr( $trimmed_line, strlen( $before_line_tags[0] ) ) );
}
// Extract all smart tags from the remaining content.
preg_match_all( '/\{([^}]+)}/i', $trimmed_line, $after_line_tags );
// Remove the smart tags from the content.
$content_without_smart_tags = str_replace( $after_line_tags[0], '', $trimmed_line );
if ( ! empty( $content_without_smart_tags ) ) {
// Wrap the content without the smart tags in a new table row.
$modified_content .= '| ' . $content_without_smart_tags . ' |
';
}
if ( ! empty( $after_line_tags[0] ) ) {
// Move all smart tags to the end of the line after the closing
tag.
$modified_content .= implode( ' ', $after_line_tags[0] );
}
}
// Return the modified content.
return $modified_content;
}
/**
* Get the list of available email templates.
*
* Given a template name, this method will return the template data.
* If no template name is provided, all available templates will be returned.
*
* Templates will go through a conditional check to make sure they are available for the current plugin edition.
*
* @since 1.8.5
*
* @param string $template Template name. If empty, all available templates will be returned.
*
* @return array
*/
public static function get_available_templates( $template = '' ) {
$templates = self::get_all_templates();
// Filter the list of available email templates based on the edition of WPForms.
if ( ! wpforms()->is_pro() ) {
$templates = array_filter(
$templates,
static function ( $instance ) {
return ! $instance['is_pro'];
}
);
}
return $templates[ $template ] ?? $templates;
}
/**
* Get the list of all email templates.
*
* Given the name of a template, this method will return the template data.
* If the template is not found, all available templates will be returned.
*
* @since 1.8.5
*
* @param string $template Template name. If empty, all templates will be returned.
*
* @return array
*/
public static function get_all_templates( $template = '' ) {
$templates = [
'classic' => [
'name' => esc_html__( 'Classic', 'wpforms-lite' ),
'path' => __NAMESPACE__ . '\Templates\Classic',
'is_pro' => false,
],
'compact' => [
'name' => esc_html__( 'Compact', 'wpforms-lite' ),
'path' => __NAMESPACE__ . '\Templates\Compact',
'is_pro' => false,
],
'modern' => [
'name' => esc_html__( 'Modern', 'wpforms-lite' ),
'path' => 'WPForms\Pro\Emails\Templates\Modern',
'is_pro' => true,
],
'elegant' => [
'name' => esc_html__( 'Elegant', 'wpforms-lite' ),
'path' => 'WPForms\Pro\Emails\Templates\Elegant',
'is_pro' => true,
],
'tech' => [
'name' => esc_html__( 'Tech', 'wpforms-lite' ),
'path' => 'WPForms\Pro\Emails\Templates\Tech',
'is_pro' => true,
],
'none' => [
'name' => esc_html__( 'Plain Text', 'wpforms-lite' ),
'path' => __NAMESPACE__ . '\Templates\Plain',
'is_pro' => false,
],
];
// Make sure the current user can preview templates.
if ( wpforms_current_user_can() ) {
// Add a preview key to each template.
foreach ( $templates as $key => &$tmpl ) {
$tmpl['preview'] = wp_nonce_url(
add_query_arg(
[
'wpforms_email_preview' => '1',
'wpforms_email_template' => $key,
],
admin_url()
),
Preview::PREVIEW_NONCE_NAME
);
}
// Make sure to unset the reference to avoid unintended changes later.
unset( $tmpl );
}
return $templates[ $template ] ?? $templates;
}
/**
* Get multiple field formatted value.
*
* @since 1.9.0
*
* @param string $value Field value.
* @param int $field_id Field ID.
* @param array $fields List of fields.
* @param string $field_key Field key to get value from.
*
* @return string
*
* @noinspection PhpUnusedParameterInspection
*/
public function get_multi_field_formatted_value( string $value, int $field_id, array $fields, string $field_key ): string {
$field_type = $fields[ $field_id ]['type'] ?? '';
// Leave early if the field type is not a multi-field.
if ( ! in_array( $field_type, wpforms_get_multi_fields(), true ) ) {
return $value;
}
// Leave early if the template is set to plain text.
if ( Helpers::is_plain_text_template( $this->current_template ) ) {
// Replace
tags with line breaks.
return str_replace( '
', "\r\n", $value );
}
return str_replace( [ "\r\n", "\r", "\n" ], '
', $value );
}
/**
* Get the current template name.
*
* @since 1.9.3
*
* @return string
*/
public function get_current_template(): string {
return $this->current_template;
}
/**
* Get the current field template markup.
*
* @since 1.9.4
*
* @return string
*/
public function get_current_field_template(): string {
return $this->field_template;
}
}
Preview.php 0000644 00000031716 15171646462 0006722 0 ustar 00 current_template = sanitize_key( $_GET['wpforms_email_template'] );
$this->plain_text = $this->current_template === 'none';
$this->hooks();
$this->preview();
}
/**
* Hooks.
*
* @since 1.8.6
*/
private function hooks() {
add_filter( 'wpforms_emails_templates_notifications_get_header_image', [ $this, 'edit_current_template_header_image' ] );
add_filter( 'wpforms_emails_helpers_style_overrides_args', [ $this, 'edit_current_template_style_overrides' ] );
}
/**
* This filter is used to override the current email template header image.
*
* This is needed to make sure the preview link is able to reflect the
* changes made in the email template style settings without saving the settings page.
*
* @since 1.8.6
*
* @param array $header_image The current email template header image.
*
* @return array
*/
public function edit_current_template_header_image( $header_image ) {
// Get style overrides.
$overrides = $this->get_style_overrides();
// Leave early if no overrides are passed for the preview.
if ( empty( $header_image ) || empty( $overrides ) ) {
return $header_image;
}
// Check for the presence of light mode header image in the query string.
if ( isset( $overrides['email_header_image'] ) ) {
$header_image['url_light'] = esc_url_raw( $overrides['email_header_image'] );
// Check for the presence of light mode header image size in the query string.
if ( ! empty( $overrides['email_header_image_size'] ) ) {
$header_image['size_light'] = sanitize_text_field( $overrides['email_header_image_size'] );
}
}
// Check for the presence of dark mode header image in the query string.
if ( isset( $overrides['email_header_image_dark'] ) ) {
$header_image['url_dark'] = esc_url_raw( $overrides['email_header_image_dark'] );
if ( ! empty( $overrides['email_header_image_size_dark'] ) ) {
$header_image['size_dark'] = sanitize_text_field( $overrides['email_header_image_size_dark'] );
}
}
return $header_image;
}
/**
* This filter is used to override the current email template style overrides.
*
* This is needed to make sure the preview link is able to reflect the
* changes made in the email template style settings without saving the settings page.
*
* @since 1.8.6
*
* @param array $styles The current email template styles.
*
* @return array
*/
public function edit_current_template_style_overrides( $styles ) {
// Get style overrides.
$overrides = $this->get_style_overrides();
// Leave early if no overrides are passed for the preview.
if ( empty( $overrides ) ) {
return $styles;
}
// Check for the presence of light mode background color in the query string.
if ( ! empty( $overrides['email_background_color'] ) ) {
$styles['email_background_color'] = sanitize_hex_color( $overrides['email_background_color'] );
}
// Check for the presence of dark mode background color in the query string.
if ( ! empty( $overrides['email_background_color_dark'] ) ) {
$styles['email_background_color_dark'] = sanitize_hex_color( $overrides['email_background_color_dark'] );
}
// Leave early if the user has the Lite version.
if ( ! wpforms()->is_pro() ) {
// The only allowed override for the Lite version is the header image size.
// This is needed to make sure the preview link is able to reflect the
// changes made in the email template style settings without saving the settings page.
if ( empty( $overrides['email_header_image_size'] ) ) {
// Return the styles if no header image size override is passed for the preview.
return $styles;
}
// Override and process the header image size.
$overrides = [ 'email_header_image_size' => $overrides['email_header_image_size'] ];
return $this->process_allowed_overrides( $styles, $overrides );
}
// Process allowed overrides using a separate function.
return $this->process_allowed_overrides( $styles, $overrides );
}
/**
* Get style overrides.
*
* @since 1.8.6
*
* @return array
*/
private function get_style_overrides() {
// phpcs:disable WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
// Check if the 'wpforms_email_style_overrides' parameter is empty.
if ( empty( $_GET['wpforms_email_style_overrides'] ) ) {
return [];
}
// Retrieve and unslash the encoded style overrides from the query string.
$style_overrides = wp_unslash( $_GET['wpforms_email_style_overrides'] );
// phpcs:enable WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$overrides = '';
$overrides_len = strlen( $style_overrides );
// Decode the overrides.
// This is needed because the overrides are encoded before being passed in the query string.
for ( $i = 0; $i < $overrides_len; $i++ ) {
$overrides .= chr( ord( $style_overrides[ $i ] ) ^ self::XOR_KEY );
}
// Return the decoded overrides as an associative array.
return json_decode( $overrides, true );
}
/**
* Process allowed style overrides.
*
* @since 1.8.6
*
* @param array $styles Current styles.
* @param array $overrides Style overrides.
*
* @return array Updated styles.
*/
private function process_allowed_overrides( $styles, $overrides ) {
// Leave early if no overrides are passed for the preview.
if ( empty( $overrides ) ) {
return $styles;
}
// Define an array of allowed query parameters.
$allowed_overrides = [
'email_body_color',
'email_text_color',
'email_links_color',
'email_typography',
'email_header_image_size',
'email_body_color_dark',
'email_text_color_dark',
'email_links_color_dark',
'email_typography_dark',
'email_header_image_size_dark',
];
// Loop through allowed parameters and update $overrides if present in the query string.
foreach ( $allowed_overrides as $param ) {
// Leave early if the parameter is not present in the query string.
if ( empty( $overrides[ $param ] ) ) {
continue;
}
$styles = $this->process_override( $param, $styles, $overrides );
}
return $styles;
}
/**
* Process a specific style override.
*
* @since 1.8.6
*
* @param string $param Style parameter.
* @param array $styles Current styles.
* @param array $overrides Style overrides.
*
* @return array Updated styles.
*/
private function process_override( $param, $styles, $overrides ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.MaxExceeded
// Use a switch to handle specific cases.
switch ( $param ) {
case 'email_body_color':
case 'email_text_color':
case 'email_links_color':
case 'email_body_color_dark':
case 'email_text_color_dark':
case 'email_links_color_dark':
$styles[ $param ] = sanitize_hex_color( $overrides[ $param ] );
break;
case 'email_typography':
case 'email_typography_dark':
$styles[ $param ] = Helpers::get_template_typography( sanitize_text_field( $overrides[ $param ] ) );
break;
case 'email_header_image_size':
$header_image_size = Helpers::get_template_header_image_size( sanitize_text_field( $overrides[ $param ] ) );
$styles['header_image_max_width'] = $header_image_size['width'];
$styles['header_image_max_height'] = $header_image_size['height'];
break;
case 'email_header_image_size_dark':
$header_image_size_dark = Helpers::get_template_header_image_size( sanitize_text_field( $overrides[ $param ] ) );
$styles['header_image_max_width_dark'] = $header_image_size_dark['width'];
$styles['header_image_max_height_dark'] = $header_image_size_dark['height'];
break;
}
return $styles;
}
/**
* Preview email template.
*
* @since 1.8.5
*/
private function preview() {
$template = Notifications::get_available_templates( $this->current_template );
/**
* Filter the email template to be previewed.
*
* @since 1.8.5
*
* @param array $template Email template.
*/
$template = (array) apply_filters( 'wpforms_emails_preview_template', $template );
// Redirect to the email settings page if the template is not set.
if ( ! isset( $template['path'] ) || ! class_exists( $template['path'] ) ) {
wp_safe_redirect(
add_query_arg(
[
'page' => 'wpforms-settings',
'view' => 'email',
],
admin_url( 'admin.php' )
)
);
exit;
}
// Set the email template, i.e. WPForms\Emails\Templates\Classic.
$template = new $template['path']( '', true );
// Set the field template.
// This is used to replace the placeholders in the email template.
$this->field_template = $template->get_field_template();
// Set the email template fields.
$template->set_field( $this->get_placeholder_message() );
// Get the email template content.
$content = $template->get();
// Return if the template is empty.
if ( ! $content ) {
return;
}
// Echo the email template content.
echo $content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
exit; // No need to continue. WordPress will die() after this.
}
/**
* Get preview content.
*
* @since 1.8.5
*
* @return string Placeholder message.
*/
private function get_placeholder_message() {
$this->fields = [
[
'type' => 'name',
'name' => __( 'Name', 'wpforms-lite' ),
'value' => 'Sullie Eloso',
],
[
'type' => 'email',
'name' => __( 'Email', 'wpforms-lite' ),
'value' => 'sullie@wpforms.com',
],
[
'type' => 'textarea',
'name' => __( 'Comment or Message', 'wpforms-lite' ),
'value' => "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Odio ut sem nulla pharetra diam sit amet. Sed risus pretium quam vulputate dignissim suspendisse in est ante. Risus ultricies tristique nulla aliquet enim tortor at auctor. Nisl tincidunt eget nullam non nisi est sit amet facilisis. Duis at tellus at urna condimentum mattis pellentesque id nibh. Curabitur vitae nunc sed velit dignissim.\r\n\r\nLeo urna molestie at elementum eu facilisis sed odio. Scelerisque mauris pellentesque pulvinar pellentesque habitant morbi. Volutpat maecenas volutpat blandit aliquam. Libero id faucibus nisl tincidunt. Et malesuada fames ac turpis egestas.",
],
];
// Early return if the template is plain text.
if ( $this->plain_text ) {
return $this->process_plain_message();
}
return $this->process_html_message();
}
/**
* Process the HTML email message.
*
* @since 1.8.5
*
* @return string
*/
private function process_html_message() {
$message = '';
foreach ( $this->fields as $field ) {
$message .= str_replace(
[ '{field_type}', '{field_name}', '{field_value}', "\r\n" ],
[ $field['type'], $field['name'], $field['value'], '
' ],
$this->field_template
);
}
return $message;
}
/**
* Process the plain text email message.
*
* @since 1.8.5
*
* @return string
*/
private function process_plain_message() {
$message = '';
foreach ( $this->fields as $field ) {
$message .= '--- ' . $field['name'] . " ---\r\n\r\n" . str_replace( [ "\n", "\r" ], '', $field['value'] ) . "\r\n\r\n";
}
return nl2br( $message );
}
}
Styler.php 0000644 00000005043 15171646462 0006555 0 ustar 00 email = $email;
$this->style_templates = is_array( $style_templates ) ? $style_templates : [];
$this->style_overrides = is_array( $style_overrides ) ? $style_overrides : [];
}
/**
* Template style overrides.
*
* @since 1.5.4
*
* @return array
*/
protected function get_style_overrides() {
$defaults = Helpers::get_current_template_style_overrides();
$overrides = \wp_parse_args( $this->style_overrides, $defaults );
return \apply_filters( 'wpforms_emails_mailer_get_style_overrides', $overrides, $this );
}
/**
* Locate template name matching styles.
*
* @since 1.5.4
*
* @param string $name Template file name part.
*
* @return string
*/
protected function get_styles( $name = 'style' ) {
if ( ! \array_key_exists( $name, $this->style_templates ) ) {
return '';
}
return Templates::get_html(
$this->style_templates[ $name ],
$this->get_style_overrides(),
true
);
}
/**
* Final processing of the template markup.
*
* @since 1.5.4
*/
public function process_markup() {
$this->styled_email = ( new CssToInlineStyles() )->convert( $this->email, $this->get_styles() );
$queries = '\n";
// Inject media queries, CssToInlineStyles strips them.
$this->styled_email = \str_replace( '', $queries, $this->styled_email );
}
/**
* Get an email with inline styles.
*
* @since 1.5.4
*
* @return string
*/
public function get() {
if ( empty( $this->styled_email ) ) {
$this->process_markup();
}
return $this->styled_email;
}
}
Summaries.php 0000644 00000034120 15171646462 0007236 0 ustar 00 hooks();
$summaries_disabled = $this->is_disabled();
if ( $summaries_disabled && wp_next_scheduled( 'wpforms_email_summaries_cron' ) ) {
wp_clear_scheduled_hook( 'wpforms_email_summaries_cron' );
}
if ( ! $summaries_disabled && ! wp_next_scheduled( 'wpforms_email_summaries_cron' ) ) {
// Since v1.9.1 we use a single event and manually reoccur it
// because a recurring event cannot guarantee
// its firing at the same time during WP_CLI execution.
wp_schedule_single_event( $this->get_next_launch_time(), 'wpforms_email_summaries_cron' );
}
}
/**
* Get the instance of a class and store it in itself.
*
* @since 1.5.4
*/
public static function get_instance() {
static $instance;
if ( ! $instance ) {
$instance = new self();
}
return $instance;
}
/**
* Email Summaries hooks.
*
* @since 1.5.4
*/
public function hooks() {
add_filter( 'wpforms_settings_defaults', [ $this, 'disable_summaries_setting' ] );
add_action( 'wpforms_settings_updated', [ $this, 'deregister_fetch_info_blocks_task' ] );
// Leave early if Email Summaries are disabled in settings.
if ( $this->is_disabled() ) {
return;
}
add_action( 'init', [ $this, 'preview' ] );
add_action( 'wpforms_email_summaries_cron', [ $this, 'cron' ] );
add_filter( 'wpforms_tasks_get_tasks', [ $this, 'register_fetch_info_blocks_task' ] );
}
/**
* Check if Email Summaries are disabled in settings.
*
* @since 1.5.4
*
* @return bool
*/
protected function is_disabled(): bool {
/**
* Allows to modify whether Email Summaries are disabled in settings.
*
* @since 1.5.4
*
* @param bool $is_disabled True if Email Summaries are disabled in settings. False by default.
*/
return (bool) apply_filters( 'wpforms_emails_summaries_is_disabled', (bool) wpforms_setting( 'email-summaries-disable', false ) );
}
/**
* Add "Disable Email Summaries" to WPForms settings.
*
* @since 1.5.4
*
* @param array $settings WPForms settings.
*
* @return mixed
*/
public function disable_summaries_setting( $settings ) {
/** This filter is documented in wpforms/src/Emails/Summaries.php */
if ( (bool) apply_filters( 'wpforms_emails_summaries_is_disabled', false ) ) {
return $settings;
}
$url = wp_nonce_url(
add_query_arg(
[
'wpforms_email_template' => 'summary',
'wpforms_email_preview' => '1',
],
admin_url()
),
Preview::PREVIEW_NONCE_NAME
);
$desc = esc_html__( 'Disable Email Summaries weekly delivery.', 'wpforms-lite' );
if ( ! $this->is_disabled() ) {
$desc .= ' ' . esc_html__( 'View Email Summary Example', 'wpforms-lite' ) . '.';
}
// Get the uninstall data setting.
$uninstall_data = $settings['misc']['uninstall-data'];
// Remove the uninstall data setting.
unset( $settings['misc']['uninstall-data'] );
// Add the email summaries setting.
$settings['misc']['email-summaries-disable'] = [
'id' => 'email-summaries-disable',
'name' => esc_html__( 'Disable Email Summaries', 'wpforms-lite' ),
'desc' => $desc,
'type' => 'toggle',
'status' => true,
];
// Add the uninstall data setting to the end.
$settings['misc']['uninstall-data'] = $uninstall_data;
return $settings;
}
/**
* Preview Email Summary.
*
* @since 1.5.4
*/
public function preview() {
// Leave early if the current request is not a preview for the summaries email template.
if ( ! $this->is_preview() ) {
return;
}
// Get form entries.
$entries = $this->get_entries();
$args = [
'body' => [
'overview' => $this->get_calculation_overview( $entries ),
'entries' => $this->format_trends_for_display( $entries ),
'has_trends' => $this->entries_has_trends( $entries ),
'notification_block' => ( new NotificationBlocks() )->get_block(),
'info_block' => ( new InfoBlocks() )->get_next(),
'icons' => $this->get_icons_url(),
],
];
$template = ( new Templates\Summary() )->set_args( $args );
/**
* Filters the summaries email template.
*
* @since 1.5.4
*
* @param Templates\Summary $template Default summaries email template.
*/
$template = apply_filters( 'wpforms_emails_summaries_template', $template );
$content = $template->get();
if ( Helpers::is_plain_text_template() ) {
$content = wpautop( $content );
}
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $content;
exit;
}
/**
* Get next cron occurrence date.
*
* @since 1.5.4
* @deprecated 1.9.1
*
* @return int
*/
protected function get_first_cron_date_gmt(): int {
_deprecated_function( __METHOD__, '1.9.1 of the WPForms plugin', __CLASS__ . '::get_next_launch_time()' );
return $this->get_next_launch_time();
}
/**
* Get next Monday 2p.m with WordPress offset.
*
* @since 1.9.1
*
* @return int
*/
protected function get_next_launch_time(): int {
$datetime = date_create( 'now', wp_timezone() );
$now_plus_week = time() + constant( 'WEEK_IN_SECONDS' );
if ( ! $datetime ) {
return $now_plus_week;
}
$hours = 14;
// If today is Monday and the current time is less than 2 p.m.,
// we can launch the cron for today.
if (
(int) $datetime->format( 'N' ) !== 1 ||
(int) $datetime->format( 'H' ) >= $hours
) {
try {
$datetime->modify( 'next monday' );
} catch ( Exception $e ) {
return $now_plus_week;
}
}
$datetime->setTime( $hours, 0 );
$timestamp = $datetime->getTimestamp();
return $timestamp > 0 ? $timestamp : $now_plus_week;
}
/**
* Add custom Email Summaries cron schedule.
*
* @since 1.5.4
* @deprecated 1.9.1
*
* @param array $schedules WP cron schedules.
*
* @return array
*/
public function add_weekly_cron_schedule( $schedules ) {
_deprecated_function( __METHOD__, '1.9.1 of the WPForms plugin' );
$schedules['wpforms_email_summaries_weekly'] = [
'interval' => $this->get_next_launch_time() - time(),
'display' => esc_html__( 'Weekly WPForms Email Summaries', 'wpforms-lite' ),
];
return $schedules;
}
/**
* Email Summaries cron callback.
*
* @since 1.5.4
*/
public function cron() {
$entries = $this->get_entries();
// Email won't be sent if there are no form entries.
if ( empty( $entries ) ) {
return;
}
$notification = new NotificationBlocks();
$notification_block = $notification->get_block();
$info_blocks = new InfoBlocks();
$next_block = $info_blocks->get_next();
$args = [
'body' => [
'overview' => $this->get_calculation_overview( $entries ),
'entries' => $this->format_trends_for_display( $entries ),
'has_trends' => $this->entries_has_trends( $entries ),
'notification_block' => $notification_block,
'info_block' => $next_block,
'icons' => $this->get_icons_url(),
],
];
$template = ( new Templates\Summary() )->set_args( $args );
/** This filter is documented in preview() method above. */
$template = apply_filters( 'wpforms_emails_summaries_template', $template );
$content = $template->get();
if ( ! $content ) {
return;
}
$parsed_home_url = wp_parse_url( home_url() );
$site_domain = $parsed_home_url['host'];
if ( is_multisite() && isset( $parsed_home_url['path'] ) ) {
$site_domain .= $parsed_home_url['path'];
}
$subject = sprintf(
/* translators: %s - site domain. */
esc_html__( 'Your Weekly WPForms Summary for %s', 'wpforms-lite' ),
$site_domain
);
/**
* Filters the summaries email subject.
*
* @since 1.5.4
*
* @param string $subject Default summaries email subject.
*/
$subject = apply_filters( 'wpforms_emails_summaries_cron_subject', $subject );
/**
* Filters the summaries recipient email address.
*
* @since 1.5.4
*
* @param string $option Default summaries recipient email address.
*/
$to_email = apply_filters( 'wpforms_emails_summaries_cron_to_email', get_option( 'admin_email' ) );
$sent = ( new Mailer() )
->template( $template )
->subject( $subject )
->to_email( $to_email )
->send();
if ( $sent === true ) {
$info_blocks->register_sent( $next_block );
// Cache the notification block shown to avoid showing it again in the future.
$notification->maybe_remember_shown_block( $notification_block );
}
}
/**
* Get form entries.
*
* @since 1.5.4
*
* @return array
*/
protected function get_entries(): array {
// The return value is intentionally left empty, as each email summary
// depending on the plugin edition Lite/Pro will have different implementation.
return [];
}
/**
* Get calculation overview.
*
* @since 1.8.8
*
* @param array $entries Form entries.
*
* @return array
*/
private function get_calculation_overview( $entries ): array {
// Check if the entries array is empty.
if ( empty( $entries ) ) {
return [];
}
// Get the sum of 'count' index in all entries.
$sum_current = array_sum( array_column( $entries, 'count' ) );
// Choose a specific 'form_id' to check if 'count_previous_week' index exists.
$sample_form_id = key( $entries );
// Check if 'count_previous_week' index doesn't exist and return early.
if ( ! isset( $entries[ $sample_form_id ]['count_previous_week'] ) ) {
return [];
}
// Get the sum of 'count_previous_week' index in all entries.
$sum_previous_week = array_sum( array_column( $entries, 'count_previous_week' ) );
// Check if the sum of counts from the previous week is 0.
// If so, return the sum of counts from the current week and trends as "+100%".
if ( $sum_previous_week === 0 ) {
return [
'total' => $sum_current,
'trends' => $this->format_trends_for_display( $sum_current === 0 ? 0 : 100 ),
];
}
// Calculate trends based on the sum of counts from the current week and the previous week.
$trends = round( ( $sum_current - $sum_previous_week ) / $sum_previous_week * 100 );
// Return an array with the total and trends.
return [
'total' => $sum_current,
'trends' => $this->format_trends_for_display( $trends ),
];
}
/**
* Register Action Scheduler task to fetch and cache Info Blocks.
*
* @since 1.6.4
*
* @param \WPForms\Tasks\Task[] $tasks List of task classes.
*
* @return array
*/
public static function register_fetch_info_blocks_task( $tasks ): array {
$tasks[] = FetchInfoBlocksTask::class;
return $tasks;
}
/**
* Deregister Action Scheduler task to fetch and cache Info Blocks.
*
* @since 1.6.4
*/
public function deregister_fetch_info_blocks_task() {
if ( ! $this->is_disabled() ) {
return;
}
// Deregister the task.
( new FetchInfoBlocksTask() )->cancel();
// Delete last run time record.
delete_option( FetchInfoBlocksTask::LAST_RUN );
// Remove the cache file if it exists.
$file_name = ( new InfoBlocks() )->get_cache_file_path();
if ( file_exists( $file_name ) ) {
// phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.unlink_unlink
@unlink( $file_name );
}
}
/**
* Check if the current request is a preview for the summaries email template.
*
* @since 1.8.8
*
* @return bool
*/
private function is_preview(): bool {
// Leave if the current user can't access.
if ( ! wpforms_current_user_can() ) {
return false;
}
// phpcs:disable WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
// Leave early if nonce verification failed.
if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( $_GET['_wpnonce'] ), Preview::PREVIEW_NONCE_NAME ) ) {
return false;
}
// Leave early if preview is not requested.
if ( ! isset( $_GET['wpforms_email_preview'], $_GET['wpforms_email_template'] ) ) {
return false;
}
// Leave early if preview is not requested for the summaries template.
if ( $_GET['wpforms_email_template'] !== 'summary' ) {
return false;
}
// phpcs:enable WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
return true;
}
/**
* Format entries trends for display.
*
* This function takes an array of entries and formats the 'trends' value for display.
*
* @since 1.8.8
*
* @param array|int $input Input data to format.
*
* @return array|string
*/
private function format_trends_for_display( $input ) {
// If input is a numeric value, format and return it.
if ( is_numeric( $input ) ) {
return sprintf( '%s%s%%', $input >= 0 ? '+' : '', $input );
}
// Loop through entries and format 'trends' values.
foreach ( $input as &$form ) {
// Leave early if 'trends' index doesn't exist.
if ( ! isset( $form['trends'] ) ) {
continue;
}
// Add percent sign to trends and + sign if value greater than zero.
$form['trends'] = sprintf( '%s%s%%', $form['trends'] >= 0 ? '+' : '', $form['trends'] );
}
return $input;
}
/**
* Check if trends can be displayed for the given entries.
*
* @since 1.8.8
*
* @param array $entries The entries data.
*
* @return bool
*/
private function entries_has_trends( array $entries ): bool {
// Return false if entries array is empty.
if ( empty( $entries ) ) {
return false;
}
// Check if at least one array item has the 'trends' key.
foreach ( $entries as $entry ) {
if ( isset( $entry['trends'] ) ) {
return true;
}
}
return false;
}
/**
* Get icons URL.
* Primarily used in the HTML version of the email template.
*
* @since 1.8.8
*
* @return array
*/
private function get_icons_url(): array {
$base_url = WPFORMS_PLUGIN_URL . 'assets/images/email/';
return [
'overview' => $base_url . 'icon-overview.png',
'upward' => $base_url . 'icon-upward.png',
'downward' => $base_url . 'icon-downward.png',
'notification_block' => $base_url . 'notification-block-icon.png',
'info_block' => $base_url . 'info-block-icon.png',
];
}
}
Templates/Classic.php 0000644 00000000450 15171646462 0010607 0 ustar 00 set_message( $message );
$this->plain_text = Helpers::is_plain_text_template();
$this->set_initial_args();
}
/**
* Set initial arguments to use in a template.
*
* @since 1.5.4
*/
public function set_initial_args() {
$header_args = [
'title' => \esc_html__( 'WPForms', 'wpforms-lite' ),
];
if ( ! $this->plain_text ) {
$header_args['header_image'] = $this->get_header_image();
}
$args = [
'header' => $header_args,
'body' => [ 'message' => $this->get_message() ],
'footer' => [],
'style' => [],
];
$args = \apply_filters( 'wpforms_emails_templates_general_set_initial_args', $args, $this );
$this->set_args( $args );
}
/**
* Get the template slug.
*
* @since 1.5.4
*
* @return string
*/
public function get_slug() {
return static::TEMPLATE_SLUG;
}
/**
* Get the template parent slug.
*
* @since 1.5.4
*
* @return string
*/
public function get_parent_slug() {
return self::TEMPLATE_SLUG;
}
/**
* Get the message.
*
* @since 1.5.4
*
* @return string
*/
public function get_message() {
return \apply_filters( 'wpforms_emails_templates_general_get_message', $this->message, $this );
}
/**
* Get the dynamic tags.
*
* @since 1.5.4
*
* @return array
*/
public function get_tags() {
return \apply_filters( 'wpforms_emails_templates_general_get_tags', $this->tags, $this );
}
/**
* Get header/footer/body arguments
*
* @since 1.5.4
*
* @param string $type Header/footer/body.
*
* @return array
*/
public function get_args( $type ) {
if ( ! empty( $type ) ) {
return isset( $this->args[ $type ] ) ? apply_filters( 'wpforms_emails_templates_general_get_args_' . $type, $this->args[ $type ], $this ) : [];
}
return apply_filters( 'wpforms_emails_templates_general_get_args', $this->args, $this );
}
/**
* Set email message.
*
* @since 1.5.4
*
* @param string $message Email message.
*
* @return General
*/
public function set_message( $message ) {
$message = \apply_filters( 'wpforms_emails_templates_general_set_message', $message, $this );
if ( ! \is_string( $message ) ) {
return $this;
}
$this->message = $message;
return $this;
}
/**
* Set the dynamic tags.
*
* @since 1.5.4
*
* @param array $tags Tags to set.
*
* @return General
*/
public function set_tags( $tags ) {
$tags = \apply_filters( 'wpforms_emails_templates_general_set_tags', $tags, $this );
if ( ! \is_array( $tags ) ) {
return $this;
}
$this->tags = $tags;
return $this;
}
/**
* Set header/footer/body/style arguments to use in a template.
*
* @since 1.5.4
*
* @param array $args Arguments to set.
* @param bool $merge Merge the arguments with existing once or replace.
*
* @return General
*/
public function set_args( $args, $merge = true ) {
$args = \apply_filters( 'wpforms_emails_templates_general_set_args', $args, $this );
if ( empty( $args ) || ! \is_array( $args ) ) {
return $this;
}
foreach ( $args as $type => $value ) {
if ( ! \is_array( $value ) ) {
continue;
}
if ( ! isset( $this->args[ $type ] ) || ! \is_array( $this->args[ $type ] ) ) {
$this->args[ $type ] = [];
}
$this->args[ $type ] = $merge ? \array_merge( $this->args[ $type ], $value ) : $value;
}
return $this;
}
/**
* Process and replace any dynamic tags.
*
* @since 1.5.4
*
* @param string $content Content to make replacements in.
*
* @return string
*/
public function process_tags( $content ) {
$tags = $this->get_tags();
if ( empty( $tags ) ) {
return $content;
}
foreach ( $tags as $tag => $value ) {
$content = \str_replace( $tag, $value, $content );
}
return $content;
}
/**
* Conditionally modify email template name.
*
* @since 1.5.4
*
* @param string $name Base template name.
*
* @return string
*/
protected function get_full_template_name( $name ) {
$name = \sanitize_file_name( $name );
if ( $this->plain_text ) {
$name .= '-plain';
}
$template = 'emails/' . $this->get_slug() . '-' . $name;
if ( ! Templates::locate( $template . '.php' ) ) {
$template = 'emails/' . $this->get_parent_slug() . '-' . $name;
}
return \apply_filters( 'wpforms_emails_templates_general_get_full_template_name', $template, $this );
}
/**
* Get header image URL from settings.
*
* @since 1.5.4
*
* @return array
*/
protected function get_header_image() {
/**
* Additional 'width' key with an integer value can be added to $img array to control image's width in pixels.
* This setting helps to scale an image in some versions of MS Outlook and old email clients.
* Percentage 'width' values have no effect in MS Outlook and will be sanitized as integer by an email template..
*
* Example:
*
* $img = [
* 'url' => \wpforms_setting( 'email-header-image' ),
* 'width' => 150,
* ];
*
*
* To set percentage values for the modern email clients, use $this->set_args() method:
*
* $this->set_args(
* [
* 'style' => [
* 'header_image_max_width' => '45%',
* ],
* ]
*);
*
* Both pixel and percentage approaches work well with 'wpforms_emails_templates_general_get_header_image' filter or this class extension.
*/
$img = [
'url' => wpforms_setting( 'email-header-image' ),
'size' => wpforms_setting( 'email-header-image-size', 'medium' ),
];
return \apply_filters( 'wpforms_emails_templates_general_get_header_image', $img, $this );
}
/**
* Get content part HTML.
*
* @since 1.5.4
*
* @param string $name Name of the content part.
*
* @return string
*/
protected function get_content_part( $name ) {
if ( ! \is_string( $name ) ) {
return '';
}
$html = Templates::get_html(
$this->get_full_template_name( $name ),
$this->get_args( $name ),
true
);
return \apply_filters( 'wpforms_emails_templates_general_get_content_part', $html, $name, $this );
}
/**
* Assemble all content parts in an array.
*
* @since 1.5.4
*
* @return array
*/
protected function get_content_parts() {
$parts = [
'header' => $this->get_content_part( 'header' ),
'body' => $this->get_content_part( 'body' ),
'footer' => $this->get_content_part( 'footer' ),
];
return \apply_filters( 'wpforms_emails_templates_general_get_content_parts', $parts, $this );
}
/**
* Apply inline styling and save email content.
*
* @since 1.5.4
*
* @param string $content Content with no styling applied.
*/
protected function save_styled( $content ) {
if ( empty( $content ) ) {
$this->content = '';
return;
}
if ( $this->plain_text ) {
$this->content = \wp_strip_all_tags( $content );
return;
}
$style_templates = [
'style' => $this->get_full_template_name( 'style' ),
'queries' => $this->get_full_template_name( 'queries' ),
];
$styler = new Styler( $content, $style_templates, $this->get_args( 'style' ) );
$this->content = \apply_filters( 'wpforms_emails_templates_general_save_styled_content', $styler->get(), $this );
}
/**
* Build an email including styling.
*
* @since 1.5.4
*
* @param bool $force Rebuild the content if it was already built and saved.
*/
protected function build( $force = false ) {
if ( $this->content && ! $force ) {
return;
}
$content = \implode( $this->get_content_parts() );
if ( empty( $content ) ) {
return;
}
$content = $this->process_tags( $content );
if ( ! $this->plain_text ) {
$content = \make_clickable( $content );
}
$content = \apply_filters( 'wpforms_emails_templates_general_build_content', $content, $this );
$this->save_styled( $content );
}
/**
* Return final email.
*
* @since 1.5.4
*
* @param bool $force Rebuild the content if it was already built and saved.
*
* @return string
*/
public function get( $force = false ) {
$this->build( $force );
return $this->content;
}
}
Templates/Notifications.php 0000644 00000005041 15171646462 0012040 0 ustar 00 is_preview = $is_preview;
$this->plain_text = ! $is_preview && Helpers::is_plain_text_template( $current_template );
// Call the parent method after to set the correct header properties.
$this->set_initial_args();
}
/**
* Set template message.
*
* @since 1.8.5
*
* @param string $message Message.
*/
public function set_field( $message ) {
// Leave if not a string.
if ( ! is_string( $message ) ) {
return;
}
// Set the template message.
$this->set_args(
[
'body' => [
'message' => $message,
],
]
);
}
/**
* Get field template.
*
* @since 1.8.5
*
* @return string
*/
public function get_field_template() {
return $this->get_content_part( 'field' );
}
/**
* Get header image URL from settings.
* This method has been overridden to add support for filtering the returned image.
*
* @since 1.8.6
*
* @return array
*/
protected function get_header_image() {
// Retrieve header image URL and size from WPForms settings.
$img = [
'url_light' => wpforms_setting( 'email-header-image' ),
'size_light' => wpforms_setting( 'email-header-image-size', 'medium' ),
'url_dark' => wpforms_setting( 'email-header-image-dark' ),
'size_dark' => wpforms_setting( 'email-header-image-size-dark', 'medium' ),
];
/**
* Filter the email header image.
*
* @since 1.8.6
*
* @param array $img Email header image.
* @param Notifications $this Current instance of the class.
*/
return (array) apply_filters( 'wpforms_emails_templates_notifications_get_header_image', $img, $this );
}
}
Templates/Plain.php 0000644 00000003015 15171646462 0010271 0 ustar 00 plain_text = true;
// Call the parent method after to set the correct header properties.
$this->set_initial_args();
}
/**
* Maybe prepare the content for the preview.
*
* @since 1.8.5
*
* @param string $content Content with no styling applied.
*/
protected function save_styled( $content ) {
// Leave early if we are not in preview mode.
if ( ! $this->is_preview ) {
// Call the parent method to handle the proper styling.
parent::save_styled( $content );
return;
}
// Leave if content is empty.
if ( empty( $content ) ) {
$this->content = '';
return;
}
// Stop here as we don't need to apply any styling for the preview.
// The only exception here is to keep the break tags to maintain the readability.
$this->content = wp_kses( $content, [ 'br' => [] ] );
}
}
Templates/Summary.php 0000644 00000005305 15171646462 0010667 0 ustar 00 set_args( $this->maybe_revert_background_color() );
}
/**
* Get header image URL from settings.
*
* @since 1.5.4
*
* @return array
*/
protected function get_header_image() {
$legacy_header_image = $this->maybe_revert_header_image();
// Bail early, if legacy behavior is enabled.
if ( ! empty( $legacy_header_image ) ) {
return $legacy_header_image;
}
// Set specific WPForms logo width in pixels for MS Outlook and old email clients.
return [
'url_light' => WPFORMS_PLUGIN_URL . 'assets/images/logo.png',
'url_dark' => WPFORMS_PLUGIN_URL . 'assets/images/logo-negative.png',
];
}
/**
* Checks if legacy header image overrides should be applied.
*
* @since 1.8.8
*
* @return array
*/
private function maybe_revert_header_image() {
/**
* This filter is designed to restore the legacy behavior, reverting the WPForms logo and template background color
* to values defined in the WPForms → Settings → Email tab.
*
* @since 1.8.8
*
* @param bool $revert_legacy_style_overrides Whether to apply legacy style overrides.
*/
if ( ! (bool) apply_filters( 'wpforms_emails_templates_summary_revert_legacy_style_overrides', false ) ) {
return [];
}
$header_image = wpforms_setting( 'email-header-image' );
// Bail early, if no custom header image if set.
if ( empty( $header_image ) ) {
return [];
}
return [ 'url_light' => esc_url( $header_image ) ];
}
/**
* Checks if legacy background color overrides should be applied.
*
* @since 1.8.8
*
* @return array
*/
private function maybe_revert_background_color() {
/**
* This filter is designed to restore the legacy behavior, reverting the WPForms logo and template background color
* to values defined in the WPForms → Settings → Email tab.
*
* @since 1.8.8
*
* @param bool $revert_legacy_style_overrides Whether to apply legacy style overrides.
*/
if ( ! (bool) apply_filters( 'wpforms_emails_templates_summary_revert_legacy_style_overrides', false ) ) {
return [ 'style' => [ 'email_background_color' => '' ] ];
}
return [
'style' => [
'email_background_color' => wpforms_setting( 'email-background-color', '#e9eaec' ),
],
];
}
}