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

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