Completed
Push — master ( 1da590...96f068 )
by Stephanie
05:18
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.4286
cc 3
eloc 8
nc 4
nop 0
1
<?php
2
3
class FrmCSVExportHelper{
1 ignored issue
show
Coding Style introduced by
Class name "" is not in camel caps format
Loading history...
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;
1 ignored issue
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
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
		self::$to_encoding = FrmAppHelper::get_post_param( 'csv_format', 'UTF-8', 'sanitize_text_field' );
76
	}
77
78
	private static function prepare_csv_headings() {
79
		$headings = array();
80
		self::csv_headings( $headings );
81
		$headings = apply_filters( 'frm_csv_columns', $headings, self::$form_id );
82
		self::$headings = $headings;
83
84
		self::print_csv_row( $headings );
85
	}
86
87
	private static function csv_headings( &$headings ) {
88
		foreach ( self::$fields as $col ) {
89
			if ( isset( $col->field_options['separate_value'] ) && $col->field_options['separate_value'] && ! in_array( $col->type, array( 'user_id', 'file', 'data', 'date' ) ) ) {
90
				$headings[ $col->id . '_label'] = strip_tags( $col->name . ' ' . __( '(label)', 'formidable' ) );
0 ignored issues
show
introduced by
Array keys should be surrounded by spaces unless they contain a string or an integer.
Loading history...
91
			}
92
93
			$headings[ $col->id ] = strip_tags( $col->name );
94
		}
95
96
		if ( self::$comment_count ) {
97
			for ( $i = 0; $i < self::$comment_count; $i++ ) {
98
				$headings[ 'comment' . $i ] = __( 'Comment', 'formidable' );
99
				$headings[ 'comment_user_id' . $i ] = __( 'Comment User', 'formidable' );
100
				$headings[ 'comment_created_at' . $i ] = __( 'Comment Date', 'formidable' );
101
			}
102
			unset($i);
103
		}
104
105
		$headings['created_at'] = __( 'Timestamp', 'formidable' );
106
		$headings['updated_at'] = __( 'Last Updated', 'formidable' );
107
		$headings['user_id'] = __( 'Created By', 'formidable' );
108
		$headings['updated_by'] = __( 'Updated By', 'formidable' );
109
		$headings['is_draft'] = __( 'Draft', 'formidable' );
110
		$headings['ip'] = __( 'IP', 'formidable' );
111
		$headings['id'] = __( 'ID', 'formidable' );
112
		$headings['item_key'] = __( 'Key', 'formidable' );
113
	}
114
115
	private static function prepare_next_csv_rows( $next_set ) {
116
		// order by parent_item_id so children will be first
117
		$entries = FrmEntry::getAll( array( 'or' => 1, 'id' => $next_set, 'parent_item_id' => $next_set ), ' ORDER BY parent_item_id DESC', '', true, false );
118
119
		foreach ( $entries as $k => $entry ) {
120
			self::$entry = $entry;
121
			unset( $entry );
122
123
			if ( self::$entry->form_id != self::$form_id ) {
124
				self::add_repeat_field_values_to_csv( $entries );
125
			} else {
126
				self::prepare_csv_row();
127
			}
128
		}
129
	}
130
131
	private static function prepare_csv_row() {
132
		$row = array();
133
		self::add_field_values_to_csv( $row );
134
		self::add_entry_data_to_csv( $row );
135
		$row = apply_filters( 'frm_csv_row', $row, array( 'entry' => self::$entry, 'date_format' => self::$wp_date_format ) );
136
		self::print_csv_row( $row );
137
	}
138
139
	private static function add_repeat_field_values_to_csv( &$entries ) {
140
		if ( isset( self::$entry->metas ) ) {
141
			// add child entries to the parent
142
			foreach ( self::$entry->metas as $meta_id => $meta_value ) {
143
				if ( ! is_numeric( $meta_id ) || $meta_value == '' ) {
144
					// if the hook is being used to include field keys in the metas array,
145
					// we need to skip the keys and only process field ids
146
					continue;
147
				}
148
149
				if ( ! isset( $entries[ self::$entry->parent_item_id ]->metas[ $meta_id ] ) ) {
150
					$entries[ self::$entry->parent_item_id ]->metas[ $meta_id ] = array();
151
				} else if ( ! is_array( $entries[ self::$entry->parent_item_id ]->metas[ $meta_id ] ) ) {
152
					// if the data is here, it should be an array but if this field has collected data
153
					// both while inside and outside of the repeating section, it's possible this is a string
154
					$entries[ self::$entry->parent_item_id ]->metas[ $meta_id ] = (array) $entries[ self::$entry->parent_item_id ]->metas[ $meta_id ];
155
				}
156
157
				//add the repeated values
158
				$entries[ self::$entry->parent_item_id ]->metas[ $meta_id ][] = $meta_value;
159
			}
160
			$entries[ self::$entry->parent_item_id ]->metas += self::$entry->metas;
161
		}
162
163
		// add the embedded form id
164
		if ( ! isset( $entries[ self::$entry->parent_item_id ]->embedded_fields ) ) {
165
			$entries[ self::$entry->parent_item_id ]->embedded_fields = array();
166
		}
167
		$entries[ self::$entry->parent_item_id ]->embedded_fields[ self::$entry->id ] = self::$entry->form_id;
168
	}
169
170
	private static function add_field_values_to_csv( &$row ) {
171
		foreach ( self::$fields as $col ) {
172
			$field_value = isset( self::$entry->metas[ $col->id ] ) ? self::$entry->metas[ $col->id ] : false;
173
174
			$field_value = maybe_unserialize( $field_value );
175
			$field_value = apply_filters( 'frm_csv_value', $field_value, array( 'field' => $col, 'entry' => self::$entry, 'separator' => self::$separator, ) );
176
177
			if ( isset( $col->field_options['separate_value'] ) && $col->field_options['separate_value'] ) {
178
				$sep_value = FrmEntriesHelper::display_value( $field_value, $col, array(
179
					'type' => $col->type, 'post_id' => self::$entry->post_id, 'show_icon' => false,
180
					'entry_id' => self::$entry->id, 'sep' => self::$separator,
181
					'embedded_field_id' => ( isset( self::$entry->embedded_fields ) && isset( self::$entry->embedded_fields[ $entry->id ] ) ) ? 'form' . self::$entry->embedded_fields[ self::$entry->id ] : 0,
0 ignored issues
show
Bug introduced by
The variable $entry does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
182
					) );
183
				$row[ $col->id . '_label' ] = $sep_value;
184
				unset( $sep_value );
185
			}
186
187
			$row[ $col->id ] = $field_value;
188
189
			unset( $col, $field_value );
190
		}
191
	}
192
193
	private static function add_entry_data_to_csv( &$row ) {
194
		$row['created_at'] = FrmAppHelper::get_formatted_time( self::$entry->created_at, self::$wp_date_format, ' ' );
195
		$row['updated_at'] = FrmAppHelper::get_formatted_time( self::$entry->updated_at, self::$wp_date_format, ' ' );
196
		$row['user_id'] = self::$entry->user_id;
197
		$row['updated_by'] = self::$entry->updated_by;
198
		$row['is_draft'] = self::$entry->is_draft ? '1' : '0';
199
		$row['ip'] = self::$entry->ip;
200
		$row['id'] = self::$entry->id;
201
		$row['item_key'] = self::$entry->item_key;
202
	}
203
204
	private static function print_csv_row( $rows ) {
205
		$col_count = count( $rows );
206
		$this_col = 0;
207
		foreach ( $rows as $k => $row ) {
208
			$this_col++;
209
210
			if ( ! isset( self::$headings[ $k ] ) ) {
211
				// this column has been removed from the csv, so skip it
212
				continue;
213
			}
214
215
			if ( is_array( $row ) ) {
216
				// implode the repeated field values
217
				$row = implode( self::$separator, FrmAppHelper::array_flatten( $row, 'reset' ) );
218
			}
219
220
			$val = self::encode_value( $row );
221
			if ( self::$line_break != 'return' ) {
1 ignored issue
show
introduced by
Found "!= '". Use Yoda Condition checks, you must
Loading history...
222
				$val = str_replace( array( "\r\n", "\r", "\n" ), self::$line_break, $val );
223
			}
224
			echo '"' . $val . '"';
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$val'
Loading history...
225
			if ( $this_col != $col_count ) {
226
				echo self::$column_separator;
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not 'self'
Loading history...
227
			}
228
			unset( $k, $row );
229
		}
230
		echo "\n";
231
	}
232
233
	public static function encode_value( $line ) {
234
		if ( $line == '' ) {
235
			return $line;
236
		}
237
238
        $convmap = false;
239
240
		switch ( self::$to_encoding ) {
241
            case 'macintosh':
242
            // this map was derived from the differences between the MacRoman and UTF-8 Charsets
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 tabs, found 3
Loading history...
243
            // Reference:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 tabs, found 3
Loading history...
244
            //   - http://www.alanwood.net/demos/macroman.html
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 tabs, found 3
Loading history...
245
                $convmap = array(
246
                    256, 304, 0, 0xffff,
247
                    306, 337, 0, 0xffff,
248
                    340, 375, 0, 0xffff,
249
                    377, 401, 0, 0xffff,
250
                    403, 709, 0, 0xffff,
251
                    712, 727, 0, 0xffff,
252
                    734, 936, 0, 0xffff,
253
                    938, 959, 0, 0xffff,
254
                    961, 8210, 0, 0xffff,
255
                    8213, 8215, 0, 0xffff,
256
                    8219, 8219, 0, 0xffff,
257
                    8227, 8229, 0, 0xffff,
258
                    8231, 8239, 0, 0xffff,
259
                    8241, 8248, 0, 0xffff,
260
                    8251, 8259, 0, 0xffff,
261
                    8261, 8363, 0, 0xffff,
262
                    8365, 8481, 0, 0xffff,
263
                    8483, 8705, 0, 0xffff,
264
                    8707, 8709, 0, 0xffff,
265
                    8711, 8718, 0, 0xffff,
266
                    8720, 8720, 0, 0xffff,
267
                    8722, 8729, 0, 0xffff,
268
                    8731, 8733, 0, 0xffff,
269
                    8735, 8746, 0, 0xffff,
270
                    8748, 8775, 0, 0xffff,
271
                    8777, 8799, 0, 0xffff,
272
                    8801, 8803, 0, 0xffff,
273
                    8806, 9673, 0, 0xffff,
274
                    9675, 63742, 0, 0xffff,
275
                    63744, 64256, 0, 0xffff,
276
                );
277
            break;
278
            case 'ISO-8859-1':
279
                $convmap = array(256, 10000, 0, 0xffff);
0 ignored issues
show
introduced by
No space after opening parenthesis of array is bad style
Loading history...
introduced by
No space before closing parenthesis of array is bad style
Loading history...
280
            break;
281
        }
282
283
		if ( is_array( $convmap ) ) {
284
			$line = mb_encode_numericentity( $line, $convmap, self::$charset );
285
		}
286
287
		if ( self::$to_encoding != self::$charset ) {
288
			$line = iconv( self::$charset, self::$to_encoding . '//IGNORE', $line );
289
		}
290
291
		return self::escape_csv( $line );
292
    }
293
294
	/**
295
	 * Escape a " in a csv with another "
296
	 * @since 2.0
297
	 */
298
	public static function escape_csv( $value ) {
299
		if ( $value[0] == '=' ) {
300
			// escape the = to prevent vulnerability
301
			$value = "'" . $value;
302
		}
303
		$value = str_replace( '"', '""', $value );
304
		return $value;
305
	}
306
}