// phpcs:ignore Generic.Commenting.DocComment.MissingShort
/** @noinspection PhpDeprecationInspection */
namespace WPForms\Emails;
use WPForms\SmartTags\SmartTag\SmartTag;
use WPForms\Tasks\Actions\EntryEmailsTask;
use WPForms\Emails\Templates\General; // phpcs:ignore WPForms.PHP.UseStatement.UnusedUseStatement
use WPForms\Pro\Emails\Templates\Modern;
use WPForms\Pro\Emails\Templates\Elegant;
use WPForms\Pro\Emails\Templates\Tech;
* Used to send email notifications.
class Notifications extends Mailer {
* List of submitted fields.
* Notification ID that is currently being processed.
public $notification_id = '';
* Current email template.
private $current_template;
protected $field_template = '';
* Default email template name.
public const DEFAULT_TEMPLATE = 'classic';
* Plain/Text email template name.
public const PLAIN_TEMPLATE = 'none';
* Legacy email template name.
public const LEGACY_TEMPLATE = 'default';
* Whether the email is being sent to a PDF.
public $rendering_context;
* Get the instance of a class.
public static function get_instance() {
* This method will initialize the class.
* Maybe use the old class for backward compatibility.
* The old class might be removed in the future.
* @param string $template Email template name.
* @param string $rendering_context Where the email is being rendered, 'mail' or 'pdf'.
* @return $this|WPForms_WP_Emails
* @noinspection PhpDeprecationInspection
public function init( string $template = '', string $rendering_context = 'mail' ) {
$this->rendering_context = $rendering_context;
// 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' ) ) {
// In case the 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.
private function hooks(): void {
add_filter( 'wpforms_smart_tags_formatted_field_value', [ $this, 'get_multi_field_formatted_value' ], 10, 4 );
add_filter( 'wpforms_smarttags_process_value', [ self::class, 'filter_smarttags_process_value' ], PHP_INT_MAX, 6 );
* Maybe send an email right away or schedule it.
* @return bool Whether the email was sent successfully.
// 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 ) {
// Don't send anything if emails have been disabled.
if ( $this->is_email_disabled() ) {
[ $to, $subject, $message ] = func_get_args();
// Don't send it if the email address is invalid.
if ( ! is_email( $to ) ) {
* 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.
* @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.
* 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.
* @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',
'headers' => $this->get_headers(),
'attachments' => $attachments,
// 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'] );
$entry_obj = wpforms()->obj( 'entry' );
* 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.
* @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',
$entry_obj ? $entry_obj->get( $this->entry_id ) : [],
// Send the email immediately.
if ( $send_same_process || ! empty( $this->form_data['settings']['disable_entries'] ) ) {
$results = parent::send();
$results = (bool) ( new EntryEmailsTask() )
$this->__get( 'to_email' ),
$this->__get( 'subject' ),
* 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.
* @param Notifications $this An instance of the "Notifications" class.
do_action( 'wpforms_email_send_after', $this ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
* Process the email template.
* @param string $message Email message.
public function process_email_template( string $message ): void {
$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'] ) ) {
// Set the email template, i.e., WPForms\Emails\Templates\Classic.
$this->template( new $template['path']( '', false, $this->current_template ) );
* @var General $email_template
$email_template = $this->__get( 'template' );
! method_exists( $email_template, 'get_field_template' ) ||
! method_exists( $email_template, 'set_field' )
// 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.
$this->message( $content );
* Format and process the email subject.
* @param string $subject Email subject.
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.
* @param string $message Email message.
private function process_message( $message ) {
$message = $this->process_tag( $message );
if ( strpos( $message, '{all_fields}' ) !== false ) {
$message = str_replace( '{all_fields}', $this->process_field_values(), $message );
* Filter and modify the email message content before sending.
* This filter allows customizing the email message content for notifications.
* @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 = (string) apply_filters( 'wpforms_emails_notifications_message', $message, $this->current_template, $this );
$message = $this->fix_table_body_markup( $message );
// Leave early if the template is set to plain text.
if ( Helpers::is_plain_text_template( $this->current_template ) ) {
return make_clickable( str_replace( "\r\n", '<br/>', $message ) );
* Process the field values.
* @noinspection PhpUnusedLocalVariableInspection
private function process_field_values() {
// If fields are empty, return an empty message.
if ( empty( $this->fields ) ) {
// 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.
* @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',
'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',
// 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 );
$message = $this->process_html_message( $show_empty_fields );
* Filter the email message content before sending.
* @param string $message The email message to be sent out.
* @param string $template The email template name.
* @param Mailer $this The instance of the "Notifications" class.
return empty( $message ) ? $default_message : apply_filters( 'wpforms_emails_notifications_process_field_values_message', $message, $this->current_template, $this );
* Get processed field values.
public function get_processed_field_values(): string {
$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'] ) ) {
// Set the email template, i.e., WPForms\Emails\Templates\Classic.