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

1,091 lines 28.9 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 }
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 $entry = self::getOne( $id, true ); // Item meta is required for conditional logic in actions with 'delete' events.
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 * @param int|object $entry by reference
340 *
341 * @since 2.0.9
342 */
343 public static function maybe_get_entry( &$entry ) {
344 if ( $entry && is_numeric( $entry ) ) {
345 $entry = self::getOne( $entry );
346 } elseif ( empty( $entry ) || 'false' === $entry ) {
347 $entry = false;
348 }
349 }
350
351 public static function getOne( $id, $meta = false ) {
352 global $wpdb;
353
354 $query = "SELECT it.*, fr.name as form_name, fr.form_key as form_key FROM {$wpdb->prefix}frm_items it
355 LEFT OUTER JOIN {$wpdb->prefix}frm_forms fr ON it.form_id=fr.id WHERE ";
356
357 $query .= is_numeric( $id ) ? 'it.id=%d' : 'it.item_key=%s';
358 $query_args = array( $id );
359 $query = $wpdb->prepare( $query, $query_args ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
360
361 if ( ! $meta ) {
362 $entry = FrmDb::check_cache( $id . '_nometa', 'frm_entry', $query, 'get_row' );
363 self::prepare_entry( $entry );
364 return $entry;
365 }
366
367 $entry = FrmDb::check_cache( $id, 'frm_entry' );
368 if ( $entry !== false ) {
369 self::prepare_entry( $entry );
370 return $entry;
371 }
372
373 $entry = $wpdb->get_row( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
374 $entry = self::get_meta( $entry );
375 self::prepare_entry( $entry );
376
377 return $entry;
378 }
379
380 /**
381 * @since 4.02.03
382 *
383 * @param object $entry
384 */
385 private static function prepare_entry( &$entry ) {
386 if ( empty( $entry ) ) {
387 return;
388 }
389
390 FrmAppHelper::unserialize_or_decode( $entry->description );
391 $entry = wp_unslash( $entry ); // TODO: Remove slashes on input only, not output.
392 }
393
394 /**
395 * @since 4.02.03
396 *
397 * @param array $entries
398 */
399 private static function prepare_entries( &$entries ) {
400 foreach ( $entries as $k => $entry ) {
401 self::prepare_entry( $entry );
402 $entries[ $k ] = $entry;
403 }
404 }
405
406 public static function get_meta( $entry ) {
407 if ( ! $entry ) {
408 return $entry;
409 }
410
411 global $wpdb;
412 $metas = FrmDb::get_results(
413 $wpdb->prefix . 'frm_item_metas m LEFT JOIN ' . $wpdb->prefix . 'frm_fields f ON m.field_id=f.id',
414 array(
415 'item_id' => $entry->id,
416 'field_id !' => 0,
417 ),
418 'field_id, meta_value, field_key, item_id, f.type'
419 );
420
421 $entry->metas = array();
422
423 $include_key = apply_filters( 'frm_include_meta_keys', false, array( 'form_id' => $entry->form_id ) );
424 foreach ( $metas as $meta_val ) {
425 FrmFieldsHelper::prepare_field_value( $meta_val->meta_value, $meta_val->type );
426
427 if ( $meta_val->item_id == $entry->id ) {
428 $entry->metas[ $meta_val->field_id ] = $meta_val->meta_value;
429 if ( $include_key ) {
430 $entry->metas[ $meta_val->field_key ] = $entry->metas[ $meta_val->field_id ];
431 }
432 continue;
433 }
434
435 // include sub entries in an array
436 if ( ! isset( $entry->metas[ $meta_val->field_id ] ) ) {
437 $entry->metas[ $meta_val->field_id ] = array();
438 }
439
440 $entry->metas[ $meta_val->field_id ][] = $meta_val->meta_value;
441
442 unset( $meta_val );
443 }
444 unset( $metas );
445
446 FrmDb::set_cache( $entry->id, $entry, 'frm_entry' );
447
448 return $entry;
449 }
450
451 /**
452 * @param string $id
453 */
454 public static function exists( $id ) {
455 global $wpdb;
456
457 if ( FrmDb::check_cache( $id, 'frm_entry' ) ) {
458 $exists = true;
459
460 return $exists;
461 }
462
463 if ( is_numeric( $id ) ) {
464 $where = array( 'id' => $id );
465 } else {
466 $where = array( 'item_key' => $id );
467 }
468 $id = FrmDb::get_var( $wpdb->prefix . 'frm_items', $where );
469
470 return ( $id && $id > 0 );
471 }
472
473 public static function getAll( $where, $order_by = '', $limit = '', $meta = false, $inc_form = true ) {
474 global $wpdb;
475
476 $limit = FrmDb::esc_limit( $limit );
477
478 $cache_key = FrmAppHelper::maybe_json_encode( $where ) . $order_by . $limit . $inc_form;
479 $entries = wp_cache_get( $cache_key, 'frm_entry' );
480
481 if ( false === $entries ) {
482 $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';
483 $table = $wpdb->prefix . 'frm_items it ';
484
485 if ( $inc_form ) {
486 $fields = 'it.*, fr.name as form_name,fr.form_key as form_key';
487 $table .= 'LEFT OUTER JOIN ' . $wpdb->prefix . 'frm_forms fr ON it.form_id=fr.id ';
488 }
489
490 if ( preg_match( '/ meta_([0-9]+)/', $order_by, $order_matches ) ) {
491 $fields .= self::sort_by_field( $order_matches[1] );
492 unset( $order_matches );
493 }
494
495 // prepare the query
496 $query = 'SELECT ' . $fields . ' FROM ' . $table . FrmDb::prepend_and_or_where( ' WHERE ', $where ) . $order_by . $limit;
497
498 $entries = $wpdb->get_results( $query, OBJECT_K ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
499 unset( $query );
500
501 FrmDb::set_cache( $cache_key, $entries, 'frm_entry' );
502 }
503
504 if ( ! $meta || ! $entries ) {
505 self::prepare_entries( $entries );
506 return $entries;
507 }
508 unset( $meta );
509
510 if ( ! is_array( $where ) && preg_match( '/^it\.form_id=\d+$/', $where ) ) {
511 $where = array( 'it.form_id' => substr( $where, 11 ) );
512 }
513
514 $meta_where = array( 'field_id !' => 0 );
515 if ( $limit == '' && is_array( $where ) && count( $where ) == 1 && isset( $where['it.form_id'] ) ) {
516 $meta_where['fi.form_id'] = $where['it.form_id'];
517 } else {
518 $meta_where['item_id'] = array_keys( $entries );
519 }
520
521 $metas = FrmDb::get_results(
522 $wpdb->prefix . 'frm_item_metas it LEFT OUTER JOIN ' . $wpdb->prefix . 'frm_fields fi ON it.field_id = fi.id',
523 $meta_where,
524 'item_id, meta_value, field_id, field_key, form_id, fi.type'
525 );
526
527 unset( $meta_where );
528
529 if ( ! $metas ) {
530 self::prepare_entries( $entries );
531 return $entries;
532 }
533
534 foreach ( $metas as $m_key => $meta_val ) {
535 if ( ! isset( $entries[ $meta_val->item_id ] ) ) {
536 continue;
537 }
538
539 if ( ! isset( $entries[ $meta_val->item_id ]->metas ) ) {
540 $entries[ $meta_val->item_id ]->metas = array();
541 }
542
543 FrmFieldsHelper::prepare_field_value( $meta_val->meta_value, $meta_val->type );
544 $entries[ $meta_val->item_id ]->metas[ $meta_val->field_id ] = $meta_val->meta_value;
545 unset( $m_key, $meta_val );
546 }
547
548 if ( ! FrmAppHelper::prevent_caching() ) {
549 foreach ( $entries as $entry ) {
550 FrmDb::set_cache( $entry->id, $entry, 'frm_entry' );
551 unset( $entry );
552 }
553 }
554
555 self::prepare_entries( $entries );
556 return $entries;
557 }
558
559 /**
560 * @param int $field_id
561 * @return string
562 */
563 private static function sort_by_field( $field_id ) {
564 global $wpdb;
565 $field_id = (int) $field_id;
566
567 $field_options = FrmDb::get_var( 'frm_fields', array( 'id' => $field_id ), 'field_options' );
568 FrmAppHelper::unserialize_or_decode( $field_options );
569
570 if ( empty( $field_options['post_field'] ) ) {
571 $sort = ', (SELECT meta_value FROM ' . $wpdb->prefix . 'frm_item_metas WHERE field_id = ' . $field_id . ' AND item_id = it.id) as meta_' . $field_id;
572 } else {
573 $sort = '';
574 }
575
576 return apply_filters( 'frm_handle_field_column_sort', $sort, $field_id, $field_options );
577 }
578
579 // Pagination Methods
580 /**
581 * @param int|array|string If int, use the form id.
582 */
583 public static function getRecordCount( $where = '' ) {
584 global $wpdb;
585 $table_join = $wpdb->prefix . 'frm_items it JOIN ' . $wpdb->prefix . 'frm_forms fr ON it.form_id=fr.id';
586
587 if ( is_numeric( $where ) ) {
588 $table_join = 'frm_items';
589 $where = array( 'form_id' => $where );
590 }
591
592 if ( is_array( $where ) ) {
593 $count = FrmDb::get_count( $table_join, $where );
594 } else {
595 $cache_key = 'count_' . FrmAppHelper::maybe_json_encode( $where );
596 $query = 'SELECT COUNT(*) FROM ' . $table_join . FrmDb::prepend_and_or_where( ' WHERE ', $where );
597 $count = FrmDb::check_cache( $cache_key, 'frm_entry', $query, 'get_var' );
598 }
599
600 return $count;
601 }
602
603 public static function getPageCount( $p_size, $where = '' ) {
604 $p_size = (int) $p_size;
605 $count = 1;
606 if ( $p_size ) {
607 if ( ! is_numeric( $where ) ) {
608 $where = self::getRecordCount( $where );
609 }
610 $count = ceil( (int) $where / $p_size );
611 }
612
613 return $count;
614 }
615
616 /**
617 * Prepare the data before inserting it into the database
618 *
619 * @since 2.0.16
620 *
621 * @param array $values
622 * @param string $type
623 *
624 * @return array $new_values
625 */
626 private static function before_insert_entry_in_database( &$values, $type ) {
627
628 self::sanitize_entry_post( $values );
629
630 if ( $type != 'xml' ) {
631 $values = apply_filters( 'frm_pre_create_entry', $values );
632 }
633
634 $new_values = self::package_entry_data( $values );
635
636 return $new_values;
637 }
638
639 /**
640 * Create an entry and perform after create actions
641 *
642 * @since 2.0.16
643 *
644 * @param array $values
645 * @param array $new_values
646 *
647 * @return boolean|int $entry_id
648 */
649 private static function continue_to_create_entry( $values, $new_values ) {
650 $entry_id = self::insert_entry_into_database( $new_values );
651 if ( ! $entry_id ) {
652 return false;
653 }
654
655 self::after_insert_entry_in_database( $values, $new_values, $entry_id );
656
657 return $entry_id;
658 }
659
660 /**
661 * Sanitize the POST values before we use them
662 *
663 * @since 2.0
664 *
665 * @param array $values The POST values by reference
666 */
667 public static function sanitize_entry_post( &$values ) {
668 $sanitize_method = array(
669 'form_id' => 'absint',
670 'frm_action' => 'sanitize_title',
671 'form_key' => 'sanitize_title',
672 'item_key' => 'sanitize_title',
673 'item_name' => 'sanitize_text_field',
674 'frm_saving_draft' => 'absint',
675 'is_draft' => 'absint',
676 'post_id' => 'absint',
677 'parent_item_id' => 'absint',
678 'created_at' => 'sanitize_text_field',
679 'updated_at' => 'sanitize_text_field',
680 );
681
682 FrmAppHelper::sanitize_request( $sanitize_method, $values );
683 }
684
685 /**
686 * Prepare the new values for inserting into the database
687 *
688 * @since 2.0.16
689 *
690 * @param array $values
691 *
692 * @return array $new_values
693 */
694 private static function package_entry_data( &$values ) {
695 global $wpdb;
696
697 if ( ! isset( $values['item_key'] ) ) {
698 $values['item_key'] = '';
699 }
700
701 $item_name = self::get_new_entry_name( $values, $values['item_key'] );
702 $new_values = array(
703 'item_key' => FrmAppHelper::get_unique_key( $values['item_key'], $wpdb->prefix . 'frm_items', 'item_key' ),
704 'name' => FrmAppHelper::truncate( $item_name, 255, 1, '' ),
705 'ip' => self::get_ip( $values ),
706 'is_draft' => self::get_is_draft_value( $values ),
707 'form_id' => (int) self::get_entry_value( $values, 'form_id', null ),
708 'post_id' => (int) self::get_entry_value( $values, 'post_id', 0 ),
709 'parent_item_id' => (int) self::get_entry_value( $values, 'parent_item_id', 0 ),
710 'created_at' => self::get_created_at( $values ),
711 'updated_at' => self::get_updated_at( $values ),
712 'description' => self::get_entry_description( $values ),
713 'user_id' => self::get_entry_user_id( $values ),
714 );
715
716 $new_values['updated_by'] = isset( $values['updated_by'] ) ? $values['updated_by'] : $new_values['user_id'];
717
718 return $new_values;
719 }
720
721 private static function get_entry_value( $values, $name, $default ) {
722 return isset( $values[ $name ] ) ? $values[ $name ] : $default;
723 }
724
725 /**
726 * Get the ip for a new entry.
727 * Allow the import to override the value.
728 *
729 * @since 2.03.10
730 *
731 * @param array $values
732 *
733 * @return string
734 */
735 private static function get_ip( $values ) {
736 if ( ! FrmAppHelper::ips_saved() ) {
737 return '';
738 }
739
740 $ip = FrmAppHelper::get_ip_address();
741 if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING ) {
742 $ip = self::get_entry_value( $values, 'ip', $ip );
743 }
744
745 return $ip;
746 }
747
748 /**
749 * Get the is_draft value for a new entry
750 *
751 * @since 2.0.16
752 *
753 * @param array $values
754 *
755 * @return int
756 */
757 private static function get_is_draft_value( $values ) {
758 if ( isset( $values['frm_saving_draft'] ) && FrmEntriesHelper::DRAFT_ENTRY_STATUS === (int) $values['frm_saving_draft'] ) {
759 return FrmEntriesHelper::DRAFT_ENTRY_STATUS;
760 }
761
762 return isset( $values['is_draft'] ) ? absint( $values['is_draft'] ) : FrmEntriesHelper::SUBMITTED_ENTRY_STATUS;
763 }
764
765 /**
766 * Get the created_at value for a new entry
767 *
768 * @since 2.0.16
769 *
770 * @param array $values
771 *
772 * @return string
773 */
774 private static function get_created_at( $values ) {
775 return self::get_entry_value( $values, 'created_at', current_time( 'mysql', 1 ) );
776 }
777
778 /**
779 * Get the updated_at value for a new entry
780 *
781 * @since 2.0.16
782 *
783 * @param array $values
784 *
785 * @return string
786 */
787 private static function get_updated_at( $values ) {
788 if ( isset( $values['updated_at'] ) ) {
789 $updated_at = $values['updated_at'];
790 } else {
791 $updated_at = self::get_created_at( $values );
792 }
793
794 return $updated_at;
795 }
796
797 /**
798 * Get the description value for a new entry
799 *
800 * @since 2.0.16
801 *
802 * @param array $values
803 *
804 * @return string
805 */
806 private static function get_entry_description( $values ) {
807 if ( isset( $values['description'] ) && ! empty( $values['description'] ) ) {
808 $description = FrmAppHelper::maybe_json_encode( $values['description'] );
809 } else {
810 $description = json_encode(
811 array(
812 'browser' => FrmAppHelper::get_server_value( 'HTTP_USER_AGENT' ),
813 'referrer' => FrmAppHelper::get_server_value( 'HTTP_REFERER' ),
814 )
815 );
816 }
817
818 return $description;
819 }
820
821 /**
822 * Get the user_id value for a new entry
823 *
824 * @since 2.0.16
825 *
826 * @param array $values
827 *
828 * @return int
829 */
830 private static function get_entry_user_id( $values ) {
831 if ( isset( $values['frm_user_id'] ) && ( is_numeric( $values['frm_user_id'] ) || FrmAppHelper::is_admin() ) ) {
832 $user_id = $values['frm_user_id'];
833 } else {
834 $current_user_id = get_current_user_id();
835 $user_id = $current_user_id ? $current_user_id : 0;
836 }
837
838 return $user_id;
839 }
840
841 /**
842 * Insert new entry into the database
843 *
844 * @since 2.0.16
845 *
846 * @param array $new_values
847 *
848 * @return int | boolean $entry_id
849 */
850 private static function insert_entry_into_database( $new_values ) {
851 global $wpdb;
852
853 $query_results = $wpdb->insert( $wpdb->prefix . 'frm_items', $new_values );
854
855 if ( ! $query_results ) {
856 $entry_id = false;
857 } else {
858 $entry_id = $wpdb->insert_id;
859 }
860
861 return $entry_id;
862 }
863
864 /**
865 * Add the new entry to global $frm_vars
866 *
867 * @since 2.0.16
868 *
869 * @param int $entry_id
870 */
871 private static function add_new_entry_to_frm_vars( $entry_id ) {
872 global $frm_vars;
873
874 if ( ! isset( $frm_vars['saved_entries'] ) ) {
875 $frm_vars['saved_entries'] = array();
876 }
877
878 $frm_vars['saved_entries'][] = (int) $entry_id;
879 }
880
881 /**
882 * Add entry metas, if there are any
883 *
884 * @since 2.0.16
885 *
886 * @param array $values
887 * @param int $entry_id
888 * @return void
889 */
890 private static function maybe_add_entry_metas( $values, $entry_id ) {
891 if ( isset( $values['item_meta'] ) ) {
892 FrmEntryMeta::update_entry_metas( $entry_id, $values['item_meta'] );
893 }
894 self::maybe_add_captcha_meta( (int) $values['form_id'], (int) $entry_id );
895 }
896
897 /**
898 * @since 5.0.15
899 *
900 * @param int $form_id
901 * @param int $entry_id
902 * @return void
903 */
904 private static function maybe_add_captcha_meta( $form_id, $entry_id ) {
905 global $frm_vars;
906 if ( array_key_exists( 'captcha_scores', $frm_vars ) && array_key_exists( $form_id, $frm_vars['captcha_scores'] ) ) {
907 $captcha_score_meta = array( 'captcha_score' => $frm_vars['captcha_scores'][ $form_id ] );
908 FrmEntryMeta::add_entry_meta( $entry_id, 0, '', maybe_serialize( $captcha_score_meta ) );
909 }
910 }
911
912 /**
913 * Trigger frm_after_create_entry hooks
914 *
915 * @since 2.0.16
916 *
917 * @param int $entry_id
918 * @param array $new_values
919 */
920 private static function after_entry_created_actions( $entry_id, $values, $new_values ) {
921 // this is a child entry
922 $is_child = isset( $values['parent_form_id'] ) && isset( $values['parent_nonce'] ) && ! empty( $values['parent_form_id'] ) && wp_verify_nonce( $values['parent_nonce'], 'parent' );
923
924 do_action( 'frm_after_create_entry', $entry_id, $new_values['form_id'], compact( 'is_child' ) );
925 do_action( 'frm_after_create_entry_' . $new_values['form_id'], $entry_id, compact( 'is_child' ) );
926 }
927
928 /**
929 * Actions to perform immediately after an entry is inserted in the frm_items database
930 *
931 * @since 2.0.16
932 *
933 * @param array $values
934 * @param array $new_values
935 * @param int $entry_id
936 */
937 private static function after_insert_entry_in_database( $values, $new_values, $entry_id ) {
938
939 self::add_new_entry_to_frm_vars( $entry_id );
940
941 self::maybe_add_entry_metas( $values, $entry_id );
942
943 self::clear_cache();
944
945 self::after_entry_created_actions( $entry_id, $values, $new_values );
946 }
947
948 /**
949 * Perform some actions right before updating an entry
950 *
951 * @since 2.0.16
952 *
953 * @param int $id
954 * @param array $values
955 * @param string $update_type
956 *
957 * @return boolean $update
958 */
959 private static function before_update_entry( $id, &$values, $update_type ) {
960 $update = true;
961
962 global $frm_vars;
963
964 if ( isset( $frm_vars['saved_entries'] ) && is_array( $frm_vars['saved_entries'] ) && in_array( (int) $id, (array) $frm_vars['saved_entries'] ) ) {
965 $update = false;
966 }
967
968 if ( $update && $update_type != 'xml' ) {
969 $values = apply_filters( 'frm_pre_update_entry', $values, $id );
970 }
971
972 return $update;
973 }
974
975 /**
976 * Package the entry data for updating
977 *
978 * @since 2.0.16
979 *
980 * @param int $id
981 * @param array $values
982 *
983 * @return array $new_values
984 */
985 private static function package_entry_to_update( $id, $values ) {
986 global $wpdb;
987
988 $new_values = array(
989 'name' => self::get_new_entry_name( $values ),
990 'form_id' => (int) self::get_entry_value( $values, 'form_id', null ),
991 'is_draft' => self::get_is_draft_value( $values ),
992 'updated_at' => current_time( 'mysql', 1 ),
993 'updated_by' => isset( $values['updated_by'] ) ? $values['updated_by'] : get_current_user_id(),
994 );
995
996 if ( isset( $values['post_id'] ) ) {
997 $new_values['post_id'] = (int) $values['post_id'];
998 }
999
1000 if ( isset( $values['item_key'] ) ) {
1001 $new_values['item_key'] = FrmAppHelper::get_unique_key( $values['item_key'], $wpdb->prefix . 'frm_items', 'item_key', $id );
1002 }
1003
1004 if ( isset( $values['parent_item_id'] ) ) {
1005 $new_values['parent_item_id'] = (int) $values['parent_item_id'];
1006 }
1007
1008 if ( isset( $values['frm_user_id'] ) && is_numeric( $values['frm_user_id'] ) ) {
1009 $new_values['user_id'] = $values['frm_user_id'];
1010 }
1011
1012 $new_values = apply_filters( 'frm_update_entry', $new_values, $id );
1013
1014 return $new_values;
1015 }
1016
1017 /**
1018 * Perform some actions right after updating an entry
1019 *
1020 * @since 2.0.16
1021 *
1022 * @param boolean|int $query_results
1023 * @param int $id
1024 * @param array $values
1025 * @param array $new_values
1026 */
1027 private static function after_update_entry( $query_results, $id, $values, $new_values ) {
1028 if ( $query_results ) {
1029 self::clear_cache();
1030 }
1031
1032 global $frm_vars;
1033 if ( ! isset( $frm_vars['saved_entries'] ) ) {
1034 $frm_vars['saved_entries'] = array();
1035 }
1036
1037 $frm_vars['saved_entries'][] = (int) $id;
1038
1039 if ( isset( $values['item_meta'] ) ) {
1040 FrmEntryMeta::update_entry_metas( $id, $values['item_meta'] );
1041 }
1042
1043 do_action( 'frm_after_update_entry', $id, $new_values['form_id'] );
1044 do_action( 'frm_after_update_entry_' . $new_values['form_id'], $id );
1045 }
1046
1047 /**
1048 * Create entry from an XML import
1049 * Certain actions aren't necessary when importing (like saving sub entries, checking for duplicates, etc.)
1050 *
1051 * @since 2.0.16
1052 *
1053 * @param array $values
1054 *
1055 * @return int | boolean $entry_id
1056 */
1057 public static function create_entry_from_xml( $values ) {
1058 $entry_id = self::create_entry( $values, 'xml' );
1059
1060 return $entry_id;
1061 }
1062
1063 /**
1064 * Update entry from an XML import
1065 * Certain actions aren't necessary when importing (like saving sub entries and modifying other vals)
1066 *
1067 * @since 2.0.16
1068 *
1069 * @param int $id
1070 * @param array $values
1071 *
1072 * @return int | boolean $updated
1073 */
1074 public static function update_entry_from_xml( $id, $values ) {
1075 $updated = self::update_entry( $id, $values, 'xml' );
1076
1077 return $updated;
1078 }
1079
1080 /**
1081 * @param string $key
1082 *
1083 * @return int entry_id
1084 */
1085 public static function get_id_by_key( $key ) {
1086 $entry_id = FrmDb::get_var( 'frm_items', array( 'item_key' => sanitize_title( $key ) ) );
1087
1088 return (int) $entry_id;
1089 }
1090 }
1091