Completed
Push — master ( f33a7f...b07113 )
by Stephanie
02:56
created

FrmField::get_option_in_object()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 2
dl 0
loc 3
rs 10
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 FrmField {
7
8
	public static $use_cache = true;
9
	public static $transient_size = 200;
10
11
	public static function field_selection() {
12
		$fields = array(
13
			'text'     => array(
14
				'name' => __( 'Text', 'formidable' ),
15
				'icon' => 'frm_icon_font frm_text2_icon',
16
			),
17
			'textarea' => array(
18
				'name' => __( 'Paragraph', 'formidable' ),
19
				'icon' => 'frm_icon_font frm_paragraph_icon',
20
			),
21
			'checkbox' => array(
22
				'name' => __( 'Checkboxes', 'formidable' ),
23
				'icon' => 'frm_icon_font frm_check_square_icon',
24
			),
25
			'radio'    => array(
26
				'name' => __( 'Radio Buttons', 'formidable' ),
27
				'icon' => 'frm_icon_font frm_radio_checked_icon',
28
			),
29
			'select'   => array(
30
				'name' => __( 'Dropdown', 'formidable' ),
31
				'icon' => 'frm_icon_font frm_caret_square_down_icon',
32
			),
33
			'email'    => array(
34
				'name' => __( 'Email', 'formidable' ),
35
				'icon' => 'frm_icon_font frm_email_icon',
36
			),
37
			'url'      => array(
38
				'name' => __( 'Website/URL', 'formidable' ),
39
				'icon' => 'frm_icon_font frm_link_icon',
40
			),
41
			'number'   => array(
42
				'name' => __( 'Number', 'formidable' ),
43
				'icon' => 'frm_icon_font frm_hashtag_icon',
44
			),
45
			'phone'    => array(
46
				'name' => __( 'Phone', 'formidable' ),
47
				'icon' => 'frm_icon_font frm_phone_icon',
48
			),
49
			'html'     => array(
50
				'name' => __( 'HTML', 'formidable' ),
51
				'icon' => 'frm_icon_font frm_code_icon',
52
			),
53
			'hidden'   => array(
54
				'name' => __( 'Hidden Field', 'formidable' ),
55
				'icon' => 'frm_icon_font frm_eye_slash_icon',
56
			),
57
			'user_id'  => array(
58
				'name' => __( 'User ID', 'formidable' ),
59
				'icon' => 'frm_icon_font frm_user_icon',
60
			),
61
			'captcha'  => array(
62
				'name' => __( 'reCAPTCHA', 'formidable' ),
63
				'icon' => 'frm_icon_font frm_shield_check_icon',
64
			),
65
		);
66
67
		return apply_filters( 'frm_available_fields', $fields );
68
	}
69
70
	public static function pro_field_selection() {
71
		$fields = array(
72
			'file'           => array(
73
				'name' => __( 'File Upload', 'formidable' ),
74
				'icon' => 'frm_icon_font frm_upload_icon',
75
			),
76
			'rte'            => array(
77
				'name' => __( 'Rich Text', 'formidable' ),
78
				'icon' => 'frm_icon_font frm_align_right_icon',
79
			),
80
			'date'           => array(
81
				'name' => __( 'Date', 'formidable' ),
82
				'icon' => 'frm_icon_font frm_calendar_icon',
83
			),
84
			'time'           => array(
85
				'name' => __( 'Time', 'formidable' ),
86
				'icon' => 'frm_icon_font frm_clock_icon',
87
			),
88
			'scale'          => array(
89
				'name' => __( 'Scale', 'formidable' ),
90
				'icon' => 'frm_icon_font frm_linear_scale_icon',
91
			),
92
			'star'           => array(
93
				'name' => __( 'Star Rating', 'formidable' ),
94
				'icon' => 'frm_icon_font frm_star_icon',
95
			),
96
			'range'          => array(
97
				'name' => __( 'Slider', 'formidable' ),
98
				'icon' => 'frm_icon_font frm_code_commit_icon',
99
			),
100
			'toggle'         => array(
101
				'name' => __( 'Toggle', 'formidable' ),
102
				'icon' => 'frm_icon_font frm_toggle_on_icon',
103
			),
104
			'data'           => array(
105
				'name' => __( 'Dynamic', 'formidable' ),
106
				'icon' => 'frm_icon_font frm_sitemap_icon',
107
			),
108
			'lookup'         => array(
109
				'name' => __( 'Lookup', 'formidable' ),
110
				'icon' => 'frm_icon_font frm_search_icon',
111
			),
112
			'divider|repeat' => array(
113
				'name' => __( 'Repeater', 'formidable' ),
114
				'icon' => 'frm_icon_font frm_repeater_icon',
115
			),
116
			'end_divider'    => array(
117
				'name'        => __( 'Section Buttons', 'formidable' ),
118
				'switch_from' => 'divider',
119
			),
120
			'divider'        => array(
121
				'name' => __( 'Section', 'formidable' ),
122
				'icon' => 'frm_icon_font frm_header_icon',
123
			),
124
			'break'          => array(
125
				'name' => __( 'Page Break', 'formidable' ),
126
				'icon' => 'frm_icon_font frm_page_break_icon',
127
			),
128
			'form'           => array(
129
				'name' => __( 'Embed Form', 'formidable' ),
130
				'icon' => 'frm_icon_font frm_file_text_icon',
131
			),
132
			'password'       => array(
133
				'name' => __( 'Password', 'formidable' ),
134
				'icon' => 'frm_icon_font frm_lock_open_icon',
135
			),
136
			'tag'            => array(
137
				'name' => __( 'Tags', 'formidable' ),
138
				'icon' => 'frm_icon_font frm_price_tags_icon',
139
			),
140
			'credit_card'    => array(
141
				'name'  => __( 'Credit Card', 'formidable' ),
142
				'icon'  => 'frm_icon_font frm_credit_card_icon frm_show_upgrade',
143
				'addon' => 'stripe',
144
			),
145
			'address'        => array(
146
				'name' => __( 'Address', 'formidable' ),
147
				'icon' => 'frm_icon_font frm_location_icon',
148
			),
149
			'signature' => array(
150
				'name'  => __( 'Signature', 'formidable' ),
151
				'icon'  => 'frm_icon_font frm_signature_icon frm_show_upgrade',
152
				'addon' => 'signature',
153
			),
154
			'quiz_score' => array(
155
				'name'  => __( 'Quiz Score', 'formidable' ),
156
				'icon'  => 'frm_icon_font frm_percent_icon frm_show_upgrade',
157
				'addon' => 'quizzes',
158
			),
159
		);
160
161
		// Since the signature field may be in a different section, don't show it twice.
162
		$lite_fields = self::field_selection();
163
		if ( isset( $lite_fields['signature'] ) ) {
164
			unset( $fields['signature'] );
165
		}
166
167
		return apply_filters( 'frm_pro_available_fields', $fields );
168
	}
169
170
	/**
171
	 * @since 4.0
172
	 */
173
	public static function all_field_selection() {
174
		$pro_field_selection = self::pro_field_selection();
175
		return array_merge( $pro_field_selection, self::field_selection() );
176
	}
177
178
	public static function create( $values, $return = true ) {
179
		global $wpdb, $frm_duplicate_ids;
180
181
		$new_values              = array();
182
		$key                     = isset( $values['field_key'] ) ? $values['field_key'] : $values['name'];
183
		$new_values['field_key'] = FrmAppHelper::get_unique_key( $key, $wpdb->prefix . 'frm_fields', 'field_key' );
184
185
		foreach ( array( 'name', 'description', 'type', 'default_value' ) as $col ) {
186
			$new_values[ $col ] = $values[ $col ];
187
		}
188
189
		$new_values['options'] = $values['options'];
190
191
		$new_values['field_order']   = isset( $values['field_order'] ) ? (int) $values['field_order'] : null;
192
		$new_values['required']      = isset( $values['required'] ) ? (int) $values['required'] : 0;
193
		$new_values['form_id']       = isset( $values['form_id'] ) ? (int) $values['form_id'] : null;
194
		$new_values['field_options'] = $values['field_options'];
195
		$new_values['created_at']    = current_time( 'mysql', 1 );
196
197
		if ( isset( $values['id'] ) ) {
198
			$frm_duplicate_ids[ $values['field_key'] ] = $new_values['field_key'];
199
			$new_values                                = apply_filters( 'frm_duplicated_field', $new_values );
200
		}
201
202
		self::preserve_format_option_backslashes( $new_values );
203
204
		foreach ( $new_values as $k => $v ) {
205
			if ( is_array( $v ) ) {
206
				$new_values[ $k ] = serialize( $v );
207
			}
208
			unset( $k, $v );
209
		}
210
211
		//if(isset($values['id']) and is_numeric($values['id']))
212
		//    $new_values['id'] = $values['id'];
213
214
		$query_results = $wpdb->insert( $wpdb->prefix . 'frm_fields', $new_values );
215
		$new_id        = 0;
216
		if ( $query_results ) {
217
			self::delete_form_transient( $new_values['form_id'] );
218
			$new_id = $wpdb->insert_id;
219
		}
220
221
		if ( ! $return ) {
222
			return false;
223
		}
224
225
		if ( $query_results ) {
226
			if ( isset( $values['id'] ) ) {
227
				$frm_duplicate_ids[ $values['id'] ] = $new_id;
228
			}
229
230
			return $new_id;
231
		} else {
232
			return false;
233
		}
234
	}
235
236
	public static function duplicate( $old_form_id, $form_id, $copy_keys = false, $blog_id = false ) {
237
		global $frm_duplicate_ids;
238
239
		$where  = array(
240
			array(
241
				'or'                => 1,
242
				'fi.form_id'        => $old_form_id,
243
				'fr.parent_form_id' => $old_form_id,
244
			),
245
		);
246
		$fields = self::getAll( $where, 'field_order', '', $blog_id );
247
248
		foreach ( (array) $fields as $field ) {
249
			$new_key = $copy_keys ? $field->field_key : '';
250
			if ( $copy_keys && substr( $field->field_key, - 1 ) == 2 ) {
251
				$new_key = rtrim( $new_key, 2 );
252
			}
253
254
			$values = array();
255
			FrmFieldsHelper::fill_field( $values, $field, $form_id, $new_key );
256
257
			// If this is a repeating section, create new form
258
			if ( self::is_repeating_field( $field ) ) {
259
				// create the repeatable form
260
				$new_repeat_form_id = apply_filters(
261
					'frm_create_repeat_form',
262
					0,
263
					array(
264
						'parent_form_id' => $form_id,
265
						'field_name'     => $field->name,
266
					)
267
				);
268
269
				// Save old form_select
270
				$old_repeat_form_id = $field->field_options['form_select'];
271
272
				// Update form_select for repeating field
273
				$values['field_options']['form_select'] = $new_repeat_form_id;
274
			}
275
276
			// If this is a field inside of a repeating section, associate it with the correct form
277
			if ( $field->form_id != $old_form_id && isset( $old_repeat_form_id ) && isset( $new_repeat_form_id ) && $field->form_id == $old_repeat_form_id ) {
278
				$values['form_id'] = $new_repeat_form_id;
279
			}
280
281
			$values['description'] = FrmFieldsHelper::switch_field_ids( $values['description'] );
282
283
			$values                                 = apply_filters( 'frm_duplicated_field', $values );
284
			$new_id                                 = self::create( $values );
285
			$frm_duplicate_ids[ $field->id ]        = $new_id;
286
			$frm_duplicate_ids[ $field->field_key ] = $new_id;
287
			unset( $field );
288
		}
289
	}
290
291
	public static function update( $id, $values ) {
292
		global $wpdb;
293
294
		$id = absint( $id );
295
296
		if ( isset( $values['field_key'] ) ) {
297
			$values['field_key'] = FrmAppHelper::get_unique_key( $values['field_key'], $wpdb->prefix . 'frm_fields', 'field_key', $id );
298
		}
299
300
		if ( isset( $values['required'] ) ) {
301
			$values['required'] = (int) $values['required'];
302
		}
303
304
		self::preserve_format_option_backslashes( $values );
305
306
		if ( isset( $values['type'] ) ) {
307
			$values = apply_filters( 'frm_clean_' . $values['type'] . '_field_options_before_update', $values );
308
309
			if ( $values['type'] == 'hidden' && isset( $values['field_options'] ) && isset( $values['field_options']['clear_on_focus'] ) ) {
310
				// don't keep the old placeholder setting for hidden fields
311
				$values['field_options']['clear_on_focus'] = 0;
312
			}
313
		}
314
315
		// serialize array values
316
		foreach ( array( 'default_value', 'field_options', 'options' ) as $opt ) {
317
			if ( isset( $values[ $opt ] ) && is_array( $values[ $opt ] ) ) {
318
				$values[ $opt ] = serialize( $values[ $opt ] );
319
			}
320
		}
321
322
		$query_results = $wpdb->update( $wpdb->prefix . 'frm_fields', $values, array( 'id' => $id ) );
323
324
		$form_id = 0;
325
		if ( isset( $values['form_id'] ) ) {
326
			$form_id = absint( $values['form_id'] );
327
		} else {
328
			$field = self::getOne( $id );
329
			if ( $field ) {
330
				$form_id = $field->form_id;
331
			}
332
			unset( $field );
333
		}
334
		unset( $values );
335
336
		if ( $query_results ) {
337
			wp_cache_delete( $id, 'frm_field' );
338
			if ( $form_id ) {
339
				self::delete_form_transient( $form_id );
340
			}
341
		}
342
343
		return $query_results;
344
	}
345
346
	/**
347
	 * Keep backslashes in the phone format option
348
	 *
349
	 * @since 2.0.8
350
	 *
351
	 * @param $values array - pass by reference
352
	 */
353
	private static function preserve_format_option_backslashes( &$values ) {
354
		if ( isset( $values['field_options']['format'] ) ) {
355
			$values['field_options']['format'] = FrmAppHelper::preserve_backslashes( $values['field_options']['format'] );
356
		}
357
	}
358
359
	public static function destroy( $id ) {
360
		global $wpdb;
361
362
		do_action( 'frm_before_destroy_field', $id );
363
364
		wp_cache_delete( $id, 'frm_field' );
365
		$field = self::getOne( $id );
366
		if ( ! $field ) {
367
			return false;
368
		}
369
370
		self::delete_form_transient( $field->form_id );
371
372
		$wpdb->query( $wpdb->prepare( 'DELETE FROM ' . $wpdb->prefix . 'frm_item_metas WHERE field_id=%d', $id ) );
373
374
		return $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . $wpdb->prefix . 'frm_fields WHERE id=%d', $id ) );
375
	}
376
377
	public static function delete_form_transient( $form_id ) {
378
		$form_id = absint( $form_id );
379
		delete_transient( 'frm_form_fields_' . $form_id . 'excludeinclude' );
380
		delete_transient( 'frm_form_fields_' . $form_id . 'includeinclude' );
381
		delete_transient( 'frm_form_fields_' . $form_id . 'includeexclude' );
382
		delete_transient( 'frm_form_fields_' . $form_id . 'excludeexclude' );
383
384
		global $wpdb;
385
		$wpdb->query( $wpdb->prepare( 'DELETE FROM ' . $wpdb->options . ' WHERE option_name LIKE %s OR option_name LIKE %s OR option_name LIKE %s OR option_name LIKE %s', '_transient_timeout_frm_form_fields_' . $form_id . 'ex%', '_transient_frm_form_fields_' . $form_id . 'ex%', '_transient_timeout_frm_form_fields_' . $form_id . 'in%', '_transient_frm_form_fields_' . $form_id . 'in%' ) );
386
387
		FrmDb::cache_delete_group( 'frm_field' );
388
389
		$form = FrmForm::getOne( $form_id );
390
		if ( $form && $form->parent_form_id && $form->parent_form_id != $form_id ) {
391
			self::delete_form_transient( $form->parent_form_id );
392
		}
393
	}
394
395
	/**
396
	 * If $field is numeric, get the field object
397
	 */
398
	public static function maybe_get_field( &$field ) {
399
		if ( ! is_object( $field ) ) {
400
			$field = self::getOne( $field );
401
		}
402
	}
403
404
	/**
405
	 * @param string|int $id The field id or key.
406
	 * @param bool $filter When true, run the frm_field filter.
407
	 */
408
	public static function getOne( $id, $filter = false ) {
0 ignored issues
show
Coding Style introduced by
The function name getOne is in camel caps, but expected get_one instead as per the coding standard.
Loading history...
409
		if ( empty( $id ) ) {
410
			return null;
411
		}
412
413
		global $wpdb;
414
415
		$where = is_numeric( $id ) ? 'id=%d' : 'field_key=%s';
416
		$query = $wpdb->prepare( 'SELECT * FROM ' . $wpdb->prefix . 'frm_fields WHERE ' . $where, $id ); // WPCS: unprepared SQL ok.
417
418
		$results = FrmDb::check_cache( $id, 'frm_field', $query, 'get_row', 0 );
419
420
		if ( empty( $results ) ) {
421
			self::filter_field( $filter, $results );
422
			return $results;
423
		}
424
425 View Code Duplication
		if ( is_numeric( $id ) ) {
426
			FrmDb::set_cache( $results->field_key, $results, 'frm_field' );
427
		} elseif ( $results ) {
428
			FrmDb::set_cache( $results->id, $results, 'frm_field' );
429
		}
430
431
		self::prepare_options( $results );
432
		self::filter_field( $filter, $results );
433
434
		return wp_unslash( $results );
435
	}
436
437
	/**
438
	 * @since 3.06.01
439
	 * @param bool   $filter When true, run the frm_field filter.
440
	 * @param object $results
441
	 */
442
	private static function filter_field( $filter, &$results ) {
443
		if ( $filter ) {
444
			/**
445
			 * @since 3.06.01
446
			 */
447
			$results = apply_filters( 'frm_field', $results );
448
		}
449
	}
450
451
	/**
452
	 * Get the field type by key or id
453
	 *
454
	 * @param int|string The field id or key
455
	 * @param mixed $col The name of the column in the fields database table
456
	 */
457
	public static function get_type( $id, $col = 'type' ) {
458
		$field = FrmDb::check_cache( $id, 'frm_field' );
459
		if ( $field ) {
460
			$type = $field->{$col};
461
		} else {
462
			$where = array(
463
				'or'        => 1,
464
				'id'        => $id,
465
				'field_key' => $id,
466
			);
467
			$type  = FrmDb::get_var( 'frm_fields', $where, $col );
468
		}
469
470
		return $type;
471
	}
472
473
	public static function get_all_types_in_form( $form_id, $type, $limit = '', $inc_sub = 'exclude' ) {
474
		if ( ! $form_id ) {
475
			return array();
476
		}
477
478
		$results = self::get_fields_from_transients(
479
			$form_id,
480
			array(
481
				'inc_embed'  => $inc_sub,
482
				'inc_repeat' => $inc_sub,
483
			)
484
		);
485
		if ( ! empty( $results ) ) {
486
			$fields = array();
487
			$count  = 0;
488
			foreach ( $results as $result ) {
489
				if ( $type != $result->type ) {
490
					continue;
491
				}
492
493
				$fields[ $result->id ] = $result;
494
				$count ++;
495
				if ( $limit == 1 ) {
496
					$fields = $result;
497
					break;
498
				}
499
500
				if ( ! empty( $limit ) && $count >= $limit ) {
501
					break;
502
				}
503
504
				unset( $result );
505
			}
506
507
			return wp_unslash( $fields );
508
		}
509
510
		self::$use_cache = false;
511
512
		$where = array(
513
			'fi.form_id' => (int) $form_id,
514
			'fi.type'    => $type,
515
		);
516
		self::maybe_include_repeating_fields( $inc_sub, $where );
517
		$results         = self::getAll( $where, 'field_order', $limit );
518
		self::$use_cache = true;
519
		self::include_sub_fields( $results, $inc_sub, $type, $form_id );
520
521
		return $results;
522
	}
523
524
	public static function get_all_for_form( $form_id, $limit = '', $inc_embed = 'exclude', $inc_repeat = 'include' ) {
525
		if ( ! (int) $form_id ) {
526
			return array();
527
		}
528
529
		$results = self::get_fields_from_transients( $form_id, compact( 'inc_embed', 'inc_repeat' ) );
530
		if ( ! empty( $results ) ) {
531
			if ( empty( $limit ) ) {
532
				return $results;
533
			}
534
535
			$fields = array();
536
			$count  = 0;
537
			foreach ( $results as $result ) {
538
				$count ++;
539
				$fields[ $result->id ] = $result;
540
				if ( ! empty( $limit ) && $count >= $limit ) {
541
					break;
542
				}
543
			}
544
545
			return $fields;
546
		}
547
548
		self::$use_cache = false;
549
550
		$where = array( 'fi.form_id' => absint( $form_id ) );
551
		self::maybe_include_repeating_fields( $inc_repeat, $where );
552
		$results = self::getAll( $where, 'field_order', $limit );
553
554
		self::$use_cache = true;
555
556
		self::include_sub_fields( $results, $inc_embed, 'all', $form_id );
557
558
		if ( empty( $limit ) ) {
559
			self::set_field_transient( $results, $form_id, 0, compact( 'inc_embed', 'inc_repeat' ) );
560
		}
561
562
		return $results;
563
	}
564
565
	/**
566
	 * If repeating fields should be included, adjust $where accordingly
567
	 *
568
	 * @param string $inc_repeat
569
	 * @param array $where - pass by reference
570
	 */
571
	private static function maybe_include_repeating_fields( $inc_repeat, &$where ) {
572
		if ( $inc_repeat == 'include' ) {
573
			$form_id = $where['fi.form_id'];
574
			$where[] = array(
575
				'or'                => 1,
576
				'fi.form_id'        => $form_id,
577
				'fr.parent_form_id' => $form_id,
578
			);
579
			unset( $where['fi.form_id'] );
580
		}
581
	}
582
583
	public static function include_sub_fields( &$results, $inc_embed, $type = 'all', $form_id = '' ) {
584
		$no_sub_forms = empty( $results ) && $type === 'all';
585
		if ( 'include' != $inc_embed || $no_sub_forms ) {
586
			return;
587
		}
588
589
		$form_fields = $results;
590
		$should_get_subforms = ( $type !== 'all' && $type !== 'form' && ! empty( $form_id ) );
591
		if ( $should_get_subforms ) {
592
			$form_fields = self::get_all_types_in_form( $form_id, 'form' );
593
		}
594
595
		$index_offset = 1;
596
		foreach ( $form_fields as $k => $field ) {
597
			if ( 'form' != $field->type || ! isset( $field->field_options['form_select'] ) ) {
598
				continue;
599
			}
600
601
			if ( $type == 'all' ) {
602
				$sub_fields = self::get_all_for_form( $field->field_options['form_select'] );
603
			} else {
604
				$sub_fields = self::get_all_types_in_form( $field->field_options['form_select'], $type );
605
			}
606
607
			if ( ! empty( $sub_fields ) ) {
608
				$index        = $k + $index_offset;
609
				$index_offset += count( $sub_fields );
610
				array_splice( $results, $index, 0, $sub_fields );
611
			}
612
			unset( $field, $sub_fields );
613
		}
614
	}
615
616
	public static function getAll( $where = array(), $order_by = '', $limit = '', $blog_id = false ) {
0 ignored issues
show
Coding Style introduced by
The function name getAll is in camel caps, but expected get_all instead as per the coding standard.
Loading history...
617
		$cache_key = maybe_serialize( $where ) . $order_by . 'l' . $limit . 'b' . $blog_id;
618
		if ( self::$use_cache ) {
619
			// make sure old cache doesn't get saved as a transient
620
			$results = wp_cache_get( $cache_key, 'frm_field' );
621
			if ( false !== $results ) {
622
				return wp_unslash( $results );
623
			}
624
		}
625
626
		global $wpdb;
627
628
		if ( $blog_id && is_multisite() ) {
629
			global $wpmuBaseTablePrefix;
630
			if ( $wpmuBaseTablePrefix ) {
631
				$prefix = $wpmuBaseTablePrefix . $blog_id . '_';
632
			} else {
633
				$prefix = $wpdb->get_blog_prefix( $blog_id );
634
			}
635
636
			$table_name      = $prefix . 'frm_fields';
637
			$form_table_name = $prefix . 'frm_forms';
638
		} else {
639
			$table_name      = $wpdb->prefix . 'frm_fields';
640
			$form_table_name = $wpdb->prefix . 'frm_forms';
641
		}
642
643
		if ( ! empty( $order_by ) && strpos( $order_by, 'ORDER BY' ) === false ) {
644
			$order_by = ' ORDER BY ' . $order_by;
645
		}
646
647
		$limit = FrmDb::esc_limit( $limit );
648
649
		$query      = "SELECT fi.*, fr.name as form_name  FROM {$table_name} fi LEFT OUTER JOIN {$form_table_name} fr ON fi.form_id=fr.id";
650
		$query_type = ( $limit == ' LIMIT 1' || $limit == 1 ) ? 'row' : 'results';
651
652
		if ( is_array( $where ) ) {
653
			$args    = array(
654
				'order_by' => $order_by,
655
				'limit'    => $limit,
656
			);
657
			$results = FrmDb::get_var( $table_name . ' fi LEFT OUTER JOIN ' . $form_table_name . ' fr ON fi.form_id=fr.id', $where, 'fi.*, fr.name as form_name', $args, '', $query_type );
658
		} else {
659
			// if the query is not an array, then it has already been prepared
660
			$query .= FrmDb::prepend_and_or_where( ' WHERE ', $where ) . $order_by . $limit;
661
662
			$function_name = ( $query_type == 'row' ) ? 'get_row' : 'get_results';
663
			$results       = $wpdb->$function_name( $query );
664
		}
665
		unset( $where );
666
667
		self::format_field_results( $results );
668
669
		FrmDb::set_cache( $cache_key, $results, 'frm_field' );
670
671
		return wp_unslash( $results );
672
	}
673
674
	/**
675
	 * @since 2.0.8
676
	 */
677
	private static function format_field_results( &$results ) {
678
		if ( is_array( $results ) ) {
679
			foreach ( $results as $r_key => $result ) {
680
				FrmDb::set_cache( $result->id, $result, 'frm_field' );
681
				FrmDb::set_cache( $result->field_key, $result, 'frm_field' );
682
683
				$results[ $r_key ]->field_options = maybe_unserialize( $result->field_options );
684
				$results[ $r_key ]->options       = maybe_unserialize( $result->options );
685
				$results[ $r_key ]->default_value = maybe_unserialize( $result->default_value );
686
687
				unset( $r_key, $result );
688
			}
689 View Code Duplication
		} elseif ( $results ) {
690
			FrmDb::set_cache( $results->id, $results, 'frm_field' );
691
			FrmDb::set_cache( $results->field_key, $results, 'frm_field' );
692
693
			self::prepare_options( $results );
694
		}
695
	}
696
697
	/**
698
	 * Unserialize all the serialized field data
699
	 *
700
	 * @since 2.0
701
	 */
702
	private static function prepare_options( &$results ) {
703
		$results->field_options = maybe_unserialize( $results->field_options );
704
705
		$results->options       = maybe_unserialize( $results->options );
706
		$results->default_value = maybe_unserialize( $results->default_value );
707
	}
708
709
	/**
710
	 * If a form has too many fields, thay won't all save into a single transient.
711
	 * We'll break them into groups of 200
712
	 *
713
	 * @since 2.0.1
714
	 */
715
	private static function get_fields_from_transients( $form_id, $args ) {
716
		$fields = array();
717
		self::get_next_transient( $fields, 'frm_form_fields_' . $form_id . $args['inc_embed'] . $args['inc_repeat'] );
718
719
		return $fields;
720
	}
721
722
	/**
723
	 * Called by get_fields_from_transients
724
	 *
725
	 * @since 2.0.1
726
	 */
727
	private static function get_next_transient( &$fields, $base_name, $next = 0 ) {
728
		$name        = $next ? $base_name . $next : $base_name;
729
		$next_fields = get_transient( $name );
730
731
		if ( $next_fields ) {
732
			$fields = array_merge( $fields, $next_fields );
733
734
			if ( count( $next_fields ) >= self::$transient_size ) {
735
				// if this transient is full, check for another
736
				$next ++;
737
				self::get_next_transient( $fields, $base_name, $next );
738
			}
739
		}
740
	}
741
742
	/**
743
	 * Save the transients in chunks for large forms
744
	 *
745
	 * @since 2.0.1
746
	 */
747
	private static function set_field_transient( &$fields, $form_id, $next = 0, $args = array() ) {
748
		$base_name    = 'frm_form_fields_' . $form_id . $args['inc_embed'] . $args['inc_repeat'];
749
		$field_chunks = array_chunk( $fields, self::$transient_size );
750
751
		foreach ( $field_chunks as $field ) {
752
			$name = $next ? $base_name . $next : $base_name;
753
			$set  = set_transient( $name, $field, 60 * 60 * 6 );
754
			if ( ! $set ) {
755
				// the transient didn't save
756
				if ( $name != $base_name ) {
757
					// if the first saved an others fail, this will show an incmoplete form
758
					self::delete_form_transient( $form_id );
759
				}
760
761
				return;
762
			}
763
764
			$next ++;
765
		}
766
	}
767
768
	public static function is_no_save_field( $type ) {
769
		return in_array( $type, self::no_save_fields() );
770
	}
771
772
	public static function no_save_fields() {
773
		return array( 'divider', 'end_divider', 'captcha', 'break', 'html', 'form' );
774
	}
775
776
	/**
777
	 * Check if this field can hold an array of values
778
	 *
779
	 * @since 2.0.9
780
	 *
781
	 * @param array|object $field
782
	 *
783
	 * @return boolean
784
	 */
785
	public static function is_field_with_multiple_values( $field ) {
786
		if ( ! $field ) {
787
			return false;
788
		}
789
790
		$field_type = self::get_original_field_type( $field );
791
792
		$is_multi_value_field = (
793
			self::is_checkbox( $field ) ||
794
			$field_type == 'address' ||
795
			self::is_multiple_select( $field )
796
		);
797
798
		return $is_multi_value_field;
799
	}
800
801
	/**
802
	 * @since 3.0
803
	 * @return string
804
	 */
805
	public static function get_field_type( $field ) {
806
		return is_array( $field ) ? $field['type'] : $field->type;
807
	}
808
809
	/**
810
	 * @since 3.0
811
	 * @return string
812
	 */
813
	public static function get_original_field_type( $field ) {
814
		$field_type    = self::get_field_type( $field );
815
		$original_type = self::get_option( $field, 'original_type' );
816
817
		if ( ! empty( $original_type ) && $original_type != $field_type ) {
818
			$field_type = $original_type; // check the original type for arrays
819
		}
820
821
		return $field_type;
822
	}
823
824
	/**
825
	 * Check if this is a multiselect dropdown field
826
	 *
827
	 * @since 2.0.9
828
	 * @return boolean
829
	 */
830
	public static function is_multiple_select( $field ) {
831
		$field_type  = self::get_field_type( $field );
832
		$is_multiple = self::is_option_true( $field, 'multiple' ) && self::is_field_type( $field, 'select' ) && $field_type !== 'hidden';
833
834
		return apply_filters( 'frm_is_multiple_select', $is_multiple, $field );
835
	}
836
837
	/**
838
	 * Check if a field is read only. Read only can be set in the field options,
839
	 * but disabled with the shortcode options
840
	 *
841
	 * @since 2.0.9
842
	 */
843
	public static function is_read_only( $field ) {
844
		global $frm_vars;
845
846
		return ( self::is_option_true( $field, 'read_only' ) && ( ! isset( $frm_vars['readonly'] ) || $frm_vars['readonly'] != 'disabled' ) );
847
	}
848
849
	/**
850
	 * @since 2.0.9
851
	 */
852
	public static function is_required( $field ) {
853
		$required = ( $field['required'] != '0' );
854
		$required = apply_filters( 'frm_is_field_required', $required, $field );
855
856
		return $required;
857
	}
858
859
	/**
860
	 * @since 2.0.9
861
	 */
862
	public static function is_option_true( $field, $option ) {
863
		if ( is_array( $field ) ) {
864
			return self::is_option_true_in_array( $field, $option );
865
		} else {
866
			return self::is_option_true_in_object( $field, $option );
867
		}
868
	}
869
870
	/**
871
	 * @since 2.0.9
872
	 */
873
	public static function is_option_empty( $field, $option ) {
874
		if ( is_array( $field ) ) {
875
			return self::is_option_empty_in_array( $field, $option );
876
		} else {
877
			return self::is_option_empty_in_object( $field, $option );
878
		}
879
	}
880
881
	public static function is_option_true_in_array( $field, $option ) {
882
		return isset( $field[ $option ] ) && $field[ $option ];
883
	}
884
885
	public static function is_option_true_in_object( $field, $option ) {
886
		return isset( $field->field_options[ $option ] ) && $field->field_options[ $option ];
887
	}
888
889
	public static function is_option_empty_in_array( $field, $option ) {
890
		return ! isset( $field[ $option ] ) || empty( $field[ $option ] );
891
	}
892
893
	public static function is_option_empty_in_object( $field, $option ) {
894
		return ! isset( $field->field_options[ $option ] ) || empty( $field->field_options[ $option ] );
895
	}
896
897
	public static function is_option_value_in_object( $field, $option ) {
898
		return isset( $field->field_options[ $option ] ) && $field->field_options[ $option ] != '';
899
	}
900
901
	/**
902
	 * @since 2.0.18
903
	 */
904
	public static function get_option( $field, $option ) {
905
		if ( is_array( $field ) ) {
906
			$option = self::get_option_in_array( $field, $option );
907
		} else {
908
			$option = self::get_option_in_object( $field, $option );
909
		}
910
911
		return $option;
912
	}
913
914
	public static function get_option_in_array( $field, $option ) {
915
		return isset( $field[ $option ] ) ? $field[ $option ] : '';
916
	}
917
918
	public static function get_option_in_object( $field, $option ) {
919
		return isset( $field->field_options[ $option ] ) ? $field->field_options[ $option ] : '';
920
	}
921
922
	/**
923
	 * @since 2.0.09
924
	 */
925
	public static function is_repeating_field( $field ) {
926
		if ( is_array( $field ) ) {
927
			$is_repeating_field = ( 'divider' == $field['type'] );
928
		} else {
929
			$is_repeating_field = ( 'divider' == $field->type );
930
		}
931
932
		return ( $is_repeating_field && self::is_option_true( $field, 'repeat' ) );
933
	}
934
935
	/**
936
	 * @param string $key
937
	 *
938
	 * @return int field id
939
	 */
940
	public static function get_id_by_key( $key ) {
941
		$id = FrmDb::get_var( 'frm_fields', array( 'field_key' => sanitize_title( $key ) ) );
942
943
		return (int) $id;
944
	}
945
946
	/**
947
	 * @param string $id
948
	 *
949
	 * @return string
950
	 */
951
	public static function get_key_by_id( $id ) {
952
		return FrmDb::get_var( 'frm_fields', array( 'id' => $id ), 'field_key' );
953
	}
954
955
	public static function is_image( $field ) {
956
		$type = self::get_field_type( $field );
957
958
		return ( $type == 'url' && self::get_option( $field, 'show_image' ) );
959
	}
960
961
	/**
962
	 * Check if field is radio or Dynamic radio
963
	 *
964
	 * @since 3.0
965
	 *
966
	 * @param array|object $field
967
	 *
968
	 * @return boolean true if field type is radio or Dynamic radio
969
	 */
970
	public static function is_radio( $field ) {
971
		return self::is_field_type( $field, 'radio' );
972
	}
973
974
	/**
975
	 * Check if field is checkbox or Dynamic checkbox
976
	 *
977
	 * @since 3.0
978
	 *
979
	 * @param array|object $field
980
	 *
981
	 * @return boolean true if field type is checkbox or Dynamic checkbox
982
	 */
983
	public static function is_checkbox( $field ) {
984
		return self::is_field_type( $field, 'checkbox' );
985
	}
986
987
	/**
988
	 * Check if field is checkbox or radio
989
	 *
990
	 * @since 3.0
991
	 *
992
	 * @param array|object $field
993
	 * @param string $is_type Options include radio, checkbox, text
994
	 *
995
	 * @return boolean true if field type is checkbox or Dynamic checkbox
996
	 */
997
	public static function is_field_type( $field, $is_type ) {
998
		$field_type = self::get_original_field_type( $field );
999
		$data_type  = self::get_option( $field, 'data_type' );
1000
1001
		return (
1002
			$is_type === $field_type ||
1003
			( 'data' === $field_type && $is_type === $data_type ) ||
1004
			( 'lookup' === $field_type && $is_type === $data_type )
1005
		);
1006
	}
1007
}
1008