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

1,114 lines 29.4 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 int | boolean $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 int | boolean $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 boolean
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 $field_id => $meta_value ) {
111 if ( ! empty( $meta_value ) ) {
112 $is_duplicate = false;
113 continue;
114 }
115 }
116
117 if ( $is_duplicate ) {
118 break;
119 }
120 }//end foreach
121
122 $frm_vars['checking_duplicates'] = false;
123
124 return $is_duplicate;
125 }
126
127 /**
128 * Convert form data to the actual value that would be saved into the database.
129 * 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.
130 *
131 * @param array $filter_vals
132 * @param int $entry_id
133 * @return array
134 */
135 private static function convert_values_to_their_saved_value( $filter_vals, $entry_id ) {
136 $reduced = array();
137 foreach ( $filter_vals as $field_id => $value ) {
138 $field = FrmFieldFactory::get_field_object( $field_id );
139 $reduced[ $field_id ] = $field->get_value_to_save( $value, array( 'entry_id' => $entry_id ) );
140 $reduced[ $field_id ] = $field->set_value_before_save( $reduced[ $field_id ] );
141 if ( '' === $reduced[ $field_id ] || ( is_array( $reduced[ $field_id ] ) && 0 === count( $reduced[ $field_id ] ) ) ) {
142 unset( $reduced[ $field_id ] );
143 }
144 }
145 return $reduced;
146 }
147
148 /**
149 * Determine if an entry needs to be checked as a possible duplicate
150 *
151 * @since 2.0.23
152 *
153 * @param array $values
154 * @param int $duplicate_entry_time
155 *
156 * @return bool
157 */
158 private static function is_duplicate_check_needed( $values, $duplicate_entry_time ) {
159 // If time for checking duplicates is set to an empty value, don't check for duplicates
160 if ( empty( $duplicate_entry_time ) ) {
161 return false;
162 }
163
164 // If CSV is importing, don't check for duplicates
165 if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING ) {
166 return false;
167 }
168
169 // If repeating field entries are getting created, don't check for duplicates
170 if ( isset( $values['parent_form_id'] ) && $values['parent_form_id'] ) {
171 return false;
172 }
173
174 return true;
175 }
176
177 public static function duplicate( $id ) {
178 global $wpdb;
179
180 $values = self::getOne( $id );
181
182 $new_values = array();
183 $new_values['item_key'] = FrmAppHelper::get_unique_key( '', $wpdb->prefix . 'frm_items', 'item_key' );
184 $new_values['name'] = $values->name;
185 $new_values['is_draft'] = $values->is_draft;
186 $new_values['user_id'] = (int) $values->user_id;
187 $new_values['updated_by'] = (int) $values->user_id;
188 $new_values['form_id'] = $values->form_id ? (int) $values->form_id : null;
189 $new_values['created_at'] = current_time( 'mysql', 1 );
190 $new_values['updated_at'] = $new_values['created_at'];
191
192 $query_results = $wpdb->insert( $wpdb->prefix . 'frm_items', $new_values );
193 if ( ! $query_results ) {
194 return false;
195 }
196
197 $entry_id = $wpdb->insert_id;
198
199 global $frm_vars;
200 if ( ! isset( $frm_vars['saved_entries'] ) ) {
201 $frm_vars['saved_entries'] = array();
202 }
203 $frm_vars['saved_entries'][] = (int) $entry_id;
204
205 FrmEntryMeta::duplicate_entry_metas( $id, $entry_id );
206 self::clear_cache();
207
208 do_action( 'frm_after_duplicate_entry', $entry_id, $new_values['form_id'], array( 'old_id' => $id ) );
209
210 return $entry_id;
211 }
212
213 /**
214 * Update an entry (not via XML)
215 *
216 * @param int $id
217 * @param array $values
218 *
219 * @return boolean|int $update_results
220 */
221 public static function update( $id, $values ) {
222 $update_results = self::update_entry( $id, $values, 'standard' );
223
224 return $update_results;
225 }
226
227 /**
228 * Update an entry with some differences depending on the update type
229 *
230 * @since 2.0.16
231 *
232 * @param int $id
233 * @param array $values
234 *
235 * @return boolean|int $query_results
236 */
237 private static function update_entry( $id, $values, $update_type ) {
238 global $wpdb;
239
240 $update = self::before_update_entry( $id, $values, $update_type );
241 if ( ! $update ) {
242 return false;
243 }
244
245 $new_values = self::package_entry_to_update( $id, $values );
246
247 $query_results = $wpdb->update( $wpdb->prefix . 'frm_items', $new_values, compact( 'id' ) );
248
249 self::after_update_entry( $query_results, $id, $values, $new_values );
250
251 return $query_results;
252 }
253
254 /**
255 * Delete an entry.
256 *
257 * @param string|int $id
258 * @return bool True on success, false if nothing was deleted.
259 */
260 public static function destroy( $id ) {
261 global $wpdb;
262 $id = (int) $id;
263
264 // Item meta is required for conditional logic in actions with 'delete' events.
265 $entry = self::getOne( $id, true );
266 if ( ! $entry ) {
267 $result = false;
268 return $result;
269 }
270
271 /**
272 * Trigger an action to run custom logic before the entry is deleted.
273 *
274 * @param int $id The id of the entry that was destroyed.
275 * @param stdClass $entry The entry object.
276 */
277 do_action( 'frm_before_destroy_entry', $id, $entry );
278
279 $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . $wpdb->prefix . 'frm_item_metas WHERE item_id=%d', $id ) );
280 $result = $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . $wpdb->prefix . 'frm_items WHERE id=%d', $id ) );
281
282 self::clear_cache();
283
284 /**
285 * Trigger an action to run custom logic after the entry is deleted.
286 * Use this hook if you need to update caching after an entry is deleted.
287 *
288 * @since 5.4.1
289 *
290 * @param int $id The id of the entry that was destroyed.
291 * @param stdClass $entry The entry object.
292 */
293 do_action( 'frm_after_destroy_entry', $id, $entry );
294
295 return $result;
296 }
297
298 public static function update_form( $id, $value, $form_id ) {
299 global $wpdb;
300 $form_id = isset( $value ) ? $form_id : null;
301 $result = $wpdb->update( $wpdb->prefix . 'frm_items', array( 'form_id' => $form_id ), array( 'id' => $id ) );
302 if ( $result ) {
303 self::clear_cache();
304 }
305
306 return $result;
307 }
308
309 /**
310 * Clear entry caching
311 * Called when an entry is changed
312 *
313 * @since 2.0.5
314 */
315 public static function clear_cache() {
316 FrmDb::cache_delete_group( 'frm_entry' );
317 FrmDb::cache_delete_group( 'frm_item' );
318 FrmDb::cache_delete_group( 'frm_entry_meta' );
319 FrmDb::cache_delete_group( 'frm_item_meta' );
320 }
321
322 /**
323 * After switching to the wp_loaded hook for processing entries,
324 * we can no longer use 'name', but check it as a fallback
325 *
326 * @since 2.0.11
327 */
328 public static function get_new_entry_name( $values, $default = '' ) {
329 $name = isset( $values['item_name'] ) ? $values['item_name'] : ( isset( $values['name'] ) ? $values['name'] : $default );
330 if ( is_array( $name ) ) {
331 $name = reset( $name );
332 }
333
334 return $name;
335 }
336
337 /**
338 * If $entry is numeric, get the entry object
339 *
340 * @param int|object $entry By reference.
341 *
342 * @since 2.0.9
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 int|array|string $where If int, use the form id.
584 */
585 public static function getRecordCount( $where = '' ) {
586 global $wpdb;
587 $table_join = $wpdb->prefix . 'frm_items it JOIN ' . $wpdb->prefix . 'frm_forms fr ON it.form_id=fr.id';
588
589 if ( is_numeric( $where ) ) {
590 $table_join = 'frm_items';
591 $where = array( 'form_id' => $where );
592 }
593
594 if ( is_array( $where ) ) {
595 $count = FrmDb::get_count( $table_join, $where );
596 } else {
597 $cache_key = 'count_' . FrmAppHelper::maybe_json_encode( $where );
598 $query = 'SELECT COUNT(*) FROM ' . $table_join . FrmDb::prepend_and_or_where( ' WHERE ', $where );
599 $count = FrmDb::check_cache( $cache_key, 'frm_entry', $query, 'get_var' );
600 }
601
602 return $count;
603 }
604
605 /**
606 * @param string|int $p_size
607 * @return int
608 */
609 public static function getPageCount( $p_size, $where = '' ) {
610 $p_size = (int) $p_size;
611 $count = 1;
612 if ( $p_size ) {
613 if ( ! is_numeric( $where ) ) {
614 $where = self::getRecordCount( $where );
615 }
616 $count = ceil( (int) $where / $p_size );
617 }
618
619 return $count;
620 }
621
622 /**
623 * Prepare the data before inserting it into the database
624 *
625 * @since 2.0.16
626 *
627 * @param array $values
628 * @param string $type
629 *
630 * @return array $new_values
631 */
632 private static function before_insert_entry_in_database( &$values, $type ) {
633
634 self::sanitize_entry_post( $values );
635
636 if ( $type != 'xml' ) {
637 $values = apply_filters( 'frm_pre_create_entry', $values );
638 }
639
640 $new_values = self::package_entry_data( $values );
641
642 return $new_values;
643 }
644
645 /**
646 * Create an entry and perform after create actions
647 *
648 * @since 2.0.16
649 *
650 * @param array $values
651 * @param array $new_values
652 *
653 * @return boolean|int $entry_id
654 */
655 private static function continue_to_create_entry( $values, $new_values ) {
656 $entry_id = self::insert_entry_into_database( $new_values );
657 if ( ! $entry_id ) {
658 return false;
659 }
660
661 self::after_insert_entry_in_database( $values, $new_values, $entry_id );
662
663 return $entry_id;
664 }
665
666 /**
667 * Sanitize the POST values before we use them
668 *
669 * @since 2.0
670 *
671 * @param array $values The POST values by reference.
672 */
673 public static function sanitize_entry_post( &$values ) {
674 $sanitize_method = array(
675 'form_id' => 'absint',
676 'frm_action' => 'sanitize_title',
677 'form_key' => 'sanitize_title',
678 'item_key' => 'sanitize_title',
679 'item_name' => 'sanitize_text_field',
680 'frm_saving_draft' => 'absint',
681 'is_draft' => 'absint',
682 'post_id' => 'absint',
683 'parent_item_id' => 'absint',
684 'created_at' => 'sanitize_text_field',
685 'updated_at' => 'sanitize_text_field',
686 );
687
688 FrmAppHelper::sanitize_request( $sanitize_method, $values );
689 }
690
691 /**
692 * Prepare the new values for inserting into the database
693 *
694 * @since 2.0.16
695 *
696 * @param array $values
697 *
698 * @return array $new_values
699 */
700 private static function package_entry_data( &$values ) {
701 global $wpdb;
702
703 if ( ! isset( $values['item_key'] ) ) {
704 $values['item_key'] = '';
705 }
706
707 $item_name = self::get_new_entry_name( $values, $values['item_key'] );
708 $new_values = array(
709 'item_key' => FrmAppHelper::get_unique_key( $values['item_key'], $wpdb->prefix . 'frm_items', 'item_key' ),
710 'name' => FrmAppHelper::truncate( $item_name, 255, 1, '' ),
711 'ip' => self::get_ip( $values ),
712 'is_draft' => self::get_is_draft_value( $values ),
713 'form_id' => (int) self::get_entry_value( $values, 'form_id', null ),
714 'post_id' => (int) self::get_entry_value( $values, 'post_id', 0 ),
715 'parent_item_id' => (int) self::get_entry_value( $values, 'parent_item_id', 0 ),
716 'created_at' => self::get_created_at( $values ),
717 'updated_at' => self::get_updated_at( $values ),
718 'description' => self::get_entry_description( $values ),
719 'user_id' => self::get_entry_user_id( $values ),
720 );
721
722 $new_values['updated_by'] = isset( $values['updated_by'] ) ? $values['updated_by'] : $new_values['user_id'];
723
724 return $new_values;
725 }
726
727 private static function get_entry_value( $values, $name, $default ) {
728 return isset( $values[ $name ] ) ? $values[ $name ] : $default;
729 }
730
731 /**
732 * Get the ip for a new entry.
733 * Allow the import to override the value.
734 *
735 * @since 2.03.10
736 *
737 * @param array $values
738 *
739 * @return string
740 */
741 private static function get_ip( $values ) {
742 if ( ! FrmAppHelper::ips_saved() ) {
743 return '';
744 }
745
746 $ip = FrmAppHelper::get_ip_address();
747 if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING ) {
748 $ip = self::get_entry_value( $values, 'ip', $ip );
749 }
750
751 return $ip;
752 }
753
754 /**
755 * Get the is_draft value for a new entry
756 *
757 * @since 2.0.16
758 *
759 * @param array $values
760 *
761 * @return int
762 */
763 private static function get_is_draft_value( $values ) {
764 if ( isset( $values['frm_saving_draft'] ) && FrmEntriesHelper::DRAFT_ENTRY_STATUS === (int) $values['frm_saving_draft'] ) {
765 return FrmEntriesHelper::DRAFT_ENTRY_STATUS;
766 }
767
768 return isset( $values['is_draft'] ) ? absint( $values['is_draft'] ) : FrmEntriesHelper::SUBMITTED_ENTRY_STATUS;
769 }
770
771 /**
772 * Get the created_at value for a new entry
773 *
774 * @since 2.0.16
775 *
776 * @param array $values
777 *
778 * @return string
779 */
780 private static function get_created_at( $values ) {
781 return self::get_entry_value( $values, 'created_at', current_time( 'mysql', 1 ) );
782 }
783
784 /**
785 * Get the updated_at value for a new entry
786 *
787 * @since 2.0.16
788 *
789 * @param array $values
790 *
791 * @return string
792 */
793 private static function get_updated_at( $values ) {
794 if ( isset( $values['updated_at'] ) ) {
795 $updated_at = $values['updated_at'];
796 } else {
797 $updated_at = self::get_created_at( $values );
798 }
799
800 return $updated_at;
801 }
802
803 /**
804 * Get the description value for a new entry
805 *
806 * @since 2.0.16
807 *
808 * @param array $values
809 *
810 * @return string
811 */
812 private static function get_entry_description( $values ) {
813 if ( isset( $values['description'] ) && ! empty( $values['description'] ) ) {
814 $description = FrmAppHelper::maybe_json_encode( $values['description'] );
815 } else {
816 $description = json_encode(
817 array(
818 'browser' => FrmAppHelper::get_server_value( 'HTTP_USER_AGENT' ),
819 'referrer' => FrmAppHelper::get_server_value( 'HTTP_REFERER' ),
820 )
821 );
822 }
823
824 return $description;
825 }
826
827 /**
828 * Get the user_id value for a new entry
829 *
830 * @since 2.0.16
831 *
832 * @param array $values
833 *
834 * @return int
835 */
836 private static function get_entry_user_id( $values ) {
837 if ( isset( $values['frm_user_id'] ) && ( is_numeric( $values['frm_user_id'] ) || FrmAppHelper::is_admin() ) ) {
838 $user_id = $values['frm_user_id'];
839 } else {
840 $current_user_id = get_current_user_id();
841 $user_id = $current_user_id ? $current_user_id : 0;
842 }
843
844 return $user_id;
845 }
846
847 /**
848 * Insert new entry into the database
849 *
850 * @since 2.0.16
851 *
852 * @param array $new_values
853 *
854 * @return int | boolean $entry_id
855 */
856 private static function insert_entry_into_database( $new_values ) {
857 global $wpdb;
858
859 $query_results = $wpdb->insert( $wpdb->prefix . 'frm_items', $new_values );
860
861 if ( ! $query_results ) {
862 $entry_id = false;
863 } else {
864 $entry_id = $wpdb->insert_id;
865 }
866
867 return $entry_id;
868 }
869
870 /**
871 * Add the new entry to global $frm_vars
872 *
873 * @since 2.0.16
874 *
875 * @param int $entry_id
876 */
877 private static function add_new_entry_to_frm_vars( $entry_id ) {
878 global $frm_vars;
879
880 if ( ! isset( $frm_vars['saved_entries'] ) ) {
881 $frm_vars['saved_entries'] = array();
882 }
883
884 $frm_vars['saved_entries'][] = (int) $entry_id;
885 }
886
887 /**
888 * Add entry metas, if there are any
889 *
890 * @since 2.0.16
891 *
892 * @param array $values
893 * @param int $entry_id
894 * @return void
895 */
896 private static function maybe_add_entry_metas( $values, $entry_id ) {
897 if ( isset( $values['item_meta'] ) ) {
898 FrmEntryMeta::update_entry_metas( $entry_id, $values['item_meta'] );
899 }
900 self::maybe_add_captcha_meta( (int) $values['form_id'], (int) $entry_id );
901 }
902
903 /**
904 * @since 5.0.15
905 *
906 * @param int $form_id
907 * @param int $entry_id
908 * @return void
909 */
910 private static function maybe_add_captcha_meta( $form_id, $entry_id ) {
911 global $frm_vars;
912 if ( array_key_exists( 'captcha_scores', $frm_vars ) && array_key_exists( $form_id, $frm_vars['captcha_scores'] ) ) {
913 $captcha_score_meta = array( 'captcha_score' => $frm_vars['captcha_scores'][ $form_id ] );
914 FrmEntryMeta::add_entry_meta( $entry_id, 0, '', maybe_serialize( $captcha_score_meta ) );
915 }
916 }
917
918 /**
919 * Trigger frm_after_create_entry hooks
920 *
921 * @since 2.0.16
922 *
923 * @param int $entry_id
924 * @param array $values
925 * @param array $new_values
926 */
927 private static function after_entry_created_actions( $entry_id, $values, $new_values ) {
928 // this is a child entry
929 $is_child = isset( $values['parent_form_id'] ) && isset( $values['parent_nonce'] ) && ! empty( $values['parent_form_id'] ) && wp_verify_nonce( $values['parent_nonce'], 'parent' );
930
931 do_action( 'frm_after_create_entry', $entry_id, $new_values['form_id'], compact( 'is_child' ) );
932 do_action( 'frm_after_create_entry_' . $new_values['form_id'], $entry_id, compact( 'is_child' ) );
933 }
934
935 /**
936 * Actions to perform immediately after an entry is inserted in the frm_items database
937 *
938 * @since 2.0.16
939 *
940 * @param array $values
941 * @param array $new_values
942 * @param int $entry_id
943 */
944 private static function after_insert_entry_in_database( $values, $new_values, $entry_id ) {
945
946 self::add_new_entry_to_frm_vars( $entry_id );
947
948 self::maybe_add_entry_metas( $values, $entry_id );
949
950 self::clear_cache();
951
952 self::after_entry_created_actions( $entry_id, $values, $new_values );
953 }
954
955 /**
956 * Perform some actions right before updating an entry
957 *
958 * @since 2.0.16
959 *
960 * @param int $id
961 * @param array $values
962 * @param string $update_type
963 *
964 * @return boolean $update
965 */
966 private static function before_update_entry( $id, &$values, $update_type ) {
967 $update = true;
968
969 global $frm_vars;
970
971 if ( isset( $frm_vars['saved_entries'] ) && is_array( $frm_vars['saved_entries'] ) && in_array( (int) $id, (array) $frm_vars['saved_entries'] ) ) {
972 $update = false;
973 }
974
975 if ( $update && $update_type != 'xml' ) {
976 $values = apply_filters( 'frm_pre_update_entry', $values, $id );
977 }
978
979 return $update;
980 }
981
982 /**
983 * Package the entry data for updating
984 *
985 * @since 2.0.16
986 *
987 * @param int $id
988 * @param array $values
989 *
990 * @return array $new_values
991 */
992 private static function package_entry_to_update( $id, $values ) {
993 global $wpdb;
994
995 $new_values = array(
996 'name' => self::get_new_entry_name( $values ),
997 'form_id' => (int) self::get_entry_value( $values, 'form_id', null ),
998 'is_draft' => self::get_is_draft_value( $values ),
999 'updated_at' => current_time( 'mysql', 1 ),
1000 'updated_by' => isset( $values['updated_by'] ) ? $values['updated_by'] : get_current_user_id(),
1001 );
1002
1003 if ( isset( $values['post_id'] ) ) {
1004 $new_values['post_id'] = (int) $values['post_id'];
1005 }
1006
1007 if ( isset( $values['item_key'] ) ) {
1008 $new_values['item_key'] = FrmAppHelper::get_unique_key( $values['item_key'], $wpdb->prefix . 'frm_items', 'item_key', $id );
1009 }
1010
1011 if ( isset( $values['parent_item_id'] ) ) {
1012 $new_values['parent_item_id'] = (int) $values['parent_item_id'];
1013 }
1014
1015 if ( isset( $values['frm_user_id'] ) && is_numeric( $values['frm_user_id'] ) ) {
1016 $new_values['user_id'] = $values['frm_user_id'];
1017 }
1018
1019 $new_values = apply_filters( 'frm_update_entry', $new_values, $id );
1020
1021 return $new_values;
1022 }
1023
1024 /**
1025 * Perform some actions right after updating an entry
1026 *
1027 * @since 2.0.16
1028 *
1029 * @param boolean|int $query_results
1030 * @param int $id
1031 * @param array $values
1032 * @param array $new_values
1033 */
1034 private static function after_update_entry( $query_results, $id, $values, $new_values ) {
1035 if ( $query_results ) {
1036 self::clear_cache();
1037 }
1038
1039 global $frm_vars;
1040 if ( ! isset( $frm_vars['saved_entries'] ) ) {
1041 $frm_vars['saved_entries'] = array();
1042 }
1043
1044 $frm_vars['saved_entries'][] = (int) $id;
1045
1046 if ( isset( $values['item_meta'] ) ) {
1047 FrmEntryMeta::update_entry_metas( $id, $values['item_meta'] );
1048 }
1049
1050 do_action( 'frm_after_update_entry', $id, $new_values['form_id'] );
1051 do_action( 'frm_after_update_entry_' . $new_values['form_id'], $id );
1052 }
1053
1054 /**
1055 * Create entry from an XML import
1056 * Certain actions aren't necessary when importing (like saving sub entries, checking for duplicates, etc.)
1057 *
1058 * @since 2.0.16
1059 *
1060 * @param array $values
1061 *
1062 * @return int | boolean $entry_id
1063 */
1064 public static function create_entry_from_xml( $values ) {
1065 $entry_id = self::create_entry( $values, 'xml' );
1066
1067 return $entry_id;
1068 }
1069
1070 /**
1071 * Update entry from an XML import
1072 * Certain actions aren't necessary when importing (like saving sub entries and modifying other vals)
1073 *
1074 * @since 2.0.16
1075 *
1076 * @param int $id
1077 * @param array $values
1078 *
1079 * @return int | boolean $updated
1080 */
1081 public static function update_entry_from_xml( $id, $values ) {
1082 $updated = self::update_entry( $id, $values, 'xml' );
1083
1084 return $updated;
1085 }
1086
1087 /**
1088 * @param string $key
1089 *
1090 * @return int entry_id
1091 */
1092 public static function get_id_by_key( $key ) {
1093 $entry_id = FrmDb::get_var( 'frm_items', array( 'item_key' => sanitize_title( $key ) ) );
1094
1095 return (int) $entry_id;
1096 }
1097
1098 /**
1099 * Get entries count.
1100 *
1101 * @since 6.8
1102 *
1103 * @return int
1104 */
1105 public static function get_entries_count() {
1106 $args = array(
1107 'or' => 1,
1108 'parent_form_id' => null,
1109 'parent_form_id <' => 1,
1110 );
1111 return self::getRecordCount( $args );
1112 }
1113 }
1114