'' . esc_html__('View Changelogs', 'themify') . '' ); return array_merge($links, $row_meta); } return (array)$links; } public static function action_links(array $links):array{ if (is_plugin_active('themify-updater/themify-updater.php')) { $tlinks = array( '' . __('Themify License', 'themify') . '', ); } else { $tlinks = array( '' . __('Themify Updater', 'themify') . '', ); } return array_merge($links, $tlinks); } public static function i18n(){ load_plugin_textdomain( 'builder-contact', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' ); self::create_post_type(); } public static function register_module() { $dir=trailingslashit(plugin_dir_path(__FILE__)); if(method_exists('Themify_Builder_Model', 'add_module')){ Themify_Builder_Model::add_module($dir . 'modules/module-contact.php' ); } else{ Themify_Builder_Model::register_directory('templates', $dir . 'templates'); Themify_Builder_Model::register_directory('modules', $dir . 'modules'); } } /** * Retrieve saved settings for a module * * @deprecated since Builder v7, @2024 * @return array */ private static function get_element_settings( $post_id, $element_id ) : array { global $ThemifyBuilder; $data = $ThemifyBuilder->get_flat_modules_list( $post_id ); if ( ! empty( $data ) ) { foreach ( $data as $module ) { if ( isset( $module['element_id'], $module['mod_settings'] ) && $module['element_id'] === $element_id ) { return $module['mod_settings']; } } } return []; } public static function contact_get_nonce() { wp_send_json_success( array( 'nonce' => wp_create_nonce( 'builder_contact_send' ) ) ); } public static function contact_send() { if ( ! isset( $_POST['post_id'], $_POST['element_id'] ) ) { return; } // CSRF protection: verify nonce if ( ! isset( $_POST['bc_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['bc_nonce'] ) ), 'builder_contact_send' ) ) { wp_send_json_error( array( 'error' => __( 'Security check failed. Please reload the page and try again.', 'builder-contact' ) ) ); return; } global $post; $post = get_post((int) $_POST['orig_id']); if ( $post ) { setup_postdata( $post ); } $post_id = (int) $_POST['post_id']; $element_id = sanitize_text_field( $_POST['element_id'] ); if ( method_exists( 'Themify_Builder_Component_Module', 'get_element_settings' ) ) { /* v7 */ $module_settings = Themify_Builder_Component_Module::get_element_settings( $post_id, $element_id ); } else { $module_settings = self::get_element_settings( $post_id, $element_id ); } // WPML translates the TBP template post to a language-specific stub post that // carries no builder data of its own — the data lives on the original-language // template post. When the lookup above returns empty, resolve the original post // via the wpml_object_id filter and retry once. if ( empty( $module_settings ) && defined( 'ICL_SITEPRESS_VERSION' ) ) { $original_post_id = apply_filters( 'wpml_object_id', $post_id, get_post_type( $post_id ), true, apply_filters( 'wpml_default_language', null ) ); if ( $original_post_id && $original_post_id !== $post_id ) { if ( method_exists( 'Themify_Builder_Component_Module', 'get_element_settings' ) ) { $module_settings = Themify_Builder_Component_Module::get_element_settings( $original_post_id, $element_id ); } else { $module_settings = self::get_element_settings( $original_post_id, $element_id ); } } } if ( empty( $module_settings ) ) { wp_send_json_error( array( 'error' => __( 'Form configuration could not be loaded. Please reload the page and try again.', 'builder-contact' ) ) ); return; } /* parse Dynamic Content on the module if applicable */ if ( is_callable( [ 'Tbp_Dynamic_Content', 'do_replace' ] ) ) { $module_settings = Tbp_Dynamic_Content::do_replace( $module_settings ); } $module_settings+= array( 'mail_contact' => get_option('admin_email'), 'specify_from_address' => '', 'specify_email_address' => '', 'bcc_mail_contact' => '', 'bcc_mail' => '', 'default_subject' => '', 'success_url' => '', 'post_type' => '', 'post_author' => '', 'success_message_text' => __('Message sent. Thank you.', 'builder-contact'), 'contact_sent_from' => 'enable', 'field_sendcopy_subject' => '', 'field_name_active' => 'yes', 'field_name_require' => 'yes', 'field_email_active' => 'yes', 'field_email_require' => 'yes', 'field_subject_active' => 'yes', 'field_subject_require' => 'yes', 'field_message_active'=>'yes', 'field_message_require'=>'', 'field_captcha_active' => '', 'field_sendcopy_active' => '', 'field_optin_active' => '', 'auto_respond' => '', 'auto_respond_subject' => __( 'Message sent. Thank you.', 'builder-contact' ), 'auto_respond_message' => '', 'user_role' => '', 'field_extra' => '{ "fields": [] }', 'custom_template' => 'off', 'include_name_mail' => '', ); foreach ( array( 'name', 'email', 'subject', 'message' ) as $field ) { if ( (isset($module_settings["field_{$field}_active"]) && $module_settings["field_{$field}_active"] === 'yes') && (isset($module_settings["field_{$field}_require"]) && $module_settings["field_{$field}_require"] === 'yes') ) { if ( empty( $_POST["contact-{$field}"] ) ) { wp_send_json_error( array( 'error' => __( 'Please fill in the required data.', 'builder-contact' ) ) ); } } } $name = isset( $_POST['contact-name'] ) ? sanitize_text_field( stripslashes($_POST['contact-name']) ) : ''; $email = isset( $_POST['contact-email'] ) ? sanitize_email( $_POST['contact-email'] ) : ''; $subject = ! empty( $_POST['contact-subject'] ) ? sanitize_text_field( stripslashes($_POST['contact-subject']) ) : $module_settings['default_subject']; if ( $module_settings['field_email_active'] === 'yes' && ! empty( $_POST['contact-email'] ) && ! is_email( $email ) ) { wp_send_json_error( array( 'error' => __( 'Invalid Email address!', 'builder-contact' ) ) ); } $extra_fields = is_string($module_settings['field_extra'])?json_decode( $module_settings['field_extra'], true ):$module_settings['field_extra']; if ( ! is_array( $extra_fields ) || ! isset( $extra_fields['fields'] ) || ! is_array( $extra_fields['fields'] ) ) { $extra_fields = array( 'fields' => array() ); } // ensure "required" extra fields are submitted foreach ( $extra_fields['fields'] as $key => $field ) { if ( isset( $field['required'] ) && $field['required'] ) { if (( $field['type'] === 'upload' && empty( $_FILES["field_extra_{$key}"] ) )|| ( $field['type'] !== 'upload' && empty( $_POST["field_extra_{$key}"] ) )) { wp_send_json_error( array( 'error' => __( 'Please fill in the required data.', 'builder-contact' ) ) ); } } if ( $field['type'] === 'email' && ! empty( $_POST["field_extra_{$key}"] ) && ! sanitize_email( $_POST["field_extra_{$key}"] ) ) { wp_send_json_error( array( 'error' => __( 'Invalid Email address is sent.', 'builder-contact' ) ) ); } } /* GDPR consent validation */ if ( isset( $module_settings['gdpr'] ) && $module_settings['gdpr'] === 'accept' ) { if ( empty( $_POST['gdpr'] ) || $_POST['gdpr'] !== '1' ) { wp_send_json_error( array( 'error' => __( 'You must accept the privacy policy to continue.', 'builder-contact' ) ) ); } } /* reCAPTCHA validation */ if ( $module_settings['field_captcha_active'] === 'yes' ) { if ( ! method_exists( 'Themify_Builder_Model', 'get_captcha_field' ) ) { wp_send_json_error( array( 'error' => __( 'Themify Builder update is required.', 'builder-contact' ) ) ); } $provider = 'recaptcha'; if ( isset( $module_settings['captcha_provider'] ) && 'r' !== $module_settings['captcha_provider'] ) { $provider = $module_settings['captcha_provider'] === 'h' ? 'hcaptcha' : $module_settings['captcha_provider']; } $response = null; if ( $provider === 'recaptcha' && ! empty( $_POST['g-recaptcha-response'] ) ) { $response = $_POST['g-recaptcha-response']; } else if ( $provider === 'hcaptcha' && ! empty( $_POST['h-captcha-response'] ) ) { $response = $_POST['h-captcha-response']; } else if ( $provider === 'turnstile' && ! empty( $_POST['cf-turnstile-response'] ) ) { $response = $_POST['cf-turnstile-response']; } if ( $response ) { $result = Themify_Builder_Model::validate_captcha( $provider, $response ); if ( is_wp_error( $result ) ) { wp_send_json_error( array( 'error' => $result->get_error_message() ) ); } } else { wp_send_json_error( array( 'error' => __( 'Empty Captcha response.', 'builder-contact' ) ) ); } } if(!isset($module_settings['send_to_admins']) || $module_settings['send_to_admins']==='true'){//old backward if ( 'sr' === $module_settings['user_role'] ) { // Index of the chosen recipient, posted by the radio/select field. $selected_recipient = isset( $_POST['contact-recipients'] ) ? (int) $_POST['contact-recipients'] : 0; if ( ! empty( $module_settings['sr'][ $selected_recipient ]['email'] ) ) { $recipients = [ $module_settings['sr'][ $selected_recipient ]['email'] ]; } else { // No valid recipient found, fall back to admin email. $recipients = array( get_option( 'admin_email' ) ); } } elseif ( 'author' === $module_settings['user_role'] ) { // orig_id is the actual viewed page; post_id may point to a builder template. $author_id = get_post_field( 'post_author', (int) $_POST['orig_id'] ); $authors_email = get_the_author_meta( 'user_email', $author_id ); $recipients = ( is_email( $authors_email ) ) ? array( $authors_email ) : array( get_option( 'admin_email' ) ); } elseif($module_settings['user_role']==='admin' || (isset($module_settings['send_to_admins']) && $module_settings['send_to_admins'] === 'true')) { $recipients = array(get_option('admin_email')); } else{ $recipients = self::filter_valid_emails( array_map( 'trim', explode( ',', $module_settings['mail_contact'] ) ) ); } } else{ $recipients = self::filter_valid_emails( array_map( 'trim', explode( ',', $module_settings['mail_contact'] ) ) ); } $recipients = self::filter_valid_emails( (array) $recipients ); if ( empty( $recipients ) ) { $fallback_email = sanitize_email( (string) get_option( 'admin_email' ) ); if ( ! is_email( $fallback_email ) ) { $fallback_email = self::get_site_from_email(); } $recipients = array( $fallback_email ); } // Current builder saves 'en'/'dis'; older data used 'true'/'enable'. // Whitelist all known on-values so stale DB data doesn't activate a disabled toggle. $toggle_on_values = [ 'en', 'enable', 'true' ]; $active_bcc = in_array( $module_settings['bcc_mail'], $toggle_on_values, true ); $active_specify_from_address = in_array( $module_settings['specify_from_address'], $toggle_on_values, true ); $active_post_type = in_array( $module_settings['post_type'], $toggle_on_values, true ); $bcc_recipients = self::filter_valid_emails( array_map( 'trim', explode( ',', $module_settings['bcc_mail_contact'] ) ) ); $specify_email_address = trim( $module_settings['specify_email_address'] ); $subject = apply_filters('builder_contact_subject', $subject); if ( empty( $subject ) ) { $subject = get_bloginfo( 'name' ); } $headers = array(); $reply_to_email = ''; if ( '' !== $email ) { $reply_to_email = $email; } // add the email address to message body // Same on-value whitelist as the other toggle fields above. $custom_template = in_array( $module_settings['custom_template'], $toggle_on_values, true ); $message = isset( $_POST['contact-message'] ) ? wpautop( sanitize_textarea_field( stripslashes($_POST['contact-message']) ) ) : ''; if ( isset( $_SERVER['HTTP_REFERER'] ) && $_SERVER['HTTP_REFERER'] != '' ) { $referer = esc_url( wp_unslash( $_SERVER['HTTP_REFERER'] ) ); } else { $referer = get_site_url(); } if ( $custom_template ) { $template_vars = array( '%name%' => $name, '%email%' => $email, '%subject%' => $subject, '%message%' => $message, '%referer%' => $referer, ); if ( empty( $module_settings['template'] ) ) { $template = self::get_default_template(); } else { // WP adds slashes on save; strip them, then convert newlines to
for HTML email. $template = stripslashes( $module_settings['template'] ); $template = preg_replace( '/\n/i', '
', $template ); $template = nl2br( $template, false ); } } else { $body = ''; if ( '' !== $name && '' === $email ) { $body = __('From:', 'builder-contact') . ' ' . $name ; } elseif ( '' === $name && '' !== $email ) { $body .= __('From:', 'builder-contact') . ' '. ' <' . $email . '>' . "
" ; } elseif ( '' !== $name && '' !== $email ) { $body .= __('From:', 'builder-contact') . ' ' . $name . ' <' . $email . '>' . "
"; } if ( 'enable' === $module_settings['include_name_mail'] ) { $body .= "
" . __('Name:', 'builder-contact').' ' . $name .'
'; $body .= "" . __('Email:', 'builder-contact').' ' . $email .'
'; $body .= "" . __('Subject:', 'builder-contact').' ' . $subject .'
'; } $body .= $message; } if ( ! empty( $module_settings['upload_dir'] ) ) { $safe_dir = sanitize_file_name( basename( $module_settings['upload_dir'] ) ); if ( $safe_dir !== '' && $safe_dir === $module_settings['upload_dir'] ) { self::$upload_dir = $safe_dir; add_filter( 'upload_dir', [ __CLASS__, 'upload_dir' ] ); } } $uploaded_files_path = $uploaded_files_url = array(); $extra_field_values = array(); // Collect extra field label => value pairs for appending to email body. foreach ( $extra_fields['fields'] as $key => $field ) { if ( $field['type'] === 'static' ) { // Display-only field — no submitted value, skip entirely. continue; } $field_label = isset( $field['label'] ) ? $field['label'] : ''; if ( $field['type'] === 'upload' ) { if ( isset( $_FILES[ "field_extra_{$key}" ] ) && 0 !== $_FILES["field_extra_{$key}"]['size'] ) { $file_info = $_FILES["field_extra_{$key}"]; $upload_file = self::upload_attachment( $file_info, $field ); if ( is_wp_error( $upload_file ) ) { wp_send_json_error( array( 'error' => $upload_file->get_error_message() ) ); } elseif ( $upload_file ) { $uploaded_files_url[ $key ] = $upload_file['url']; $uploaded_files_path[ $key ] = $upload_file['file']; // Make uploaded file URL available as a template variable. if ( $custom_template && $field_label !== '' ) { $template_key = html_entity_decode( $field_label, ENT_QUOTES | ENT_HTML5, 'UTF-8' ); $template_vars[ '%' . $template_key . '%' ] = $upload_file['url']; } } } elseif ( $custom_template && $field_label !== '' ) { // No file — leave placeholder empty. $template_key = html_entity_decode( $field_label, ENT_QUOTES | ENT_HTML5, 'UTF-8' ); $template_vars[ '%' . $template_key . '%' ] = ''; } continue; } // Checkboxes post as an array when checked, and are absent from $_POST when not. if ( $field['type'] === 'checkbox' ) { if ( isset( $_POST[ "field_extra_{$key}" ] ) && is_array( $_POST[ "field_extra_{$key}" ] ) ) { $value = ''; foreach ( $_POST[ "field_extra_{$key}" ] as $val ) { $value .= sanitize_text_field( stripslashes( $val ) ) . ', '; } $value = rtrim( $value, ', ' ); } else { // Nothing checked. $value = ''; } } elseif ( ! isset( $_POST[ "field_extra_{$key}" ] ) ) { continue; } elseif ( is_array( $_POST[ "field_extra_{$key}" ] ) ) { // Other array-type fields. $value = ''; foreach ( $_POST[ "field_extra_{$key}" ] as $val ) { $value .= sanitize_text_field( stripslashes( $val ) ) . ', '; } $value = rtrim( $value, ', ' ); } else { if ( $field['type'] === 'textarea' ) { $value = sanitize_textarea_field( stripslashes( $_POST[ "field_extra_{$key}" ] ) ); } elseif ( $field['type'] === 'email' ) { $value = sanitize_email( stripslashes( $_POST[ "field_extra_{$key}" ] ) ); } elseif ( $field['type'] === 'date' ) { if ( empty( $field['show'] ) ) { $separator = _x( ' @ ', 'Separator between date and time', 'builder-contact' ); $format = get_option( 'date_format' ) . $separator . get_option( 'time_format' ); } else { $format = get_option( "{$field['show']}_format" ); } $value = date_i18n( $format, strtotime( stripslashes( $_POST[ "field_extra_{$key}" ] ) ) ); } else { // text, number, tel, radio, select $value = sanitize_text_field( stripslashes( $_POST[ "field_extra_{$key}" ] ) ); } } $value_display = $field['type'] === 'textarea' ? nl2br( esc_html( $value ), false ) : esc_html( $value ); if ( $custom_template ) { if ( $field_label !== '' ) { // Labels may contain HTML entities; decode so the key matches the template placeholder. $template_key = html_entity_decode( $field_label, ENT_QUOTES | ENT_HTML5, 'UTF-8' ); $template_vars[ '%' . $template_key . '%' ] = $value_display; } // Always track extra field values so they can be appended when the template lacks a matching placeholder. $extra_field_values[] = array( 'label' => $field_label, 'display' => $value_display ); } else { $body .= '
'; $body .= '' . wp_kses_post( $field_label ) . " :
" . $value_display . "
"; } } if ( $custom_template ) { // Finalize body by replacing all template vars including any from extra fields $body = strtr( $template, $template_vars ); // Append extra fields whose placeholders were not present in the template, // so custom fields always appear in the email even when the template does not // explicitly include them. $extra_append = ''; foreach ( $extra_field_values as $ef ) { $placeholder = '%' . html_entity_decode( $ef['label'], ENT_QUOTES | ENT_HTML5, 'UTF-8' ) . '%'; if ( $ef['label'] === '' || strpos( $template, $placeholder ) === false ) { $extra_append .= '
'; $extra_append .= '' . wp_kses_post( $ef['label'] ) . " :
" . $ef['display'] . "
"; } } if ( $extra_append !== '' ) { $body .= $extra_append; } } elseif ( 'enable' === $module_settings['contact_sent_from'] ) { $body .= '
' . __( 'Sent from:', 'builder-contact' ) . ' ' . $referer . '

'; } if ( $module_settings['field_sendcopy_active'] === 'yes' && isset( $_POST['contact-sendcopy'] ) && $_POST['contact-sendcopy'] == '1' ) { if ( ! empty( $email ) && is_email( $email ) ) { $sendcopy_headers = array(); if ( ! empty( $recipients[0] ) && is_email( $recipients[0] ) ) { $sendcopy_headers[] = 'Reply-To: ' . $recipients[0]; } $sendcopy_subject = self::build_sendcopy_subject( $subject, (string) $module_settings['field_sendcopy_subject'] ); self::send_mail( $email, $sendcopy_subject, $body, $sendcopy_headers ); } } if ( $module_settings['field_optin_active'] && isset( $_POST['contact-optin'] ) && $_POST['contact-optin'] == '1' ) { if ( ! class_exists( 'Builder_Optin_Service',false ) ){ include_once( THEMIFY_BUILDER_INCLUDES_DIR. '/optin-services/base.php' ); } $provider=sanitize_text_field($_POST['contact-optin-provider']); $optin_instance = method_exists('Builder_Optin_Service', 'get_settings')?Builder_Optin_Service::get_providers( $provider,true ):Builder_Optin_Service::get_providers( $provider ); if ( $optin_instance ) { // collect the data for optin service $data = array( 'email' => $email, 'fname' => $name, 'lname' => '', ); foreach ( $_POST as $key => $value ) { if ( preg_match( '/^contact-optin-/', $key ) ) { $key = preg_replace( '/^contact-optin-/', '', $key ); $data[ $key ] = sanitize_text_field( trim( stripslashes($value) ) ); } } if(is_string($optin_instance)){ $optin_instance::subscribe( $data ); } else{ $optin_instance->subscribe( $data ); } } unset($optin_instance); } if ( $active_post_type ) { $files_links = '';// for add file link to the post if ( ! empty( $uploaded_files_url ) ) { $files_links .= '
' . __( 'Attachments : ', 'builder-contact' ); foreach ( $uploaded_files_url as $link ) { $files_links .= "
" . $link . "
"; } } $post_author_id=false; if ( $module_settings['post_author'] === 'add' ) { $post_author_email = $recipients[0]; $post_author_id = self::create_new_author( $post_author_email ); } self::send_via_post_type( $subject, $body . $files_links, $post_author_id ); } $auto_respond_sent = false; $headerStr = $headers; $cc_recipients = $recipients; unset( $cc_recipients[0] ); $cc_recipients = self::filter_valid_emails( $cc_recipients ); if ( ! empty( $cc_recipients ) ) { $headerStr[] = 'Cc: ' . implode( ',', $cc_recipients ); } if ( $active_bcc && ! empty( $bcc_recipients ) ) { $headerStr[] = 'Bcc: ' . implode( ',', $bcc_recipients ); } $sent = self::send_mail( $recipients[0], $subject, $body, $headerStr, $uploaded_files_path, $reply_to_email ); if ( ! $sent ) { // Retrieve the PHPMailer error if available. $mailer_error = ''; global $phpmailer; if ( isset( $phpmailer ) && ! empty( $phpmailer->ErrorInfo ) ) { $mailer_error = $phpmailer->ErrorInfo; } $failed_hook_error = self::$last_mail_error; $log_detail = $mailer_error ?: $failed_hook_error; error_log( 'Builder Contact: wp_mail() failed.' . ( $log_detail !== '' ? ' Error: ' . $log_detail : '' ) ); $error_message = __( 'There was an error. Please try again.', 'builder-contact' ); if ( current_user_can( 'manage_options' ) && $log_detail !== '' ) { $error_message .= ' ' . __( 'Error:', 'builder-contact' ) . ' ' . esc_html( $log_detail ); } // Clean up any uploaded files before exiting. if ( $uploaded_files_path && ! $active_post_type ) { foreach ( $uploaded_files_path as $attachment ) { if ( file_exists( $attachment ) ) { wp_delete_file( $attachment ); } } } wp_send_json_error( array( 'error' => $error_message ) ); return; } // Mail accepted — send auto-response if configured. if ( ! empty( $module_settings['auto_respond'] ) && ! empty( $module_settings['auto_respond_message'] ) ) { $ar_subject = trim( stripslashes( $module_settings['auto_respond_subject'] ) ); $ar_message = wpautop( trim( stripslashes( $module_settings['auto_respond_message'] ) ) ); if ( $custom_template ) { $ar_message = strtr( $ar_message, $template_vars ); } $ar_headers = array(); self::send_mail( $email, $ar_subject, $ar_message, $ar_headers ); } do_action( 'builder_contact_mail_sent' ); if ( $uploaded_files_path && ! $active_post_type ) { foreach ( $uploaded_files_path as $attachment ) { if ( file_exists( $attachment ) ) { wp_delete_file( $attachment ); } } } wp_send_json_success( array( 'msg' => $module_settings['success_message_text'], 'redirect_url' => $module_settings['success_url'], 'nw' => ! empty( $module_settings['nw'] ) ? 1 : '', ) ); } private static function upload_attachment( $file_info, $field ) { if ( ! empty( $file_info ) ) { if ( ! $file_info['error'] && $file_info['size'] <= wp_max_upload_size() ) { $allowed_types = ! empty( $field['allowed'] ) ? self::get_allowed_mime_types( $field['allowed'] ) : null; $movefile = wp_handle_upload( $file_info, array( 'test_form' => false, 'mimes' => $allowed_types ) ); if ( $movefile && ! isset( $movefile['error'] ) ) { return $movefile; } return new WP_Error( 'error_filetype', __('WordPress doesn\'t allow this type of uploads.', 'builder-contact' ) ); } return new WP_Error( 'error_filesize', __('The selected file size is larger than the limit.', 'builder-contact') ); } return false; } public static function upload_dir( $dir ) { $new_path = trailingslashit( $dir['basedir'] ) . self::$upload_dir; if ( wp_mkdir_p( $new_path ) ) { $dir['path'] = $new_path; $dir['url'] = trailingslashit( $dir['baseurl'] ) . self::$upload_dir; } return $dir; } /** * Return a list of $extension => $mime_type from a comma-separated list * */ private static function get_allowed_mime_types(string $allowed ):array { $output = []; $mime_types = wp_get_mime_types(); foreach ( explode( ',', $allowed ) as $allowed_ext ) { foreach ( $mime_types as $exts => $mime ) { if ( preg_match( '!^(' . $exts . ')$!i', $allowed_ext ) ) { $output[ $exts ] = $mime; } } } return $output; } /** * Returns the HTML-friendly list of acceptable types for "file" input * */ public static function get_allowed_types_attr(string $allowed ):string { $types = self::get_allowed_mime_types( $allowed ); $output = []; foreach ( $types as $ext => $mime_type ) { if ( strpos( $ext, '|' ) !== false ) { $ext = join( ',.', explode( '|', $ext ) ); } $output[] = ".{$ext},{$mime_type}"; } return implode( ',', $output ); } private static function filter_valid_emails( array $emails ): array { $valid = array(); foreach ( $emails as $email ) { $email = sanitize_email( trim( (string) $email ) ); if ( $email !== '' && is_email( $email ) ) { $valid[] = $email; } } return array_values( array_unique( $valid ) ); } private static function build_sendcopy_subject( string $subject, string $append_text ): string { $subject = trim( $subject ); $append_text = trim( stripslashes( $append_text ) ); if ( $append_text === '' ) { return $subject; } // If the configured text contains %subject%, replace it with the real subject. // e.g. "COPY: %subject%" → "COPY: My Subject" if ( strpos( $append_text, '%subject%' ) !== false ) { return trim( str_replace( '%subject%', $subject, $append_text ) ); } // No placeholder — treat the configured text as a prefix. // e.g. "COPY:" → "COPY: My Subject" return trim( $append_text . ' ' . $subject ); } private static $last_mail_error = ''; /** * Turn stored HTML email body into readable plain text (for wp_mail message + text/plain part). * Hosts that ignore PHPMailer and send only the wp_mail() body string then deliver readable mail. */ private static function html_email_to_plain( string $html ): string { if ( $html === '' ) { return ''; } $s = preg_replace( '~~i', "\n", $html ); $s = preg_replace( '~

\s*~i', "\n\n", $s ); $s = html_entity_decode( (string) $s, ENT_QUOTES | ENT_HTML5, 'UTF-8' ); $s = wp_strip_all_tags( $s ); $s = preg_replace( "/\r\n|\r/", "\n", $s ); $s = preg_replace( '/[ \t]+/u', ' ', $s ); $s = preg_replace( "/\n{3,}/", "\n\n", $s ); return trim( $s ); } /** * Apply HTML body + plain alternative; Reply-To. No wp_mail() header changes. * Runs very late so SMTP plugins do not replace Body with a plain-text copy afterward. */ private static function apply_phpmailer_html_body( $phpmailer, string $html_body, string $reply_to = '' ): void { $phpmailer->msgHTML( $html_body ); $plain = self::html_email_to_plain( $html_body ); if ( $plain !== '' ) { $phpmailer->AltBody = $plain; } if ( $reply_to !== '' ) { $phpmailer->addReplyTo( $reply_to ); } } /** * Send an email via wp_mail(). * $body_html is the HTML version (used in phpmailer_init). The message passed to wp_mail() is plain text * derived from that HTML so transports that bypass PHPMailer still send readable content. */ private static function send_mail( $to, string $subject, string $body_html, array $headers = array(), array $attachments = array(), string $reply_to = '' ) { self::$last_mail_error = ''; $capture_error = function( $wp_error ) { if ( ! is_wp_error( $wp_error ) ) { return; } $msg = $wp_error->get_error_message(); $data = $wp_error->get_error_data(); if ( ! empty( $data ) ) { error_log( 'Builder Contact: wp_mail_failed detail: ' . $msg . ' | ' . print_r( $data, true ) ); } self::$last_mail_error = $msg; if ( is_array( $data ) && isset( $data['phpmailer_exception_code'] ) ) { self::$last_mail_error .= ' (code ' . (int) $data['phpmailer_exception_code'] . ')'; } }; self::$from_email = self::get_site_from_email(); self::$from_name = wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ); $plain_for_transport = self::html_email_to_plain( $body_html ); // Late priority: some mail plugins hook phpmailer_init after 999 and reset Body to plain text. $pm_priority = 100000; $configure_phpmailer = function( $phpmailer ) use ( $body_html, $reply_to ) { self::apply_phpmailer_html_body( $phpmailer, $body_html, $reply_to ); }; add_action( 'phpmailer_init', $configure_phpmailer, $pm_priority ); // Priority 5: runs BEFORE SMTP plugins (default priority 10) so they can override. add_filter( 'wp_mail_from', array( __CLASS__, 'set_from_email' ), 5 ); add_filter( 'wp_mail_from_name', array( __CLASS__, 'set_from_name' ), 5 ); add_action( 'wp_mail_failed', $capture_error ); try { $result = wp_mail( $to, $subject, $plain_for_transport, $headers, $attachments ); // If send failed, retry without Reply-To (still keep HTML). if ( ! $result && $reply_to !== '' ) { remove_action( 'phpmailer_init', $configure_phpmailer, $pm_priority ); $configure_html_only = function( $phpmailer ) use ( $body_html ) { self::apply_phpmailer_html_body( $phpmailer, $body_html, '' ); }; add_action( 'phpmailer_init', $configure_html_only, $pm_priority ); $result = wp_mail( $to, $subject, $plain_for_transport, $headers, $attachments ); } return $result; } finally { remove_filter( 'wp_mail_from', array( __CLASS__, 'set_from_email' ), 5 ); remove_filter( 'wp_mail_from_name', array( __CLASS__, 'set_from_name' ), 5 ); remove_action( 'wp_mail_failed', $capture_error ); remove_action( 'phpmailer_init', $configure_phpmailer, $pm_priority ); if ( isset( $configure_html_only ) ) { remove_action( 'phpmailer_init', $configure_html_only, $pm_priority ); } } } private static function get_site_mail_domain(): string { $host = wp_parse_url( network_home_url(), PHP_URL_HOST ); $host = is_string( $host ) ? strtolower( $host ) : ''; return preg_replace( '/^www\./', '', $host ); } private static function get_site_from_email(): string { $admin_email = sanitize_email( (string) get_option( 'admin_email' ) ); if ( function_exists( 'wp_is_localhost' ) && wp_is_localhost() ) { return is_email( $admin_email ) ? $admin_email : 'wordpress@localhost'; } $site_domain = self::get_site_mail_domain(); if ( $admin_email && is_email( $admin_email ) ) { $parts = explode( '@', $admin_email ); $admin_domain = isset( $parts[1] ) ? strtolower( $parts[1] ) : ''; $admin_domain = preg_replace( '/^www\./', '', $admin_domain ); if ( $site_domain !== '' && $admin_domain === $site_domain ) { return $admin_email; } } return $site_domain !== '' ? 'wordpress@' . $site_domain : 'wordpress@localhost'; } private static function get_site_from_header(): string { $name = wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ); $name = trim( preg_replace( '/[\n\r]+/', ' ', (string) $name ) ); $email = self::get_site_from_email(); return sprintf( 'From: %s <%s>', $name !== '' ? $name : 'WordPress', $email ); } public static function set_from_email( $email ): string { return self::$from_email; } public static function set_from_name( $name ): string { return self::$from_name; } private static function create_new_author($email){ $exists = email_exists($email); if (false !== $exists) { return $exists; } $random_password = wp_generate_password(12, false); return wp_create_user($email, $random_password, $email); } private static function send_via_post_type($title, $message, $author = false){ $post_info = array( 'post_title' => $title, 'post_type' => 'contact_messages', 'post_content' => $message ); if (false !== $author) { $post_info['post_author'] = $author; } remove_filter('content_save_pre', 'wp_filter_post_kses', 10); $result = wp_insert_post($post_info); add_filter('content_save_pre', 'wp_filter_post_kses', 10); return $result; } private static function create_post_type(){ return register_post_type('contact_messages', array( 'labels' => array( 'name' => __('Builder Contact Submissions', 'builder-contact'), 'singular_name' => __('Builder Contact Submission', 'builder-contact'), 'all_items' => __('Contact Submissions', 'builder-contact'), 'menu_name' => __('Builder Contact', 'builder-contact'), ), 'public' => false, 'supports' => array('title', 'editor', 'author'), 'show_ui' => true, 'show_in_admin_bar' => false ) ); } public static function set_custom_columns(array $columns):array{ unset($columns['date'], $columns['author']); $columns['sender'] = __('Sender', 'builder-contact'); $columns['subject'] = __('Subject', 'builder-contact'); $columns['date'] = __('Date', 'builder-contact'); return $columns; } public static function custom_contact_messages_columns($column, $post_id){ switch ($column) { case 'sender' : $content_post = get_post($post_id); $content = $content_post->post_content; preg_match('/[a-z0-9_\-\+\.]+@[a-z0-9\-]+\.([a-z]{2,4})(?:\.[a-z]{2})?/i', $content, $result); echo (isset($result[0])) ? esc_html( $result[0] ) : ''; break; case 'subject' : echo esc_html( get_the_title($post_id) ); break; } } public static function get_default_template():string { return __( 'From:', 'builder-contact' ) . ' %name% @ %email%
' . "\n" . '' . __( 'Subject:', 'builder-contact' ) . ' %subject%' . "\n\n" . '%message%' . "\n" . '
' . __( 'Sent from:', 'builder-contact' ) . ' %referer%
'; } } Builder_Contact::init();