PluginProbe ʕ •ᴥ•ʔ
Everest Forms – Contact Form, Payment Form, Quiz, Survey & Custom Form Builder with AI / 2.0.4
Everest Forms – Contact Form, Payment Form, Quiz, Survey & Custom Form Builder with AI v2.0.4
3.5.2 3.5.1 3.5.0 3.4.8 3.4.7 3.4.6 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.5.1 1.1.6 1.1.7 1.1.8 1.1.9 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.5.0 1.5.1 1.5.10 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.6.1 1.6.7 1.7.0 1.7.0.1 1.7.0.2 1.7.0.3 1.7.1 1.7.2 1.7.2.1 1.7.2.2 1.7.3 1.7.4 1.7.5 1.7.5.1 1.7.5.2 1.7.6 1.7.7 1.7.7.1 1.7.7.2 1.7.8 1.7.9 1.8.0 1.8.0.1 1.8.1 1.8.2 1.8.2.1 1.8.2.2 1.8.2.3 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9.0 1.9.0.1 1.9.1 1.9.2 1.9.3 1.9.4 1.9.4.1 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.0.1 2.0.1 2.0.2 2.0.3 2.0.3.1 2.0.4 2.0.4.1 2.0.5 2.0.6 2.0.7 2.0.8 2.0.8.1 2.0.9 3.0.0 3.0.0.1 3.0.1 3.0.2 3.0.3 3.0.3.1 3.0.4 3.0.4.1 3.0.4.2 3.0.5 3.0.5.1 3.0.5.2 3.0.6 3.0.6.1 3.0.7.1 3.0.8 3.0.8.1 3.0.9 3.0.9.1 3.0.9.2 3.0.9.3 3.0.9.4 3.0.9.5 3.1.0 3.1.1 3.1.2 3.2.0 3.2.1 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.3.0 3.4.0 3.4.1 3.4.2 3.4.2.1 3.4.3 3.4.4 3.4.5 trunk 1.0 1.0.1 1.0.2 1.0.3
everest-forms / includes / export / abstract-evf-csv-exporter.php
everest-forms / includes / export Last commit date
abstract-evf-csv-exporter.php 6 years ago class-evf-entry-csv-exporter.php 3 years ago
abstract-evf-csv-exporter.php
405 lines
1 <?php
2 /**
3 * Handles CSV export.
4 *
5 * @package EverestForms/Export
6 * @version 1.3.0
7 */
8
9 defined( 'ABSPATH' ) || exit;
10
11 /**
12 * EVF_CSV_Exporter Class.
13 */
14 abstract class EVF_CSV_Exporter {
15
16 /**
17 * Type of export used in filter names.
18 *
19 * @var string
20 */
21 protected $export_type = '';
22
23 /**
24 * Filename to export to.
25 *
26 * @var string
27 */
28 protected $filename = 'evf-export.csv';
29
30 /**
31 * Raw data to export.
32 *
33 * @var array
34 */
35 protected $row_data = array();
36
37 /**
38 * Columns ids and names.
39 *
40 * @var array
41 */
42 protected $column_names = array();
43
44 /**
45 * List of columns to export, or empty for all.
46 *
47 * @var array
48 */
49 protected $columns_to_export = array();
50
51 /**
52 * The delimiter parameter sets the field delimiter (one character only).
53 *
54 * @var string
55 */
56 protected $delimiter = ',';
57
58 /**
59 * Prepare data that will be exported.
60 */
61 abstract public function prepare_data_to_export();
62
63 /**
64 * Get quiz report in CSV format.
65 */
66 abstract public function get_quiz_report();
67
68 /**
69 * Return the delimiter to use in CSV file
70 *
71 * @since 1.7.0
72 * @return string
73 */
74 public function get_delimiter() {
75 return apply_filters( "everest_forms_{$this->export_type}_export_delimiter", $this->delimiter );
76 }
77
78 /**
79 * Return an array of supported column names and ids.
80 *
81 * @return array
82 */
83 public function get_column_names() {
84 return apply_filters( "everest_forms_{$this->export_type}_export_column_names", $this->column_names, $this );
85 }
86
87 /**
88 * Set column names.
89 *
90 * @param array $column_names Column names array.
91 */
92 public function set_column_names( $column_names ) {
93 $this->column_names = array();
94
95 foreach ( $column_names as $column_id => $column_name ) {
96 $this->column_names[ evf_clean( $column_id ) ] = evf_clean( $column_name );
97 }
98 }
99
100 /**
101 * Return an array of columns to export.
102 *
103 * @return array
104 */
105 public function get_columns_to_export() {
106 return $this->columns_to_export;
107 }
108
109 /**
110 * Set columns to export.
111 *
112 * @param array $columns Columns array.
113 */
114 public function set_columns_to_export( $columns ) {
115 $this->columns_to_export = array_map( 'evf_clean', $columns );
116 }
117
118 /**
119 * See if a column is to be exported or not.
120 *
121 * @param string $column_id ID of the column being exported.
122 * @return boolean
123 */
124 public function is_column_exporting( $column_id ) {
125 $column_id = strstr( $column_id, ':' ) ? current( explode( ':', $column_id ) ) : $column_id;
126 $columns_to_export = $this->get_columns_to_export();
127
128 if ( empty( $columns_to_export ) ) {
129 return true;
130 }
131
132 if ( in_array( $column_id, $columns_to_export, true ) || 'meta' === $column_id ) {
133 return true;
134 }
135
136 return false;
137 }
138
139 /**
140 * Return default columns.
141 *
142 * @return array
143 */
144 public function get_default_column_names() {
145 return array();
146 }
147
148 /**
149 * Do the export.
150 *
151 * @since 1.3.0
152 */
153 public function export() {
154 $this->prepare_data_to_export();
155 $this->send_headers();
156 $this->send_content( chr( 239 ) . chr( 187 ) . chr( 191 ) . $this->export_column_headers() . $this->get_csv_data() );
157 die();
158 }
159
160 /**
161 * Export quiz report.
162 */
163 public function export_quiz_report() {
164 $this->send_headers();
165 $this->send_content( $this->get_quiz_report() );
166 die();
167 }
168
169 /**
170 * Set the export headers.
171 *
172 * @since 1.3.0
173 */
174 public function send_headers() {
175 if ( function_exists( 'gc_enable' ) ) {
176 gc_enable(); // phpcs:ignore PHPCompatibility.FunctionUse.gc_enableFound
177 }
178 if ( function_exists( 'apache_setenv' ) ) {
179 @apache_setenv( 'no-gzip', 1 ); // @codingStandardsIgnoreLine
180 }
181 @ini_set( 'zlib.output_compression', 'Off' ); // @codingStandardsIgnoreLine
182 @ini_set( 'output_buffering', 'Off' ); // @codingStandardsIgnoreLine
183 @ini_set( 'output_handler', '' ); // @codingStandardsIgnoreLine
184 ignore_user_abort( true );
185 evf_set_time_limit( 0 );
186 evf_nocache_headers();
187 header( 'Content-Type: text/csv; charset=utf-8' );
188 header( 'Content-Disposition: attachment; filename=' . $this->get_filename() );
189 header( 'Pragma: no-cache' );
190 header( 'Expires: 0' );
191 }
192
193 /**
194 * Set filename to export to.
195 *
196 * @param string $filename Filename to export to.
197 */
198 public function set_filename( $filename ) {
199 $this->filename = sanitize_file_name( str_replace( '.csv', '', $filename ) . '.csv' );
200 }
201
202 /**
203 * Generate and return a filename.
204 *
205 * @return string
206 */
207 public function get_filename() {
208 return sanitize_file_name( apply_filters( "everest_forms_{$this->export_type}_export_get_filename", $this->filename ) );
209 }
210
211 /**
212 * Set the export content.
213 *
214 * @param string $csv_data All CSV content.
215 */
216 public function send_content( $csv_data ) {
217 echo $csv_data; // @codingStandardsIgnoreLine
218 }
219
220 /**
221 * Get CSV data for this export.
222 *
223 * @return string
224 */
225 protected function get_csv_data() {
226 return $this->export_rows();
227 }
228
229 /**
230 * Export column headers in CSV format.
231 *
232 * @since 3.1.0
233 * @return string
234 */
235 protected function export_column_headers() {
236 $columns = $this->get_column_names();
237 $export_row = array();
238 $buffer = fopen( 'php://output', 'w' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fopen
239 ob_start();
240
241 foreach ( $columns as $column_id => $column_name ) {
242 if ( ! $this->is_column_exporting( $column_id ) ) {
243 continue;
244 }
245 $export_row[] = $this->format_data( $column_name );
246 }
247
248 $this->fputcsv( $buffer, $export_row );
249
250 return ob_get_clean();
251 }
252
253 /**
254 * Get data that will be exported.
255 *
256 * @return array
257 */
258 protected function get_data_to_export() {
259 return $this->row_data;
260 }
261
262 /**
263 * Export rows in CSV format.
264 *
265 * @return string
266 */
267 protected function export_rows() {
268 $data = $this->get_data_to_export();
269 $buffer = fopen( 'php://output', 'w' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fopen
270 ob_start();
271
272 array_walk( $data, array( $this, 'export_row' ), $buffer );
273
274 return apply_filters( "everest_forms_{$this->export_type}_export_rows", ob_get_clean(), $this );
275 }
276
277 /**
278 * Export rows to an array ready for the CSV.
279 *
280 * @param array $row_data Data to export.
281 * @param string $key Column being exported.
282 * @param resource $buffer Output buffer.
283 */
284 protected function export_row( $row_data, $key, $buffer ) {
285 $columns = $this->get_column_names();
286 $export_row = array();
287
288 foreach ( $columns as $column_id => $column_name ) {
289 if ( ! $this->is_column_exporting( $column_id ) ) {
290 continue;
291 }
292 if ( isset( $row_data[ $column_id ] ) ) {
293 $export_row[] = $this->format_data( $row_data[ $column_id ] );
294 } else {
295 $export_row[] = '';
296 }
297 }
298
299 $this->fputcsv( $buffer, $export_row );
300 }
301
302 /**
303 * Escape a string to be used in a CSV context
304 *
305 * Malicious input can inject formulas into CSV files, opening up the possibility
306 * for phishing attacks and disclosure of sensitive information.
307 *
308 * Additionally, Excel exposes the ability to launch arbitrary commands through
309 * the DDE protocol.
310 *
311 * @see http://www.contextis.com/resources/blog/comma-separated-vulnerabilities/
312 * @see https://hackerone.com/reports/72785
313 *
314 * @param string $data CSV field to escape.
315 * @return string
316 */
317 public function escape_data( $data ) {
318 $active_content_triggers = array( '=', '+', '-', '@' );
319
320 if ( in_array( mb_substr( $data, 0, 1 ), $active_content_triggers, true ) ) { // @codingStandardsIgnoreLine
321 $data = "'" . $data;
322 }
323
324 return $data;
325 }
326
327 /**
328 * Format and escape data ready for the CSV file.
329 *
330 * @param string $data Data to format.
331 * @return string
332 */
333 public function format_data( $data ) {
334 if ( ! is_scalar( $data ) ) {
335 $data = ''; // Not supported.
336 } elseif ( is_bool( $data ) ) {
337 $data = $data ? 1 : 0;
338 }
339
340 $use_mb = function_exists( 'mb_convert_encoding' );
341
342 if ( $use_mb ) {
343 $encoding = mb_detect_encoding( $data, 'UTF-8, ISO-8859-1', true );
344 $data = 'UTF-8' === $encoding ? $data : utf8_encode( $data );
345 }
346
347 return $this->escape_data( $data );
348 }
349
350 /**
351 * Implode CSV cell values using commas by default, and wrapping values
352 * which contain the separator.
353 *
354 * @param array $values Values to implode.
355 * @return string
356 */
357 protected function implode_values( $values ) {
358 $values_to_implode = array();
359
360 // For checkbox and radio.
361 if ( ! empty( $values['label'] ) ) {
362 $values = $values['label'];
363 }
364
365 if ( is_array( $values ) ) {
366 foreach ( $values as $value ) {
367 $value = is_scalar( $value ) ? (string) $value : '';
368 $values_to_implode[] = str_replace( ',', '\\,', $value );
369 }
370 } else {
371 $values_to_implode[] = str_replace( ',', '\\,', $values );
372 }
373
374 return implode( ', ', $values_to_implode );
375 }
376
377 /**
378 * Write to the CSV file, ensuring escaping works across versions of
379 * PHP.
380 *
381 * PHP 5.5.4 uses '\' as the default escape character. This is not RFC-4180 compliant.
382 * \0 disables the escape character.
383 *
384 * @see https://bugs.php.net/bug.php?id=43225
385 * @see https://bugs.php.net/bug.php?id=50686
386 * @see https://github.com/woocommerce/woocommerce/issues/19514
387 *
388 * @param resource $buffer Resource we are writing to.
389 * @param array $export_row Row to export.
390 */
391 protected function fputcsv( $buffer, $export_row ) {
392 if ( version_compare( PHP_VERSION, '5.5.4', '<' ) ) {
393 ob_start();
394 $temp = fopen( 'php://output', 'w' ); // @codingStandardsIgnoreLine
395 fputcsv( $temp, $export_row, $this->get_delimiter(), '"' ); // @codingStandardsIgnoreLine
396 fclose( $temp ); // @codingStandardsIgnoreLine
397 $row = ob_get_clean();
398 $row = str_replace( '\\"', '\\""', $row );
399 fwrite( $buffer, $row ); // @codingStandardsIgnoreLine
400 } else {
401 fputcsv( $buffer, $export_row, $this->get_delimiter(), '"', "\0" ); // @codingStandardsIgnoreLine
402 }
403 }
404 }
405