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

1,144 lines 34.0 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_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 'name' => array(
46 'name' => __( 'Name', 'formidable' ),
47 'icon' => 'frm_icon_font frm_user_name_icon',
48 ),
49 'phone' => array(
50 'name' => __( 'Phone', 'formidable' ),
51 'icon' => 'frm_icon_font frm_phone_icon',
52 ),
53 'html' => array(
54 'name' => __( 'HTML', 'formidable' ),
55 'icon' => 'frm_icon_font frm_code_icon',
56 ),
57 'hidden' => array(
58 'name' => __( 'Hidden', 'formidable' ),
59 'icon' => 'frm_icon_font frm_eye_slash_icon',
60 ),
61 'user_id' => array(
62 'name' => __( 'User ID', 'formidable' ),
63 'icon' => 'frm_icon_font frm_user_icon',
64 ),
65 'captcha' => array(
66 'name' => __( 'reCAPTCHA', 'formidable' ),
67 'icon' => 'frm_icon_font frm_shield_check_icon',
68 ),
69 );
70
71 return apply_filters( 'frm_available_fields', $fields );
72 }
73
74 public static function pro_field_selection() {
75 $fields = array(
76 'file' => array(
77 'name' => __( 'File Upload', 'formidable' ),
78 'icon' => 'frm_icon_font frm_upload_icon',
79 'message' => 'Add file uploads to save time and cut down on back-and-forth. Upgrade to Pro to get Upload fields and more.',
80 ),
81 'rte' => array(
82 'name' => __( 'Rich Text', 'formidable' ),
83 'icon' => 'frm_icon_font frm_align_right_icon',
84 ),
85 'date' => array(
86 'name' => __( 'Date', 'formidable' ),
87 'icon' => 'frm_icon_font frm_calendar_icon',
88 ),
89 'time' => array(
90 'name' => __( 'Time', 'formidable' ),
91 'icon' => 'frm_icon_font frm_clock_icon',
92 ),
93 'scale' => array(
94 'name' => __( 'Scale', 'formidable' ),
95 'icon' => 'frm_icon_font frm_linear_scale_icon',
96 'message' => 'Add a set of radio buttons with whatever range you choose. <img src="https://s3.amazonaws.com/fp.strategy11.com/images/knowledgebase/scale_field.png" alt="Scale Field" />',
97 ),
98 'star' => array(
99 'name' => __( 'Star Rating', 'formidable' ),
100 'icon' => 'frm_icon_font frm_star_icon',
101 ),
102 'range' => array(
103 'name' => __( 'Slider', 'formidable' ),
104 'icon' => 'frm_icon_font frm_code_commit_icon',
105 ),
106 'toggle' => array(
107 'name' => __( 'Toggle', 'formidable' ),
108 'icon' => 'frm_icon_font frm_toggle_on_icon',
109 ),
110 'data' => array(
111 'name' => __( 'Dynamic', 'formidable' ),
112 'icon' => 'frm_icon_font frm_sitemap_icon',
113 'message' => 'Create relationships between multiple forms. You can link a member to a team, a rating to a product, a comment to a submission, and much more.',
114 ),
115 'lookup' => array(
116 'name' => __( 'Lookup', 'formidable' ),
117 'icon' => 'frm_icon_font frm_search_icon',
118 'message' => 'Filter the options in the next field and automatically add values to other fields. Upgrade to Pro to get Lookup fields and more. <img src="https://s3.amazonaws.com/fp.strategy11.com/images/knowledgebase/look-up_year-make-model.gif" alt="cascading lookup fields" />',
119 ),
120 'divider|repeat' => array(
121 'name' => __( 'Repeater', 'formidable' ),
122 'icon' => 'frm_icon_font frm_repeater_icon',
123 'message' => 'Allow your visitors to add new sets of fields while filling out forms. Increase conversions while saving building time and server resources. <img src="https://s3.amazonaws.com/fp.strategy11.com/images/knowledgebase/repeatable-section_frontend.gif" alt="Dynamically Add Form Fields with repeatable sections" />',
124 ),
125 'end_divider' => array(
126 'name' => __( 'Section Buttons', 'formidable' ),
127 'switch_from' => 'divider',
128 ),
129 'divider' => array(
130 'name' => __( 'Section', 'formidable' ),
131 'icon' => 'frm_icon_font frm_header_icon',
132 ),
133 'break' => array(
134 'name' => __( 'Page Break', 'formidable' ),
135 'icon' => 'frm_icon_font frm_page_break_icon',
136 'message' => 'Get multi-paged forms with progress bars. Did you know you can upgrade to PRO to unlock multi-step forms with more awesome features?',
137 ),
138 'form' => array(
139 'name' => __( 'Embed Form', 'formidable' ),
140 'icon' => 'frm_icon_font frm_file_text_icon',
141 ),
142 'likert' => array(
143 'name' => __( 'Likert Scale', 'formidable' ),
144 'icon' => 'frm_icon_font frm_likert_scale frm_show_upgrade',
145 'addon' => 'surveys',
146 ),
147 'nps' => array(
148 'name' => __( 'NPS', 'formidable' ),
149 'icon' => 'frm_icon_font frm_nps frm_show_upgrade',
150 'addon' => 'surveys',
151 ),
152 'password' => array(
153 'name' => __( 'Password', 'formidable' ),
154 'icon' => 'frm_icon_font frm_lock_open_icon',
155 ),
156 'tag' => array(
157 'name' => __( 'Tags', 'formidable' ),
158 'icon' => 'frm_icon_font frm_price_tags_icon',
159 ),
160 'credit_card' => array(
161 'name' => __( 'Credit Card', 'formidable' ),
162 'icon' => 'frm_icon_font frm_credit_card_icon frm_show_upgrade',
163 'addon' => 'stripe',
164 ),
165 'address' => array(
166 'name' => __( 'Address', 'formidable' ),
167 'icon' => 'frm_icon_font frm_location_icon',
168 ),
169 'summary' => array(
170 'name' => __( 'Summary', 'formidable' ),
171 'icon' => 'frm_icon_font frm_file_text_icon',
172 'message' => 'Allow visitors to review their responses before a form is submitted. Upgrade to Pro to get Summary fields and more.',
173 ),
174 'signature' => array(
175 'name' => __( 'Signature', 'formidable' ),
176 'icon' => 'frm_icon_font frm_signature_icon frm_show_upgrade',
177 'addon' => 'signature',
178 ),
179 'quiz_score' => array(
180 'name' => __( 'Quiz Score', 'formidable' ),
181 'icon' => 'frm_icon_font frm_percent_icon frm_show_upgrade',
182 'addon' => 'quizzes',
183 ),
184 'ssa-appointment' => array(
185 'name' => __( 'Appointment', 'formidable' ),
186 'icon' => 'frm_icon_font frm_calendar_icon frm_show_upgrade',
187 'require' => 'Simply Schedule Appointments',
188 'message' => 'Appointment fields are an integration with <a href="https://simplyscheduleappointments.com/meet/formidable/">Simply Schedule Appointments</a>. Get started now to schedule appointments directly from your forms.
189 <img src="https://s3.amazonaws.com/fp.strategy11.com/images/appointments/appointments.png" alt="Scheduling" />',
190 'link' => 'https://simplyscheduleappointments.com/meet/formidable/',
191 ),
192 'product' => array(
193 'name' => __( 'Product', 'formidable' ),
194 'icon' => 'frm_icon_font frm_product_icon',
195 'section' => 'pricing',
196 ),
197 'quantity' => array(
198 'name' => __( 'Quantity', 'formidable' ),
199 'icon' => 'frm_icon_font frm_quantity_icon',
200 'section' => 'pricing',
201 ),
202 'total' => array(
203 'name' => __( 'Total', 'formidable' ),
204 'icon' => 'frm_icon_font frm_total_icon',
205 'section' => 'pricing',
206 ),
207 );
208
209 // Since the signature field may be in a different section, don't show it twice.
210 $lite_fields = self::field_selection();
211 if ( isset( $lite_fields['signature'] ) ) {
212 unset( $fields['signature'] );
213 }
214
215 return apply_filters( 'frm_pro_available_fields', $fields );
216 }
217
218 /**
219 * @since 4.0
220 */
221 public static function all_field_selection() {
222 $pro_field_selection = self::pro_field_selection();
223 return array_merge( $pro_field_selection, self::field_selection() );
224 }
225
226 public static function create( $values, $return = true ) {
227 global $wpdb, $frm_duplicate_ids;
228
229 $new_values = array();
230 $key = isset( $values['field_key'] ) ? $values['field_key'] : $values['name'];
231 $new_values['field_key'] = FrmAppHelper::get_unique_key( $key, $wpdb->prefix . 'frm_fields', 'field_key' );
232
233 foreach ( array( 'name', 'description', 'type', 'default_value' ) as $col ) {
234 $new_values[ $col ] = $values[ $col ];
235 }
236
237 $new_values['options'] = $values['options'];
238
239 $new_values['field_order'] = isset( $values['field_order'] ) ? (int) $values['field_order'] : null;
240 $new_values['required'] = isset( $values['required'] ) ? (int) $values['required'] : 0;
241 $new_values['form_id'] = isset( $values['form_id'] ) ? (int) $values['form_id'] : null;
242 $new_values['field_options'] = $values['field_options'];
243 $new_values['created_at'] = current_time( 'mysql', 1 );
244
245 if ( isset( $values['id'] ) ) {
246 $frm_duplicate_ids[ $values['field_key'] ] = $new_values['field_key'];
247 $new_values = apply_filters( 'frm_duplicated_field', $new_values );
248 }
249
250 self::preserve_format_option_backslashes( $new_values );
251
252 foreach ( $new_values as $k => $v ) {
253 if ( is_array( $v ) ) {
254 if ( $k === 'default_value' ) {
255 $new_values[ $k ] = FrmAppHelper::maybe_json_encode( $v );
256 } else {
257 $new_values[ $k ] = serialize( $v );
258 }
259 }
260 unset( $k, $v );
261 }
262
263 //if(isset($values['id']) and is_numeric($values['id']))
264 // $new_values['id'] = $values['id'];
265
266 $query_results = $wpdb->insert( $wpdb->prefix . 'frm_fields', $new_values );
267 $new_id = 0;
268 if ( $query_results ) {
269 self::delete_form_transient( $new_values['form_id'] );
270 $new_id = $wpdb->insert_id;
271 }
272
273 if ( ! $return ) {
274 return false;
275 }
276
277 if ( $query_results ) {
278 if ( isset( $values['id'] ) ) {
279 $frm_duplicate_ids[ $values['id'] ] = $new_id;
280 }
281
282 return $new_id;
283 } else {
284 return false;
285 }
286 }
287
288 /**
289 * Process the field duplication.
290 *
291 * @since 5.0.05
292 */
293 public static function duplicate_single_field( $field_id, $form_id ) {
294 $copy_field = self::getOne( $field_id );
295 if ( ! $copy_field ) {
296 return false;
297 }
298
299 do_action( 'frm_duplicate_field', $copy_field, $form_id );
300 do_action( 'frm_duplicate_field_' . $copy_field->type, $copy_field, $form_id );
301
302 $values = array(
303 'id' => $copy_field->id,
304 );
305 FrmFieldsHelper::fill_field( $values, $copy_field, $copy_field->form_id );
306 $values = apply_filters( 'frm_prepare_single_field_for_duplication', $values );
307
308 $field_id = self::create( $values );
309
310 /**
311 * Fires after duplicating a field.
312 *
313 * @since 5.0.04
314 *
315 * @param array $args {
316 * The arguments.
317 *
318 * @type int $field_id New field ID.
319 * @type array $values Values before inserting.
320 * @type object $copy_field Copy field data.
321 * @type int $form_id Form ID.
322 * }
323 */
324 do_action( 'frm_after_duplicate_field', compact( 'field_id', 'values', 'copy_field', 'form_id' ) );
325
326 return compact( 'field_id', 'values' );
327 }
328
329 public static function duplicate( $old_form_id, $form_id, $copy_keys = false, $blog_id = false ) {
330 global $frm_duplicate_ids;
331
332 $where = array(
333 array(
334 'or' => 1,
335 'fi.form_id' => $old_form_id,
336 'fr.parent_form_id' => $old_form_id,
337 ),
338 );
339 $fields = self::getAll( $where, 'field_order', '', $blog_id );
340
341 foreach ( (array) $fields as $field ) {
342 $new_key = $copy_keys ? $field->field_key : '';
343 if ( $copy_keys && substr( $field->field_key, - 1 ) == 2 ) {
344 $new_key = rtrim( $new_key, 2 );
345 }
346
347 $values = array();
348 FrmFieldsHelper::fill_field( $values, $field, $form_id, $new_key );
349
350 // If this is a repeating section, create new form
351 if ( self::is_repeating_field( $field ) ) {
352 // create the repeatable form
353 $new_repeat_form_id = apply_filters(
354 'frm_create_repeat_form',
355 0,
356 array(
357 'parent_form_id' => $form_id,
358 'field_name' => $field->name,
359 )
360 );
361
362 // Save old form_select
363 $old_repeat_form_id = $field->field_options['form_select'];
364
365 // Update form_select for repeating field
366 $values['field_options']['form_select'] = $new_repeat_form_id;
367 }
368
369 // If this is a field inside of a repeating section, associate it with the correct form
370 if ( $field->form_id != $old_form_id && isset( $old_repeat_form_id ) && isset( $new_repeat_form_id ) && $field->form_id == $old_repeat_form_id ) {
371 $values['form_id'] = $new_repeat_form_id;
372 }
373
374 $values['description'] = FrmFieldsHelper::switch_field_ids( $values['description'] );
375
376 $values = apply_filters( 'frm_duplicated_field', $values );
377 $new_id = self::create( $values );
378 $frm_duplicate_ids[ $field->id ] = $new_id;
379 $frm_duplicate_ids[ $field->field_key ] = $new_id;
380 unset( $field );
381 }
382 }
383
384 public static function update( $id, $values ) {
385 global $wpdb;
386
387 $id = absint( $id );
388 $values = FrmAppHelper::maybe_filter_array( $values, array( 'name', 'description' ) );
389
390 if ( isset( $values['field_key'] ) ) {
391 $values['field_key'] = FrmAppHelper::get_unique_key( $values['field_key'], $wpdb->prefix . 'frm_fields', 'field_key', $id );
392 }
393
394 if ( isset( $values['required'] ) ) {
395 $values['required'] = (int) $values['required'];
396 }
397
398 self::preserve_format_option_backslashes( $values );
399
400 if ( isset( $values['type'] ) ) {
401 $values = apply_filters( 'frm_clean_' . $values['type'] . '_field_options_before_update', $values );
402
403 if ( $values['type'] === 'hidden' && isset( $values['field_options'] ) && isset( $values['field_options']['clear_on_focus'] ) ) {
404 // don't keep the old placeholder setting for hidden fields
405 $values['field_options']['clear_on_focus'] = 0;
406 }
407 }
408
409 // serialize array values
410 foreach ( array( 'field_options', 'options' ) as $opt ) {
411 if ( isset( $values[ $opt ] ) && is_array( $values[ $opt ] ) ) {
412 $values[ $opt ] = FrmAppHelper::maybe_filter_array( $values[ $opt ], array( 'custom_html' ) );
413 $values[ $opt ] = serialize( $values[ $opt ] );
414 }
415 }
416 if ( isset( $values['default_value'] ) && is_array( $values['default_value'] ) ) {
417 $values['default_value'] = json_encode( $values['default_value'] );
418 }
419
420 $query_results = $wpdb->update( $wpdb->prefix . 'frm_fields', $values, array( 'id' => $id ) );
421
422 $form_id = 0;
423 if ( isset( $values['form_id'] ) ) {
424 $form_id = absint( $values['form_id'] );
425 } else {
426 $field = self::getOne( $id );
427 if ( $field ) {
428 $form_id = $field->form_id;
429 }
430 unset( $field );
431 }
432 unset( $values );
433
434 if ( $query_results ) {
435 wp_cache_delete( $id, 'frm_field' );
436 if ( $form_id ) {
437 self::delete_form_transient( $form_id );
438 }
439 }
440
441 return $query_results;
442 }
443
444 /**
445 * Keep backslashes in the phone format option
446 *
447 * @since 2.0.8
448 *
449 * @param $values array - pass by reference
450 */
451 private static function preserve_format_option_backslashes( &$values ) {
452 if ( isset( $values['field_options']['format'] ) ) {
453 $values['field_options']['format'] = FrmAppHelper::preserve_backslashes( $values['field_options']['format'] );
454 }
455 }
456
457 public static function destroy( $id ) {
458 global $wpdb;
459
460 do_action( 'frm_before_destroy_field', $id );
461
462 wp_cache_delete( $id, 'frm_field' );
463 $field = self::getOne( $id );
464 if ( ! $field ) {
465 return false;
466 }
467
468 self::delete_form_transient( $field->form_id );
469
470 $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . $wpdb->prefix . 'frm_item_metas WHERE field_id=%d', $id ) );
471
472 return $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . $wpdb->prefix . 'frm_fields WHERE id=%d', $id ) );
473 }
474
475 public static function delete_form_transient( $form_id ) {
476 $form_id = absint( $form_id );
477 delete_transient( 'frm_form_fields_' . $form_id . 'excludeinclude' );
478 delete_transient( 'frm_form_fields_' . $form_id . 'includeinclude' );
479 delete_transient( 'frm_form_fields_' . $form_id . 'includeexclude' );
480 delete_transient( 'frm_form_fields_' . $form_id . 'excludeexclude' );
481
482 global $wpdb;
483 $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%' ) );
484
485 FrmDb::cache_delete_group( 'frm_field' );
486
487 $form = FrmForm::getOne( $form_id );
488 if ( $form && $form->parent_form_id && $form->parent_form_id != $form_id ) {
489 self::delete_form_transient( $form->parent_form_id );
490 }
491 }
492
493 /**
494 * If $field is numeric, get the field object
495 */
496 public static function maybe_get_field( &$field ) {
497 if ( ! is_object( $field ) ) {
498 $field = self::getOne( $field );
499 }
500 }
501
502 /**
503 * @param string|int $id The field id or key.
504 * @param bool $filter When true, run the frm_field filter.
505 */
506 public static function getOne( $id, $filter = false ) {
507 if ( empty( $id ) ) {
508 return null;
509 }
510
511 global $wpdb;
512
513 $where = is_numeric( $id ) ? 'id=%d' : 'field_key=%s';
514 $query = $wpdb->prepare( 'SELECT * FROM ' . $wpdb->prefix . 'frm_fields WHERE ' . $where, $id ); // WPCS: unprepared SQL ok.
515
516 $results = FrmDb::check_cache( $id, 'frm_field', $query, 'get_row', 0 );
517
518 if ( empty( $results ) ) {
519 self::filter_field( $filter, $results );
520 return $results;
521 }
522
523 if ( is_numeric( $id ) ) {
524 FrmDb::set_cache( $results->field_key, $results, 'frm_field' );
525 } elseif ( $results ) {
526 FrmDb::set_cache( $results->id, $results, 'frm_field' );
527 }
528
529 self::prepare_options( $results );
530 self::filter_field( $filter, $results );
531
532 return wp_unslash( $results );
533 }
534
535 /**
536 * @since 3.06.01
537 * @param bool $filter When true, run the frm_field filter.
538 * @param object $results
539 */
540 private static function filter_field( $filter, &$results ) {
541 if ( $filter ) {
542 /**
543 * @since 3.06.01
544 */
545 $results = apply_filters( 'frm_field', $results );
546 }
547 }
548
549 /**
550 * Get the field type by key or id
551 *
552 * @param int|string The field id or key
553 * @param mixed $col The name of the column in the fields database table
554 */
555 public static function get_type( $id, $col = 'type' ) {
556 $field = FrmDb::check_cache( $id, 'frm_field' );
557 if ( $field ) {
558 $type = $field->{$col};
559 } else {
560 $where = array(
561 'or' => 1,
562 'id' => $id,
563 'field_key' => $id,
564 );
565 $type = FrmDb::get_var( 'frm_fields', $where, $col );
566 }
567
568 return $type;
569 }
570
571 public static function get_all_types_in_form( $form_id, $type, $limit = '', $inc_sub = 'exclude' ) {
572 if ( ! $form_id ) {
573 return array();
574 }
575
576 $results = self::get_fields_from_transients(
577 $form_id,
578 array(
579 'inc_embed' => $inc_sub,
580 'inc_repeat' => $inc_sub,
581 )
582 );
583 if ( ! empty( $results ) ) {
584 $fields = array();
585 $count = 0;
586 foreach ( $results as $result ) {
587 if ( $type != $result->type ) {
588 continue;
589 }
590
591 $fields[ $result->id ] = $result;
592 $count ++;
593 if ( $limit == 1 ) {
594 $fields = $result;
595 break;
596 }
597
598 if ( ! empty( $limit ) && $count >= $limit ) {
599 break;
600 }
601
602 unset( $result );
603 }
604
605 return wp_unslash( $fields );
606 }
607
608 self::$use_cache = false;
609
610 $where = array(
611 'fi.form_id' => (int) $form_id,
612 'fi.type' => $type,
613 );
614 self::maybe_include_repeating_fields( $inc_sub, $where );
615 $results = self::getAll( $where, 'field_order', $limit );
616 self::$use_cache = true;
617 self::include_sub_fields( $results, $inc_sub, $type, $form_id );
618
619 return $results;
620 }
621
622 public static function get_all_for_form( $form_id, $limit = '', $inc_embed = 'exclude', $inc_repeat = 'include' ) {
623 if ( ! (int) $form_id ) {
624 return array();
625 }
626
627 $results = self::get_fields_from_transients( $form_id, compact( 'inc_embed', 'inc_repeat' ) );
628 if ( ! empty( $results ) ) {
629 if ( empty( $limit ) ) {
630 return $results;
631 }
632
633 $fields = array();
634 $count = 0;
635 foreach ( $results as $result ) {
636 $count ++;
637 $fields[ $result->id ] = $result;
638 if ( ! empty( $limit ) && $count >= $limit ) {
639 break;
640 }
641 }
642
643 return $fields;
644 }
645
646 self::$use_cache = false;
647
648 $where = array( 'fi.form_id' => absint( $form_id ) );
649 self::maybe_include_repeating_fields( $inc_repeat, $where );
650 $results = self::getAll( $where, 'field_order', $limit );
651
652 self::$use_cache = true;
653
654 self::include_sub_fields( $results, $inc_embed, 'all', $form_id );
655
656 if ( empty( $limit ) ) {
657 self::set_field_transient( $results, $form_id, 0, compact( 'inc_embed', 'inc_repeat' ) );
658 }
659
660 return $results;
661 }
662
663 /**
664 * If repeating fields should be included, adjust $where accordingly
665 *
666 * @param string $inc_repeat
667 * @param array $where - pass by reference
668 */
669 private static function maybe_include_repeating_fields( $inc_repeat, &$where ) {
670 if ( $inc_repeat == 'include' ) {
671 $form_id = $where['fi.form_id'];
672 $where[] = array(
673 'or' => 1,
674 'fi.form_id' => $form_id,
675 'fr.parent_form_id' => $form_id,
676 );
677 unset( $where['fi.form_id'] );
678 }
679 }
680
681 public static function include_sub_fields( &$results, $inc_embed, $type = 'all', $form_id = '' ) {
682 $no_sub_forms = empty( $results ) && $type === 'all';
683 if ( 'include' != $inc_embed || $no_sub_forms ) {
684 return;
685 }
686
687 $form_fields = $results;
688 $should_get_subforms = ( $type !== 'all' && $type !== 'form' && ! empty( $form_id ) );
689 if ( $should_get_subforms ) {
690 $form_fields = self::get_all_types_in_form( $form_id, 'form' );
691 }
692
693 $index_offset = 1;
694 foreach ( $form_fields as $k => $field ) {
695 if ( 'form' != $field->type || ! isset( $field->field_options['form_select'] ) ) {
696 continue;
697 }
698
699 if ( $type == 'all' ) {
700 $sub_fields = self::get_all_for_form( $field->field_options['form_select'] );
701 } else {
702 $sub_fields = self::get_all_types_in_form( $field->field_options['form_select'], $type );
703 }
704
705 if ( ! empty( $sub_fields ) ) {
706 $index = $k + $index_offset;
707 $index_offset += count( $sub_fields );
708 array_splice( $results, $index, 0, $sub_fields );
709 }
710 unset( $field, $sub_fields );
711 }
712 }
713
714 public static function getAll( $where = array(), $order_by = '', $limit = '', $blog_id = false ) {
715 $cache_key = FrmAppHelper::maybe_json_encode( $where ) . $order_by . 'l' . $limit . 'b' . $blog_id;
716 if ( self::$use_cache ) {
717 // make sure old cache doesn't get saved as a transient
718 $results = wp_cache_get( $cache_key, 'frm_field' );
719 if ( false !== $results ) {
720 return wp_unslash( $results );
721 }
722 }
723
724 global $wpdb;
725
726 if ( $blog_id && is_multisite() ) {
727 global $wpmuBaseTablePrefix;
728 if ( $wpmuBaseTablePrefix ) {
729 $prefix = $wpmuBaseTablePrefix . $blog_id . '_';
730 } else {
731 $prefix = $wpdb->get_blog_prefix( $blog_id );
732 }
733
734 $table_name = $prefix . 'frm_fields';
735 $form_table_name = $prefix . 'frm_forms';
736 } else {
737 $table_name = $wpdb->prefix . 'frm_fields';
738 $form_table_name = $wpdb->prefix . 'frm_forms';
739 }
740
741 if ( ! empty( $order_by ) && strpos( $order_by, 'ORDER BY' ) === false ) {
742 $order_by = ' ORDER BY ' . $order_by;
743 }
744
745 $limit = FrmDb::esc_limit( $limit );
746
747 $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";
748 $query_type = ( $limit == ' LIMIT 1' || $limit == 1 ) ? 'row' : 'results';
749
750 if ( is_array( $where ) ) {
751 $args = array(
752 'order_by' => $order_by,
753 'limit' => $limit,
754 );
755 $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 );
756 } else {
757 // if the query is not an array, then it has already been prepared
758 $query .= FrmDb::prepend_and_or_where( ' WHERE ', $where ) . $order_by . $limit;
759
760 $function_name = ( $query_type == 'row' ) ? 'get_row' : 'get_results';
761 $results = $wpdb->$function_name( $query );
762 }
763 unset( $where );
764
765 self::format_field_results( $results );
766
767 FrmDb::set_cache( $cache_key, $results, 'frm_field' );
768
769 return wp_unslash( $results );
770 }
771
772 /**
773 * @since 2.0.8
774 */
775 private static function format_field_results( &$results ) {
776 if ( is_array( $results ) ) {
777 foreach ( $results as $r_key => $result ) {
778 FrmDb::set_cache( $result->id, $result, 'frm_field' );
779 FrmDb::set_cache( $result->field_key, $result, 'frm_field' );
780
781 self::prepare_options( $result );
782 $results[ $r_key ]->field_options = $result->field_options;
783 $results[ $r_key ]->options = $result->options;
784 $results[ $r_key ]->default_value = $result->default_value;
785
786 unset( $r_key, $result );
787 }
788 } elseif ( $results ) {
789 FrmDb::set_cache( $results->id, $results, 'frm_field' );
790 FrmDb::set_cache( $results->field_key, $results, 'frm_field' );
791
792 self::prepare_options( $results );
793 }
794 }
795
796 /**
797 * Unserialize all the serialized field data
798 *
799 * @since 2.0
800 */
801 private static function prepare_options( &$results ) {
802 FrmAppHelper::unserialize_or_decode( $results->field_options );
803 FrmAppHelper::unserialize_or_decode( $results->options );
804
805 // Allow a single box to be checked for the default value.
806 $before = $results->default_value;
807 FrmAppHelper::unserialize_or_decode( $results->default_value );
808 if ( $before === $results->default_value && ! is_array( $before ) && strpos( $before, '["' ) === 0 ) {
809 $results->default_value = FrmAppHelper::maybe_json_decode( $results->default_value );
810 }
811 }
812
813 /**
814 * If a form has too many fields, thay won't all save into a single transient.
815 * We'll break them into groups of 200
816 *
817 * @since 2.0.1
818 */
819 private static function get_fields_from_transients( $form_id, $args ) {
820 $fields = array();
821 self::get_next_transient( $fields, 'frm_form_fields_' . $form_id . $args['inc_embed'] . $args['inc_repeat'] );
822
823 return $fields;
824 }
825
826 /**
827 * Called by get_fields_from_transients
828 *
829 * @since 2.0.1
830 */
831 private static function get_next_transient( &$fields, $base_name, $next = 0 ) {
832 $name = $next ? $base_name . $next : $base_name;
833 $next_fields = get_transient( $name );
834
835 if ( $next_fields ) {
836 $fields = array_merge( $fields, $next_fields );
837
838 if ( count( $next_fields ) >= self::$transient_size ) {
839 // if this transient is full, check for another
840 $next ++;
841 self::get_next_transient( $fields, $base_name, $next );
842 }
843 }
844 }
845
846 /**
847 * Save the transients in chunks for large forms
848 *
849 * @since 2.0.1
850 */
851 private static function set_field_transient( &$fields, $form_id, $next = 0, $args = array() ) {
852 $base_name = 'frm_form_fields_' . $form_id . $args['inc_embed'] . $args['inc_repeat'];
853 $field_chunks = array_chunk( $fields, self::$transient_size );
854
855 foreach ( $field_chunks as $field ) {
856 $name = $next ? $base_name . $next : $base_name;
857 $set = set_transient( $name, $field, 60 * 60 * 6 );
858 if ( ! $set ) {
859 // the transient didn't save
860 if ( $name != $base_name ) {
861 // if the first saved an others fail, this will show an incmoplete form
862 self::delete_form_transient( $form_id );
863 }
864
865 return;
866 }
867
868 $next ++;
869 }
870 }
871
872 public static function is_no_save_field( $type ) {
873 return in_array( $type, self::no_save_fields() );
874 }
875
876 public static function no_save_fields() {
877 return array( 'divider', 'end_divider', 'captcha', 'break', 'html', 'form', 'summary' );
878 }
879
880 /**
881 * Check if this field can hold an array of values
882 *
883 * @since 2.0.9
884 *
885 * @param array|object $field
886 *
887 * @return boolean
888 */
889 public static function is_field_with_multiple_values( $field ) {
890 if ( ! $field ) {
891 return false;
892 }
893
894 $field_type = self::get_original_field_type( $field );
895
896 $is_multi_value_field = (
897 self::is_checkbox( $field ) ||
898 $field_type == 'address' ||
899 self::is_multiple_select( $field )
900 );
901
902 return $is_multi_value_field;
903 }
904
905 /**
906 * @since 3.0
907 * @return string
908 */
909 public static function get_field_type( $field ) {
910 return is_array( $field ) ? $field['type'] : $field->type;
911 }
912
913 /**
914 * @since 3.0
915 * @return string
916 */
917 public static function get_original_field_type( $field ) {
918 $field_type = self::get_field_type( $field );
919 $original_type = self::get_option( $field, 'original_type' );
920
921 if ( ! empty( $original_type ) && $original_type != $field_type ) {
922 $field_type = $original_type; // check the original type for arrays
923 }
924
925 return $field_type;
926 }
927
928 /**
929 * Check if this is a multiselect dropdown field
930 *
931 * @since 2.0.9
932 * @return boolean
933 */
934 public static function is_multiple_select( $field ) {
935 $field_type = self::get_field_type( $field );
936 $is_multiple = self::is_option_true( $field, 'multiple' ) && self::is_field_type( $field, 'select' ) && $field_type !== 'hidden';
937
938 return apply_filters( 'frm_is_multiple_select', $is_multiple, $field );
939 }
940
941 /**
942 * Check if a field is read only. Read only can be set in the field options,
943 * but disabled with the shortcode options
944 *
945 * @since 2.0.9
946 */
947 public static function is_read_only( $field ) {
948 global $frm_vars;
949
950 return ( self::is_option_true( $field, 'read_only' ) && ( ! isset( $frm_vars['readonly'] ) || $frm_vars['readonly'] != 'disabled' ) );
951 }
952
953 /**
954 * @since 2.0.9
955 */
956 public static function is_required( $field ) {
957 $required = ( $field['required'] != '0' );
958 $required = apply_filters( 'frm_is_field_required', $required, $field );
959
960 return $required;
961 }
962
963 /**
964 * @since 2.0.9
965 */
966 public static function is_option_true( $field, $option ) {
967 if ( is_array( $field ) ) {
968 return self::is_option_true_in_array( $field, $option );
969 } else {
970 return self::is_option_true_in_object( $field, $option );
971 }
972 }
973
974 /**
975 * @since 2.0.9
976 */
977 public static function is_option_empty( $field, $option ) {
978 if ( is_array( $field ) ) {
979 return self::is_option_empty_in_array( $field, $option );
980 } else {
981 return self::is_option_empty_in_object( $field, $option );
982 }
983 }
984
985 public static function is_option_true_in_array( $field, $option ) {
986 return isset( $field[ $option ] ) && $field[ $option ];
987 }
988
989 public static function is_option_true_in_object( $field, $option ) {
990 return isset( $field->field_options[ $option ] ) && $field->field_options[ $option ];
991 }
992
993 public static function is_option_empty_in_array( $field, $option ) {
994 return ! isset( $field[ $option ] ) || empty( $field[ $option ] );
995 }
996
997 public static function is_option_empty_in_object( $field, $option ) {
998 return ! isset( $field->field_options[ $option ] ) || empty( $field->field_options[ $option ] );
999 }
1000
1001 public static function is_option_value_in_object( $field, $option ) {
1002 return isset( $field->field_options[ $option ] ) && $field->field_options[ $option ] != '';
1003 }
1004
1005 /**
1006 * @since 2.0.18
1007 */
1008 public static function get_option( $field, $option ) {
1009 if ( is_array( $field ) ) {
1010 $option = self::get_option_in_array( $field, $option );
1011 } else {
1012 $option = self::get_option_in_object( $field, $option );
1013 }
1014
1015 return $option;
1016 }
1017
1018 public static function get_option_in_array( $field, $option ) {
1019
1020 if ( isset( $field[ $option ] ) ) {
1021 $this_option = $field[ $option ];
1022 } elseif ( isset( $field['field_options'] ) && is_array( $field['field_options'] ) && isset( $field['field_options'][ $option ] ) ) {
1023 $this_option = $field['field_options'][ $option ];
1024 } else {
1025 $this_option = '';
1026 }
1027
1028 return $this_option;
1029 }
1030
1031 public static function get_option_in_object( $field, $option ) {
1032 return isset( $field->field_options[ $option ] ) ? $field->field_options[ $option ] : '';
1033 }
1034
1035 /**
1036 * @since 2.0.09
1037 */
1038 public static function is_repeating_field( $field ) {
1039 if ( is_array( $field ) ) {
1040 $is_repeating_field = ( 'divider' == $field['type'] );
1041 } else {
1042 $is_repeating_field = ( 'divider' == $field->type );
1043 }
1044
1045 return ( $is_repeating_field && self::is_option_true( $field, 'repeat' ) );
1046 }
1047
1048 /**
1049 * @param string $key
1050 *
1051 * @return int field id
1052 */
1053 public static function get_id_by_key( $key ) {
1054 $id = FrmDb::get_var( 'frm_fields', array( 'field_key' => sanitize_title( $key ) ) );
1055
1056 return (int) $id;
1057 }
1058
1059 /**
1060 * @param string $id
1061 *
1062 * @return string
1063 */
1064 public static function get_key_by_id( $id ) {
1065 return FrmDb::get_var( 'frm_fields', array( 'id' => $id ), 'field_key' );
1066 }
1067
1068 public static function is_image( $field ) {
1069 $type = self::get_field_type( $field );
1070
1071 return ( $type == 'url' && self::get_option( $field, 'show_image' ) );
1072 }
1073
1074 /**
1075 * Check if field is radio or Dynamic radio
1076 *
1077 * @since 3.0
1078 *
1079 * @param array|object $field
1080 *
1081 * @return boolean true if field type is radio or Dynamic radio
1082 */
1083 public static function is_radio( $field ) {
1084 return self::is_field_type( $field, 'radio' );
1085 }
1086
1087 /**
1088 * Check if field is checkbox or Dynamic checkbox
1089 *
1090 * @since 3.0
1091 *
1092 * @param array|object $field
1093 *
1094 * @return boolean true if field type is checkbox or Dynamic checkbox
1095 */
1096 public static function is_checkbox( $field ) {
1097 return self::is_field_type( $field, 'checkbox' );
1098 }
1099
1100 /**
1101 * Check if field is checkbox or radio
1102 *
1103 * @since 3.0
1104 *
1105 * @param array|object $field
1106 * @param string $is_type Options include radio, checkbox, text
1107 *
1108 * @return boolean true if field type is checkbox or Dynamic checkbox
1109 */
1110 public static function is_field_type( $field, $is_type ) {
1111 $field_type = self::get_original_field_type( $field );
1112 $data_type = self::get_option( $field, 'data_type' );
1113
1114 $is_field_type = (
1115 $is_type === $field_type ||
1116 ( 'data' === $field_type && $is_type === $data_type ) ||
1117 ( 'lookup' === $field_type && $is_type === $data_type ) ||
1118 ( 'product' === $field_type && $is_type === $data_type )
1119 );
1120
1121 /**
1122 * When a field type is checked, allow individual fields
1123 * to set the type.
1124 *
1125 * @since 4.04
1126 */
1127 return apply_filters( 'frm_is_field_type', $is_field_type, compact( 'field', 'is_type' ) );
1128 }
1129
1130 /**
1131 * Checks if the given field array is a combo field.
1132 *
1133 * @since 4.10.02
1134 *
1135 * @param array $field Field array.
1136 * @return bool
1137 */
1138 public static function is_combo_field( $field ) {
1139 $field_type_obj = FrmFieldFactory::get_field_factory( $field );
1140
1141 return ! empty( $field_type_obj->is_combo_field );
1142 }
1143 }
1144