Completed
Push — master ( 2e2e06...542ae8 )
by Stephanie
03:16
created

FrmCSVExportHelper::set_class_paramters()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 10
Code Lines 8

Duplication

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