PluginProbe ʕ •ᴥ•ʔ
Superb Addons: Blocks, Patterns, Pre-built Pages, Sliders, Popups, Free Forms, Animations & More / 4.0.0
Superb Addons: Blocks, Patterns, Pre-built Pages, Sliders, Popups, Free Forms, Animations & More v4.0.0
4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 4.0.2 4.0.1 4.0.0 trunk 1.0.0 2.0.0 2.0.1 2.0.2 2.0.3 3.0 3.0.1 3.0.2 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.2 3.1.3 3.2.0 3.2.1 3.2.2 3.2.4 3.2.5 3.2.7 3.2.8 3.2.9 3.3.0 3.3.1 3.3.2 3.4.0 3.4.1 3.4.2 3.4.5 3.4.6 3.5.0 3.5.1 3.5.2 3.5.3 3.5.4 3.5.6 3.5.7 3.5.8 3.5.9 3.6.0 3.6.1 3.6.2 3.7.0 3.7.1
superb-blocks / src / gutenberg / form / class-form-exporter.php
superb-blocks / src / gutenberg / form Last commit date
class-form-access-control.php 2 months ago class-form-captcha-handler.php 2 months ago class-form-controller.php 2 months ago class-form-email-config-check.php 2 months ago class-form-email-handler.php 2 months ago class-form-encryption.php 2 months ago class-form-exporter.php 2 months ago class-form-field-validator.php 2 months ago class-form-file-handler.php 2 months ago class-form-google-auth.php 2 months ago class-form-integration-handler.php 2 months ago class-form-math-parser.php 2 months ago class-form-permissions.php 2 months ago class-form-registry.php 2 months ago class-form-settings.php 2 months ago class-form-submission-cpt.php 2 months ago class-form-submission-handler.php 2 months ago
class-form-exporter.php
227 lines
1 <?php
2
3 namespace SuperbAddons\Gutenberg\Form;
4
5 defined('ABSPATH') || exit();
6
7 class FormExporter
8 {
9 /**
10 * Generate and stream a CSV export for a form's submissions.
11 *
12 * @param string $form_id
13 * @param array $form_fields Field definitions from form config.
14 * @param bool $include_sensitive Whether to include decrypted sensitive fields.
15 * @param string $status Filter by status ('new', 'read', 'spam', or '').
16 * @param string $starred Filter by starred ('1' or '').
17 * @param string $search Search term to filter submissions.
18 * @param string $date_after ISO date string for date range start.
19 * @param string $date_before ISO date string for date range end.
20 * @param bool $pending_delete Whether the form is pending deletion (config unavailable).
21 * @return void Streams CSV directly and exits.
22 */
23 public static function Export($form_id, $form_fields, $include_sensitive, $status = '', $starred = '', $search = '', $date_after = '', $date_before = '', $include_notes = false, $export_fields = null, $pending_delete = false)
24 {
25 // Build field label map and sensitive field list
26 $field_labels = array();
27 $sensitive_field_ids = array();
28 $field_order = array();
29 foreach ($form_fields as $field) {
30 if (!isset($field['fieldId'])) {
31 continue;
32 }
33 $fid = $field['fieldId'];
34 $field_order[] = $fid;
35 $field_labels[$fid] = isset($field['label']) ? $field['label'] : $fid;
36 if (!empty($field['sensitive'])) {
37 $sensitive_field_ids[] = $fid;
38 }
39 }
40
41 // Fetch all matching submissions (no pagination)
42 $all_submissions = array();
43 $page = 1;
44 $batch_size = 100;
45 do {
46 $result = FormSubmissionHandler::GetSubmissions($form_id, $page, $batch_size, $status, $starred, $search, $date_after, $date_before);
47 if (empty($result['submissions'])) {
48 break;
49 }
50 foreach ($result['submissions'] as $sub) {
51 // When config is gone, derive field order and sensitive IDs from submission data
52 if ($pending_delete && empty($form_fields)) {
53 foreach ($sub['fields'] as $fid => $value) {
54 if (!in_array($fid, $field_order, true)) {
55 $field_order[] = $fid;
56 $field_labels[$fid] = $fid;
57 }
58 if (FormEncryption::IsEncrypted($value) && !in_array($fid, $sensitive_field_ids, true)) {
59 $sensitive_field_ids[] = $fid;
60 }
61 }
62 }
63 // Decrypt sensitive fields if included
64 if ($include_sensitive) {
65 $sub['fields'] = self::DecryptFields($form_fields, $sub['fields'], $pending_delete);
66 }
67 $all_submissions[] = $sub;
68 }
69 $page++;
70 } while (count($result['submissions']) === $batch_size);
71
72 // Determine which fields to include
73 $visible_fields = array();
74 if ($export_fields !== null) {
75 // Use user's field preference, respecting sensitive field visibility
76 foreach ($export_fields as $fid) {
77 if (!$include_sensitive && in_array($fid, $sensitive_field_ids, true)) {
78 continue;
79 }
80 if (in_array($fid, $field_order, true)) {
81 $visible_fields[] = $fid;
82 }
83 }
84 }
85 if (empty($visible_fields)) {
86 foreach ($field_order as $fid) {
87 if (!$include_sensitive && in_array($fid, $sensitive_field_ids, true)) {
88 continue;
89 }
90 $visible_fields[] = $fid;
91 }
92 }
93
94 // Build the form name for the filename
95 $form_name = FormRegistry::GetName($form_id);
96 $safe_name = sanitize_file_name($form_name);
97 if (empty($safe_name)) {
98 $safe_name = 'form-export';
99 }
100 $filename = $safe_name . '-' . gmdate('Y-m-d') . '.csv';
101
102 // Stream CSV
103 // Disable output buffering to stream directly
104 while (ob_get_level()) {
105 ob_end_clean();
106 }
107
108 header('Content-Type: text/csv; charset=UTF-8');
109 header('Content-Disposition: attachment; filename="' . $filename . '"');
110 header('Cache-Control: no-cache, no-store, must-revalidate');
111 header('Pragma: no-cache');
112 header('Expires: 0');
113
114 // UTF-8 BOM for Excel compatibility. Emitted via echo (same destination as the stream below) so we do not need fwrite, which the plugin checker flags.
115 echo "\xEF\xBB\xBF";
116
117 $output = fopen('php://output', 'w');
118
119 // Header row: Date, Status, then field labels, optionally Notes
120 $header = array('Date', 'Status');
121 foreach ($visible_fields as $fid) {
122 $header[] = isset($field_labels[$fid]) ? $field_labels[$fid] : $fid;
123 }
124 if ($include_notes) {
125 $header[] = 'Notes';
126 }
127 fputcsv($output, $header);
128
129 // Data rows
130 foreach ($all_submissions as $sub) {
131 $row = array();
132
133 // Date
134 $date = isset($sub['date']) ? $sub['date'] : '';
135 if (!empty($date)) {
136 $timestamp = strtotime($date);
137 $row[] = $timestamp !== false ? gmdate('Y-m-d H:i:s', $timestamp) : $date;
138 } else {
139 $row[] = '';
140 }
141
142 // Status
143 $row[] = isset($sub['status']) && $sub['status'] === 'new' ? 'Unread' : 'Read';
144
145 // Fields
146 $fields = isset($sub['fields']) ? $sub['fields'] : array();
147 foreach ($visible_fields as $fid) {
148 $value = isset($fields[$fid]) ? $fields[$fid] : '';
149 $row[] = self::FormatFieldValue($value);
150 }
151
152 // Notes
153 if ($include_notes) {
154 $sub_id = isset($sub['id']) ? intval($sub['id']) : 0;
155 $notes = $sub_id > 0 ? FormSubmissionHandler::GetNotes($sub_id) : array();
156 $note_texts = array();
157 foreach ($notes as $note) {
158 $note_texts[] = $note['author_name'] . ' (' . $note['date'] . '): ' . $note['text'];
159 }
160 $row[] = implode("\n", $note_texts);
161 }
162
163 fputcsv($output, $row);
164 }
165
166 exit;
167 }
168
169 /**
170 * Format a field value for CSV output.
171 * Arrays (file uploads) are formatted as comma-separated filenames.
172 *
173 * @param mixed $value
174 * @return string
175 */
176 private static function FormatFieldValue($value)
177 {
178 if (is_array($value)) {
179 // File upload field: array of {name, path, type}
180 $names = array();
181 foreach ($value as $file) {
182 if (is_array($file) && isset($file['name'])) {
183 $names[] = $file['name'];
184 }
185 }
186 return implode(', ', $names);
187 }
188 return is_string($value) ? $value : '';
189 }
190
191 /**
192 * Decrypt sensitive fields in submission data.
193 *
194 * @param array $form_fields
195 * @param array $fields
196 * @param bool $pending_delete Whether the form is pending deletion (config unavailable).
197 * @return array
198 */
199 private static function DecryptFields($form_fields, $fields, $pending_delete = false)
200 {
201 if (empty($form_fields) && $pending_delete) {
202 foreach ($fields as $fid => $value) {
203 if (FormEncryption::IsEncrypted($value)) {
204 $decrypted = FormEncryption::Decrypt($value);
205 if ($decrypted !== false) {
206 $fields[$fid] = $decrypted;
207 }
208 }
209 }
210 return $fields;
211 }
212
213 foreach ($form_fields as $field_def) {
214 if (!empty($field_def['sensitive']) && !empty($field_def['fieldId'])) {
215 $sfid = $field_def['fieldId'];
216 if (isset($fields[$sfid]) && is_string($fields[$sfid])) {
217 $decrypted = FormEncryption::Decrypt($fields[$sfid]);
218 if ($decrypted !== false) {
219 $fields[$sfid] = $decrypted;
220 }
221 }
222 }
223 }
224 return $fields;
225 }
226 }
227