Completed
Push — master ( 94ce40...81d0ae )
by Stephanie
02:19
created

FrmEntry::get_is_draft_value()   A

Complexity

Conditions 5
Paths 10

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
nc 10
nop 1
dl 0
loc 3
rs 9.6111
c 0
b 0
f 0
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 >'] = date( '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'] );
58
		unset( $check_val['is_draft'], $check_val['id'], $check_val['item_key'] );
59
60
		if ( $new_values['item_key'] == $new_values['name'] ) {
61
			unset( $check_val['name'] );
62
		}
63
64
		global $wpdb;
65
		$entry_exists = FrmDb::get_col( $wpdb->prefix . 'frm_items', $check_val, 'id', array( 'order_by' => 'created_at DESC' ) );
66
67
		if ( ! $entry_exists || empty( $entry_exists ) || ! isset( $values['item_meta'] ) ) {
68
			return false;
69
		}
70
71
		$is_duplicate = false;
72
		foreach ( $entry_exists as $entry_exist ) {
0 ignored issues
show
Bug introduced by
The expression $entry_exists of type array|string|object is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
73
			$is_duplicate = true;
74
75
			// make sure it's a duplicate
76
			$metas       = FrmEntryMeta::get_entry_meta_info( $entry_exist );
77
			$field_metas = array();
78
			foreach ( $metas as $meta ) {
0 ignored issues
show
Bug introduced by
The expression $metas of type array|null|string|object is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
79
				$field_metas[ $meta->field_id ] = $meta->meta_value;
80
			}
81
82
			// If prev entry is empty and current entry is not, they are not duplicates
83
			$filtered_vals = array_filter( $values['item_meta'] );
84
			$field_metas   = array_filter( $field_metas );
85
			if ( empty( $field_metas ) && ! empty( $filtered_vals ) ) {
86
				return false;
87
			}
88
89
			// compare serialized values and not arrays
90
			$new_meta = array_map( 'maybe_serialize', $filtered_vals );
91
92
			if ( $field_metas === $new_meta ) {
93
				$is_duplicate = true;
94
				break;
95
			}
96
97
			if ( count( $field_metas ) !== count( $new_meta ) ) {
98
				// TODO: compare values saved in the post also
99
				$is_duplicate = false;
100
				continue;
101
			}
102
103
			$diff = array_diff_assoc( $field_metas, $new_meta );
104
			foreach ( $diff as $field_id => $meta_value ) {
105
				if ( ! empty( $meta_value ) ) {
106
					$is_duplicate = false;
107
					continue;
108
				}
109
			}
110
111
			if ( $is_duplicate ) {
112
				break;
113
			}
114
		}
115
116
		return $is_duplicate;
117
	}
118
119
	/**
120
	 * Determine if an entry needs to be checked as a possible duplicate
121
	 *
122
	 * @since 2.0.23
123
	 *
124
	 * @param array $values
125
	 * @param int $duplicate_entry_time
126
	 *
127
	 * @return bool
128
	 */
129
	private static function is_duplicate_check_needed( $values, $duplicate_entry_time ) {
130
		// If time for checking duplicates is set to an empty value, don't check for duplicates
131
		if ( empty( $duplicate_entry_time ) ) {
132
			return false;
133
		}
134
135
		// If CSV is importing, don't check for duplicates
136
		if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING ) {
137
			return false;
138
		}
139
140
		// If repeating field entries are getting created, don't check for duplicates
141
		if ( isset( $values['parent_form_id'] ) && $values['parent_form_id'] ) {
142
			return false;
143
		}
144
145
		return true;
146
	}
147
148
	public static function duplicate( $id ) {
149
		global $wpdb;
150
151
		$values = self::getOne( $id );
152
153
		$new_values               = array();
154
		$new_values['item_key']   = FrmAppHelper::get_unique_key( '', $wpdb->prefix . 'frm_items', 'item_key' );
155
		$new_values['name']       = $values->name;
156
		$new_values['is_draft']   = $values->is_draft;
157
		$new_values['user_id']    = (int) $values->user_id;
158
		$new_values['updated_by'] = (int) $values->user_id;
159
		$new_values['form_id']    = $values->form_id ? (int) $values->form_id : null;
160
		$new_values['created_at'] = current_time( 'mysql', 1 );
161
		$new_values['updated_at'] = $new_values['created_at'];
162
163
		$query_results = $wpdb->insert( $wpdb->prefix . 'frm_items', $new_values );
164
		if ( ! $query_results ) {
165
			return false;
166
		}
167
168
		$entry_id = $wpdb->insert_id;
169
170
		global $frm_vars;
171
		if ( ! isset( $frm_vars['saved_entries'] ) ) {
172
			$frm_vars['saved_entries'] = array();
173
		}
174
		$frm_vars['saved_entries'][] = (int) $entry_id;
175
176
		FrmEntryMeta::duplicate_entry_metas( $id, $entry_id );
177
		self::clear_cache();
178
179
		do_action( 'frm_after_duplicate_entry', $entry_id, $new_values['form_id'], array( 'old_id' => $id ) );
180
181
		return $entry_id;
182
	}
183
184
	/**
185
	 * Update an entry (not via XML)
186
	 *
187
	 * @param int $id
188
	 * @param array $values
189
	 *
190
	 * @return boolean|int $update_results
191
	 */
192
	public static function update( $id, $values ) {
193
		$update_results = self::update_entry( $id, $values, 'standard' );
194
195
		return $update_results;
196
	}
197
198
	/**
199
	 * Update an entry with some differences depending on the update type
200
	 *
201
	 * @since 2.0.16
202
	 *
203
	 * @param int $id
204
	 * @param array $values
205
	 *
206
	 * @return boolean|int $query_results
207
	 */
208
	private static function update_entry( $id, $values, $update_type ) {
209
		global $wpdb;
210
211
		$update = self::before_update_entry( $id, $values, $update_type );
212
		if ( ! $update ) {
213
			return false;
214
		}
215
216
		$new_values = self::package_entry_to_update( $id, $values );
217
218
		$query_results = $wpdb->update( $wpdb->prefix . 'frm_items', $new_values, compact( 'id' ) );
219
220
		self::after_update_entry( $query_results, $id, $values, $new_values );
221
222
		return $query_results;
223
	}
224
225
	public static function destroy( $id ) {
226
		global $wpdb;
227
		$id = (int) $id;
228
229
		$entry = self::getOne( $id );
230
		if ( ! $entry ) {
231
			$result = false;
232
233
			return $result;
234
		}
235
236
		do_action( 'frm_before_destroy_entry', $id, $entry );
237
238
		$wpdb->query( $wpdb->prepare( 'DELETE FROM ' . $wpdb->prefix . 'frm_item_metas WHERE item_id=%d', $id ) );
239
		$result = $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . $wpdb->prefix . 'frm_items WHERE id=%d', $id ) );
240
241
		self::clear_cache();
242
243
		return $result;
244
	}
245
246
	public static function update_form( $id, $value, $form_id ) {
247
		global $wpdb;
248
		$form_id = isset( $value ) ? $form_id : null;
249
		$result  = $wpdb->update( $wpdb->prefix . 'frm_items', array( 'form_id' => $form_id ), array( 'id' => $id ) );
250
		if ( $result ) {
251
			self::clear_cache();
252
		}
253
254
		return $result;
255
	}
256
257
	/**
258
	 * Clear entry caching
259
	 * Called when an entry is changed
260
	 *
261
	 * @since 2.0.5
262
	 */
263
	public static function clear_cache() {
264
		FrmDb::cache_delete_group( 'frm_entry' );
265
		FrmDb::cache_delete_group( 'frm_item' );
266
		FrmDb::cache_delete_group( 'frm_entry_meta' );
267
		FrmDb::cache_delete_group( 'frm_item_meta' );
268
	}
269
270
	/**
271
	 * After switching to the wp_loaded hook for processing entries,
272
	 * we can no longer use 'name', but check it as a fallback
273
	 *
274
	 * @since 2.0.11
275
	 */
276
	public static function get_new_entry_name( $values, $default = '' ) {
277
		$name = isset( $values['item_name'] ) ? $values['item_name'] : ( isset( $values['name'] ) ? $values['name'] : $default );
278
		if ( is_array( $name ) ) {
279
			$name = reset( $name );
280
		}
281
282
		return $name;
283
	}
284
285
	/**
286
	 * If $entry is numeric, get the entry object
287
	 *
288
	 * @param int|object $entry by reference
289
	 *
290
	 * @since 2.0.9
291
	 */
292
	public static function maybe_get_entry( &$entry ) {
293
		if ( $entry && is_numeric( $entry ) ) {
294
			$entry = self::getOne( $entry );
295
		} elseif ( empty( $entry ) ) {
296
			$entry = false;
297
		}
298
	}
299
300
	public static function getOne( $id, $meta = false ) {
301
		global $wpdb;
302
303
		$query = "SELECT it.*, fr.name as form_name, fr.form_key as form_key FROM {$wpdb->prefix}frm_items it
304
                  LEFT OUTER JOIN {$wpdb->prefix}frm_forms fr ON it.form_id=fr.id WHERE ";
305
306
		$query      .= is_numeric( $id ) ? 'it.id=%d' : 'it.item_key=%s';
307
		$query_args = array( $id );
308
		$query      = $wpdb->prepare( $query, $query_args ); // WPCS: unprepared SQL ok.
309
310
		if ( ! $meta ) {
311
			$entry = FrmDb::check_cache( $id . '_nometa', 'frm_entry', $query, 'get_row' );
312
			self::prepare_entry( $entry );
313
			return $entry;
314
		}
315
316
		$entry = FrmDb::check_cache( $id, 'frm_entry' );
317
		if ( $entry !== false ) {
318
			self::prepare_entry( $entry );
319
			return $entry;
320
		}
321
322
		$entry = $wpdb->get_row( $query ); // WPCS: unprepared SQL ok.
323
		$entry = self::get_meta( $entry );
324
		self::prepare_entry( $entry );
325
326
		return $entry;
327
	}
328
329
	/**
330
	 * @since 4.02.03
331
	 *
332
	 * @param object $entry
333
	 */
334
	private static function prepare_entry( &$entry ) {
335
		FrmAppHelper::unserialize_or_decode( $entry->description );
336
		wp_unslash( $entry );
337
	}
338
339
	/**
340
	 * @since 4.02.03
341
	 *
342
	 * @param array $entries
343
	 */
344
	private static function prepare_entries( &$entries ) {
345
		foreach ( $entries as $k => $entry ) {
346
			self::prepare_entry( $entry );
347
			$entries[ $k ] = $entry;
348
		}
349
	}
350
351
	public static function get_meta( $entry ) {
352
		if ( ! $entry ) {
353
			return $entry;
354
		}
355
356
		global $wpdb;
357
		$metas = FrmDb::get_results(
358
			$wpdb->prefix . 'frm_item_metas m LEFT JOIN ' . $wpdb->prefix . 'frm_fields f ON m.field_id=f.id',
359
			array(
360
				'item_id'    => $entry->id,
361
				'field_id !' => 0,
362
			),
363
			'field_id, meta_value, field_key, item_id'
364
		);
365
366
		$entry->metas = array();
367
368
		$include_key = apply_filters( 'frm_include_meta_keys', false, array( 'form_id' => $entry->form_id ) );
369
		foreach ( $metas as $meta_val ) {
0 ignored issues
show
Bug introduced by
The expression $metas of type array|null|string|object is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
370
			if ( $meta_val->item_id == $entry->id ) {
371
				$entry->metas[ $meta_val->field_id ] = $meta_val->meta_value;
372
				FrmAppHelper::unserialize_or_decode( $entry->metas[ $meta_val->field_id ] );
373
				if ( $include_key ) {
374
					$entry->metas[ $meta_val->field_key ] = $entry->metas[ $meta_val->field_id ];
375
				}
376
				continue;
377
			}
378
379
			// include sub entries in an array
380
			if ( ! isset( $entry_metas[ $meta_val->field_id ] ) ) {
381
				$entry->metas[ $meta_val->field_id ] = array();
382
			}
383
384
			FrmAppHelper::unserialize_or_decode( $meta_val->meta_value );
385
			$entry->metas[ $meta_val->field_id ][] = $meta_val->meta_value;
386
387
			unset( $meta_val );
388
		}
389
		unset( $metas );
390
391
		FrmDb::set_cache( $entry->id, $entry, 'frm_entry' );
392
393
		return $entry;
394
	}
395
396
	/**
397
	 * @param string $id
398
	 */
399
	public static function exists( $id ) {
400
		global $wpdb;
401
402
		if ( FrmDb::check_cache( $id, 'frm_entry' ) ) {
403
			$exists = true;
404
405
			return $exists;
406
		}
407
408
		if ( is_numeric( $id ) ) {
409
			$where = array( 'id' => $id );
410
		} else {
411
			$where = array( 'item_key' => $id );
412
		}
413
		$id = FrmDb::get_var( $wpdb->prefix . 'frm_items', $where );
414
415
		return ( $id && $id > 0 );
416
	}
417
418
	public static function getAll( $where, $order_by = '', $limit = '', $meta = false, $inc_form = true ) {
419
		global $wpdb;
420
421
		$limit = FrmDb::esc_limit( $limit );
422
423
		$cache_key = FrmAppHelper::maybe_json_encode( $where ) . $order_by . $limit . $inc_form;
424
		$entries   = wp_cache_get( $cache_key, 'frm_entry' );
425
426
		if ( false === $entries ) {
427
			$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';
428
			$table  = $wpdb->prefix . 'frm_items it ';
429
430
			if ( $inc_form ) {
431
				$fields = 'it.*, fr.name as form_name,fr.form_key as form_key';
432
				$table  .= 'LEFT OUTER JOIN ' . $wpdb->prefix . 'frm_forms fr ON it.form_id=fr.id ';
433
			}
434
435
			if ( preg_match( '/ meta_([0-9]+)/', $order_by, $order_matches ) ) {
436
				// sort by a requested field
437
				$field_id = (int) $order_matches[1];
438
				$fields   .= ', (SELECT meta_value FROM ' . $wpdb->prefix . 'frm_item_metas WHERE field_id = ' . $field_id . ' AND item_id = it.id) as meta_' . $field_id;
439
				unset( $order_matches, $field_id );
440
			}
441
442
			// prepare the query
443
			$query = 'SELECT ' . $fields . ' FROM ' . $table . FrmDb::prepend_and_or_where( ' WHERE ', $where ) . $order_by . $limit;
444
445
			$entries = $wpdb->get_results( $query, OBJECT_K ); // WPCS: unprepared SQL ok.
446
			unset( $query );
447
448
			FrmDb::set_cache( $cache_key, $entries, 'frm_entry' );
449
		}
450
451
		if ( ! $meta || ! $entries ) {
452
			self::prepare_entries( $entries );
453
			return $entries;
454
		}
455
		unset( $meta );
456
457
		if ( ! is_array( $where ) && preg_match( '/^it\.form_id=\d+$/', $where ) ) {
458
			$where = array( 'it.form_id' => substr( $where, 11 ) );
459
		}
460
461
		$meta_where = array( 'field_id !' => 0 );
462
		if ( $limit == '' && is_array( $where ) && count( $where ) == 1 && isset( $where['it.form_id'] ) ) {
463
			$meta_where['fi.form_id'] = $where['it.form_id'];
464
		} else {
465
			$meta_where['item_id'] = array_keys( $entries );
466
		}
467
468
		$metas = FrmDb::get_results( $wpdb->prefix . 'frm_item_metas it LEFT OUTER JOIN ' . $wpdb->prefix . 'frm_fields fi ON (it.field_id = fi.id)', $meta_where, 'item_id, meta_value, field_id, field_key, form_id' );
469
470
		unset( $meta_where );
471
472
		if ( ! $metas ) {
473
			self::prepare_entries( $entries );
474
			return $entries;
475
		}
476
477
		foreach ( $metas as $m_key => $meta_val ) {
0 ignored issues
show
Bug introduced by
The expression $metas of type array|string|object is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
478
			if ( ! isset( $entries[ $meta_val->item_id ] ) ) {
479
				continue;
480
			}
481
482
			if ( ! isset( $entries[ $meta_val->item_id ]->metas ) ) {
483
				$entries[ $meta_val->item_id ]->metas = array();
484
			}
485
486
			FrmAppHelper::unserialize_or_decode( $meta_val->meta_value );
487
			$entries[ $meta_val->item_id ]->metas[ $meta_val->field_id ] = $meta_val->meta_value;
488
			unset( $m_key, $meta_val );
489
		}
490
491
		if ( ! FrmAppHelper::prevent_caching() ) {
492
			foreach ( $entries as $entry ) {
493
				FrmDb::set_cache( $entry->id, $entry, 'frm_entry' );
494
				unset( $entry );
495
			}
496
		}
497
498
		self::prepare_entries( $entries );
499
		return $entries;
500
	}
501
502
	// Pagination Methods
503
	/**
504
	 * @param int|array|string If int, use the form id.
505
	 */
506
	public static function getRecordCount( $where = '' ) {
507
		global $wpdb;
508
		$table_join = $wpdb->prefix . 'frm_items it LEFT OUTER JOIN ' . $wpdb->prefix . 'frm_forms fr ON it.form_id=fr.id';
509
510
		if ( is_numeric( $where ) ) {
511
			$table_join = 'frm_items';
512
			$where      = array( 'form_id' => $where );
513
		}
514
515
		if ( is_array( $where ) ) {
516
			$count = FrmDb::get_count( $table_join, $where );
517
		} else {
518
			$cache_key = 'count_' . FrmAppHelper::maybe_json_encode( $where );
519
			$query     = 'SELECT COUNT(*) FROM ' . $table_join . FrmDb::prepend_and_or_where( ' WHERE ', $where );
520
			$count     = FrmDb::check_cache( $cache_key, 'frm_entry', $query, 'get_var' );
521
		}
522
523
		return $count;
524
	}
525
526
	public static function getPageCount( $p_size, $where = '' ) {
527
		$p_size = (int) $p_size;
528
		$count  = 1;
529
		if ( $p_size ) {
530
			if ( ! is_numeric( $where ) ) {
531
				$where = self::getRecordCount( $where );
532
			}
533
			$count = ceil( (int) $where / $p_size );
534
		}
535
536
		return $count;
537
	}
538
539
	/**
540
	 * Prepare the data before inserting it into the database
541
	 *
542
	 * @since 2.0.16
543
	 *
544
	 * @param array $values
545
	 * @param string $type
546
	 *
547
	 * @return array $new_values
548
	 */
549
	private static function before_insert_entry_in_database( &$values, $type ) {
550
551
		self::sanitize_entry_post( $values );
552
553
		if ( $type != 'xml' ) {
554
			$values = apply_filters( 'frm_pre_create_entry', $values );
555
		}
556
557
		$new_values = self::package_entry_data( $values );
558
559
		return $new_values;
560
	}
561
562
	/**
563
	 * Create an entry and perform after create actions
564
	 *
565
	 * @since 2.0.16
566
	 *
567
	 * @param array $values
568
	 * @param array $new_values
569
	 *
570
	 * @return boolean|int $entry_id
571
	 */
572
	private static function continue_to_create_entry( $values, $new_values ) {
573
		$entry_id = self::insert_entry_into_database( $new_values );
574
		if ( ! $entry_id ) {
575
			return false;
576
		}
577
578
		self::after_insert_entry_in_database( $values, $new_values, $entry_id );
579
580
		return $entry_id;
581
	}
582
583
	/**
584
	 * Sanitize the POST values before we use them
585
	 *
586
	 * @since 2.0
587
	 *
588
	 * @param array $values The POST values by reference
589
	 */
590
	public static function sanitize_entry_post( &$values ) {
591
		$sanitize_method = array(
592
			'form_id'          => 'absint',
593
			'frm_action'       => 'sanitize_title',
594
			'form_key'         => 'sanitize_title',
595
			'item_key'         => 'sanitize_title',
596
			'item_name'        => 'sanitize_text_field',
597
			'frm_saving_draft' => 'absint',
598
			'is_draft'         => 'absint',
599
			'post_id'          => 'absint',
600
			'parent_item_id'   => 'absint',
601
			'created_at'       => 'sanitize_text_field',
602
			'updated_at'       => 'sanitize_text_field',
603
		);
604
605
		FrmAppHelper::sanitize_request( $sanitize_method, $values );
606
	}
607
608
	/**
609
	 * Prepare the new values for inserting into the database
610
	 *
611
	 * @since 2.0.16
612
	 *
613
	 * @param array $values
614
	 *
615
	 * @return array $new_values
616
	 */
617
	private static function package_entry_data( &$values ) {
618
		global $wpdb;
619
620
		if ( ! isset( $values['item_key'] ) ) {
621
			$values['item_key'] = '';
622
		}
623
624
		$item_name  = self::get_new_entry_name( $values, $values['item_key'] );
625
		$new_values = array(
626
			'item_key'       => FrmAppHelper::get_unique_key( $values['item_key'], $wpdb->prefix . 'frm_items', 'item_key' ),
627
			'name'           => FrmAppHelper::truncate( $item_name, 255, 1, '' ),
628
			'ip'             => self::get_ip( $values ),
629
			'is_draft'       => self::get_is_draft_value( $values ),
630
			'form_id'        => (int) self::get_entry_value( $values, 'form_id', null ),
631
			'post_id'        => (int) self::get_entry_value( $values, 'post_id', 0 ),
632
			'parent_item_id' => (int) self::get_entry_value( $values, 'parent_item_id', 0 ),
633
			'created_at'     => self::get_created_at( $values ),
634
			'updated_at'     => self::get_updated_at( $values ),
635
			'description'    => self::get_entry_description( $values ),
636
			'user_id'        => self::get_entry_user_id( $values ),
637
		);
638
639
		$new_values['updated_by'] = isset( $values['updated_by'] ) ? $values['updated_by'] : $new_values['user_id'];
640
641
		return $new_values;
642
	}
643
644
	private static function get_entry_value( $values, $name, $default ) {
645
		return isset( $values[ $name ] ) ? $values[ $name ] : $default;
646
	}
647
648
	/**
649
	 * Get the ip for a new entry.
650
	 * Allow the import to override the value.
651
	 *
652
	 * @since 2.03.10
653
	 *
654
	 * @param array $values
655
	 *
656
	 * @return string
657
	 */
658
	private static function get_ip( $values ) {
659
		if ( ! FrmAppHelper::ips_saved() ) {
660
			return '';
661
		}
662
663
		$ip = FrmAppHelper::get_ip_address();
664
		if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING ) {
665
			$ip = self::get_entry_value( $values, 'ip', $ip );
666
		}
667
668
		return $ip;
669
	}
670
671
	/**
672
	 * Get the is_draft value for a new entry
673
	 *
674
	 * @since 2.0.16
675
	 *
676
	 * @param array $values
677
	 *
678
	 * @return int
679
	 */
680
	private static function get_is_draft_value( $values ) {
681
		return ( ( isset( $values['frm_saving_draft'] ) && $values['frm_saving_draft'] == 1 ) || ( isset( $values['is_draft'] ) && $values['is_draft'] == 1 ) ) ? 1 : 0;
682
	}
683
684
	/**
685
	 * Get the created_at value for a new entry
686
	 *
687
	 * @since 2.0.16
688
	 *
689
	 * @param array $values
690
	 *
691
	 * @return string
692
	 */
693
	private static function get_created_at( $values ) {
694
		return self::get_entry_value( $values, 'created_at', current_time( 'mysql', 1 ) );
695
	}
696
697
	/**
698
	 * Get the updated_at value for a new entry
699
	 *
700
	 * @since 2.0.16
701
	 *
702
	 * @param array $values
703
	 *
704
	 * @return string
705
	 */
706
	private static function get_updated_at( $values ) {
707
		if ( isset( $values['updated_at'] ) ) {
708
			$updated_at = $values['updated_at'];
709
		} else {
710
			$updated_at = self::get_created_at( $values );
711
		}
712
713
		return $updated_at;
714
	}
715
716
	/**
717
	 * Get the description value for a new entry
718
	 *
719
	 * @since 2.0.16
720
	 *
721
	 * @param array $values
722
	 *
723
	 * @return string
724
	 */
725
	private static function get_entry_description( $values ) {
726
		if ( isset( $values['description'] ) && ! empty( $values['description'] ) ) {
727
			$description = FrmAppHelper::maybe_json_encode( $values['description'] );
728
		} else {
729
			$description = json_encode(
730
				array(
731
					'browser'  => FrmAppHelper::get_server_value( 'HTTP_USER_AGENT' ),
732
					'referrer' => FrmAppHelper::get_server_value( 'HTTP_REFERER' ),
733
				)
734
			);
735
		}
736
737
		return $description;
738
	}
739
740
	/**
741
	 * Get the user_id value for a new entry
742
	 *
743
	 * @since 2.0.16
744
	 *
745
	 * @param array $values
746
	 *
747
	 * @return int
748
	 */
749
	private static function get_entry_user_id( $values ) {
750
		if ( isset( $values['frm_user_id'] ) && ( is_numeric( $values['frm_user_id'] ) || FrmAppHelper::is_admin() ) ) {
751
			$user_id = $values['frm_user_id'];
752
		} else {
753
			$current_user_id = get_current_user_id();
754
			$user_id         = $current_user_id ? $current_user_id : 0;
755
		}
756
757
		return $user_id;
758
	}
759
760
	/**
761
	 * Insert new entry into the database
762
	 *
763
	 * @since 2.0.16
764
	 *
765
	 * @param array $new_values
766
	 *
767
	 * @return int | boolean $entry_id
768
	 */
769
	private static function insert_entry_into_database( $new_values ) {
770
		global $wpdb;
771
772
		$query_results = $wpdb->insert( $wpdb->prefix . 'frm_items', $new_values );
773
774
		if ( ! $query_results ) {
775
			$entry_id = false;
776
		} else {
777
			$entry_id = $wpdb->insert_id;
778
		}
779
780
		return $entry_id;
781
	}
782
783
	/**
784
	 * Add the new entry to global $frm_vars
785
	 *
786
	 * @since 2.0.16
787
	 *
788
	 * @param int $entry_id
789
	 */
790
	private static function add_new_entry_to_frm_vars( $entry_id ) {
791
		global $frm_vars;
792
793
		if ( ! isset( $frm_vars['saved_entries'] ) ) {
794
			$frm_vars['saved_entries'] = array();
795
		}
796
797
		$frm_vars['saved_entries'][] = (int) $entry_id;
798
	}
799
800
	/**
801
	 * Add entry metas, if there are any
802
	 *
803
	 * @since 2.0.16
804
	 *
805
	 * @param array $values
806
	 * @param int $entry_id
807
	 */
808
	private static function maybe_add_entry_metas( $values, $entry_id ) {
809
		if ( isset( $values['item_meta'] ) ) {
810
			FrmEntryMeta::update_entry_metas( $entry_id, $values['item_meta'] );
811
		}
812
	}
813
814
	/**
815
	 * Trigger frm_after_create_entry hooks
816
	 *
817
	 * @since 2.0.16
818
	 *
819
	 * @param int $entry_id
820
	 * @param array $new_values
821
	 */
822
	private static function after_entry_created_actions( $entry_id, $values, $new_values ) {
823
		// this is a child entry
824
		$is_child = isset( $values['parent_form_id'] ) && isset( $values['parent_nonce'] ) && ! empty( $values['parent_form_id'] ) && wp_verify_nonce( $values['parent_nonce'], 'parent' );
825
826
		do_action( 'frm_after_create_entry', $entry_id, $new_values['form_id'], compact( 'is_child' ) );
827
		do_action( 'frm_after_create_entry_' . $new_values['form_id'], $entry_id, compact( 'is_child' ) );
828
	}
829
830
	/**
831
	 * Actions to perform immediately after an entry is inserted in the frm_items database
832
	 *
833
	 * @since 2.0.16
834
	 *
835
	 * @param array $values
836
	 * @param array $new_values
837
	 * @param int $entry_id
838
	 */
839
	private static function after_insert_entry_in_database( $values, $new_values, $entry_id ) {
840
841
		self::add_new_entry_to_frm_vars( $entry_id );
842
843
		self::maybe_add_entry_metas( $values, $entry_id );
844
845
		self::clear_cache();
846
847
		self::after_entry_created_actions( $entry_id, $values, $new_values );
848
	}
849
850
	/**
851
	 * Perform some actions right before updating an entry
852
	 *
853
	 * @since 2.0.16
854
	 *
855
	 * @param int $id
856
	 * @param array $values
857
	 * @param string $update_type
858
	 *
859
	 * @return boolean $update
860
	 */
861
	private static function before_update_entry( $id, &$values, $update_type ) {
862
		$update = true;
863
864
		global $frm_vars;
865
866
		if ( isset( $frm_vars['saved_entries'] ) && is_array( $frm_vars['saved_entries'] ) && in_array( (int) $id, (array) $frm_vars['saved_entries'] ) ) {
867
			$update = false;
868
		}
869
870
		if ( $update && $update_type != 'xml' ) {
871
			$values = apply_filters( 'frm_pre_update_entry', $values, $id );
872
		}
873
874
		return $update;
875
	}
876
877
	/**
878
	 * Package the entry data for updating
879
	 *
880
	 * @since 2.0.16
881
	 *
882
	 * @param int $id
883
	 * @param array $values
884
	 *
885
	 * @return array $new_values
886
	 */
887
	private static function package_entry_to_update( $id, $values ) {
888
		global $wpdb;
889
890
		$new_values = array(
891
			'name'       => self::get_new_entry_name( $values ),
892
			'form_id'    => (int) self::get_entry_value( $values, 'form_id', null ),
893
			'is_draft'   => self::get_is_draft_value( $values ),
894
			'updated_at' => current_time( 'mysql', 1 ),
895
			'updated_by' => isset( $values['updated_by'] ) ? $values['updated_by'] : get_current_user_id(),
896
		);
897
898
		if ( isset( $values['post_id'] ) ) {
899
			$new_values['post_id'] = (int) $values['post_id'];
900
		}
901
902
		if ( isset( $values['item_key'] ) ) {
903
			$new_values['item_key'] = FrmAppHelper::get_unique_key( $values['item_key'], $wpdb->prefix . 'frm_items', 'item_key', $id );
904
		}
905
906
		if ( isset( $values['parent_item_id'] ) ) {
907
			$new_values['parent_item_id'] = (int) $values['parent_item_id'];
908
		}
909
910
		if ( isset( $values['frm_user_id'] ) && is_numeric( $values['frm_user_id'] ) ) {
911
			$new_values['user_id'] = $values['frm_user_id'];
912
		}
913
914
		$new_values = apply_filters( 'frm_update_entry', $new_values, $id );
915
916
		return $new_values;
917
	}
918
919
	/**
920
	 * Perform some actions right after updating an entry
921
	 *
922
	 * @since 2.0.16
923
	 *
924
	 * @param boolean|int $query_results
925
	 * @param int $id
926
	 * @param array $values
927
	 * @param array $new_values
928
	 */
929
	private static function after_update_entry( $query_results, $id, $values, $new_values ) {
930
		if ( $query_results ) {
931
			self::clear_cache();
932
		}
933
934
		global $frm_vars;
935
		if ( ! isset( $frm_vars['saved_entries'] ) ) {
936
			$frm_vars['saved_entries'] = array();
937
		}
938
939
		$frm_vars['saved_entries'][] = (int) $id;
940
941
		if ( isset( $values['item_meta'] ) ) {
942
			FrmEntryMeta::update_entry_metas( $id, $values['item_meta'] );
943
		}
944
945
		do_action( 'frm_after_update_entry', $id, $new_values['form_id'] );
946
		do_action( 'frm_after_update_entry_' . $new_values['form_id'], $id );
947
	}
948
949
	/**
950
	 * Create entry from an XML import
951
	 * Certain actions aren't necessary when importing (like saving sub entries, checking for duplicates, etc.)
952
	 *
953
	 * @since 2.0.16
954
	 *
955
	 * @param array $values
956
	 *
957
	 * @return int | boolean $entry_id
958
	 */
959
	public static function create_entry_from_xml( $values ) {
960
		$entry_id = self::create_entry( $values, 'xml' );
961
962
		return $entry_id;
963
	}
964
965
	/**
966
	 * Update entry from an XML import
967
	 * Certain actions aren't necessary when importing (like saving sub entries and modifying other vals)
968
	 *
969
	 * @since 2.0.16
970
	 *
971
	 * @param int $id
972
	 * @param array $values
973
	 *
974
	 * @return int | boolean $updated
975
	 */
976
	public static function update_entry_from_xml( $id, $values ) {
977
		$updated = self::update_entry( $id, $values, 'xml' );
978
979
		return $updated;
980
	}
981
982
	/**
983
	 * @param string $key
984
	 *
985
	 * @return int entry_id
986
	 */
987
	public static function get_id_by_key( $key ) {
988
		$entry_id = FrmDb::get_var( 'frm_items', array( 'item_key' => sanitize_title( $key ) ) );
989
990
		return (int) $entry_id;
991
	}
992
}
993