PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 4.05.02
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v4.05.02
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
formidable / classes / helpers / FrmCSVExportHelper.php

FrmCSVExportHelper.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 4.05.02, at classes/helpers/FrmCSVExportHelper.php

369 lines 12.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 class FrmCSVExportHelper {
4
5 protected static $separator = ', ';
6 protected static $column_separator = ',';
7 protected static $line_break = 'return';
8 protected static $charset = 'UTF-8';
9 protected static $to_encoding = 'UTF-8';
10 protected static $wp_date_format = 'Y-m-d H:i:s';
11 protected static $comment_count = 0;
12 protected static $form_id = 0;
13 protected static $headings = array();
14 protected static $fields = array();
15 protected static $entry;
16 protected static $has_parent_id;
17
18 public static function csv_format_options() {
19 $formats = array( 'UTF-8', 'ISO-8859-1', 'windows-1256', 'windows-1251', 'macintosh' );
20 $formats = apply_filters( 'frm_csv_format_options', $formats );
21
22 return $formats;
23 }
24
25 public static function generate_csv( $atts ) {
26 global $frm_vars;
27 $frm_vars['prevent_caching'] = true;
28
29 self::$fields = $atts['form_cols'];
30 self::$form_id = $atts['form']->id;
31 self::set_class_paramters();
32 self::set_has_parent_id( $atts['form'] );
33
34 $filename = apply_filters( 'frm_csv_filename', gmdate( 'ymdHis', time() ) . '_' . sanitize_title_with_dashes( $atts['form']->name ) . '_formidable_entries.csv', $atts['form'] );
35 unset( $atts['form'], $atts['form_cols'] );
36
37 self::print_file_headers( $filename );
38 unset( $filename );
39
40 $comment_count = FrmDb::get_count(
41 'frm_item_metas',
42 array(
43 'item_id' => $atts['entry_ids'],
44 'field_id' => 0,
45 'meta_value like' => '{',
46 ),
47 array(
48 'group_by' => 'item_id',
49 'order_by' => 'count(*) DESC',
50 'limit' => 1,
51 )
52 );
53 self::$comment_count = $comment_count;
54
55 self::prepare_csv_headings();
56
57 // fetch 20 posts at a time rather than loading the entire table into memory
58 while ( $next_set = array_splice( $atts['entry_ids'], 0, 20 ) ) {
59 self::prepare_next_csv_rows( $next_set );
60 }
61 }
62
63 private static function set_class_paramters() {
64 self::$separator = apply_filters( 'frm_csv_sep', self::$separator );
65 self::$line_break = apply_filters( 'frm_csv_line_break', self::$line_break );
66 self::$wp_date_format = apply_filters( 'frm_csv_date_format', self::$wp_date_format );
67 self::get_csv_format();
68 self::$charset = get_option( 'blog_charset' );
69
70 $col_sep = ( isset( $_POST['csv_col_sep'] ) && ! empty( $_POST['csv_col_sep'] ) ) ? sanitize_text_field( wp_unslash( $_POST['csv_col_sep'] ) ) : self::$column_separator;
71
72 self::$column_separator = apply_filters( 'frm_csv_column_sep', $col_sep );
73 }
74
75 private static function set_has_parent_id( $form ) {
76 self::$has_parent_id = $form->parent_form_id > 0;
77 }
78
79 private static function print_file_headers( $filename ) {
80 header( 'Content-Description: File Transfer' );
81 header( 'Content-Disposition: attachment; filename="' . esc_attr( $filename ) . '"' );
82 header( 'Content-Type: text/csv; charset=' . self::$charset, true );
83 header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', mktime( gmdate( 'H' ) + 2, gmdate( 'i' ), gmdate( 's' ), gmdate( 'm' ), gmdate( 'd' ), gmdate( 'Y' ) ) ) . ' GMT' );
84 header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s' ) . ' GMT' );
85 header( 'Cache-Control: no-cache, must-revalidate' );
86 header( 'Pragma: no-cache' );
87
88 do_action(
89 'frm_csv_headers',
90 array(
91 'form_id' => self::$form_id,
92 'fields' => self::$fields,
93 )
94 );
95 }
96
97 public static function get_csv_format() {
98 $csv_format = FrmAppHelper::get_post_param( 'csv_format', 'UTF-8', 'sanitize_text_field' );
99 $csv_format = apply_filters( 'frm_csv_format', $csv_format );
100 self::$to_encoding = $csv_format;
101 }
102
103 private static function prepare_csv_headings() {
104 $headings = array();
105 self::csv_headings( $headings );
106 $headings = apply_filters(
107 'frm_csv_columns',
108 $headings,
109 self::$form_id,
110 array(
111 'fields' => self::$fields,
112 )
113 );
114 self::$headings = $headings;
115
116 self::print_csv_row( $headings );
117 }
118
119 private static function csv_headings( &$headings ) {
120 foreach ( self::$fields as $col ) {
121 $field_headings = array();
122 $separate_values = array( 'user_id', 'file', 'data', 'date' );
123 if ( isset( $col->field_options['separate_value'] ) && $col->field_options['separate_value'] && ! in_array( $col->type, $separate_values, true ) ) {
124 $field_headings[ $col->id . '_label' ] = strip_tags( $col->name . ' ' . __( '(label)', 'formidable' ) );
125 }
126
127 $field_headings[ $col->id ] = strip_tags( $col->name );
128 $field_headings = apply_filters(
129 'frm_csv_field_columns',
130 $field_headings,
131 array(
132 'field' => $col,
133 )
134 );
135
136 $headings += $field_headings;
137 }
138
139 if ( self::$comment_count ) {
140 for ( $i = 0; $i < self::$comment_count; $i ++ ) {
141 $headings[ 'comment' . $i ] = __( 'Comment', 'formidable' );
142 $headings[ 'comment_user_id' . $i ] = __( 'Comment User', 'formidable' );
143 $headings[ 'comment_created_at' . $i ] = __( 'Comment Date', 'formidable' );
144 }
145 unset( $i );
146 }
147
148 $headings['created_at'] = __( 'Timestamp', 'formidable' );
149 $headings['updated_at'] = __( 'Last Updated', 'formidable' );
150 $headings['user_id'] = __( 'Created By', 'formidable' );
151 $headings['updated_by'] = __( 'Updated By', 'formidable' );
152 $headings['is_draft'] = __( 'Draft', 'formidable' );
153 $headings['ip'] = __( 'IP', 'formidable' );
154 $headings['id'] = __( 'ID', 'formidable' );
155 $headings['item_key'] = __( 'Key', 'formidable' );
156 if ( self::has_parent_id() ) {
157 $headings['parent_id'] = __( 'Parent ID', 'formidable' );
158 }
159 }
160
161 private static function has_parent_id() {
162 return self::$has_parent_id;
163 }
164
165 private static function prepare_next_csv_rows( $next_set ) {
166 // order by parent_item_id so children will be first
167 $where = array(
168 'or' => 1,
169 'id' => $next_set,
170 'parent_item_id' => $next_set,
171 );
172 $entries = FrmEntry::getAll( $where, ' ORDER BY parent_item_id DESC', '', true, false );
173
174 foreach ( $entries as $k => $entry ) {
175 self::$entry = $entry;
176 unset( $entry );
177
178 if ( self::$entry->form_id !== self::$form_id ) {
179 self::add_repeat_field_values_to_csv( $entries );
180 } else {
181 self::prepare_csv_row();
182 }
183 }
184 }
185
186 private static function prepare_csv_row() {
187 $row = array();
188 self::add_field_values_to_csv( $row );
189 self::add_entry_data_to_csv( $row );
190 $row = apply_filters(
191 'frm_csv_row',
192 $row,
193 array(
194 'entry' => self::$entry,
195 'date_format' => self::$wp_date_format,
196 'comment_count' => self::$comment_count,
197 )
198 );
199 self::print_csv_row( $row );
200 }
201
202 private static function add_repeat_field_values_to_csv( &$entries ) {
203 if ( isset( self::$entry->metas ) ) {
204 // add child entries to the parent
205 foreach ( self::$entry->metas as $meta_id => $meta_value ) {
206 if ( ! is_numeric( $meta_id ) || '' === $meta_value ) {
207 // if the hook is being used to include field keys in the metas array,
208 // we need to skip the keys and only process field ids
209 continue;
210 }
211
212 if ( ! isset( $entries[ self::$entry->parent_item_id ]->metas[ $meta_id ] ) ) {
213 $entries[ self::$entry->parent_item_id ]->metas[ $meta_id ] = array();
214 } elseif ( ! is_array( $entries[ self::$entry->parent_item_id ]->metas[ $meta_id ] ) ) {
215 // if the data is here, it should be an array but if this field has collected data
216 // both while inside and outside of the repeating section, it's possible this is a string
217 $entries[ self::$entry->parent_item_id ]->metas[ $meta_id ] = (array) $entries[ self::$entry->parent_item_id ]->metas[ $meta_id ];
218 }
219
220 //add the repeated values
221 $entries[ self::$entry->parent_item_id ]->metas[ $meta_id ][] = $meta_value;
222 }
223 $entries[ self::$entry->parent_item_id ]->metas += self::$entry->metas;
224 }
225
226 // add the embedded form id
227 if ( ! isset( $entries[ self::$entry->parent_item_id ]->embedded_fields ) ) {
228 $entries[ self::$entry->parent_item_id ]->embedded_fields = array();
229 }
230 $entries[ self::$entry->parent_item_id ]->embedded_fields[ self::$entry->id ] = self::$entry->form_id;
231 }
232
233 private static function add_field_values_to_csv( &$row ) {
234 foreach ( self::$fields as $col ) {
235 $field_value = isset( self::$entry->metas[ $col->id ] ) ? self::$entry->metas[ $col->id ] : false;
236
237 FrmAppHelper::unserialize_or_decode( $field_value );
238 self::add_array_values_to_columns( $row, compact( 'col', 'field_value' ) );
239
240 $field_value = apply_filters(
241 'frm_csv_value',
242 $field_value,
243 array(
244 'field' => $col,
245 'entry' => self::$entry,
246 'separator' => self::$separator,
247 )
248 );
249
250 if ( isset( $col->field_options['separate_value'] ) && $col->field_options['separate_value'] ) {
251 $sep_value = FrmEntriesHelper::display_value(
252 $field_value,
253 $col,
254 array(
255 'type' => $col->type,
256 'post_id' => self::$entry->post_id,
257 'show_icon' => false,
258 'entry_id' => self::$entry->id,
259 'sep' => self::$separator,
260 'embedded_field_id' => ( isset( self::$entry->embedded_fields ) && isset( self::$entry->embedded_fields[ self::$entry->id ] ) ) ? 'form' . self::$entry->embedded_fields[ self::$entry->id ] : 0,
261 )
262 );
263
264 $row[ $col->id . '_label' ] = $sep_value;
265 unset( $sep_value );
266 }
267
268 $row[ $col->id ] = $field_value;
269
270 unset( $col, $field_value );
271 }
272 }
273
274 /**
275 * @since 2.0.23
276 */
277 private static function add_array_values_to_columns( &$row, $atts ) {
278 if ( is_array( $atts['field_value'] ) ) {
279 foreach ( $atts['field_value'] as $key => $sub_value ) {
280 $column_key = $atts['col']->id . '_' . $key;
281 if ( ! is_numeric( $key ) && isset( self::$headings[ $column_key ] ) ) {
282 $row[ $column_key ] = $sub_value;
283 }
284 }
285 }
286 }
287
288 private static function add_entry_data_to_csv( &$row ) {
289 $row['created_at'] = FrmAppHelper::get_formatted_time( self::$entry->created_at, self::$wp_date_format, ' ' );
290 $row['updated_at'] = FrmAppHelper::get_formatted_time( self::$entry->updated_at, self::$wp_date_format, ' ' );
291 $row['user_id'] = self::$entry->user_id;
292 $row['updated_by'] = self::$entry->updated_by;
293 $row['is_draft'] = self::$entry->is_draft ? '1' : '0';
294 $row['ip'] = self::$entry->ip;
295 $row['id'] = self::$entry->id;
296 $row['item_key'] = self::$entry->item_key;
297 if ( self::has_parent_id() ) {
298 $row['parent_id'] = self::$entry->parent_item_id;
299 }
300 }
301
302 private static function print_csv_row( $rows ) {
303 $sep = '';
304
305 foreach ( self::$headings as $k => $heading ) {
306 $row = isset( $rows[ $k ] ) ? $rows[ $k ] : '';
307 if ( is_array( $row ) ) {
308 // implode the repeated field values
309 $row = implode( self::$separator, FrmAppHelper::array_flatten( $row, 'reset' ) );
310 }
311
312 $val = self::encode_value( $row );
313 if ( 'return' !== self::$line_break ) {
314 $val = str_replace( array( "\r\n", "\r", "\n" ), self::$line_break, $val );
315 }
316
317 echo $sep . '"' . $val . '"'; // WPCS: XSS ok.
318 $sep = self::$column_separator;
319
320 unset( $k, $row );
321 }
322 echo "\n";
323 }
324
325 public static function encode_value( $line ) {
326 if ( '' === $line ) {
327 return $line;
328 }
329
330 $convmap = false;
331
332 switch ( self::$to_encoding ) {
333 case 'macintosh':
334 // this map was derived from the differences between the MacRoman and UTF-8 Charsets
335 // Reference:
336 // - http://www.alanwood.net/demos/macroman.html
337 $convmap = array( 256, 304, 0, 0xffff, 306, 337, 0, 0xffff, 340, 375, 0, 0xffff, 377, 401, 0, 0xffff, 403, 709, 0, 0xffff, 712, 727, 0, 0xffff, 734, 936, 0, 0xffff, 938, 959, 0, 0xffff, 961, 8210, 0, 0xffff, 8213, 8215, 0, 0xffff, 8219, 8219, 0, 0xffff, 8227, 8229, 0, 0xffff, 8231, 8239, 0, 0xffff, 8241, 8248, 0, 0xffff, 8251, 8259, 0, 0xffff, 8261, 8363, 0, 0xffff, 8365, 8481, 0, 0xffff, 8483, 8705, 0, 0xffff, 8707, 8709, 0, 0xffff, 8711, 8718, 0, 0xffff, 8720, 8720, 0, 0xffff, 8722, 8729, 0, 0xffff, 8731, 8733, 0, 0xffff, 8735, 8746, 0, 0xffff, 8748, 8775, 0, 0xffff, 8777, 8799, 0, 0xffff, 8801, 8803, 0, 0xffff, 8806, 9673, 0, 0xffff, 9675, 63742, 0, 0xffff, 63744, 64256, 0, 0xffff );
338 break;
339 case 'ISO-8859-1':
340 $convmap = array( 256, 10000, 0, 0xffff );
341 }
342
343 if ( is_array( $convmap ) ) {
344 $line = mb_encode_numericentity( $line, $convmap, self::$charset );
345 }
346
347 if ( self::$to_encoding !== self::$charset ) {
348 $line = iconv( self::$charset, self::$to_encoding . '//IGNORE', $line );
349 }
350
351 return self::escape_csv( $line );
352 }
353
354 /**
355 * Escape a " in a csv with another "
356 *
357 * @since 2.0
358 */
359 public static function escape_csv( $value ) {
360 if ( '=' === $value[0] ) {
361 // escape the = to prevent vulnerability
362 $value = "'" . $value;
363 }
364 $value = str_replace( '"', '""', $value );
365
366 return $value;
367 }
368 }
369