PluginProbe ʕ •ᴥ•ʔ
Kirki – Freeform Page Builder, Website Builder & Customizer / 6.1.1
Kirki – Freeform Page Builder, Website Builder & Customizer v6.1.1
6.1.1 6.1.0 6.0.14 6.0.13 6.0.12 6.0.11 6.0.10 6.0.9 6.0.8 6.0.7 6.0.6 6.0.5 6.0.4 6.0.3 6.0.2 6.0.1 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 4.0.19 4.0.20 4.0.21 4.0.22 4.0.23 4.0.24 4.1 4.2.0 5.0.0 5.1.0 5.1.1 5.2.0 5.2.1 5.2.2 5.2.3 6.0.0 trunk 3.0.40 3.0.41 3.0.42 3.0.43 3.0.44 3.0.45 3.1.0 3.1.1 3.1.2
kirki / includes / Ajax / Form.php
kirki / includes / Ajax Last commit date
Collaboration 2 weeks ago Apps.php 2 weeks ago Collection.php 1 month ago Comments.php 2 months ago DynamicContent.php 1 month ago ExportImport.php 2 days ago Form.php 2 weeks ago Media.php 2 days ago Page.php 2 days ago PageSettings.php 2 weeks ago RBAC.php 2 weeks ago Symbol.php 2 weeks ago Taxonomy.php 2 months ago TemplateExportImport.php 2 months ago UserData.php 2 weeks ago Users.php 2 months ago Walkthrough.php 2 months ago WordpressData.php 2 months ago WpAdmin.php 2 days ago
Form.php
854 lines
1 <?php
2 /**
3 * Manage dynamic form data api calls
4 *
5 * @package kirki
6 */
7
8 namespace Kirki\Ajax;
9
10 if ( ! defined( 'ABSPATH' ) ) {
11 exit; // Exit if accessed directly.
12 }
13 use Kirki\DbQueryUtils;
14 use Kirki\HelperFunctions;
15 use Exception;
16
17 define(
18 'KIRKI_CONDITIONS_TEXT_TYPE',
19 array(
20 'contains' => 'contains',
21 'does_not_contain' => 'does_not_contain',
22 'start_with' => 'start_with',
23 'end_with' => 'end_with',
24 'is' => 'is',
25 'is_not' => 'is_not',
26 'cell_is_not_empty' => 'cell_is_not_empty',
27 'cell_is_empty' => 'cell_is_empty',
28 )
29 );
30
31 define(
32 'KIRKI_CONDITIONS_DATE_TYPE',
33 array(
34 'today' => 'today',
35 'this_week' => 'this_week',
36 'last_month' => 'last_month',
37 'last_year' => 'last_year',
38 'between' => 'between',
39 'before' => 'before',
40 'after' => 'after',
41 'is_not' => 'is_not',
42 )
43 );
44
45 define(
46 'KIRKI_FORM_TABLE_SORT_OPTIONS',
47 array(
48 'new_to_old' => 'new_to_old',
49 'old_to_new' => 'old_to_new',
50 'a_z' => 'a_z',
51 'z_a' => 'z_a',
52 )
53 );
54
55 /**
56 * Form API Class
57 */
58 class Form {
59
60 /**
61 * Get forms
62 *
63 * @return void wp send json
64 */
65 public static function get_forms() {
66 global $wpdb;
67 //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
68 $limit = HelperFunctions::sanitize_text( isset( $_GET['limit'] ) && is_numeric( $_GET['limit'] ) ? absint( $_GET['limit'] ) : 10 );
69 //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
70 $page = HelperFunctions::sanitize_text( isset( $_GET['page'] ) && is_numeric( $_GET['page'] ) ? absint( $_GET['page'] ) : 0 );
71 $offset = $page * $limit;
72
73 $query = $wpdb->prepare(
74 'SELECT * FROM %1s LIMIT %d OFFSET %d',
75 $wpdb->prefix . KIRKI_FORM_TABLE,
76 $limit,
77 $offset
78 );
79 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared
80 $forms = $wpdb->get_results( $query, ARRAY_A );
81
82 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared
83 $total_query = $wpdb->prepare( 'SELECT COUNT(*) as total FROM %1s', $wpdb->prefix . KIRKI_FORM_TABLE );
84 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared
85 $total_count_data = $wpdb->get_results( $total_query, ARRAY_A );
86 $total_count = (int) $total_count_data[0]['total'];
87
88 $done = ( $total_count - ( $offset + $limit ) ) <= 0;
89
90 foreach ( $forms as &$form ) {
91 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
92 $result = $wpdb->get_results(
93 $wpdb->prepare(
94 'SELECT COUNT(DISTINCT timestamp) AS total_entries FROM %1s WHERE form_id=%d',
95 $wpdb->prefix . KIRKI_FORM_DATA_TABLE,
96 $form['id']
97 ),
98 ARRAY_A
99 );
100 $form['total_entries'] = $result[0]['total_entries'];
101 }
102
103 wp_send_json( array(
104 'data' => $forms,
105 'done' => $done,
106 'total_count' => $total_count
107 ) );
108 }
109
110 /**
111 * Get forms data
112 *
113 * @return void wp send json
114 */
115 public static function get_form_data() {
116 global $wpdb;
117 //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
118 $form_id = HelperFunctions::sanitize_text( isset( $_GET['form_id'] ) ? absint( $_GET['form_id'] ) : null );
119 //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
120 $limit = HelperFunctions::sanitize_text( isset( $_GET['limit'] ) && is_numeric( $_GET['limit'] ) ? absint( $_GET['limit'] ) : 10 );
121 //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
122 $page = HelperFunctions::sanitize_text( isset( $_GET['page'] ) && is_numeric( $_GET['page'] ) ? absint( $_GET['page'] ) : 0 );
123 $offset = $page * $limit;
124 //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
125 $filters = HelperFunctions::sanitize_text( isset( $_GET['filters'] ) ? $_GET['filters'] : null );
126 //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
127 $sort = HelperFunctions::sanitize_text( isset( $_GET['sort'] ) ? $_GET['sort'] : null );
128 //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
129 $search_text = HelperFunctions::sanitize_text( isset( $_GET['search_text'] ) ? $_GET['search_text'] : '' );
130
131 $filters = $filters ? json_decode( stripslashes( $filters ), true ) : null;
132 $sort = $sort ? json_decode( stripslashes( $sort ), true ) : null;
133
134 $timestamp_query = self::prepare_timestamp_query(
135 $form_id,
136 array(
137 'filters' => $filters,
138 'sort' => $sort,
139 'search_text' => $search_text,
140 )
141 );
142 $count_query = 'SELECT COUNT(timestamp) AS total FROM (' . $timestamp_query . ') AS T';
143 $limit_query = $wpdb->prepare( ' LIMIT %d, %d', array( $offset, $limit ) );
144
145 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared
146 $total_count_data = $wpdb->get_results( $count_query, ARRAY_A );
147 $total_count = (int) $total_count_data[0]['total'];
148
149 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared
150 $timestamps = $wpdb->get_results( $timestamp_query . $limit_query, ARRAY_A );
151 $form_data = array();
152 if ( count( $timestamps ) ) {
153 $form_data_query = self::prepare_form_data_query( $timestamps, isset( $sort ) ? true : false );
154 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared
155 $form_data = $wpdb->get_results( $form_data_query, ARRAY_A );
156 }
157 $table_name = $wpdb->prefix . KIRKI_FORM_DATA_TABLE;
158 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
159 $input_keys = $wpdb->get_col(
160 $wpdb->prepare(
161 'SELECT DISTINCT input_key from %1s WHERE form_id=%d',
162 $table_name,
163 $form_id
164 )
165 );
166
167 $res = self::prepare_form_data_response(
168 $form_data,
169 $input_keys,
170 $total_count,
171 $total_count - ( $offset + $limit ) <= 0
172 );
173 wp_send_json( $res );
174 }
175
176 /**
177 * Delete a from
178 *
179 * @return void wp send json
180 */
181 public static function delete_form() {
182 global $wpdb;
183
184 //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
185 $form_id = HelperFunctions::sanitize_text( isset( $_REQUEST['form_id'] ) ? absint( $_REQUEST['form_id'] ) : null );
186
187 // check if the form id is not null
188 if ( is_null( $form_id ) ) {
189 wp_send_json( false );
190 };
191
192 $form_table = $wpdb->prefix . KIRKI_FORM_TABLE;
193
194 // check if the form id exist in the database
195 $form_exist = $wpdb->get_row(
196 $wpdb->prepare(
197 "SELECT * FROM $form_table WHERE id=%d",
198 $form_id
199 )
200 );
201
202 // if the form id does not exist
203 if ( empty( $form_exist ) ) {
204 wp_send_json( false );
205 }
206
207 try {
208 $wpdb->query( 'START TRANSACTION' );
209
210 // Firstly, get file ids and delete the attachments
211 $form_data_table = esc_sql( $wpdb->prefix . KIRKI_FORM_DATA_TABLE );
212 $file_type_ids = $wpdb->get_col(
213 $wpdb->prepare(
214 "SELECT input_value FROM $form_data_table WHERE form_id=%d AND input_type='file'",
215 $form_id
216 )
217 );
218
219 if ( count( $file_type_ids ) ) {
220 self::delete_attachments( $file_type_ids );
221 }
222
223 // Secondly, delete all data related to the form id (foreign key) from the form data table first
224 $form_data_table_name = $wpdb->prefix . KIRKI_FORM_DATA_TABLE;
225 $form_data_delete_query = $wpdb->prepare(
226 "DELETE FROM $form_data_table_name WHERE form_id=%s",
227 $form_id
228 );
229
230 // Finally, delete the form
231 $form_delete_query = $wpdb->prepare(
232 "DELETE FROM $form_table WHERE id=%s",
233 $form_id
234 );
235
236 // execute the delete queries
237 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared
238 $form_data_delete_res = $wpdb->query( $form_data_delete_query );
239 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared
240 $form_delete_res = $wpdb->query( $form_delete_query );
241
242 if ( $form_data_delete_res !== false && $form_delete_res !== false && $form_delete_res > 0 ) {
243 $wpdb->query( 'COMMIT' );
244 wp_send_json( true );
245 } else {
246 $wpdb->query( 'ROLLBACK' );
247 }
248 } catch ( Exception $e ) {
249 $wpdb->query( 'ROLLBACK' );
250 }
251
252 wp_send_json( false );
253 }
254
255 /**
256 * Delete from rows
257 *
258 * @return void wp send json
259 */
260 public static function delete_form_row() {
261 global $wpdb;
262
263 //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
264 $form_id = HelperFunctions::sanitize_text( isset( $_REQUEST['form_id'] ) ? absint( $_REQUEST['form_id'] ) : null );
265 //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
266 $row_ids = json_decode( HelperFunctions::sanitize_text( $_REQUEST['row_ids'] ), true );
267
268
269 if ( ! is_null( $form_id ) && ! empty( $row_ids ) ) {
270 if ( is_array( $row_ids ) ) {
271 }
272
273 $form_data_table_name = esc_sql( $wpdb->prefix . KIRKI_FORM_DATA_TABLE );
274
275 // get file ids from as input value from the form data table
276 $file_type_ids = $wpdb->get_col(
277 $wpdb->prepare(
278 "SELECT input_value FROM $form_data_table_name WHERE form_id=%s AND input_type='file' AND timestamp IN (" . implode( ',', array_fill( 0, count( $row_ids ), '%s' ) ) . ")",
279 array_merge( array( $form_id ), $row_ids )
280 )
281 );
282
283 if ( count( $file_type_ids ) ) {
284 self::delete_attachments( $file_type_ids );
285 }
286
287 // delete the rows from the form data table
288 $res = $wpdb->query(
289 $wpdb->prepare(
290 "DELETE FROM $form_data_table_name WHERE form_id=%s AND timestamp IN (" . implode( ',', array_fill( 0, count( $row_ids ), '%s' ) ) . ")",
291 array_merge( array( $form_id ), $row_ids )
292 )
293 );
294
295 if ( $res !== false ) {
296 wp_send_json( true );
297 }
298 }
299 wp_send_json( false );
300 }
301
302 /**
303 * Update form single data
304 *
305 * @return void wp send json
306 */
307 public static function update_form_row() {
308 //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
309 $form_id = HelperFunctions::sanitize_text( isset( $_REQUEST['form_id'] ) ? absint( $_REQUEST['form_id'] ) : null );
310 //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
311 $row_id = HelperFunctions::sanitize_text( isset( $_REQUEST['row_id'] ) ? absint( $_REQUEST['row_id'] ) : null );
312 //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
313 $data = HelperFunctions::sanitize_text( isset( $_REQUEST['data'] ) ? $_REQUEST['data'] : null );
314 $flag = false;
315
316 $data = $data ? json_decode( stripslashes( $data ), true ) : null;
317
318 if ( is_array( $data ) && count( $data ) ) {
319 foreach ( $data as $column => $value ) {
320 $res = self::update_form_cell( $form_id, $row_id, $column, $value );
321 if ( $res ) {
322 $flag = true;
323 }
324 }
325 }
326 wp_send_json( $flag );
327 }
328
329 /**
330 * Update forn data single cell
331 *
332 * @param int $form_id form id.
333 * @param int $row_id row id.
334 * @param string $column column name.
335 * @param int $value value.
336 *
337 * @return boolean boolean
338 */
339 private static function update_form_cell( $form_id, $row_id, $column, $value ) {
340 global $wpdb;
341
342 if ( ! empty( $form_id ) && ! empty( $row_id ) && ! empty( $column ) ) {
343 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
344 $res = $wpdb->update(
345 $wpdb->prefix . KIRKI_FORM_DATA_TABLE,
346 array(
347 'input_value' => $value,
348 ),
349 array(
350 'form_id' => $form_id,
351 'timestamp' => $row_id,
352 'input_key' => $column,
353 ),
354 array(
355 '%s',
356 ),
357 array(
358 '%1s',
359 '%1s',
360 '%s',
361 )
362 );
363
364 if ( $res !== false ) {
365 return true;
366 }
367 }
368 return false;
369 }
370
371 /**
372 * Download form data
373 *
374 * @return void download data
375 */
376 public static function download_form_data() {
377 global $wpdb;
378 //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
379 $form_id = HelperFunctions::sanitize_text( isset( $_REQUEST['form_id'] ) ? absint( $_REQUEST['form_id'] ) : null );
380 //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
381 $all_selected = isset( $_REQUEST['allSelected'] ) ? json_decode( HelperFunctions::sanitize_text( $_REQUEST['allSelected'] ) ) : false;
382 //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
383 $entries = isset( $_REQUEST['entries'] ) ? explode( ',', HelperFunctions::sanitize_text( $_REQUEST['entries'] ) ) : null;
384 //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
385 $filter = isset( $_REQUEST['filter'] ) ? json_decode( stripslashes( HelperFunctions::sanitize_text( $_REQUEST['filter'] ) ), true ) : null;
386 //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
387 $search_text = isset( $_REQUEST['searchText'] ) ? HelperFunctions::sanitize_text( $_REQUEST['searchText'] ) : null;
388 $columns_to_select = 'id, form_id, timestamp, input_key, input_value, created_at, updated_at';
389
390 $table_name = $wpdb->prefix . KIRKI_FORM_DATA_TABLE;
391
392 if ( $all_selected ) {
393 if ( ! empty( $filter ) && ! empty( $search_text ) ) {
394 $filter_query = self::prepare_filter_query( $form_id, $filter );
395 $text_query = 'SELECT DISTINCT timestamp FROM (' . $filter_query . ') AS T WHERE ' . DbQueryUtils::contains( 'input_value', $search_text );
396 $query = 'SELECT ' . $columns_to_select . ' FROM ' . $wpdb->prefix . KIRKI_FORM_DATA_TABLE . ' WHERE form_id=' . $form_id . ' AND timestamp IN(' . $text_query . ')';
397 } elseif ( ! empty( $filter ) && empty( $search_text ) ) {
398 $filter_query = self::prepare_filter_query( $form_id, $filter );
399 $query = $filter_query;
400 } elseif ( ! empty( $search_text ) && empty( $filter ) ) {
401 $text_query = 'SELECT DISTINCT timestamp FROM ' . $wpdb->prefix . KIRKI_FORM_DATA_TABLE . ' WHERE ' . DbQueryUtils::contains( 'input_value', $search_text );
402 $query = 'SELECT ' . $columns_to_select . ' FROM ' . $wpdb->prefix . KIRKI_FORM_DATA_TABLE . ' WHERE timestamp IN(' . $text_query . ')';
403 } else {
404 $query = 'SELECT ' . $columns_to_select . ' FROM ' . $wpdb->prefix . KIRKI_FORM_DATA_TABLE . ' WHERE form_id=' . $form_id;
405 }
406 } elseif ( isset( $entries ) && count( $entries ) ) {
407 $placeholder = array();
408 $values = array();
409
410 foreach ( $entries as $entry ) {
411 $placeholder[] = '%s';
412 $values[] = $entry;
413 }
414 $placeholder_str = implode( ', ', $placeholder );
415
416 // Escape table name for security
417 $table_name_esc = esc_sql( $table_name );
418 $columns_to_select_esc = esc_sql( $columns_to_select );
419
420 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
421 $query = $wpdb->prepare(
422 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
423 "SELECT $columns_to_select_esc FROM $table_name_esc WHERE form_id=%d AND timestamp IN($placeholder_str)",
424 array_merge(
425 array(
426 $form_id,
427 ),
428 $entries
429 )
430 );
431 }
432
433 $form_data = $wpdb->get_results( $query, ARRAY_A ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
434 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
435 $table_name_esc = esc_sql( $table_name );
436 $input_keys = $wpdb->get_col(
437 $wpdb->prepare(
438 "SELECT DISTINCT input_key from $table_name_esc WHERE form_id=%d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
439 $form_id
440 )
441 );
442 $res = self::prepare_form_data_response(
443 $form_data,
444 $input_keys,
445 0,
446 true
447 );
448
449 self::array_to_csv_download( $res['data'], $input_keys );
450 die();
451 }
452
453 /**
454 * Array to CSV download
455 *
456 * @param array $array data in array.
457 * @param array $columns heading names.
458 * @param string $filename filename.
459 * @param string $delimiter delimiter.
460 *
461 * @return void
462 */
463 private static function array_to_csv_download( $array, $columns, $filename = 'form-data.csv', $delimiter = ',' ) {
464 header( 'Content-Type: application/csv' );
465 header( 'Content-Disposition: attachment; filename="' . $filename . '";' );
466
467 // open the "output" stream.
468 // see http://www.php.net/manual/en/wrappers.php.php#refsect2-wrappers.php-unknown-unknown-unknown-descriptioq.
469 $f = fopen( 'php://output', 'w' );
470 $file_content = array();
471
472 foreach ( $array as $line ) {
473 $row = array();
474 foreach ( $columns as $column ) {
475 $row[] = isset( $line[ $column ] ) ? $line[ $column ] : '';
476 }
477 $file_content[] = $row;
478 }
479
480 // Column names.
481 fputcsv( $f, $columns, $delimiter );
482 // Data.
483 foreach ( $file_content as $row ) {
484 fputcsv( $f, $row, $delimiter );
485 }
486
487 // php://output stream auto-closes when script ends, no need to manually close
488 }
489
490 /**
491 * Prepare timestamp query
492 *
493 * @param int $form_id form id.
494 * @param object $filter_conditions filter conditions.
495 * @return string query.
496 */
497 private static function prepare_timestamp_query( $form_id, $filter_conditions ) {
498 global $wpdb;
499 $query = '';
500 $filters = null;
501 $sort = null;
502 $search_text = $filter_conditions['search_text'];
503 $text_search_query = '';
504
505 $table_name = $wpdb->prefix . KIRKI_FORM_DATA_TABLE;
506
507 if ( ! empty( $filter_conditions['sort'] ) ) {
508 $sort = $filter_conditions['sort'];
509 }
510 if ( ! empty( $filter_conditions['filters'] ) ) {
511 $filters = $filter_conditions['filters'];
512 }
513
514 if ( ! empty( $search_text ) ) {
515 $text_search_query = DbQueryUtils::contains( 'input_value', $search_text );
516 }
517
518 if ( isset( $sort, $filters ) && count( $sort ) && count( $filters ) ) {
519 $filter_query = self::prepare_filter_query( $form_id, $filters );
520 $sort_query = self::prepare_sort_query( $sort );
521 if ( ! empty( $text_search_query ) ) {
522 $filter_query_mod = $filter_query;
523 $filter_query = 'SELECT * FROM (' . $filter_query_mod . ') AS T WHERE ' . $text_search_query;
524 }
525
526 $query = 'SELECT DISTINCT(timestamp) FROM (' . $filter_query . ') AS T' . $sort_query;
527 } elseif ( isset( $sort ) && count( $sort ) && ! isset( $filters ) ) {
528 $additional_search_query = '';
529 if ( ! empty( $text_search_query ) ) {
530 $additional_search_query = ' AND ' . $text_search_query;
531 }
532
533 $query = $wpdb->prepare( 'SELECT DISTINCT timestamp FROM (SELECT * FROM %1s WHERE form_id=%d', $table_name, $form_id ) . $additional_search_query . ') AS T' . self::prepare_sort_query( $sort );
534 } elseif ( isset( $filters ) && count( $filters ) && ! isset( $sort ) ) {
535 $filter_query = self::prepare_filter_query( $form_id, $filters );
536 if ( ! empty( $text_search_query ) ) {
537 $filter_query_mod = $filter_query;
538 $filter_query = 'SELECT * FROM (' . $filter_query_mod . ') AS T WHERE ' . $text_search_query;
539 }
540
541 $query = 'SELECT DISTINCT(timestamp) FROM (' . $filter_query . ') AS T';
542 } else {
543 $additional_search_query = '';
544 if ( ! empty( $text_search_query ) ) {
545 $additional_search_query = ' AND ' . $text_search_query;
546 }
547 $query = $wpdb->prepare( 'SELECT DISTINCT(timestamp) FROM %1s WHERE form_id=%d', $table_name, $form_id ) . $additional_search_query;
548 }
549
550 return $query;
551 }
552
553 /**
554 * Prepare sort query
555 *
556 * @param string $sort sort data.
557 * @return string sort query
558 */
559 private static function prepare_sort_query( $sort ) {
560 global $wpdb;
561 $sort_query = '';
562 $sort_clause = '';
563 $constant_fields = array( 'created_at', 'updated_at' );
564 $field_name = $sort['field_name'];
565 $sort_option = $sort['option'];
566
567 switch ( $sort_option ) {
568 case 'new_to_old':
569 case 'z_a': {
570 $sort_clause = 'DESC';
571 break;
572 }
573
574 case 'old_to_new':
575 case 'a_z':
576 default:
577 $sort_clause = 'ASC';
578 break;
579 }
580
581 if ( isset( $sort ) ) {
582 if ( in_array( $field_name, $constant_fields, true ) ) {
583 $sort_query = $wpdb->prepare( ' ORDER BY %1s %1s', array( $field_name, $sort_clause ) );
584 } else {
585 $sort_column_name = '';
586
587 switch ( $sort_option ) {
588 case 'old_to_new':
589 //phpcs:ignore PSR2.ControlStructures.SwitchDeclaration.WrongOpenercase
590 case 'new_to_old': {
591 $sort_column_name = 'created_at';
592 break;
593 }
594
595 case 'a_z':
596 case 'z_a':
597 //phpcs:ignore PSR2.ControlStructures.SwitchDeclaration.WrongOpenerdefault
598 default: {
599 $sort_column_name = 'input_value';
600 break;
601 }
602 }
603
604 // $sort_query = " WHERE input_key='" . $field_name . "' ORDER BY " . $sort_column_name . $sort_clause;
605
606 $sort_query = $wpdb->prepare( ' WHERE input_key=%s ORDER BY %1s %1s', array( $field_name, $sort_column_name, $sort_clause ) );
607 }
608 }
609
610 return $sort_query;
611 }
612
613
614 /**
615 * Prepare filter query
616 *
617 * @param int $form_id form id.
618 * @param object $filters filter conditions.
619 * @return string query.
620 */
621 private static function prepare_filter_query( $form_id, $filters ) {
622 global $wpdb;
623 $table_name = $wpdb->prefix . KIRKI_FORM_DATA_TABLE;
624 $groups = array();
625 $constant_fields = array( 'created_at', 'updated_at' );
626 $from_query = $table_name;
627
628 if ( is_array( $filters ) ) {
629 foreach ( $filters as $filter ) {
630 if ( isset( $groups[ $filter['fieldName'] ] ) ) {
631 $groups[ $filter['fieldName'] ][] = $filter;
632 } else {
633 $groups[ $filter['fieldName'] ] = array( $filter );
634 }
635 }
636
637 foreach ( $groups as $group_name => $group ) {
638 $this_query_list = array();
639
640 foreach ( $group as $filter ) {
641 $this_query = '(';
642 if ( isset( $filter['fieldName'], $filter['condition'] ) ) {
643 $condition = $filter['condition'];
644 $column_name = '';
645
646 if ( ! in_array( $group_name, $constant_fields, true ) ) {
647 $column_name = 'input_value';
648 $this_query .= $wpdb->prepare( 'input_key=%s AND ', array( $group_name ) );
649 } else {
650 $column_name = $group_name;
651 }
652
653 switch ( $condition ) {
654 case KIRKI_CONDITIONS_TEXT_TYPE['contains']: {
655 $value = $filter['value'];
656 $this_query .= DbQueryUtils::contains( $column_name, $value );
657 break;
658 }
659
660 case KIRKI_CONDITIONS_TEXT_TYPE['does_not_contain']: {
661 $value = $filter['value'];
662 $this_query .= DbQueryUtils::does_not_contain( $column_name, $value );
663 break;
664 }
665
666 case KIRKI_CONDITIONS_TEXT_TYPE['start_with']: {
667 $value = $filter['value'];
668 $this_query .= DbQueryUtils::start_with( $column_name, $value );
669 break;
670 }
671
672 case KIRKI_CONDITIONS_TEXT_TYPE['end_with']: {
673 $value = $filter['value'];
674 $this_query .= DbQueryUtils::end_with( $column_name, $value );
675 break;
676 }
677
678 case KIRKI_CONDITIONS_TEXT_TYPE['is']: {
679 $value = $filter['value'];
680 $this_query .= DbQueryUtils::is( $column_name, $value );
681 break;
682 }
683
684 case KIRKI_CONDITIONS_TEXT_TYPE['is_not']: {
685 $value = $filter['value'];
686 $this_query .= DbQueryUtils::is_not( $column_name, $value );
687 break;
688 }
689
690 case KIRKI_CONDITIONS_TEXT_TYPE['cell_is_not_empty']: {
691 $this_query .= DbQueryUtils::cell_is_not_empty( $column_name );
692 break;
693 }
694
695 case KIRKI_CONDITIONS_TEXT_TYPE['cell_is_empty']: {
696 $this_query .= DbQueryUtils::cell_is_empty( $column_name );
697 break;
698 }
699
700 case KIRKI_CONDITIONS_DATE_TYPE['today']: {
701 $this_query .= DbQueryUtils::today( $column_name );
702 break;
703 }
704
705 case KIRKI_CONDITIONS_DATE_TYPE['this_week']: {
706 $this_query .= DbQueryUtils::this_week( $column_name );
707 break;
708 }
709
710 case KIRKI_CONDITIONS_DATE_TYPE['last_month']: {
711 $this_query .= DbQueryUtils::last_month( $column_name );
712 break;
713 }
714
715 case KIRKI_CONDITIONS_DATE_TYPE['last_year']: {
716 $this_query .= DbQueryUtils::last_year( $column_name );
717 break;
718 }
719
720 case KIRKI_CONDITIONS_DATE_TYPE['between']: {
721 $start = $filter['from'];
722 $end = $filter['to'];
723 $this_query .= DbQueryUtils::between( $column_name, $start, $end );
724 break;
725 }
726
727 case KIRKI_CONDITIONS_DATE_TYPE['before']: {
728 $date = $filter['value'];
729 $this_query .= DbQueryUtils::before( $column_name, $date );
730 break;
731 }
732
733 case KIRKI_CONDITIONS_DATE_TYPE['after']: {
734 $date = $filter['value'];
735 $this_query .= DbQueryUtils::after( $column_name, $date );
736 break;
737 }
738
739 default:
740 break;
741 }
742 }
743
744 $this_query .= ')';
745 $this_query_list[] = $this_query;
746 }
747
748 $timestamp_filter = 'SELECT DISTINCT(timestamp) FROM ' . $from_query . $wpdb->prepare( ' WHERE form_id=%s', array( $form_id ) ) . ' AND ' . implode( ' AND ', $this_query_list );
749
750 $full_filter_query = 'SELECT * FROM ' . $table_name . ' WHERE timestamp IN(';
751 $full_filter_query .= $timestamp_filter;
752 $full_filter_query .= ')';
753 $from_query = '(' . $full_filter_query . ') AS T';
754 }
755 }
756
757 return $full_filter_query;
758 }
759
760 /**
761 * Prepare form data query
762 *
763 * @param string $timestamps timestamps.
764 * @param boolean $ordered true|false.
765 * @return string query.
766 */
767 private static function prepare_form_data_query( $timestamps, $ordered = false ) {
768 global $wpdb;
769 $timestamps_values = array();
770 foreach ( $timestamps as $timestamp ) {
771 $timestamps_values[] = $timestamp['timestamp'];
772 }
773
774 $timestamps_str = implode( ',', $timestamps_values );
775 $timestamp_order = $ordered ? ' ORDER BY FIELD(timestamp, ' . $timestamps_str . ')' : '';
776 return 'SELECT id, form_id, timestamp, input_key, input_value, input_type, created_at, updated_at from ' . $wpdb->prefix . KIRKI_FORM_DATA_TABLE . ' WHERE timestamp IN(' . $timestamps_str . ')' . $timestamp_order;
777 }
778
779 /**
780 * Prepare form data response
781 *
782 * @param array $form_data_unstractured form_data_unstractured.
783 * @param string $keys keys.
784 * @param int $total_count total_count.
785 * @param boolean $done done.
786 * @return array
787 */
788 private static function prepare_form_data_response( $form_data_unstractured, $keys, $total_count, $done = false ) {
789 $data = array();
790 array_push( $keys, 'created_at' );
791 $types = array();
792
793 if ( count( $form_data_unstractured ) ) {
794 foreach ( $form_data_unstractured as $form_data_item ) {
795 if ( empty( $data[ $form_data_item['timestamp'] ]['created_at'] ) ) {
796 $data[ $form_data_item['timestamp'] ]['created_at'] = $form_data_item['created_at'];
797 }
798
799 if ( empty( $data[ $form_data_item['timestamp'] ]['form_id'] ) ) {
800 $data[ $form_data_item['timestamp'] ]['form_id'] = $form_data_item['form_id'];
801 }
802
803 if ( empty( $data[ $form_data_item['timestamp'] ]['id'] ) ) {
804 $data[ $form_data_item['timestamp'] ]['id'] = $form_data_item['timestamp'];
805 }
806
807 // Handle array values by unserializing them
808 $input_value = $form_data_item['input_value'];
809 if (is_string($input_value) && @unserialize($input_value, ['allowed_classes' => false]) !== false) {
810 $input_value = unserialize($input_value, ['allowed_classes' => false]);
811 }
812
813 if ( isset( $data[ $form_data_item['timestamp'] ] ) ) {
814 $data[$form_data_item['timestamp']][$form_data_item['input_key']] = $input_value;
815 } else {
816 $data[ $form_data_item['timestamp'] ] = array(
817 $form_data_item['input_key'] => $input_value,
818 );
819 }
820
821 $types[ $form_data_item['input_key'] ] = $form_data_item['input_type'];
822
823 if ( isset( $form_data_item['input_type'] ) && $form_data_item['input_type'] === 'file' ) {
824 $file_url = wp_get_attachment_url( $form_data_item['input_value'] );
825 $file_url = ! empty( $file_url ) ? esc_url( $file_url ) : '';
826 $data[ $form_data_item['timestamp'] ][ $form_data_item['input_key'] ] = $file_url;
827 }
828 }
829 }
830
831 return array(
832 'fields' => array_values( $keys ),
833 'data' => array_values( $data ),
834 'types' => $types,
835 'total_count' => $total_count,
836 'done' => $done,
837 );
838 }
839
840 /**
841 * Delete file from post and postmeta table
842 *
843 * @param array $file_ids file ids
844 *
845 * @return void
846 */
847
848 private static function delete_attachments( $file_ids ) {
849 foreach ( $file_ids as $file_id ) {
850 wp_delete_attachment( $file_id, true );
851 }
852 }
853 }
854