PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 3.06.06
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v3.06.06
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 / FrmField.php

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

977 lines 28.5 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 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_css_icon frm_text_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 Button', '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_envelope_icon',
36 ),
37 'url' => array(
38 'name' => __( 'Website/URL', 'formidable' ),
39 'icon' => 'dashicons dashicons-admin-links',
40 ),
41 'number' => array(
42 'name' => __( 'Number', 'formidable' ),
43 'icon' => 'frm_css_icon frm_number_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_embed2_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_upload2_icon',
75 ),
76 'rte' => array(
77 'name' => __( 'Rich Text', 'formidable' ),
78 'icon' => 'dashicons dashicons-editor-alignright',
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-o_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-full_icon',
95 ),
96 'range' => array(
97 'name' => __( 'Slider', 'formidable' ),
98 'icon' => 'frm_icon_font frm_sliders_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_repeat_icon',
115 ),
116 'end_divider' => array(
117 'name' => __( 'End Section', 'formidable' ),
118 'switch_from' => 'divider',
119 ),
120 'divider' => array(
121 'name' => __( 'Section', 'formidable' ),
122 'icon' => 'frm_css_icon frm_heading_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' => 'dashicons dashicons-editor-table',
131 ),
132 'password' => array(
133 'name' => __( 'Password', 'formidable' ),
134 'icon' => 'frm_icon_font frm_key_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-alt_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_pencil_icon frm_show_upgrade',
152 'addon' => 'signature',
153 ),
154 'quiz_score' => array(
155 'name' => __( 'Quiz Score', 'formidable' ),
156 'icon' => 'frm_icon_font frm_calculator_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 public static function create( $values, $return = true ) {
171 global $wpdb, $frm_duplicate_ids;
172
173 $new_values = array();
174 $key = isset( $values['field_key'] ) ? $values['field_key'] : $values['name'];
175 $new_values['field_key'] = FrmAppHelper::get_unique_key( $key, $wpdb->prefix . 'frm_fields', 'field_key' );
176
177 foreach ( array( 'name', 'description', 'type', 'default_value' ) as $col ) {
178 $new_values[ $col ] = $values[ $col ];
179 }
180
181 $new_values['options'] = $values['options'];
182
183 $new_values['field_order'] = isset( $values['field_order'] ) ? (int) $values['field_order'] : null;
184 $new_values['required'] = isset( $values['required'] ) ? (int) $values['required'] : 0;
185 $new_values['form_id'] = isset( $values['form_id'] ) ? (int) $values['form_id'] : null;
186 $new_values['field_options'] = $values['field_options'];
187 $new_values['created_at'] = current_time( 'mysql', 1 );
188
189 if ( isset( $values['id'] ) ) {
190 $frm_duplicate_ids[ $values['field_key'] ] = $new_values['field_key'];
191 $new_values = apply_filters( 'frm_duplicated_field', $new_values );
192 }
193
194 self::preserve_format_option_backslashes( $new_values );
195
196 foreach ( $new_values as $k => $v ) {
197 if ( is_array( $v ) ) {
198 $new_values[ $k ] = serialize( $v );
199 }
200 unset( $k, $v );
201 }
202
203 //if(isset($values['id']) and is_numeric($values['id']))
204 // $new_values['id'] = $values['id'];
205
206 $query_results = $wpdb->insert( $wpdb->prefix . 'frm_fields', $new_values );
207 $new_id = 0;
208 if ( $query_results ) {
209 self::delete_form_transient( $new_values['form_id'] );
210 $new_id = $wpdb->insert_id;
211 }
212
213 if ( ! $return ) {
214 return false;
215 }
216
217 if ( $query_results ) {
218 if ( isset( $values['id'] ) ) {
219 $frm_duplicate_ids[ $values['id'] ] = $new_id;
220 }
221 return $new_id;
222 } else {
223 return false;
224 }
225 }
226
227 public static function duplicate( $old_form_id, $form_id, $copy_keys = false, $blog_id = false ) {
228 global $frm_duplicate_ids;
229
230 $where = array(
231 array(
232 'or' => 1,
233 'fi.form_id' => $old_form_id,
234 'fr.parent_form_id' => $old_form_id,
235 ),
236 );
237 $fields = self::getAll( $where, 'field_order', '', $blog_id );
238
239 foreach ( (array) $fields as $field ) {
240 $new_key = $copy_keys ? $field->field_key : '';
241 if ( $copy_keys && substr( $field->field_key, -1 ) == 2 ) {
242 $new_key = rtrim( $new_key, 2 );
243 }
244
245 $values = array();
246 FrmFieldsHelper::fill_field( $values, $field, $form_id, $new_key );
247
248 // If this is a repeating section, create new form
249 if ( self::is_repeating_field( $field ) ) {
250 // create the repeatable form
251 $new_repeat_form_id = apply_filters(
252 'frm_create_repeat_form',
253 0,
254 array(
255 'parent_form_id' => $form_id,
256 'field_name' => $field->name,
257 )
258 );
259
260 // Save old form_select
261 $old_repeat_form_id = $field->field_options['form_select'];
262
263 // Update form_select for repeating field
264 $values['field_options']['form_select'] = $new_repeat_form_id;
265 }
266
267 // If this is a field inside of a repeating section, associate it with the correct form
268 if ( $field->form_id != $old_form_id && isset( $old_repeat_form_id ) && isset( $new_repeat_form_id ) && $field->form_id == $old_repeat_form_id ) {
269 $values['form_id'] = $new_repeat_form_id;
270 }
271
272 $values['description'] = FrmFieldsHelper::switch_field_ids( $values['description'] );
273
274 $values = apply_filters( 'frm_duplicated_field', $values );
275 $new_id = self::create( $values );
276 $frm_duplicate_ids[ $field->id ] = $new_id;
277 $frm_duplicate_ids[ $field->field_key ] = $new_id;
278 unset( $field );
279 }
280 }
281
282 public static function update( $id, $values ) {
283 global $wpdb;
284
285 $id = absint( $id );
286
287 if ( isset( $values['field_key'] ) ) {
288 $values['field_key'] = FrmAppHelper::get_unique_key( $values['field_key'], $wpdb->prefix . 'frm_fields', 'field_key', $id );
289 }
290
291 if ( isset( $values['required'] ) ) {
292 $values['required'] = (int) $values['required'];
293 }
294
295 self::preserve_format_option_backslashes( $values );
296
297 if ( isset( $values['type'] ) ) {
298 $values = apply_filters( 'frm_clean_' . $values['type'] . '_field_options_before_update', $values );
299
300 if ( $values['type'] == 'hidden' && isset( $values['field_options'] ) && isset( $values['field_options']['clear_on_focus'] ) ) {
301 // don't keep the old placeholder setting for hidden fields
302 $values['field_options']['clear_on_focus'] = 0;
303 }
304 }
305
306 // serialize array values
307 foreach ( array( 'default_value', 'field_options', 'options' ) as $opt ) {
308 if ( isset( $values[ $opt ] ) && is_array( $values[ $opt ] ) ) {
309 $values[ $opt ] = serialize( $values[ $opt ] );
310 }
311 }
312
313 $query_results = $wpdb->update( $wpdb->prefix . 'frm_fields', $values, array( 'id' => $id ) );
314
315 $form_id = 0;
316 if ( isset( $values['form_id'] ) ) {
317 $form_id = absint( $values['form_id'] );
318 } else {
319 $field = self::getOne( $id );
320 if ( $field ) {
321 $form_id = $field->form_id;
322 }
323 unset( $field );
324 }
325 unset( $values );
326
327 if ( $query_results ) {
328 wp_cache_delete( $id, 'frm_field' );
329 if ( $form_id ) {
330 self::delete_form_transient( $form_id );
331 }
332 }
333
334 return $query_results;
335 }
336
337 /**
338 * Keep backslashes in the phone format option
339 *
340 * @since 2.0.8
341 * @param $values array - pass by reference
342 */
343 private static function preserve_format_option_backslashes( &$values ) {
344 if ( isset( $values['field_options']['format'] ) ) {
345 $values['field_options']['format'] = FrmAppHelper::preserve_backslashes( $values['field_options']['format'] );
346 }
347 }
348
349 public static function destroy( $id ) {
350 global $wpdb;
351
352 do_action( 'frm_before_destroy_field', $id );
353
354 wp_cache_delete( $id, 'frm_field' );
355 $field = self::getOne( $id );
356 if ( ! $field ) {
357 return false;
358 }
359
360 self::delete_form_transient( $field->form_id );
361
362 $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . $wpdb->prefix . 'frm_item_metas WHERE field_id=%d', $id ) );
363 return $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . $wpdb->prefix . 'frm_fields WHERE id=%d', $id ) );
364 }
365
366 public static function delete_form_transient( $form_id ) {
367 $form_id = absint( $form_id );
368 delete_transient( 'frm_form_fields_' . $form_id . 'excludeinclude' );
369 delete_transient( 'frm_form_fields_' . $form_id . 'includeinclude' );
370 delete_transient( 'frm_form_fields_' . $form_id . 'includeexclude' );
371 delete_transient( 'frm_form_fields_' . $form_id . 'excludeexclude' );
372
373 global $wpdb;
374 $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%' ) );
375
376 FrmDb::cache_delete_group( 'frm_field' );
377
378 $form = FrmForm::getOne( $form_id );
379 if ( $form && $form->parent_form_id && $form->parent_form_id != $form_id ) {
380 self::delete_form_transient( $form->parent_form_id );
381 }
382 }
383
384 /**
385 * If $field is numeric, get the field object
386 */
387 public static function maybe_get_field( &$field ) {
388 if ( ! is_object( $field ) ) {
389 $field = self::getOne( $field );
390 }
391 }
392
393 /**
394 * @param string|int $id The field id or key.
395 * @param bool $filter When true, run the frm_field filter.
396 */
397 public static function getOne( $id, $filter = false ) {
398 if ( empty( $id ) ) {
399 return null;
400 }
401
402 global $wpdb;
403
404 $where = is_numeric( $id ) ? 'id=%d' : 'field_key=%s';
405 $query = $wpdb->prepare( 'SELECT * FROM ' . $wpdb->prefix . 'frm_fields WHERE ' . $where, $id ); // WPCS: unprepared SQL ok.
406
407 $results = FrmDb::check_cache( $id, 'frm_field', $query, 'get_row', 0 );
408
409 if ( empty( $results ) ) {
410 self::filter_field( $filter, $results );
411 return $results;
412 }
413
414 if ( is_numeric( $id ) ) {
415 FrmDb::set_cache( $results->field_key, $results, 'frm_field' );
416 } else if ( $results ) {
417 FrmDb::set_cache( $results->id, $results, 'frm_field' );
418 }
419
420 self::prepare_options( $results );
421 self::filter_field( $filter, $results );
422
423 return stripslashes_deep( $results );
424 }
425
426 /**
427 * @since 3.06.01
428 * @param bool $filter When true, run the frm_field filter.
429 * @param object $results
430 */
431 private static function filter_field( $filter, &$results ) {
432 if ( $filter ) {
433 /**
434 * @since 3.06.01
435 */
436 $results = apply_filters( 'frm_field', $results );
437 }
438 }
439
440 /**
441 * Get the field type by key or id
442 *
443 * @param int|string The field id or key
444 * @param mixed $col The name of the column in the fields database table
445 */
446 public static function get_type( $id, $col = 'type' ) {
447 $field = FrmDb::check_cache( $id, 'frm_field' );
448 if ( $field ) {
449 $type = $field->{$col};
450 } else {
451 $where = array(
452 'or' => 1,
453 'id' => $id,
454 'field_key' => $id,
455 );
456 $type = FrmDb::get_var( 'frm_fields', $where, $col );
457 }
458
459 return $type;
460 }
461
462 public static function get_all_types_in_form( $form_id, $type, $limit = '', $inc_sub = 'exclude' ) {
463 if ( ! $form_id ) {
464 return array();
465 }
466
467 $results = self::get_fields_from_transients(
468 $form_id,
469 array(
470 'inc_embed' => $inc_sub,
471 'inc_repeat' => $inc_sub,
472 )
473 );
474 if ( ! empty( $results ) ) {
475 $fields = array();
476 $count = 0;
477 foreach ( $results as $result ) {
478 if ( $type != $result->type ) {
479 continue;
480 }
481
482 $fields[ $result->id ] = $result;
483 $count++;
484 if ( $limit == 1 ) {
485 $fields = $result;
486 break;
487 }
488
489 if ( ! empty( $limit ) && $count >= $limit ) {
490 break;
491 }
492
493 unset( $result );
494 }
495 return stripslashes_deep( $fields );
496 }
497
498 self::$use_cache = false;
499
500 $where = array(
501 'fi.form_id' => (int) $form_id,
502 'fi.type' => $type,
503 );
504 self::maybe_include_repeating_fields( $inc_sub, $where );
505 $results = self::getAll( $where, 'field_order', $limit );
506 self::$use_cache = true;
507 self::include_sub_fields( $results, $inc_sub, $type );
508
509 return $results;
510 }
511
512 public static function get_all_for_form( $form_id, $limit = '', $inc_embed = 'exclude', $inc_repeat = 'include' ) {
513 if ( ! (int) $form_id ) {
514 return array();
515 }
516
517 $results = self::get_fields_from_transients( $form_id, compact( 'inc_embed', 'inc_repeat' ) );
518 if ( ! empty( $results ) ) {
519 if ( empty( $limit ) ) {
520 return $results;
521 }
522
523 $fields = array();
524 $count = 0;
525 foreach ( $results as $result ) {
526 $count++;
527 $fields[ $result->id ] = $result;
528 if ( ! empty( $limit ) && $count >= $limit ) {
529 break;
530 }
531 }
532
533 return $fields;
534 }
535
536 self::$use_cache = false;
537
538 $where = array( 'fi.form_id' => absint( $form_id ) );
539 self::maybe_include_repeating_fields( $inc_repeat, $where );
540 $results = self::getAll( $where, 'field_order', $limit );
541
542 self::$use_cache = true;
543
544 self::include_sub_fields( $results, $inc_embed, 'all' );
545
546 if ( empty( $limit ) ) {
547 self::set_field_transient( $results, $form_id, 0, compact( 'inc_embed', 'inc_repeat' ) );
548 }
549
550 return $results;
551 }
552
553 /**
554 * If repeating fields should be included, adjust $where accordingly
555 *
556 * @param string $inc_repeat
557 * @param array $where - pass by reference
558 */
559 private static function maybe_include_repeating_fields( $inc_repeat, &$where ) {
560 if ( $inc_repeat == 'include' ) {
561 $form_id = $where['fi.form_id'];
562 $where[] = array(
563 'or' => 1,
564 'fi.form_id' => $form_id,
565 'fr.parent_form_id' => $form_id,
566 );
567 unset( $where['fi.form_id'] );
568 }
569 }
570
571 public static function include_sub_fields( &$results, $inc_embed, $type = 'all' ) {
572 if ( 'include' != $inc_embed || empty( $results ) ) {
573 return;
574 }
575
576 $form_fields = $results;
577 $index_offset = 1;
578 foreach ( $form_fields as $k => $field ) {
579 if ( 'form' != $field->type || ! isset( $field->field_options['form_select'] ) ) {
580 continue;
581 }
582
583 if ( $type == 'all' ) {
584 $sub_fields = self::get_all_for_form( $field->field_options['form_select'] );
585 } else {
586 $sub_fields = self::get_all_types_in_form( $field->form_id, $type );
587 }
588
589 if ( ! empty( $sub_fields ) ) {
590 $index = $k + $index_offset;
591 $index_offset += count( $sub_fields );
592 array_splice( $results, $index, 0, $sub_fields );
593 }
594 unset( $field, $sub_fields );
595 }
596 }
597
598 public static function getAll( $where = array(), $order_by = '', $limit = '', $blog_id = false ) {
599 $cache_key = maybe_serialize( $where ) . $order_by . 'l' . $limit . 'b' . $blog_id;
600 if ( self::$use_cache ) {
601 // make sure old cache doesn't get saved as a transient
602 $results = wp_cache_get( $cache_key, 'frm_field' );
603 if ( false !== $results ) {
604 return stripslashes_deep( $results );
605 }
606 }
607
608 global $wpdb;
609
610 if ( $blog_id && is_multisite() ) {
611 global $wpmuBaseTablePrefix;
612 if ( $wpmuBaseTablePrefix ) {
613 $prefix = $wpmuBaseTablePrefix . $blog_id . '_';
614 } else {
615 $prefix = $wpdb->get_blog_prefix( $blog_id );
616 }
617
618 $table_name = $prefix . 'frm_fields';
619 $form_table_name = $prefix . 'frm_forms';
620 } else {
621 $table_name = $wpdb->prefix . 'frm_fields';
622 $form_table_name = $wpdb->prefix . 'frm_forms';
623 }
624
625 if ( ! empty( $order_by ) && strpos( $order_by, 'ORDER BY' ) === false ) {
626 $order_by = ' ORDER BY ' . $order_by;
627 }
628
629 $limit = FrmDb::esc_limit( $limit );
630
631 $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";
632 $query_type = ( $limit == ' LIMIT 1' || $limit == 1 ) ? 'row' : 'results';
633
634 if ( is_array( $where ) ) {
635 $args = array(
636 'order_by' => $order_by,
637 'limit' => $limit,
638 );
639 $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 );
640 } else {
641 // if the query is not an array, then it has already been prepared
642 $query .= FrmDb::prepend_and_or_where( ' WHERE ', $where ) . $order_by . $limit;
643
644 $function_name = ( $query_type == 'row' ) ? 'get_row' : 'get_results';
645 $results = $wpdb->$function_name( $query );
646 }
647 unset( $where );
648
649 self::format_field_results( $results );
650
651 FrmDb::set_cache( $cache_key, $results, 'frm_field' );
652
653 return stripslashes_deep( $results );
654 }
655
656 /**
657 * @since 2.0.8
658 */
659 private static function format_field_results( &$results ) {
660 if ( is_array( $results ) ) {
661 foreach ( $results as $r_key => $result ) {
662 FrmDb::set_cache( $result->id, $result, 'frm_field' );
663 FrmDb::set_cache( $result->field_key, $result, 'frm_field' );
664
665 $results[ $r_key ]->field_options = maybe_unserialize( $result->field_options );
666 $results[ $r_key ]->options = maybe_unserialize( $result->options );
667 $results[ $r_key ]->default_value = maybe_unserialize( $result->default_value );
668
669 unset( $r_key, $result );
670 }
671 } else if ( $results ) {
672 FrmDb::set_cache( $results->id, $results, 'frm_field' );
673 FrmDb::set_cache( $results->field_key, $results, 'frm_field' );
674
675 self::prepare_options( $results );
676 }
677 }
678
679 /**
680 * Unserialize all the serialized field data
681 *
682 * @since 2.0
683 */
684 private static function prepare_options( &$results ) {
685 $results->field_options = maybe_unserialize( $results->field_options );
686
687 $results->options = maybe_unserialize( $results->options );
688 $results->default_value = maybe_unserialize( $results->default_value );
689 }
690
691 /**
692 * If a form has too many fields, thay won't all save into a single transient.
693 * We'll break them into groups of 200
694 *
695 * @since 2.0.1
696 */
697 private static function get_fields_from_transients( $form_id, $args ) {
698 $fields = array();
699 self::get_next_transient( $fields, 'frm_form_fields_' . $form_id . $args['inc_embed'] . $args['inc_repeat'] );
700 return $fields;
701 }
702
703 /**
704 * Called by get_fields_from_transients
705 *
706 * @since 2.0.1
707 */
708 private static function get_next_transient( &$fields, $base_name, $next = 0 ) {
709 $name = $next ? $base_name . $next : $base_name;
710 $next_fields = get_transient( $name );
711
712 if ( $next_fields ) {
713 $fields = array_merge( $fields, $next_fields );
714
715 if ( count( $next_fields ) >= self::$transient_size ) {
716 // if this transient is full, check for another
717 $next++;
718 self::get_next_transient( $fields, $base_name, $next );
719 }
720 }
721 }
722
723 /**
724 * Save the transients in chunks for large forms
725 *
726 * @since 2.0.1
727 */
728 private static function set_field_transient( &$fields, $form_id, $next = 0, $args = array() ) {
729 $base_name = 'frm_form_fields_' . $form_id . $args['inc_embed'] . $args['inc_repeat'];
730 $field_chunks = array_chunk( $fields, self::$transient_size );
731
732 foreach ( $field_chunks as $field ) {
733 $name = $next ? $base_name . $next : $base_name;
734 $set = set_transient( $name, $field, 60 * 60 * 6 );
735 if ( ! $set ) {
736 // the transient didn't save
737 if ( $name != $base_name ) {
738 // if the first saved an others fail, this will show an incmoplete form
739 self::delete_form_transient( $form_id );
740 }
741 return;
742 }
743
744 $next++;
745 }
746 }
747
748 public static function is_no_save_field( $type ) {
749 return in_array( $type, self::no_save_fields() );
750 }
751
752 public static function no_save_fields() {
753 return array( 'divider', 'end_divider', 'captcha', 'break', 'html', 'form' );
754 }
755
756 /**
757 * Check if this field can hold an array of values
758 *
759 * @since 2.0.9
760 *
761 * @param array|object $field
762 * @return boolean
763 */
764 public static function is_field_with_multiple_values( $field ) {
765 if ( ! $field ) {
766 return false;
767 }
768
769 $field_type = self::get_original_field_type( $field );
770
771 $is_multi_value_field = (
772 self::is_checkbox( $field ) ||
773 $field_type == 'address' ||
774 self::is_multiple_select( $field )
775 );
776
777 return $is_multi_value_field;
778 }
779
780 /**
781 * @since 3.0
782 * @return string
783 */
784 public static function get_field_type( $field ) {
785 return is_array( $field ) ? $field['type'] : $field->type;
786 }
787
788 /**
789 * @since 3.0
790 * @return string
791 */
792 public static function get_original_field_type( $field ) {
793 $field_type = self::get_field_type( $field );
794 $original_type = self::get_option( $field, 'original_type' );
795
796 if ( ! empty( $original_type ) && $original_type != $field_type ) {
797 $field_type = $original_type; // check the original type for arrays
798 }
799
800 return $field_type;
801 }
802
803 /**
804 * Check if this is a multiselect dropdown field
805 *
806 * @since 2.0.9
807 * @return boolean
808 */
809 public static function is_multiple_select( $field ) {
810 $field_type = self::get_field_type( $field );
811 $data_type = self::get_option( $field, 'data_type' );
812
813 $is_multiple = self::is_option_true( $field, 'multiple' ) && ( ( $field_type == 'select' || ( $field_type == 'data' && $data_type == 'select' ) ) );
814 return apply_filters( 'frm_is_multiple_select', $is_multiple, $field );
815 }
816
817 /**
818 * Check if a field is read only. Read only can be set in the field options,
819 * but disabled with the shortcode options
820 *
821 * @since 2.0.9
822 */
823 public static function is_read_only( $field ) {
824 global $frm_vars;
825 return ( self::is_option_true( $field, 'read_only' ) && ( ! isset( $frm_vars['readonly'] ) || $frm_vars['readonly'] != 'disabled' ) );
826 }
827
828 /**
829 * @since 2.0.9
830 */
831 public static function is_required( $field ) {
832 $required = ( $field['required'] != '0' );
833 $required = apply_filters( 'frm_is_field_required', $required, $field );
834 return $required;
835 }
836
837 /**
838 * @since 2.0.9
839 */
840 public static function is_option_true( $field, $option ) {
841 if ( is_array( $field ) ) {
842 return self::is_option_true_in_array( $field, $option );
843 } else {
844 return self::is_option_true_in_object( $field, $option );
845 }
846 }
847
848 /**
849 * @since 2.0.9
850 */
851 public static function is_option_empty( $field, $option ) {
852 if ( is_array( $field ) ) {
853 return self::is_option_empty_in_array( $field, $option );
854 } else {
855 return self::is_option_empty_in_object( $field, $option );
856 }
857 }
858
859 public static function is_option_true_in_array( $field, $option ) {
860 return isset( $field[ $option ] ) && $field[ $option ];
861 }
862
863 public static function is_option_true_in_object( $field, $option ) {
864 return isset( $field->field_options[ $option ] ) && $field->field_options[ $option ];
865 }
866
867 public static function is_option_empty_in_array( $field, $option ) {
868 return ! isset( $field[ $option ] ) || empty( $field[ $option ] );
869 }
870
871 public static function is_option_empty_in_object( $field, $option ) {
872 return ! isset( $field->field_options[ $option ] ) || empty( $field->field_options[ $option ] );
873 }
874
875 public static function is_option_value_in_object( $field, $option ) {
876 return isset( $field->field_options[ $option ] ) && $field->field_options[ $option ] != '';
877 }
878
879 /**
880 * @since 2.0.18
881 */
882 public static function get_option( $field, $option ) {
883 if ( is_array( $field ) ) {
884 $option = self::get_option_in_array( $field, $option );
885 } else {
886 $option = self::get_option_in_object( $field, $option );
887 }
888 return $option;
889 }
890
891 public static function get_option_in_array( $field, $option ) {
892 return isset( $field[ $option ] ) ? $field[ $option ] : '';
893 }
894
895 public static function get_option_in_object( $field, $option ) {
896 return isset( $field->field_options[ $option ] ) ? $field->field_options[ $option ] : '';
897 }
898
899 /**
900 * @since 2.0.09
901 */
902 public static function is_repeating_field( $field ) {
903 if ( is_array( $field ) ) {
904 $is_repeating_field = ( 'divider' == $field['type'] );
905 } else {
906 $is_repeating_field = ( 'divider' == $field->type );
907 }
908 return ( $is_repeating_field && self::is_option_true( $field, 'repeat' ) );
909 }
910
911 /**
912 * @param string $key
913 * @return int field id
914 */
915 public static function get_id_by_key( $key ) {
916 $id = FrmDb::get_var( 'frm_fields', array( 'field_key' => sanitize_title( $key ) ) );
917 return (int) $id;
918 }
919
920 /**
921 * @param string $id
922 * @return string
923 */
924 public static function get_key_by_id( $id ) {
925 return FrmDb::get_var( 'frm_fields', array( 'id' => $id ), 'field_key' );
926 }
927
928 public static function is_image( $field ) {
929 $type = self::get_field_type( $field );
930 return ( $type == 'url' && self::get_option( $field, 'show_image' ) );
931 }
932
933 /**
934 * Check if field is radio or Dynamic radio
935 *
936 * @since 3.0
937 *
938 * @param array|object $field
939 * @return boolean true if field type is radio or Dynamic radio
940 */
941 public static function is_radio( $field ) {
942 return self::is_field_type( $field, 'radio' );
943 }
944
945 /**
946 * Check if field is checkbox or Dynamic checkbox
947 *
948 * @since 3.0
949 *
950 * @param array|object $field
951 * @return boolean true if field type is checkbox or Dynamic checkbox
952 */
953 public static function is_checkbox( $field ) {
954 return self::is_field_type( $field, 'checkbox' );
955 }
956
957 /**
958 * Check if field is checkbox or radio
959 *
960 * @since 3.0
961 *
962 * @param array|object $field
963 * @param string $is_type Options include radio, checkbox, text
964 * @return boolean true if field type is checkbox or Dynamic checkbox
965 */
966 public static function is_field_type( $field, $is_type ) {
967 $field_type = self::get_original_field_type( $field );
968 $data_type = self::get_option( $field, 'data_type' );
969
970 return (
971 $is_type === $field_type ||
972 ( 'data' === $field_type && $is_type === $data_type ) ||
973 ( 'lookup' === $field_type && $is_type === $data_type )
974 );
975 }
976 }
977