PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.10
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.10
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 / models / FrmEntry.php

FrmEntry.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 6.10, at classes/models/FrmEntry.php

1,115 lines 29.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 die( 'You are not allowed to call this page directly.' );
4 }
5
6 class FrmEntry {
7
8 /**
9 * Create a new entry
10 *
11 * @param array $values
12 *
13 * @return bool|int $entry_id
14 */
15 public static function create( $values ) {
16 $entry_id = self::create_entry( $values, 'standard' );
17
18 return $entry_id;
19 }
20
21 /**
22 * Create a new entry with some differences depending on type
23 *
24 * @param array $values
25 * @param string $type
26 *
27 * @return bool|int $entry_id
28 */
29 private static function create_entry( $values, $type ) {
30 $new_values = self::before_insert_entry_in_database( $values, $type );
31
32 // Don't check XML entries for duplicates
33 if ( $type !== 'xml' && self::is_duplicate( $new_values, $values ) ) {
34 return false;
35 }
36
37 $entry_id = self::continue_to_create_entry( $values, $new_values );
38
39 return $entry_id;
40 }
41
42 /**
43 * Check for duplicate entries created in the last minute
44 *
45 * @return bool
46 */
47 public static function is_duplicate( $new_values, $values ) {
48 $duplicate_entry_time = apply_filters( 'frm_time_to_check_duplicates', 60, $new_values );
49
50 if ( false === self::is_duplicate_check_needed( $values, $duplicate_entry_time ) ) {
51 return false;
52 }
53
54 $check_val = $new_values;
55 $check_val['created_at >'] = gmdate( 'Y-m-d H:i:s', strtotime( $new_values['created_at'] ) - absint( $duplicate_entry_time ) );
56
57 unset( $check_val['created_at'], $check_val['updated_at'], $check_val['is_draft'], $check_val['id'], $check_val['item_key'] );
58
59 if ( $new_values['item_key'] == $new_values['name'] ) {
60 unset( $check_val['name'] );
61 }
62
63 $check_val = apply_filters( 'frm_duplicate_check_val', $check_val );
64
65 global $wpdb;
66 $entry_exists = FrmDb::get_col( $wpdb->prefix . 'frm_items', $check_val, 'id', array( 'order_by' => 'created_at DESC' ) );
67
68 if ( ! $entry_exists || ! isset( $values['item_meta'] ) ) {
69 return false;
70 }
71
72 global $frm_vars;
73 $frm_vars['checking_duplicates'] = true;
74
75 $is_duplicate = false;
76 foreach ( $entry_exists as $entry_exist ) {
77 $is_duplicate = true;
78
79 // make sure it's a duplicate
80 $metas = FrmEntryMeta::get_entry_meta_info( $entry_exist );
81 $field_metas = array();
82 foreach ( $metas as $meta ) {
83 $field_metas[ $meta->field_id ] = $meta->meta_value;
84 }
85
86 $filtered_vals = array_filter( $values['item_meta'] );
87 $filtered_vals = self::convert_values_to_their_saved_value( $filtered_vals, $entry_exist );
88 $field_metas = array_filter( $field_metas );
89
90 // If prev entry is empty and current entry is not, they are not duplicates
91 if ( empty( $field_metas ) && ! empty( $filtered_vals ) ) {
92 return false;
93 }
94
95 // compare serialized values and not arrays
96 $new_meta = array_map( 'maybe_serialize', $filtered_vals );
97
98 if ( $field_metas === $new_meta ) {
99 $is_duplicate = true;
100 break;
101 }
102
103 if ( count( $field_metas ) !== count( $new_meta ) ) {
104 // TODO: compare values saved in the post also
105 $is_duplicate = false;
106 continue;
107 }
108
109 $diff = array_diff_assoc( $field_metas, $new_meta );
110 foreach ( $diff as $meta_value ) {
111 if ( ! empty( $meta_value ) ) {
112 $is_duplicate = false;
113 }
114 }
115
116 if ( $is_duplicate ) {
117 break;
118 }
119 }//end foreach
120
121 $frm_vars['checking_duplicates'] = false;
122
123 return $is_duplicate;
124 }
125
126 /**
127 * Convert form data to the actual value that would be saved into the database.
128 * This is important for the duplicate check as something like 'a:2:{s:5:"typed";s:0:"";s:6:"output";s:0:"";}' (a signature value) is actually an empty string and does not get saved.
129 *
130 * @param array $filter_vals
131 * @param int $entry_id
132 * @return array
133 */
134 private static function convert_values_to_their_saved_value( $filter_vals, $entry_id ) {
135 $reduced = array();
136 foreach ( $filter_vals as $field_id => $value ) {
137 $field = FrmFieldFactory::get_field_object( $field_id );
138 $reduced[ $field_id ] = $field->get_value_to_save( $value, array( 'entry_id' => $entry_id ) );
139 $reduced[ $field_id ] = $field->set_value_before_save( $reduced[ $field_id ] );
140 if ( '' === $reduced[ $field_id ] || ( is_array( $reduced[ $field_id ] ) && 0 === count( $reduced[ $field_id ] ) ) ) {
141 unset( $reduced[ $field_id ] );
142 }
143 }
144 return $reduced;
145 }
146
147 /**
148 * Determine if an entry needs to be checked as a possible duplicate
149 *
150 * @since 2.0.23
151 *
152 * @param array $values
153 * @param int $duplicate_entry_time
154 *
155 * @return bool
156 */
157 private static function is_duplicate_check_needed( $values, $duplicate_entry_time ) {
158 // If time for checking duplicates is set to an empty value, don't check for duplicates
159 if ( empty( $duplicate_entry_time ) ) {
160 return false;
161 }
162
163 // If CSV is importing, don't check for duplicates
164 if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING ) {
165 return false;
166 }
167
168 // If repeating field entries are getting created, don't check for duplicates
169 if ( isset( $values['parent_form_id'] ) && $values['parent_form_id'] ) {
170 return false;
171 }
172
173 return true;
174 }
175
176 public static function duplicate( $id ) {
177 global $wpdb;
178
179 $values = self::getOne( $id );
180
181 $new_values = array();
182 $new_values['item_key'] = FrmAppHelper::get_unique_key( '', $wpdb->prefix . 'frm_items', 'item_key' );
183 $new_values['name'] = $values->name;
184 $new_values['is_draft'] = $values->is_draft;
185 $new_values['user_id'] = (int) $values->user_id;
186 $new_values['updated_by'] = (int) $values->user_id;
187 $new_values['form_id'] = $values->form_id ? (int) $values->form_id : null;
188 $new_values['created_at'] = current_time( 'mysql', 1 );
189 $new_values['updated_at'] = $new_values['created_at'];
190
191 $query_results = $wpdb->insert( $wpdb->prefix . 'frm_items', $new_values );
192 if ( ! $query_results ) {
193 return false;
194 }
195
196 $entry_id = $wpdb->insert_id;
197
198 global $frm_vars;
199 if ( ! isset( $frm_vars['saved_entries'] ) ) {
200 $frm_vars['saved_entries'] = array();
201 }
202 $frm_vars['saved_entries'][] = (int) $entry_id;
203
204 FrmEntryMeta::duplicate_entry_metas( $id, $entry_id );
205 self::clear_cache();
206
207 do_action( 'frm_after_duplicate_entry', $entry_id, $new_values['form_id'], array( 'old_id' => $id ) );
208
209 return $entry_id;
210 }
211
212 /**
213 * Update an entry (not via XML)
214 *
215 * @param int $id
216 * @param array $values
217 *
218 * @return bool|int $update_results
219 */
220 public static function update( $id, $values ) {
221 $update_results = self::update_entry( $id, $values, 'standard' );
222
223 return $update_results;
224 }
225
226 /**
227 * Update an entry with some differences depending on the update type
228 *
229 * @since 2.0.16
230 *
231 * @param int $id
232 * @param array $values
233 *
234 * @return bool|int $query_results
235 */
236 private static function update_entry( $id, $values, $update_type ) {
237 global $wpdb;
238
239 $update = self::before_update_entry( $id, $values, $update_type );
240 if ( ! $update ) {
241 return false;
242 }
243
244 $new_values = self::package_entry_to_update( $id, $values );
245
246 $query_results = $wpdb->update( $wpdb->prefix . 'frm_items', $new_values, compact( 'id' ) );
247
248 self::after_update_entry( $query_results, $id, $values, $new_values );
249
250 return $query_results;
251 }
252
253 /**
254 * Delete an entry.
255 *
256 * @param int|string $id
257 * @return bool True on success, false if nothing was deleted.
258 */
259 public static function destroy( $id ) {
260 global $wpdb;
261 $id = (int) $id;
262
263 // Item meta is required for conditional logic in actions with 'delete' events.
264 $entry = self::getOne( $id, true );
265 if ( ! $entry ) {
266 $result = false;
267 return $result;
268 }
269
270 /**
271 * Trigger an action to run custom logic before the entry is deleted.
272 *
273 * @param int $id The id of the entry that was destroyed.
274 * @param stdClass $entry The entry object.
275 */
276 do_action( 'frm_before_destroy_entry', $id, $entry );
277
278 $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . $wpdb->prefix . 'frm_item_metas WHERE item_id=%d', $id ) );
279 $result = $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . $wpdb->prefix . 'frm_items WHERE id=%d', $id ) );
280
281 self::clear_cache();
282
283 /**
284 * Trigger an action to run custom logic after the entry is deleted.
285 * Use this hook if you need to update caching after an entry is deleted.
286 *
287 * @since 5.4.1
288 *
289 * @param int $id The id of the entry that was destroyed.
290 * @param stdClass $entry The entry object.
291 */
292 do_action( 'frm_after_destroy_entry', $id, $entry );
293
294 return $result;
295 }
296
297 public static function update_form( $id, $value, $form_id ) {
298 global $wpdb;
299 $form_id = isset( $value ) ? $form_id : null;
300 $result = $wpdb->update( $wpdb->prefix . 'frm_items', array( 'form_id' => $form_id ), array( 'id' => $id ) );
301 if ( $result ) {
302 self::clear_cache();
303 }
304
305 return $result;
306 }
307
308 /**
309 * Clear entry caching
310 * Called when an entry is changed
311 *
312 * @since 2.0.5
313 */
314 public static function clear_cache() {
315 FrmDb::cache_delete_group( 'frm_entry' );
316 FrmDb::cache_delete_group( 'frm_item' );
317 FrmDb::cache_delete_group( 'frm_entry_meta' );
318 FrmDb::cache_delete_group( 'frm_item_meta' );
319 }
320
321 /**
322 * After switching to the wp_loaded hook for processing entries,
323 * we can no longer use 'name', but check it as a fallback
324 *
325 * @since 2.0.11
326 */
327 public static function get_new_entry_name( $values, $default = '' ) {
328 $name = isset( $values['item_name'] ) ? $values['item_name'] : ( isset( $values['name'] ) ? $values['name'] : $default );
329 if ( is_array( $name ) ) {
330 $name = reset( $name );
331 }
332
333 return $name;
334 }
335
336 /**
337 * If $entry is numeric, get the entry object
338 *
339 * @since 2.0.9
340 *
341 * @param int|object $entry By reference.
342 * @return void
343 */
344 public static function maybe_get_entry( &$entry ) {
345 if ( $entry && is_numeric( $entry ) ) {
346 $entry = self::getOne( $entry );
347 } elseif ( empty( $entry ) || 'false' === $entry ) {
348 $entry = false;
349 }
350 }
351
352 public static function getOne( $id, $meta = false ) {
353 global $wpdb;
354
355 $query = "SELECT it.*, fr.name as form_name, fr.form_key as form_key FROM {$wpdb->prefix}frm_items it
356 LEFT OUTER JOIN {$wpdb->prefix}frm_forms fr ON it.form_id=fr.id WHERE ";
357
358 $query .= is_numeric( $id ) ? 'it.id=%d' : 'it.item_key=%s';
359 $query_args = array( $id );
360 $query = $wpdb->prepare( $query, $query_args ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
361
362 if ( ! $meta ) {
363 $entry = FrmDb::check_cache( $id . '_nometa', 'frm_entry', $query, 'get_row' );
364 self::prepare_entry( $entry );
365 return $entry;
366 }
367
368 $entry = FrmDb::check_cache( $id, 'frm_entry' );
369 if ( $entry !== false ) {
370 self::prepare_entry( $entry );
371 return $entry;
372 }
373
374 $entry = $wpdb->get_row( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
375 $entry = self::get_meta( $entry );
376 self::prepare_entry( $entry );
377
378 return $entry;
379 }
380
381 /**
382 * @since 4.02.03
383 *
384 * @param object $entry
385 */
386 private static function prepare_entry( &$entry ) {
387 if ( empty( $entry ) ) {
388 return;
389 }
390
391 FrmAppHelper::unserialize_or_decode( $entry->description );
392 // TODO: Remove slashes on input only, not output.
393 $entry = wp_unslash( $entry );
394 }
395
396 /**
397 * @since 4.02.03
398 *
399 * @param array $entries
400 */
401 private static function prepare_entries( &$entries ) {
402 foreach ( $entries as $k => $entry ) {
403 self::prepare_entry( $entry );
404 $entries[ $k ] = $entry;
405 }
406 }
407
408 public static function get_meta( $entry ) {
409 if ( ! $entry ) {
410 return $entry;
411 }
412
413 global $wpdb;
414 $metas = FrmDb::get_results(
415 $wpdb->prefix . 'frm_item_metas m LEFT JOIN ' . $wpdb->prefix . 'frm_fields f ON m.field_id=f.id',
416 array(
417 'item_id' => $entry->id,
418 'field_id !' => 0,
419 ),
420 'field_id, meta_value, field_key, item_id, f.type'
421 );
422
423 $entry->metas = array();
424
425 $include_key = apply_filters( 'frm_include_meta_keys', false, array( 'form_id' => $entry->form_id ) );
426 foreach ( $metas as $meta_val ) {
427 FrmFieldsHelper::prepare_field_value( $meta_val->meta_value, $meta_val->type );
428
429 if ( $meta_val->item_id == $entry->id ) {
430 $entry->metas[ $meta_val->field_id ] = $meta_val->meta_value;
431 if ( $include_key ) {
432 $entry->metas[ $meta_val->field_key ] = $entry->metas[ $meta_val->field_id ];
433 }
434 continue;
435 }
436
437 // include sub entries in an array
438 if ( ! isset( $entry->metas[ $meta_val->field_id ] ) ) {
439 $entry->metas[ $meta_val->field_id ] = array();
440 }
441
442 $entry->metas[ $meta_val->field_id ][] = $meta_val->meta_value;
443
444 unset( $meta_val );
445 }
446 unset( $metas );
447
448 FrmDb::set_cache( $entry->id, $entry, 'frm_entry' );
449
450 return $entry;
451 }
452
453 /**
454 * @param string $id
455 */
456 public static function exists( $id ) {
457 global $wpdb;
458
459 if ( FrmDb::check_cache( $id, 'frm_entry' ) ) {
460 $exists = true;
461
462 return $exists;
463 }
464
465 if ( is_numeric( $id ) ) {
466 $where = array( 'id' => $id );
467 } else {
468 $where = array( 'item_key' => $id );
469 }
470 $id = FrmDb::get_var( $wpdb->prefix . 'frm_items', $where );
471
472 return $id && $id > 0;
473 }
474
475 public static function getAll( $where, $order_by = '', $limit = '', $meta = false, $inc_form = true ) {
476 global $wpdb;
477
478 $limit = FrmDb::esc_limit( $limit );
479
480 $cache_key = FrmAppHelper::maybe_json_encode( $where ) . $order_by . $limit . $inc_form;
481 $entries = wp_cache_get( $cache_key, 'frm_entry' );
482
483 if ( false === $entries ) {
484 $fields = 'it.id, it.item_key, it.name, it.ip, it.form_id, it.post_id, it.user_id, it.parent_item_id, it.updated_by, it.created_at, it.updated_at, it.is_draft, it.description';
485 $table = $wpdb->prefix . 'frm_items it ';
486
487 if ( $inc_form ) {
488 $fields = 'it.*, fr.name as form_name,fr.form_key as form_key';
489 $table .= 'LEFT OUTER JOIN ' . $wpdb->prefix . 'frm_forms fr ON it.form_id=fr.id ';
490 }
491
492 if ( preg_match( '/ meta_([0-9]+)/', $order_by, $order_matches ) ) {
493 $fields .= self::sort_by_field( $order_matches[1] );
494 unset( $order_matches );
495 }
496
497 // prepare the query
498 $query = 'SELECT ' . $fields . ' FROM ' . $table . FrmDb::prepend_and_or_where( ' WHERE ', $where ) . $order_by . $limit;
499
500 $entries = $wpdb->get_results( $query, OBJECT_K ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
501 unset( $query );
502
503 FrmDb::set_cache( $cache_key, $entries, 'frm_entry' );
504 }//end if
505
506 if ( ! $meta || ! $entries ) {
507 self::prepare_entries( $entries );
508 return $entries;
509 }
510 unset( $meta );
511
512 if ( ! is_array( $where ) && preg_match( '/^it\.form_id=\d+$/', $where ) ) {
513 $where = array( 'it.form_id' => substr( $where, 11 ) );
514 }
515
516 $meta_where = array( 'field_id !' => 0 );
517 if ( $limit == '' && is_array( $where ) && count( $where ) == 1 && isset( $where['it.form_id'] ) ) {
518 $meta_where['fi.form_id'] = $where['it.form_id'];
519 } else {
520 $meta_where['item_id'] = array_keys( $entries );
521 }
522
523 $metas = FrmDb::get_results(
524 $wpdb->prefix . 'frm_item_metas it LEFT OUTER JOIN ' . $wpdb->prefix . 'frm_fields fi ON it.field_id = fi.id',
525 $meta_where,
526 'item_id, meta_value, field_id, field_key, form_id, fi.type'
527 );
528
529 unset( $meta_where );
530
531 if ( ! $metas ) {
532 self::prepare_entries( $entries );
533 return $entries;
534 }
535
536 foreach ( $metas as $m_key => $meta_val ) {
537 if ( ! isset( $entries[ $meta_val->item_id ] ) ) {
538 continue;
539 }
540
541 if ( ! isset( $entries[ $meta_val->item_id ]->metas ) ) {
542 $entries[ $meta_val->item_id ]->metas = array();
543 }
544
545 FrmFieldsHelper::prepare_field_value( $meta_val->meta_value, $meta_val->type );
546 $entries[ $meta_val->item_id ]->metas[ $meta_val->field_id ] = $meta_val->meta_value;
547 unset( $m_key, $meta_val );
548 }
549
550 if ( ! FrmAppHelper::prevent_caching() ) {
551 foreach ( $entries as $entry ) {
552 FrmDb::set_cache( $entry->id, $entry, 'frm_entry' );
553 unset( $entry );
554 }
555 }
556
557 self::prepare_entries( $entries );
558 return $entries;
559 }
560
561 /**
562 * @param int $field_id
563 * @return string
564 */
565 private static function sort_by_field( $field_id ) {
566 global $wpdb;
567 $field_id = (int) $field_id;
568
569 $field_options = FrmDb::get_var( 'frm_fields', array( 'id' => $field_id ), 'field_options' );
570 FrmAppHelper::unserialize_or_decode( $field_options );
571
572 if ( empty( $field_options['post_field'] ) ) {
573 $sort = ', (SELECT meta_value FROM ' . $wpdb->prefix . 'frm_item_metas WHERE field_id = ' . $field_id . ' AND item_id = it.id) as meta_' . $field_id;
574 } else {
575 $sort = '';
576 }
577
578 return apply_filters( 'frm_handle_field_column_sort', $sort, $field_id, $field_options );
579 }
580
581 // Pagination Methods
582 /**
583 * @param array|int|string $where If int, use the form id.
584 * @return int|string
585 */
586 public static function getRecordCount( $where = '' ) {
587 global $wpdb;
588 $table_join = $wpdb->prefix . 'frm_items it JOIN ' . $wpdb->prefix . 'frm_forms fr ON it.form_id=fr.id';
589
590 if ( is_numeric( $where ) ) {
591 $table_join = 'frm_items';
592 $where = array( 'form_id' => $where );
593 }
594
595 if ( is_array( $where ) ) {
596 $count = FrmDb::get_count( $table_join, $where );
597 } else {
598 $cache_key = 'count_' . FrmAppHelper::maybe_json_encode( $where );
599 $query = 'SELECT COUNT(*) FROM ' . $table_join . FrmDb::prepend_and_or_where( ' WHERE ', $where );
600 $count = FrmDb::check_cache( $cache_key, 'frm_entry', $query, 'get_var' );
601 }
602
603 return $count;
604 }
605
606 /**
607 * @param int|string $p_size
608 * @return int
609 */
610 public static function getPageCount( $p_size, $where = '' ) {
611 $p_size = (int) $p_size;
612 $count = 1;
613 if ( $p_size ) {
614 if ( ! is_numeric( $where ) ) {
615 $where = self::getRecordCount( $where );
616 }
617 $count = ceil( (int) $where / $p_size );
618 }
619
620 return $count;
621 }
622
623 /**
624 * Prepare the data before inserting it into the database
625 *
626 * @since 2.0.16
627 *
628 * @param array $values
629 * @param string $type
630 *
631 * @return array $new_values
632 */
633 private static function before_insert_entry_in_database( &$values, $type ) {
634
635 self::sanitize_entry_post( $values );
636
637 if ( $type !== 'xml' ) {
638 $values = apply_filters( 'frm_pre_create_entry', $values );
639 }
640
641 $new_values = self::package_entry_data( $values );
642
643 return $new_values;
644 }
645
646 /**
647 * Create an entry and perform after create actions
648 *
649 * @since 2.0.16
650 *
651 * @param array $values
652 * @param array $new_values
653 *
654 * @return bool|int $entry_id
655 */
656 private static function continue_to_create_entry( $values, $new_values ) {
657 $entry_id = self::insert_entry_into_database( $new_values );
658 if ( ! $entry_id ) {
659 return false;
660 }
661
662 self::after_insert_entry_in_database( $values, $new_values, $entry_id );
663
664 return $entry_id;
665 }
666
667 /**
668 * Sanitize the POST values before we use them
669 *
670 * @since 2.0
671 *
672 * @param array $values The POST values by reference.
673 */
674 public static function sanitize_entry_post( &$values ) {
675 $sanitize_method = array(
676 'form_id' => 'absint',
677 'frm_action' => 'sanitize_title',
678 'form_key' => 'sanitize_title',
679 'item_key' => 'sanitize_title',
680 'item_name' => 'sanitize_text_field',
681 'frm_saving_draft' => 'absint',
682 'is_draft' => 'absint',
683 'post_id' => 'absint',
684 'parent_item_id' => 'absint',
685 'created_at' => 'sanitize_text_field',
686 'updated_at' => 'sanitize_text_field',
687 );
688
689 FrmAppHelper::sanitize_request( $sanitize_method, $values );
690 }
691
692 /**
693 * Prepare the new values for inserting into the database
694 *
695 * @since 2.0.16
696 *
697 * @param array $values
698 *
699 * @return array $new_values
700 */
701 private static function package_entry_data( &$values ) {
702 global $wpdb;
703
704 if ( ! isset( $values['item_key'] ) ) {
705 $values['item_key'] = '';
706 }
707
708 $item_name = self::get_new_entry_name( $values, $values['item_key'] );
709 $new_values = array(
710 'item_key' => FrmAppHelper::get_unique_key( $values['item_key'], $wpdb->prefix . 'frm_items', 'item_key' ),
711 'name' => FrmAppHelper::truncate( $item_name, 255, 1, '' ),
712 'ip' => self::get_ip( $values ),
713 'is_draft' => self::get_is_draft_value( $values ),
714 'form_id' => (int) self::get_entry_value( $values, 'form_id', null ),
715 'post_id' => (int) self::get_entry_value( $values, 'post_id', 0 ),
716 'parent_item_id' => (int) self::get_entry_value( $values, 'parent_item_id', 0 ),
717 'created_at' => self::get_created_at( $values ),
718 'updated_at' => self::get_updated_at( $values ),
719 'description' => self::get_entry_description( $values ),
720 'user_id' => self::get_entry_user_id( $values ),
721 );
722
723 $new_values['updated_by'] = isset( $values['updated_by'] ) ? $values['updated_by'] : $new_values['user_id'];
724
725 return $new_values;
726 }
727
728 private static function get_entry_value( $values, $name, $default ) {
729 return isset( $values[ $name ] ) ? $values[ $name ] : $default;
730 }
731
732 /**
733 * Get the ip for a new entry.
734 * Allow the import to override the value.
735 *
736 * @since 2.03.10
737 *
738 * @param array $values
739 *
740 * @return string
741 */
742 private static function get_ip( $values ) {
743 if ( ! FrmAppHelper::ips_saved() ) {
744 return '';
745 }
746
747 $ip = FrmAppHelper::get_ip_address();
748 if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING ) {
749 $ip = self::get_entry_value( $values, 'ip', $ip );
750 }
751
752 return $ip;
753 }
754
755 /**
756 * Get the is_draft value for a new entry
757 *
758 * @since 2.0.16
759 *
760 * @param array $values
761 *
762 * @return int
763 */
764 private static function get_is_draft_value( $values ) {
765 if ( isset( $values['frm_saving_draft'] ) && FrmEntriesHelper::DRAFT_ENTRY_STATUS === (int) $values['frm_saving_draft'] ) {
766 return FrmEntriesHelper::DRAFT_ENTRY_STATUS;
767 }
768
769 return isset( $values['is_draft'] ) ? absint( $values['is_draft'] ) : FrmEntriesHelper::SUBMITTED_ENTRY_STATUS;
770 }
771
772 /**
773 * Get the created_at value for a new entry
774 *
775 * @since 2.0.16
776 *
777 * @param array $values
778 *
779 * @return string
780 */
781 private static function get_created_at( $values ) {
782 return self::get_entry_value( $values, 'created_at', current_time( 'mysql', 1 ) );
783 }
784
785 /**
786 * Get the updated_at value for a new entry
787 *
788 * @since 2.0.16
789 *
790 * @param array $values
791 *
792 * @return string
793 */
794 private static function get_updated_at( $values ) {
795 if ( isset( $values['updated_at'] ) ) {
796 $updated_at = $values['updated_at'];
797 } else {
798 $updated_at = self::get_created_at( $values );
799 }
800
801 return $updated_at;
802 }
803
804 /**
805 * Get the description value for a new entry
806 *
807 * @since 2.0.16
808 *
809 * @param array $values
810 *
811 * @return string
812 */
813 private static function get_entry_description( $values ) {
814 if ( ! empty( $values['description'] ) ) {
815 $description = FrmAppHelper::maybe_json_encode( $values['description'] );
816 } else {
817 $description = json_encode(
818 array(
819 'browser' => FrmAppHelper::get_server_value( 'HTTP_USER_AGENT' ),
820 'referrer' => FrmAppHelper::get_server_value( 'HTTP_REFERER' ),
821 )
822 );
823 }
824
825 return $description;
826 }
827
828 /**
829 * Get the user_id value for a new entry
830 *
831 * @since 2.0.16
832 *
833 * @param array $values
834 *
835 * @return int
836 */
837 private static function get_entry_user_id( $values ) {
838 if ( isset( $values['frm_user_id'] ) && ( is_numeric( $values['frm_user_id'] ) || FrmAppHelper::is_admin() ) ) {
839 $user_id = $values['frm_user_id'];
840 } else {
841 $current_user_id = get_current_user_id();
842 $user_id = $current_user_id ? $current_user_id : 0;
843 }
844
845 return $user_id;
846 }
847
848 /**
849 * Insert new entry into the database
850 *
851 * @since 2.0.16
852 *
853 * @param array $new_values
854 *
855 * @return bool|int $entry_id
856 */
857 private static function insert_entry_into_database( $new_values ) {
858 global $wpdb;
859
860 $query_results = $wpdb->insert( $wpdb->prefix . 'frm_items', $new_values );
861
862 if ( ! $query_results ) {
863 $entry_id = false;
864 } else {
865 $entry_id = $wpdb->insert_id;
866 }
867
868 return $entry_id;
869 }
870
871 /**
872 * Add the new entry to global $frm_vars
873 *
874 * @since 2.0.16
875 *
876 * @param int $entry_id
877 */
878 private static function add_new_entry_to_frm_vars( $entry_id ) {
879 global $frm_vars;
880
881 if ( ! isset( $frm_vars['saved_entries'] ) ) {
882 $frm_vars['saved_entries'] = array();
883 }
884
885 $frm_vars['saved_entries'][] = (int) $entry_id;
886 }
887
888 /**
889 * Add entry metas, if there are any
890 *
891 * @since 2.0.16
892 *
893 * @param array $values
894 * @param int $entry_id
895 * @return void
896 */
897 private static function maybe_add_entry_metas( $values, $entry_id ) {
898 if ( isset( $values['item_meta'] ) ) {
899 FrmEntryMeta::update_entry_metas( $entry_id, $values['item_meta'] );
900 }
901 self::maybe_add_captcha_meta( (int) $values['form_id'], (int) $entry_id );
902 }
903
904 /**
905 * @since 5.0.15
906 *
907 * @param int $form_id
908 * @param int $entry_id
909 * @return void
910 */
911 private static function maybe_add_captcha_meta( $form_id, $entry_id ) {
912 global $frm_vars;
913 if ( array_key_exists( 'captcha_scores', $frm_vars ) && array_key_exists( $form_id, $frm_vars['captcha_scores'] ) ) {
914 $captcha_score_meta = array( 'captcha_score' => $frm_vars['captcha_scores'][ $form_id ] );
915 FrmEntryMeta::add_entry_meta( $entry_id, 0, '', maybe_serialize( $captcha_score_meta ) );
916 }
917 }
918
919 /**
920 * Trigger frm_after_create_entry hooks
921 *
922 * @since 2.0.16
923 *
924 * @param int $entry_id
925 * @param array $values
926 * @param array $new_values
927 */
928 private static function after_entry_created_actions( $entry_id, $values, $new_values ) {
929 // This is a child entry.
930 $is_child = isset( $values['parent_nonce'] ) && ! empty( $values['parent_form_id'] ) && wp_verify_nonce( $values['parent_nonce'], 'parent' );
931
932 do_action( 'frm_after_create_entry', $entry_id, $new_values['form_id'], compact( 'is_child' ) );
933 do_action( 'frm_after_create_entry_' . $new_values['form_id'], $entry_id, compact( 'is_child' ) );
934 }
935
936 /**
937 * Actions to perform immediately after an entry is inserted in the frm_items database
938 *
939 * @since 2.0.16
940 *
941 * @param array $values
942 * @param array $new_values
943 * @param int $entry_id
944 */
945 private static function after_insert_entry_in_database( $values, $new_values, $entry_id ) {
946
947 self::add_new_entry_to_frm_vars( $entry_id );
948
949 self::maybe_add_entry_metas( $values, $entry_id );
950
951 self::clear_cache();
952
953 self::after_entry_created_actions( $entry_id, $values, $new_values );
954 }
955
956 /**
957 * Perform some actions right before updating an entry
958 *
959 * @since 2.0.16
960 *
961 * @param int $id
962 * @param array $values
963 * @param string $update_type
964 *
965 * @return bool $update
966 */
967 private static function before_update_entry( $id, &$values, $update_type ) {
968 $update = true;
969
970 global $frm_vars;
971
972 if ( isset( $frm_vars['saved_entries'] ) && is_array( $frm_vars['saved_entries'] ) && in_array( (int) $id, $frm_vars['saved_entries'] ) ) {
973 $update = false;
974 }
975
976 if ( $update && $update_type !== 'xml' ) {
977 $values = apply_filters( 'frm_pre_update_entry', $values, $id );
978 }
979
980 return $update;
981 }
982
983 /**
984 * Package the entry data for updating
985 *
986 * @since 2.0.16
987 *
988 * @param int $id
989 * @param array $values
990 *
991 * @return array $new_values
992 */
993 private static function package_entry_to_update( $id, $values ) {
994 global $wpdb;
995
996 $new_values = array(
997 'name' => self::get_new_entry_name( $values ),
998 'form_id' => (int) self::get_entry_value( $values, 'form_id', null ),
999 'is_draft' => self::get_is_draft_value( $values ),
1000 'updated_at' => current_time( 'mysql', 1 ),
1001 'updated_by' => isset( $values['updated_by'] ) ? $values['updated_by'] : get_current_user_id(),
1002 );
1003
1004 if ( isset( $values['post_id'] ) ) {
1005 $new_values['post_id'] = (int) $values['post_id'];
1006 }
1007
1008 if ( isset( $values['item_key'] ) ) {
1009 $new_values['item_key'] = FrmAppHelper::get_unique_key( $values['item_key'], $wpdb->prefix . 'frm_items', 'item_key', $id );
1010 }
1011
1012 if ( isset( $values['parent_item_id'] ) ) {
1013 $new_values['parent_item_id'] = (int) $values['parent_item_id'];
1014 }
1015
1016 if ( isset( $values['frm_user_id'] ) && is_numeric( $values['frm_user_id'] ) ) {
1017 $new_values['user_id'] = $values['frm_user_id'];
1018 }
1019
1020 $new_values = apply_filters( 'frm_update_entry', $new_values, $id );
1021
1022 return $new_values;
1023 }
1024
1025 /**
1026 * Perform some actions right after updating an entry
1027 *
1028 * @since 2.0.16
1029 *
1030 * @param bool|int $query_results
1031 * @param int $id
1032 * @param array $values
1033 * @param array $new_values
1034 */
1035 private static function after_update_entry( $query_results, $id, $values, $new_values ) {
1036 if ( $query_results ) {
1037 self::clear_cache();
1038 }
1039
1040 global $frm_vars;
1041 if ( ! isset( $frm_vars['saved_entries'] ) ) {
1042 $frm_vars['saved_entries'] = array();
1043 }
1044
1045 $frm_vars['saved_entries'][] = (int) $id;
1046
1047 if ( isset( $values['item_meta'] ) ) {
1048 FrmEntryMeta::update_entry_metas( $id, $values['item_meta'] );
1049 }
1050
1051 do_action( 'frm_after_update_entry', $id, $new_values['form_id'] );
1052 do_action( 'frm_after_update_entry_' . $new_values['form_id'], $id );
1053 }
1054
1055 /**
1056 * Create entry from an XML import
1057 * Certain actions aren't necessary when importing (like saving sub entries, checking for duplicates, etc.)
1058 *
1059 * @since 2.0.16
1060 *
1061 * @param array $values
1062 *
1063 * @return bool|int $entry_id
1064 */
1065 public static function create_entry_from_xml( $values ) {
1066 $entry_id = self::create_entry( $values, 'xml' );
1067
1068 return $entry_id;
1069 }
1070
1071 /**
1072 * Update entry from an XML import
1073 * Certain actions aren't necessary when importing (like saving sub entries and modifying other vals)
1074 *
1075 * @since 2.0.16
1076 *
1077 * @param int $id
1078 * @param array $values
1079 *
1080 * @return bool|int $updated
1081 */
1082 public static function update_entry_from_xml( $id, $values ) {
1083 $updated = self::update_entry( $id, $values, 'xml' );
1084
1085 return $updated;
1086 }
1087
1088 /**
1089 * @param string $key
1090 *
1091 * @return int entry_id
1092 */
1093 public static function get_id_by_key( $key ) {
1094 $entry_id = FrmDb::get_var( 'frm_items', array( 'item_key' => sanitize_title( $key ) ) );
1095
1096 return (int) $entry_id;
1097 }
1098
1099 /**
1100 * Get entries count.
1101 *
1102 * @since 6.8
1103 *
1104 * @return int|string
1105 */
1106 public static function get_entries_count() {
1107 $args = array(
1108 'or' => 1,
1109 'parent_form_id' => null,
1110 'parent_form_id <' => 1,
1111 );
1112 return self::getRecordCount( $args );
1113 }
1114 }
1115