Import/Export to download selected forms as a human‑readable .txt file * including fields, confirmations, notifications, and (optionally) Gravity Flow notification steps. * When multiple forms are selected, a .zip archive will be generated containing one .txt per form. * * REQUIREMENTS: * - Gravity Forms core (admin access with gravityforms_edit_forms capability). * - Optional: Gravity Flow for exporting workflow notifications. * * FEATURES: * - Streams large outputs to avoid memory spikes. * - Customizable filename with timestamp to seconds. Use filter 'bld_gf_text_export_filename' to override. * - Optional inclusion of field IDs, admin labels, choice lists, conditional logic, validation rules, * entry count, and high‑level form settings via UI checkboxes. * - Robust error handling via GF admin notices. * * FILTERS: * - bld_gf_text_export_filename (string $filename, array $context): Filter the final filename (already sanitized). * $context includes: ['form_ids' => int[]] * * NOTES: * - When exporting multiple forms, the response is a ZIP archive with one TXT file per form. For a single form, * a plain text download is streamed. */ ( static function () { // Helper: render a single form to TXT according to include options. $render_form_txt = static function ( $form, $include_details ) { $form_id = isset( $form['id'] ) ? absint( $form['id'] ) : 0; $form_title = isset( $form['title'] ) ? (string) $form['title'] : 'Untitled Form'; $out = 'Form: ' . $form_title . ' (ID: ' . $form_id . ')' . PHP_EOL; if ( ! empty( $include_details['form_settings'] ) ) { $out .= 'Settings:' . PHP_EOL; $out .= ' Label Placement: ' . ( $form['labelPlacement'] ?? '' ) . PHP_EOL; if ( ! empty( $form['description'] ) ) { $out .= ' Description: ' . wp_strip_all_tags( (string) $form['description'] ) . PHP_EOL; } } if ( ! empty( $include_details['entry_count'] ) && class_exists( 'GFAPI' ) && $form_id ) { try { $count = GFAPI::count_entries( $form_id ); } catch ( Exception $e ) { $count = 0; } $out .= 'Entries: ' . $count . PHP_EOL; } $out .= PHP_EOL . 'Fields:' . PHP_EOL; if ( isset( $form['fields'] ) && is_array( $form['fields'] ) ) { foreach ( $form['fields'] as $field ) { $fid = is_object( $field ) && isset( $field->id ) ? (int) $field->id : ( isset( $field['id'] ) ? (int) $field['id'] : 0 ); $label = is_object( $field ) && isset( $field->label ) ? (string) $field->label : ( isset( $field['label'] ) ? (string) $field['label'] : '' ); $type = is_object( $field ) && isset( $field->type ) ? (string) $field->type : ( isset( $field['type'] ) ? (string) $field['type'] : '' ); $admin = ''; if ( ! empty( $include_details['admin_labels'] ) ) { $admin = is_object( $field ) && isset( $field->adminLabel ) ? (string) $field->adminLabel : ( isset( $field['adminLabel'] ) ? (string) $field['adminLabel'] : '' ); } $out .= ' - ' . ( ! empty( $include_details['field_ids'] ) ? 'ID ' . $fid . ': ' : '' ) . $label . ( $admin ? ' [' . $admin . ']' : '' ) . ' (' . $type . ')' . PHP_EOL; $is_req = (bool) ( is_object( $field ) ? ( $field->isRequired ?? false ) : ( $field['isRequired'] ?? false ) ); $out .= ' Required: ' . ( $is_req ? 'true' : 'false' ) . PHP_EOL; if ( ! empty( $include_details['choices'] ) ) { $choices = is_object( $field ) && isset( $field->choices ) ? (array) $field->choices : ( isset( $field['choices'] ) ? (array) $field['choices'] : [] ); if ( array_filter( $choices ) ) { $out .= ' Choices:' . PHP_EOL; foreach ( $choices as $c ) { $ct = isset( $c['text'] ) ? (string) $c['text'] : ''; $cv = isset( $c['value'] ) ? (string) $c['value'] : ''; $df = ! empty( $c['isSelected'] ) ? ' (default)' : ''; $out .= ' - ' . $ct . ' [' . $cv . ']' . $df . PHP_EOL; } } } if ( ! empty( $include_details['validation_rules'] ) ) { $max_len = is_object( $field ) ? ( $field->maxLength ?? '' ) : ( $field['maxLength'] ?? '' ); $range_min = is_object( $field ) ? ( $field->rangeMin ?? '' ) : ( $field['rangeMin'] ?? '' ); $range_max = is_object( $field ) ? ( $field->rangeMax ?? '' ) : ( $field['rangeMax'] ?? '' ); if ( $max_len || '' !== $range_min || '' !== $range_max ) { $out .= ' Validation:' . PHP_EOL; if ( $max_len ) { $out .= ' maxLength: ' . $max_len . PHP_EOL; } if ( '' !== $range_min ) { $out .= ' rangeMin: ' . $range_min . PHP_EOL; } if ( '' !== $range_max ) { $out .= ' rangeMax: ' . $range_max . PHP_EOL; } } } if ( ! empty( $include_details['conditional_logic'] ) ) { $logic = is_object( $field ) ? ( $field->conditionalLogic ?? [] ) : ( $field['conditionalLogic'] ?? [] ); if ( ! empty( $logic ) && isset( $logic['rules'] ) && is_array( $logic['rules'] ) ) { $out .= ' Conditional Logic:' . PHP_EOL; $action = $logic['actionType'] ?? 'show'; $match = $logic['logicType'] ?? 'all'; $out .= ' action: ' . $action . ', match: ' . $match . PHP_EOL; foreach ( $logic['rules'] as $r ) { $out .= ' - fieldId: ' . $r['fieldId'] . ', operator: ' . $r['operator'] . ', value: ' . $r['value'] . PHP_EOL; } } } } } if ( isset( $form['confirmations'] ) && is_array( $form['confirmations'] ) ) { $out .= PHP_EOL . 'Confirmations:' . PHP_EOL; foreach ( $form['confirmations'] as $confirmation ) { $name = (string) ( $confirmation['name'] ?? '' ); $message = isset( $confirmation['message'] ) ? wp_strip_all_tags( (string) $confirmation['message'] ) : ''; $out .= ' - ' . $name . PHP_EOL; if ( isset( $confirmation['url'] ) && '' !== $confirmation['url'] ) { $match = ( 0 === strpos( (string) $confirmation['url'], 'http' ) ) ? 'url' : 'page'; $out .= ' ' . $match . ': ' . $confirmation['url'] . PHP_EOL; } $out .= ' message: ' . $message . PHP_EOL; } } if ( isset( $form['notifications'] ) && is_array( $form['notifications'] ) ) { $out .= PHP_EOL . 'Notifications:' . PHP_EOL; foreach ( $form['notifications'] as $notification ) { $out .= ' - name: ' . ( $notification['name'] ?? '' ) . PHP_EOL; $out .= ' to: ' . ( $notification['to'] ?? '' ) . PHP_EOL; $out .= ' event: ' . ( $notification['event'] ?? '' ) . PHP_EOL; $out .= ' subject: ' . ( $notification['subject'] ?? '' ) . PHP_EOL; $out .= ' message: ' . ( isset( $notification['message'] ) ? wp_strip_all_tags( (string) $notification['message'] ) : '' ) . PHP_EOL; } } if ( function_exists( 'gravity_flow' ) && is_object( gravity_flow() ) ) { $output_header = false; $steps = gravity_flow()->get_steps( $form['id'] ); foreach ( $steps as $step ) { if ( is_object( $step ) && method_exists( $step, 'get_type' ) && 'notification' === $step->get_type() ) { if ( ! $output_header ) { $out .= PHP_EOL . 'Workflow Notifications:' . PHP_EOL; $output_header = true; } $out .= ' - step: ' . $step->__get( 'step_name' ) . PHP_EOL; $out .= ' subject: ' . wp_strip_all_tags( (string) $step->__get( 'workflow_notification_subject' ) ) . PHP_EOL; $out .= ' message: ' . wp_strip_all_tags( (string) $step->__get( 'workflow_notification_message' ) ) . PHP_EOL; $out .= str_repeat( '-', 40 ) . PHP_EOL; } } } return $out; }; // Export runner closure. $do_export = static function ( $form_ids, $include_details = [] ) use ( $render_form_txt ) { if ( empty( $form_ids ) || ! is_array( $form_ids ) ) { return; } $forms = RGFormsModel::get_form_meta_by_id( $form_ids ); if ( empty( $forms ) || ! is_array( $forms ) ) { GFCommon::add_error_message( 'No forms found to export.' ); return; } $defaults = [ 'field_ids' => false, 'admin_labels' => false, 'choices' => false, 'conditional_logic' => false, 'validation_rules' => false, 'entry_count' => false, 'form_settings' => false, ]; $include_details = wp_parse_args( (array) $include_details, $defaults ); $context = [ 'form_ids' => array_map( 'absint', $form_ids ), ]; $ext = 'txt'; $filename = sprintf( 'gravityforms_export_%s.%s', ( function_exists( 'wp_date' ) ? wp_date( 'Y-m-d-H-i-s' ) : date( 'Y-m-d-H-i-s' ) ), $ext ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date $filename = sanitize_file_name( $filename ); $filename = apply_filters( 'bld_gf_text_export_filename', $filename, $context ); $filename = sanitize_file_name( $filename ); // If multiple forms are selected, generate a ZIP with one TXT per form. if ( count( $forms ) > 1 && class_exists( 'ZipArchive' ) ) { $tmp_zip = tempnam( sys_get_temp_dir(), 'gfexp_' ); $zip = new ZipArchive(); if ( true === $zip->open( $tmp_zip, ZipArchive::OVERWRITE ) || true === $zip->open( $tmp_zip, ZipArchive::CREATE ) ) { foreach ( $forms as $form ) { $per_name = ( isset( $form['title'] ) && '' !== $form['title'] ? $form['title'] : 'form-' . ( $form['id'] ?? 'unknown' ) ); $per_name = sanitize_file_name( $per_name . '-' . ( $form['id'] ?? '' ) . '.txt' ); $body = $render_form_txt( $form, $include_details ); $zip->addFromString( $per_name, $body ); } $zip->close(); header( 'Content-Description: File Transfer' ); header( 'Content-Type: application/zip' ); header( 'Content-Disposition: attachment; filename=' . sanitize_file_name( str_replace( '.txt', '.zip', $filename ) ) ); header( 'Content-Length: ' . filesize( $tmp_zip ) ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_readfile -- Needed to stream a temp file download efficiently. readfile( $tmp_zip ); wp_delete_file( $tmp_zip ); exit; } } // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped -- Intentional raw output for file download (headers + stream) header( 'Content-Description: File Transfer' ); header( 'Content-Disposition: attachment; filename=' . $filename ); header( 'Content-Type: text/plain; charset=' . get_option( 'blog_charset' ) ); echo $render_form_txt( $forms[0], $include_details ); exit; // phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped }; $prepare_export = static function ( $selected_forms ) use ( $do_export ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing --already checked $include_raw = isset( $_POST['gf_include'] ) && is_array( $_POST['gf_include'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_POST['gf_include'] ) ) : []; $include = [ 'field_ids' => ! empty( $include_raw['field_ids'] ), 'admin_labels' => ! empty( $include_raw['admin_labels'] ), 'choices' => ! empty( $include_raw['choices'] ), 'conditional_logic' => ! empty( $include_raw['conditional_logic'] ), 'validation_rules' => ! empty( $include_raw['validation_rules'] ), 'entry_count' => ! empty( $include_raw['entry_count'] ), 'form_settings' => ! empty( $include_raw['form_settings'] ), ]; $do_export( $selected_forms, $include ); }; // Render custom export page content. add_action( 'gform_export_page_export_forms_as_text', static function () use ( $do_export, $prepare_export ) { if ( true !== GFCommon::current_user_can_any( 'gravityforms_edit_forms' ) ) { GFCommon::add_error_message( 'You do not have permission to access this page' ); wp_safe_redirect( admin_url( 'admin.php?page=gf_export' ) ); exit; } // Handle form submission first to stream the file before page output. if ( isset( $_POST['export_forms_as_text'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing --about to check check_admin_referer( 'gf_export_forms_as_text', 'gf_export_forms_as_text_nonce' ); $selected_forms = []; if ( isset( $_POST['gf_form_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing --already checked $raw_ids = (array) wp_unslash( $_POST['gf_form_id'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- about to sanitize and already checked $selected_forms = array_map( 'absint', $raw_ids ); } if ( empty( $selected_forms ) ) { GFCommon::add_error_message( 'Please select the forms to be exported' ); } else { $prepare_export( $selected_forms ); return; } } GFExport::page_header(); ?>