File ManagerCurrent Directory: wp-content/plugins/wpforms/src/Pro/Admin/Builder/Notifications/AdvancedLinux appserver-0d5e4f1e-php-cc8da225320a42ba9b7d66cba40b1f03 6.6.123+ #1 SMP PREEMPT_DYNAMIC Wed Mar 11 09:04:28 UTC 2026 x86_64Edit File: EntryCsvAttachment.php <?php namespace WPForms\Pro\Admin\Builder\Notifications\Advanced; use WPForms\Pro\Helpers\CSV; use WPForms\Pro\Tasks\Actions\EntryEmailCSVCleanupTask; use WPForms_Builder_Panel_Settings; use WPForms_WP_Emails; use Exception; /** * Class EntryCsvAttachment. * * Contains functionality for Entry CSV Attachment in email notifications. * * @since 1.7.7 */ class EntryCsvAttachment { /** * Default Entry CSV Attachment file name. * * @since 1.7.7 * * @var string */ const DEFAULT_FILE_NAME = 'entry-details'; /** * Number of attempts when creating a new folder for Entry CSV Attachment. * * @since 1.7.7 * * @var int */ const CREATE_FOLDER_MAX_ATTEMPT = 5; /** * Length of the random name of the folder for Entry CSV Attachment. * * @since 1.7.7 * * @var int */ const RANDOM_FOLDER_NAME_LENGTH = 5; /** * Default Entry CSV Attachment folder name. * * @since 1.7.7 * * @var string */ const FOLDER_NAME = 'entry-attachment-csv'; /** * The max character length of a file name. * * @since 1.7.7 * * @var int */ const FILE_NAME_MAX_LENGTH = 200; /** * CSV helper class instance. * * @since 1.7.7 * * @var CSV */ private $csv; /** * Initialize class. * * @since 1.7.7 */ public function init() { $this->csv = new CSV(); $this->hooks(); } /** * Hooks. * * @since 1.7.7 */ private function hooks() { add_filter( 'wpforms_builder_strings', [ $this, 'javascript_strings' ], 10, 2 ); add_filter( 'wpforms_pro_admin_builder_notifications_advanced_settings_content', [ $this, 'settings' ], 10, 3 ); add_filter( 'wpforms_save_form_args', [ $this, 'format_data_on_save' ], 10, 3 ); add_filter( 'wpforms_emails_send_email_data', [ $this, 'attach_entry_csv_in_email' ], 10, 2 ); add_filter( 'wpforms_tasks_get_tasks', [ $this, 'add_tasks' ] ); } /** * Add localized strings. * * @since 1.7.7 * * @param array $strings Form builder JS strings. * @param object $form Current form. * * @return array */ public function javascript_strings( $strings, $form ) { $strings['entry_information'] = [ 'default_file_name' => self::DEFAULT_FILE_NAME, 'excluded_tags' => array_keys( $this->get_entry_information_excluded_tags() ), 'localized' => $this->get_all_fields_string(), 'replacement_tags' => $this->get_entry_information_replacement_tag(), 'excluded_field_types' => $this->get_entry_information_excluded_field_types(), ]; return $strings; } /** * Get the tags excluded in Entry Information select field. * * @since 1.7.7 * * @return array */ private function get_entry_information_excluded_tags() { return [ 'date format="m/d/Y"' => '', 'query_var key=""' => '', 'user_meta key=""' => '', ]; } /** * Get key and label for "All fields" string. * * @since 1.7.7 * * @return array */ private function get_all_fields_string() { return [ 'all_fields' => esc_html__( 'All Fields', 'wpforms' ), ]; } /** * Returns an array of tags to be replaced on Entry Information. * * @since 1.7.7 * * @return array */ private function get_entry_information_replacement_tag() { return [ 'entry_date format="d/m/Y"' => 'entry_date', ]; } /** * Returns an array containing field types that are excluded in Entry Information field. * * @since 1.7.7 * * @return array */ private function get_entry_information_excluded_field_types() { return [ 'captcha', 'divider', 'entry-preview', 'html', 'content', 'internal-information', 'layout', 'pagebreak', ]; } /** * Entry CSV Attachment settings. * * @since 1.7.7 * * @param string $content Notification > Advanced content. * @param WPForms_Builder_Panel_Settings $settings Builder panel settings. * @param int $id Notification id. * * @return string */ public function settings( $content, $settings, $id ) { $content .= wpforms_panel_field( 'toggle', 'notifications', 'entry_csv_attachment_enable', $settings->form_data, esc_html__( 'Enable Entry CSV Attachment', 'wpforms' ), [ 'input_class' => 'notifications_enable_entry_csv_attachment_toggle', 'parent' => 'settings', 'subsection' => $id, ], false ); $content .= $this->entry_information_panel_field( $settings->form_data, $id ); $content .= wpforms_panel_field( 'text', 'notifications', 'entry_csv_attachment_file_name', $settings->form_data, esc_html__( 'File Name', 'wpforms' ), [ 'class' => 'entry_csv_attachment_file_name_wrap', 'default' => self::DEFAULT_FILE_NAME, 'input_class' => 'entry_csv_attachment_file_name', 'parent' => 'settings', 'subsection' => $id, ], false ); return $content; } /** * Get Entry Information select output build on saved values. * * @since 1.7.7 * * @param array $form_data Form data. * @param int $notification_id Notification ID. * * @return string */ private function entry_information_panel_field( $form_data, $notification_id ) { $field = 'entry_csv_attachment_entry_information'; $values = Settings::get_array_value_from_field( $form_data, $notification_id, $field ); // We use "+" instead of `array_merge()` preserve numeric keys. $options = $this->get_all_fields_string() + Settings::get_fields_from_form_data( $form_data, $values, $this->get_entry_information_excluded_field_types() ) + $this->get_entry_information_other_tags(); return Settings::get_choicesjs_field( $notification_id, $field, $values, $options, __( 'Entry Information', 'wpforms' ), [ 'tooltip' => __( 'At least one item must be selected for inclusion in the CSV file.', 'wpforms' ), ] ); } /** * Get the tags under "Other" group in Entry Information select field. * * @since 1.7.7 * * @return array */ private function get_entry_information_other_tags() { $included_tags = array_diff_key( wpforms()->obj( 'smart_tags' )->builder(), $this->get_entry_information_excluded_tags() ); foreach ( $this->get_entry_information_replacement_tag() as $tag => $replace_tag ) { if ( ! isset( $included_tags[ $tag ] ) ) { continue; } $included_tags[ $replace_tag ] = $included_tags[ $tag ]; unset( $included_tags[ $tag ] ); } return $included_tags; } /** * Format the Entry CSV Attachment > Entry Information data. * * @since 1.7.7 * * @param array $form Form array which is usable with `wp_update_post()`. * @param array $data Data retrieved from $_POST and processed. * @param array $args Empty by default, may have custom data not intended to be saved, but used for processing. * * @return array */ public function format_data_on_save( $form, $data, $args ) { return Settings::attach_notification_data_in_form_data( $form, $this->get_entry_information( $data ) ); } /** * Returns an array of `entry_csv_attachment_entry_information` from $_POST. * * The array keys are the notification ID of the Entry CSV Attachment -> Entry Information. * * @since 1.7.7 * * @param array $post_data Data retrieved from $_POST and processed. * * @return array */ private function get_entry_information( $post_data ) { if ( empty( $post_data['settings']['notifications'] ) ) { return []; } $new_entry_csv_attachments = []; foreach ( $post_data['settings']['notifications'] as $id => $notification ) { // Save a notification ID. $new_entry_csv_attachments[ $id ] = []; // Sanitize a File Name value. $new_entry_csv_attachments[ $id ]['entry_csv_attachment_file_name'] = substr( $this->get_file_name( $notification ), 0, self::FILE_NAME_MAX_LENGTH ); if ( ! isset( $notification['entry_csv_attachment_entry_information']['hidden'] ) ) { continue; } // Sanitize a Entry Information items. $entry_information = []; $selected_fields = json_decode( wp_unslash( $notification['entry_csv_attachment_entry_information']['hidden'] ) ); if ( is_array( $selected_fields ) ) { $entry_information = array_map( [ $this, 'convert_numeric_to_int' ], $selected_fields ); } $new_entry_csv_attachments[ $id ]['entry_csv_attachment_entry_information'] = $entry_information; } return $new_entry_csv_attachments; } /** * Convert numeric value to int. * * @since 1.7.7 * * @param mixed $val Value to convert. * * @return mixed */ private function convert_numeric_to_int( $val ) { if ( is_numeric( $val ) ) { return absint( $val ); } return $val; } /** * Add the Entry CSV Attachment in the email. * * @since 1.7.7 * * @param array $email Email data to be used when sending email. * @param WPForms_WP_Emails $email_obj WPForms_WP_Emails object in context. * * @return array */ public function attach_entry_csv_in_email( $email, $email_obj ) { if ( ! isset( $email_obj->form_data, $email_obj->notification_id, $email_obj->fields ) ) { return $email; } $form_data = $email_obj->form_data; $notification_id = $email_obj->notification_id; $entry_fields = $email_obj->fields; if ( empty( $entry_fields ) || empty( $form_data['settings']['notifications'][ $notification_id ]['entry_csv_attachment_enable'] ) ) { return $email; } $notification = $form_data['settings']['notifications'][ $notification_id ]; if ( empty( $notification['entry_csv_attachment_entry_information'] ) ) { Settings::log_error( 'Entry CSV Attachment', [ 'error' => 'At least one item must be selected in the Entry Information dropdown.', 'notification' => $notification, ], $form_data['id'], $email_obj->entry_id ); return $email; } $notification['id'] = $notification_id; try { $csv_attachment = $this->generate_csv( $notification, $form_data, $entry_fields, $email_obj->entry_id ); } catch ( Exception $e ) { Settings::log_error( 'Entry CSV Attachment', [ 'error' => $e->getMessage(), 'notification' => $notification, ], $form_data['id'], $email_obj->entry_id ); return $email; } if ( ! $csv_attachment ) { return $email; } $email['attachments'] = array_merge( (array) $email['attachments'], [ $csv_attachment ] ); /** * Fires after the CSV attachment was attached to the email. * * @since 1.7.7 * * @param array $email Email data used on the email sent. */ do_action( 'wpforms_attach_entry_csv_in_email_complete', $email ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName return $email; } /** * Generate the Entry CSV Attachment. * * @since 1.7.7 * * @param array $notification Notification data. * @param array $form_data Form data. * @param array $entry_fields Entry data. * @param int $entry_id Entry ID. * * @throws Exception When unable to create the CSV file. * * @return string Returns full path to the generated CSV. */ private function generate_csv( $notification, $form_data, $entry_fields, $entry_id ) { $content = $this->get_csv_header_body_content( $notification['entry_csv_attachment_entry_information'], $form_data, $entry_fields, $entry_id ); /** * Give devs the ability to modify the content for the Entry CSV Attachment. * * @since 1.8.4 * * @param array $content Content. * @param int $entry_id Entry ID. */ $content = apply_filters( 'wpforms_pro_admin_builder_notifications_advanced_entry_csv_attachment_content', $content, $entry_id ); $csv_content = [ $content['header'], $content['body'] ]; $file_name = $this->get_file_name( $notification ); /** * Give devs an ability to modify the file name for the Entry CSV Attachment. * * @since 1.7.7 * * @param string $file_name File name. * @param array $notification Notification data. * @param array $form_data Form data. * @param array $entry_fields Entry data. * @param int $entry_id Entry ID. */ $filename = apply_filters( 'wpforms_entry_csv_attachment_filename', $file_name, $notification, $form_data, $entry_fields, $entry_id ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName return $this->create_csv( $csv_content, $filename ); } /** * Returns an array containing the values to be inserted in CSV. * * @since 1.7.7 * * @param array $attachment_fields Array containing the field ID or smart tag to be added in CSV. * @param array $form_data Form data. * @param array $entry_fields Entry data. * @param int $entry_id Entry ID. * * @return array */ private function get_csv_header_body_content( $attachment_fields, $form_data, $entry_fields, $entry_id ) { /** * Filter whether to include hidden entry field. * * @since 1.7.7 * * @param bool $include_hidden Whether to include hidden entry field. Default `false`. */ $include_hidden = apply_filters( 'wpforms_entry_csv_attachment_include_hidden', false ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName $output = [ 'header' => [], 'body' => [], ]; $filtered = $this->handle_all_fields( $attachment_fields, $form_data['fields'] ); // Allowed Smart Tags. $allowed_smart_tags = $this->get_entry_information_other_tags(); // Loop through each of the fields to include in CSV. foreach ( $filtered as $field ) { if ( ! empty( $entry_fields[ $field ] ) ) { $entry_field = $this->get_field( $field, $entry_fields, $form_data, $include_hidden ); if ( empty( $entry_field ) ) { continue; } $output['header'][] = $this->csv->escape_value( $entry_field['name'] ); $output['body'][] = $this->csv->escape_value( $entry_field['value'] ); continue; } if ( empty( $allowed_smart_tags[ $field ] ) ) { continue; } $output['header'][] = $this->csv->escape_value( $allowed_smart_tags[ $field ] ); $output['body'][] = $this->csv->escape_value( wpforms_process_smart_tags( '{' . $field . '}', $form_data, $entry_fields, $entry_id ) ); } return $output; } /** * Get field. * * @since 1.9.2 * * @param int $field_id Field ID. * @param array $entry_fields Entry data. * @param array $form_data Form data. * @param bool $include_hidden Whether to include hidden entry field. * * @return array */ private function get_field( int $field_id, array $entry_fields, array $form_data, bool $include_hidden ): array { $entry_field = $entry_fields[ $field_id ]; if ( ! $include_hidden && $this->is_field_hidden( $entry_field ) ) { return []; } // Add quantity for the field. if ( wpforms_payment_has_quantity( $entry_field, $form_data ) ) { $entry_field['value'] = wpforms_payment_format_quantity( $entry_field ); return $entry_field; } if ( ! wpforms_is_repeated_field( $field_id, $form_data['fields'] ) ) { return $entry_field; } $field_smart_tag = '{field_id="' . $field_id . '"}'; $entry_field['value'] = wpforms_process_smart_tags( $field_smart_tag, $form_data, $entry_fields, $field_id ); return $entry_field; } /** * This function handle the 'All Fields' on Entry Information. * * If there is NO 'All Fields', this function returns the passed `$attachment_fields`. * Otherwise, it will return an array that was processed by the following steps: * 1. Remove all other Field IDs in `$attachment_fields`. * 2. It will get all the Field IDs in the form and add them after 'all_fields'. * * @since 1.7.7 * * @param array $attachment_fields An array containing the possible data to be included in CSV file. * @param array $form_data_fields Array containing the Form Fields. * * @return array */ private function handle_all_fields( $attachment_fields, $form_data_fields ) { if ( ! in_array( 'all_fields', $attachment_fields, true ) ) { return $attachment_fields; } // Remove all other Field IDs. $filtered = array_filter( $attachment_fields, [ $this, 'not_is_numeric' ] ); if ( empty( $form_data_fields ) ) { return $filtered; } // We should exclude repeater fields. We process them separately via smart tags. $processed_field_ids = array_filter( array_keys( $form_data_fields ), 'is_numeric' ); // Add all the Form Field IDs after the 'all_fields'. return wpforms_array_insert( $filtered, $processed_field_ids, array_search( 'all_fields', $filtered, true ) ); } /** * Check whether a given value is not numeric. * * @since 1.7.7 * * @param mixed $value Value to check. * * @return bool */ private function not_is_numeric( $value ) { return ! is_numeric( $value ); } /** * Returns whether the entry field is hidden. * * @since 1.7.7 * * @param array $field Array containing information about an entry field. * * @return bool */ private function is_field_hidden( $field ) { return isset( $field['visible'] ) && $field['visible'] === false; } /** * Get the file name for the Entry CSV Attachment. * * @since 1.7.7 * * @param array $notification Notification data. * * @return string */ private function get_file_name( $notification ) { if ( empty( $notification['entry_csv_attachment_file_name'] ) ) { return self::DEFAULT_FILE_NAME; } $file_name = sanitize_file_name( trim( $notification['entry_csv_attachment_file_name'] ) ); if ( empty( $file_name ) ) { return self::DEFAULT_FILE_NAME; } $path_info = pathinfo( $file_name ); if ( empty( $path_info['filename'] ) ) { return self::DEFAULT_FILE_NAME; } return $path_info['filename']; } /** * Create Entry CSV Attachment. * * @since 1.7.7 * * @param array $csv_content Array containing the content. * @param string $file_name File name of the CSV to be created. * * @throws Exception When unable to create the CSV file. * * @return string Full path of the created CSV file. */ private function create_csv( $csv_content, $file_name ) { $csv_file = wp_normalize_path( $this->get_csv_dir_path() . '/' . "{$file_name}.csv" ); // Open a stream. // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen $fp = fopen( $csv_file, 'w' ); if ( ! $fp ) { throw new Exception( sprintf( 'Unable to create the CSV file: %s', esc_html( str_replace( wpforms_upload_dir()['path'] , '', $csv_file ) ) ) ); } foreach ( $csv_content as $csv_fields ) { fputcsv( $fp, $csv_fields ); } // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose fclose( $fp ); return $csv_file; } /** * Create a new folder where the Entry CSV Attachment file will be generated. * * @since 1.7.7 * * @throws Exception When unable to create a new folder for the Entry CSV Attachment. * * @return string Full path to the created directory. */ private function get_csv_dir_path() { $upload_dir = wpforms_upload_dir(); if ( ! empty( $upload_dir['error'] ) ) { throw new Exception( 'WPForms uploads folder does not exists.' ); } $parent_folder = trailingslashit( $upload_dir['path'] ) . self::FOLDER_NAME; $dir_path = ''; wpforms_create_index_html_file( $parent_folder ); for ( $attempt_count = 0; $attempt_count < self::CREATE_FOLDER_MAX_ATTEMPT; $attempt_count++ ) { $random_folder_name = sanitize_file_name( wp_generate_password( self::RANDOM_FOLDER_NAME_LENGTH, false ) ); $dir_path = $parent_folder . '/' . $random_folder_name; if ( file_exists( $dir_path ) ) { continue; } if ( wp_mkdir_p( $dir_path ) ) { break; } } if ( ! file_exists( $dir_path ) ) { throw new Exception( 'Unable to create a folder for a CSV file.' ); } return $dir_path; } /** * Add Entry CSV Attachment-related tasks. * * @since 1.7.7 * * @param array $tasks List of task classes. * * @return array */ public function add_tasks( $tasks ) { $tasks[] = EntryEmailCSVCleanupTask::class; return $tasks; } } Upload File Directory Listing NameTypeSizeActions.. (Parent Directory)DirEntryCsvAttachment.phpFile19.72 KB Rename | Delete | EditFileUploadAttachment.phpFile7.93 KB Rename | Delete | EditSettings.phpFile7.71 KB Rename | Delete | Edit