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

1,101 lines 32.7 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 public static function duplicate( $old_form_id, $form_id, $copy_keys = false, $blog_id = false ) {
289 global $frm_duplicate_ids;
290
291 $where = array(
292 array(
293 'or' => 1,
294 'fi.form_id' => $old_form_id,
295 'fr.parent_form_id' => $old_form_id,
296 ),
297 );
298 $fields = self::getAll( $where, 'field_order', '', $blog_id );
299
300 foreach ( (array) $fields as $field ) {
301 $new_key = $copy_keys ? $field->field_key : '';
302 if ( $copy_keys && substr( $field->field_key, - 1 ) == 2 ) {
303 $new_key = rtrim( $new_key, 2 );
304 }
305
306 $values = array();
307 FrmFieldsHelper::fill_field( $values, $field, $form_id, $new_key );
308
309 // If this is a repeating section, create new form
310 if ( self::is_repeating_field( $field ) ) {
311 // create the repeatable form
312 $new_repeat_form_id = apply_filters(
313 'frm_create_repeat_form',
314 0,
315 array(
316 'parent_form_id' => $form_id,
317 'field_name' => $field->name,
318 )
319 );
320
321 // Save old form_select
322 $old_repeat_form_id = $field->field_options['form_select'];
323
324 // Update form_select for repeating field
325 $values['field_options']['form_select'] = $new_repeat_form_id;
326 }
327
328 // If this is a field inside of a repeating section, associate it with the correct form
329 if ( $field->form_id != $old_form_id && isset( $old_repeat_form_id ) && isset( $new_repeat_form_id ) && $field->form_id == $old_repeat_form_id ) {
330 $values['form_id'] = $new_repeat_form_id;
331 }
332
333 $values['description'] = FrmFieldsHelper::switch_field_ids( $values['description'] );
334
335 $values = apply_filters( 'frm_duplicated_field', $values );
336 $new_id = self::create( $values );
337 $frm_duplicate_ids[ $field->id ] = $new_id;
338 $frm_duplicate_ids[ $field->field_key ] = $new_id;
339 unset( $field );
340 }
341 }
342
343 public static function update( $id, $values ) {
344 global $wpdb;
345
346 $id = absint( $id );
347
348 if ( isset( $values['field_key'] ) ) {
349 $values['field_key'] = FrmAppHelper::get_unique_key( $values['field_key'], $wpdb->prefix . 'frm_fields', 'field_key', $id );
350 }
351
352 if ( isset( $values['required'] ) ) {
353 $values['required'] = (int) $values['required'];
354 }
355
356 self::preserve_format_option_backslashes( $values );
357
358 if ( isset( $values['type'] ) ) {
359 $values = apply_filters( 'frm_clean_' . $values['type'] . '_field_options_before_update', $values );
360
361 if ( $values['type'] == 'hidden' && isset( $values['field_options'] ) && isset( $values['field_options']['clear_on_focus'] ) ) {
362 // don't keep the old placeholder setting for hidden fields
363 $values['field_options']['clear_on_focus'] = 0;
364 }
365 }
366
367 // serialize array values
368 foreach ( array( 'field_options', 'options' ) as $opt ) {
369 if ( isset( $values[ $opt ] ) && is_array( $values[ $opt ] ) ) {
370 $values[ $opt ] = serialize( $values[ $opt ] );
371 }
372 }
373 if ( isset( $values['default_value'] ) && is_array( $values['default_value'] ) ) {
374 $values['default_value'] = json_encode( $values['default_value'] );
375 }
376
377 $query_results = $wpdb->update( $wpdb->prefix . 'frm_fields', $values, array( 'id' => $id ) );
378
379 $form_id = 0;
380 if ( isset( $values['form_id'] ) ) {
381 $form_id = absint( $values['form_id'] );
382 } else {
383 $field = self::getOne( $id );
384 if ( $field ) {
385 $form_id = $field->form_id;
386 }
387 unset( $field );
388 }
389 unset( $values );
390
391 if ( $query_results ) {
392 wp_cache_delete( $id, 'frm_field' );
393 if ( $form_id ) {
394 self::delete_form_transient( $form_id );
395 }
396 }
397
398 return $query_results;
399 }
400
401 /**
402 * Keep backslashes in the phone format option
403 *
404 * @since 2.0.8
405 *
406 * @param $values array - pass by reference
407 */
408 private static function preserve_format_option_backslashes( &$values ) {
409 if ( isset( $values['field_options']['format'] ) ) {
410 $values['field_options']['format'] = FrmAppHelper::preserve_backslashes( $values['field_options']['format'] );
411 }
412 }
413
414 public static function destroy( $id ) {
415 global $wpdb;
416
417 do_action( 'frm_before_destroy_field', $id );
418
419 wp_cache_delete( $id, 'frm_field' );
420 $field = self::getOne( $id );
421 if ( ! $field ) {
422 return false;
423 }
424
425 self::delete_form_transient( $field->form_id );
426
427 $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . $wpdb->prefix . 'frm_item_metas WHERE field_id=%d', $id ) );
428
429 return $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . $wpdb->prefix . 'frm_fields WHERE id=%d', $id ) );
430 }
431
432 public static function delete_form_transient( $form_id ) {
433 $form_id = absint( $form_id );
434 delete_transient( 'frm_form_fields_' . $form_id . 'excludeinclude' );
435 delete_transient( 'frm_form_fields_' . $form_id . 'includeinclude' );
436 delete_transient( 'frm_form_fields_' . $form_id . 'includeexclude' );
437 delete_transient( 'frm_form_fields_' . $form_id . 'excludeexclude' );
438
439 global $wpdb;
440 $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%' ) );
441
442 FrmDb::cache_delete_group( 'frm_field' );
443
444 $form = FrmForm::getOne( $form_id );
445 if ( $form && $form->parent_form_id && $form->parent_form_id != $form_id ) {
446 self::delete_form_transient( $form->parent_form_id );
447 }
448 }
449
450 /**
451 * If $field is numeric, get the field object
452 */
453 public static function maybe_get_field( &$field ) {
454 if ( ! is_object( $field ) ) {
455 $field = self::getOne( $field );
456 }
457 }
458
459 /**
460 * @param string|int $id The field id or key.
461 * @param bool $filter When true, run the frm_field filter.
462 */
463 public static function getOne( $id, $filter = false ) {
464 if ( empty( $id ) ) {
465 return null;
466 }
467
468 global $wpdb;
469
470 $where = is_numeric( $id ) ? 'id=%d' : 'field_key=%s';
471 $query = $wpdb->prepare( 'SELECT * FROM ' . $wpdb->prefix . 'frm_fields WHERE ' . $where, $id ); // WPCS: unprepared SQL ok.
472
473 $results = FrmDb::check_cache( $id, 'frm_field', $query, 'get_row', 0 );
474
475 if ( empty( $results ) ) {
476 self::filter_field( $filter, $results );
477 return $results;
478 }
479
480 if ( is_numeric( $id ) ) {
481 FrmDb::set_cache( $results->field_key, $results, 'frm_field' );
482 } elseif ( $results ) {
483 FrmDb::set_cache( $results->id, $results, 'frm_field' );
484 }
485
486 self::prepare_options( $results );
487 self::filter_field( $filter, $results );
488
489 return wp_unslash( $results );
490 }
491
492 /**
493 * @since 3.06.01
494 * @param bool $filter When true, run the frm_field filter.
495 * @param object $results
496 */
497 private static function filter_field( $filter, &$results ) {
498 if ( $filter ) {
499 /**
500 * @since 3.06.01
501 */
502 $results = apply_filters( 'frm_field', $results );
503 }
504 }
505
506 /**
507 * Get the field type by key or id
508 *
509 * @param int|string The field id or key
510 * @param mixed $col The name of the column in the fields database table
511 */
512 public static function get_type( $id, $col = 'type' ) {
513 $field = FrmDb::check_cache( $id, 'frm_field' );
514 if ( $field ) {
515 $type = $field->{$col};
516 } else {
517 $where = array(
518 'or' => 1,
519 'id' => $id,
520 'field_key' => $id,
521 );
522 $type = FrmDb::get_var( 'frm_fields', $where, $col );
523 }
524
525 return $type;
526 }
527
528 public static function get_all_types_in_form( $form_id, $type, $limit = '', $inc_sub = 'exclude' ) {
529 if ( ! $form_id ) {
530 return array();
531 }
532
533 $results = self::get_fields_from_transients(
534 $form_id,
535 array(
536 'inc_embed' => $inc_sub,
537 'inc_repeat' => $inc_sub,
538 )
539 );
540 if ( ! empty( $results ) ) {
541 $fields = array();
542 $count = 0;
543 foreach ( $results as $result ) {
544 if ( $type != $result->type ) {
545 continue;
546 }
547
548 $fields[ $result->id ] = $result;
549 $count ++;
550 if ( $limit == 1 ) {
551 $fields = $result;
552 break;
553 }
554
555 if ( ! empty( $limit ) && $count >= $limit ) {
556 break;
557 }
558
559 unset( $result );
560 }
561
562 return wp_unslash( $fields );
563 }
564
565 self::$use_cache = false;
566
567 $where = array(
568 'fi.form_id' => (int) $form_id,
569 'fi.type' => $type,
570 );
571 self::maybe_include_repeating_fields( $inc_sub, $where );
572 $results = self::getAll( $where, 'field_order', $limit );
573 self::$use_cache = true;
574 self::include_sub_fields( $results, $inc_sub, $type, $form_id );
575
576 return $results;
577 }
578
579 public static function get_all_for_form( $form_id, $limit = '', $inc_embed = 'exclude', $inc_repeat = 'include' ) {
580 if ( ! (int) $form_id ) {
581 return array();
582 }
583
584 $results = self::get_fields_from_transients( $form_id, compact( 'inc_embed', 'inc_repeat' ) );
585 if ( ! empty( $results ) ) {
586 if ( empty( $limit ) ) {
587 return $results;
588 }
589
590 $fields = array();
591 $count = 0;
592 foreach ( $results as $result ) {
593 $count ++;
594 $fields[ $result->id ] = $result;
595 if ( ! empty( $limit ) && $count >= $limit ) {
596 break;
597 }
598 }
599
600 return $fields;
601 }
602
603 self::$use_cache = false;
604
605 $where = array( 'fi.form_id' => absint( $form_id ) );
606 self::maybe_include_repeating_fields( $inc_repeat, $where );
607 $results = self::getAll( $where, 'field_order', $limit );
608
609 self::$use_cache = true;
610
611 self::include_sub_fields( $results, $inc_embed, 'all', $form_id );
612
613 if ( empty( $limit ) ) {
614 self::set_field_transient( $results, $form_id, 0, compact( 'inc_embed', 'inc_repeat' ) );
615 }
616
617 return $results;
618 }
619
620 /**
621 * If repeating fields should be included, adjust $where accordingly
622 *
623 * @param string $inc_repeat
624 * @param array $where - pass by reference
625 */
626 private static function maybe_include_repeating_fields( $inc_repeat, &$where ) {
627 if ( $inc_repeat == 'include' ) {
628 $form_id = $where['fi.form_id'];
629 $where[] = array(
630 'or' => 1,
631 'fi.form_id' => $form_id,
632 'fr.parent_form_id' => $form_id,
633 );
634 unset( $where['fi.form_id'] );
635 }
636 }
637
638 public static function include_sub_fields( &$results, $inc_embed, $type = 'all', $form_id = '' ) {
639 $no_sub_forms = empty( $results ) && $type === 'all';
640 if ( 'include' != $inc_embed || $no_sub_forms ) {
641 return;
642 }
643
644 $form_fields = $results;
645 $should_get_subforms = ( $type !== 'all' && $type !== 'form' && ! empty( $form_id ) );
646 if ( $should_get_subforms ) {
647 $form_fields = self::get_all_types_in_form( $form_id, 'form' );
648 }
649
650 $index_offset = 1;
651 foreach ( $form_fields as $k => $field ) {
652 if ( 'form' != $field->type || ! isset( $field->field_options['form_select'] ) ) {
653 continue;
654 }
655
656 if ( $type == 'all' ) {
657 $sub_fields = self::get_all_for_form( $field->field_options['form_select'] );
658 } else {
659 $sub_fields = self::get_all_types_in_form( $field->field_options['form_select'], $type );
660 }
661
662 if ( ! empty( $sub_fields ) ) {
663 $index = $k + $index_offset;
664 $index_offset += count( $sub_fields );
665 array_splice( $results, $index, 0, $sub_fields );
666 }
667 unset( $field, $sub_fields );
668 }
669 }
670
671 public static function getAll( $where = array(), $order_by = '', $limit = '', $blog_id = false ) {
672 $cache_key = FrmAppHelper::maybe_json_encode( $where ) . $order_by . 'l' . $limit . 'b' . $blog_id;
673 if ( self::$use_cache ) {
674 // make sure old cache doesn't get saved as a transient
675 $results = wp_cache_get( $cache_key, 'frm_field' );
676 if ( false !== $results ) {
677 return wp_unslash( $results );
678 }
679 }
680
681 global $wpdb;
682
683 if ( $blog_id && is_multisite() ) {
684 global $wpmuBaseTablePrefix;
685 if ( $wpmuBaseTablePrefix ) {
686 $prefix = $wpmuBaseTablePrefix . $blog_id . '_';
687 } else {
688 $prefix = $wpdb->get_blog_prefix( $blog_id );
689 }
690
691 $table_name = $prefix . 'frm_fields';
692 $form_table_name = $prefix . 'frm_forms';
693 } else {
694 $table_name = $wpdb->prefix . 'frm_fields';
695 $form_table_name = $wpdb->prefix . 'frm_forms';
696 }
697
698 if ( ! empty( $order_by ) && strpos( $order_by, 'ORDER BY' ) === false ) {
699 $order_by = ' ORDER BY ' . $order_by;
700 }
701
702 $limit = FrmDb::esc_limit( $limit );
703
704 $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";
705 $query_type = ( $limit == ' LIMIT 1' || $limit == 1 ) ? 'row' : 'results';
706
707 if ( is_array( $where ) ) {
708 $args = array(
709 'order_by' => $order_by,
710 'limit' => $limit,
711 );
712 $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 );
713 } else {
714 // if the query is not an array, then it has already been prepared
715 $query .= FrmDb::prepend_and_or_where( ' WHERE ', $where ) . $order_by . $limit;
716
717 $function_name = ( $query_type == 'row' ) ? 'get_row' : 'get_results';
718 $results = $wpdb->$function_name( $query );
719 }
720 unset( $where );
721
722 self::format_field_results( $results );
723
724 FrmDb::set_cache( $cache_key, $results, 'frm_field' );
725
726 return wp_unslash( $results );
727 }
728
729 /**
730 * @since 2.0.8
731 */
732 private static function format_field_results( &$results ) {
733 if ( is_array( $results ) ) {
734 foreach ( $results as $r_key => $result ) {
735 FrmDb::set_cache( $result->id, $result, 'frm_field' );
736 FrmDb::set_cache( $result->field_key, $result, 'frm_field' );
737
738 self::prepare_options( $result );
739 $results[ $r_key ]->field_options = $result->field_options;
740 $results[ $r_key ]->options = $result->options;
741 $results[ $r_key ]->default_value = $result->default_value;
742
743 unset( $r_key, $result );
744 }
745 } elseif ( $results ) {
746 FrmDb::set_cache( $results->id, $results, 'frm_field' );
747 FrmDb::set_cache( $results->field_key, $results, 'frm_field' );
748
749 self::prepare_options( $results );
750 }
751 }
752
753 /**
754 * Unserialize all the serialized field data
755 *
756 * @since 2.0
757 */
758 private static function prepare_options( &$results ) {
759 FrmAppHelper::unserialize_or_decode( $results->field_options );
760 FrmAppHelper::unserialize_or_decode( $results->options );
761
762 // Allow a single box to be checked for the default value.
763 $before = $results->default_value;
764 FrmAppHelper::unserialize_or_decode( $results->default_value );
765 if ( $before === $results->default_value && ! is_array( $before ) && strpos( $before, '["' ) === 0 ) {
766 $results->default_value = FrmAppHelper::maybe_json_decode( $results->default_value );
767 }
768 }
769
770 /**
771 * If a form has too many fields, thay won't all save into a single transient.
772 * We'll break them into groups of 200
773 *
774 * @since 2.0.1
775 */
776 private static function get_fields_from_transients( $form_id, $args ) {
777 $fields = array();
778 self::get_next_transient( $fields, 'frm_form_fields_' . $form_id . $args['inc_embed'] . $args['inc_repeat'] );
779
780 return $fields;
781 }
782
783 /**
784 * Called by get_fields_from_transients
785 *
786 * @since 2.0.1
787 */
788 private static function get_next_transient( &$fields, $base_name, $next = 0 ) {
789 $name = $next ? $base_name . $next : $base_name;
790 $next_fields = get_transient( $name );
791
792 if ( $next_fields ) {
793 $fields = array_merge( $fields, $next_fields );
794
795 if ( count( $next_fields ) >= self::$transient_size ) {
796 // if this transient is full, check for another
797 $next ++;
798 self::get_next_transient( $fields, $base_name, $next );
799 }
800 }
801 }
802
803 /**
804 * Save the transients in chunks for large forms
805 *
806 * @since 2.0.1
807 */
808 private static function set_field_transient( &$fields, $form_id, $next = 0, $args = array() ) {
809 $base_name = 'frm_form_fields_' . $form_id . $args['inc_embed'] . $args['inc_repeat'];
810 $field_chunks = array_chunk( $fields, self::$transient_size );
811
812 foreach ( $field_chunks as $field ) {
813 $name = $next ? $base_name . $next : $base_name;
814 $set = set_transient( $name, $field, 60 * 60 * 6 );
815 if ( ! $set ) {
816 // the transient didn't save
817 if ( $name != $base_name ) {
818 // if the first saved an others fail, this will show an incmoplete form
819 self::delete_form_transient( $form_id );
820 }
821
822 return;
823 }
824
825 $next ++;
826 }
827 }
828
829 public static function is_no_save_field( $type ) {
830 return in_array( $type, self::no_save_fields() );
831 }
832
833 public static function no_save_fields() {
834 return array( 'divider', 'end_divider', 'captcha', 'break', 'html', 'form', 'summary' );
835 }
836
837 /**
838 * Check if this field can hold an array of values
839 *
840 * @since 2.0.9
841 *
842 * @param array|object $field
843 *
844 * @return boolean
845 */
846 public static function is_field_with_multiple_values( $field ) {
847 if ( ! $field ) {
848 return false;
849 }
850
851 $field_type = self::get_original_field_type( $field );
852
853 $is_multi_value_field = (
854 self::is_checkbox( $field ) ||
855 $field_type == 'address' ||
856 self::is_multiple_select( $field )
857 );
858
859 return $is_multi_value_field;
860 }
861
862 /**
863 * @since 3.0
864 * @return string
865 */
866 public static function get_field_type( $field ) {
867 return is_array( $field ) ? $field['type'] : $field->type;
868 }
869
870 /**
871 * @since 3.0
872 * @return string
873 */
874 public static function get_original_field_type( $field ) {
875 $field_type = self::get_field_type( $field );
876 $original_type = self::get_option( $field, 'original_type' );
877
878 if ( ! empty( $original_type ) && $original_type != $field_type ) {
879 $field_type = $original_type; // check the original type for arrays
880 }
881
882 return $field_type;
883 }
884
885 /**
886 * Check if this is a multiselect dropdown field
887 *
888 * @since 2.0.9
889 * @return boolean
890 */
891 public static function is_multiple_select( $field ) {
892 $field_type = self::get_field_type( $field );
893 $is_multiple = self::is_option_true( $field, 'multiple' ) && self::is_field_type( $field, 'select' ) && $field_type !== 'hidden';
894
895 return apply_filters( 'frm_is_multiple_select', $is_multiple, $field );
896 }
897
898 /**
899 * Check if a field is read only. Read only can be set in the field options,
900 * but disabled with the shortcode options
901 *
902 * @since 2.0.9
903 */
904 public static function is_read_only( $field ) {
905 global $frm_vars;
906
907 return ( self::is_option_true( $field, 'read_only' ) && ( ! isset( $frm_vars['readonly'] ) || $frm_vars['readonly'] != 'disabled' ) );
908 }
909
910 /**
911 * @since 2.0.9
912 */
913 public static function is_required( $field ) {
914 $required = ( $field['required'] != '0' );
915 $required = apply_filters( 'frm_is_field_required', $required, $field );
916
917 return $required;
918 }
919
920 /**
921 * @since 2.0.9
922 */
923 public static function is_option_true( $field, $option ) {
924 if ( is_array( $field ) ) {
925 return self::is_option_true_in_array( $field, $option );
926 } else {
927 return self::is_option_true_in_object( $field, $option );
928 }
929 }
930
931 /**
932 * @since 2.0.9
933 */
934 public static function is_option_empty( $field, $option ) {
935 if ( is_array( $field ) ) {
936 return self::is_option_empty_in_array( $field, $option );
937 } else {
938 return self::is_option_empty_in_object( $field, $option );
939 }
940 }
941
942 public static function is_option_true_in_array( $field, $option ) {
943 return isset( $field[ $option ] ) && $field[ $option ];
944 }
945
946 public static function is_option_true_in_object( $field, $option ) {
947 return isset( $field->field_options[ $option ] ) && $field->field_options[ $option ];
948 }
949
950 public static function is_option_empty_in_array( $field, $option ) {
951 return ! isset( $field[ $option ] ) || empty( $field[ $option ] );
952 }
953
954 public static function is_option_empty_in_object( $field, $option ) {
955 return ! isset( $field->field_options[ $option ] ) || empty( $field->field_options[ $option ] );
956 }
957
958 public static function is_option_value_in_object( $field, $option ) {
959 return isset( $field->field_options[ $option ] ) && $field->field_options[ $option ] != '';
960 }
961
962 /**
963 * @since 2.0.18
964 */
965 public static function get_option( $field, $option ) {
966 if ( is_array( $field ) ) {
967 $option = self::get_option_in_array( $field, $option );
968 } else {
969 $option = self::get_option_in_object( $field, $option );
970 }
971
972 return $option;
973 }
974
975 public static function get_option_in_array( $field, $option ) {
976
977 if ( isset( $field[ $option ] ) ) {
978 $this_option = $field[ $option ];
979 } elseif ( isset( $field['field_options'] ) && is_array( $field['field_options'] ) && isset( $field['field_options'][ $option ] ) ) {
980 $this_option = $field['field_options'][ $option ];
981 } else {
982 $this_option = '';
983 }
984
985 return $this_option;
986 }
987
988 public static function get_option_in_object( $field, $option ) {
989 return isset( $field->field_options[ $option ] ) ? $field->field_options[ $option ] : '';
990 }
991
992 /**
993 * @since 2.0.09
994 */
995 public static function is_repeating_field( $field ) {
996 if ( is_array( $field ) ) {
997 $is_repeating_field = ( 'divider' == $field['type'] );
998 } else {
999 $is_repeating_field = ( 'divider' == $field->type );
1000 }
1001
1002 return ( $is_repeating_field && self::is_option_true( $field, 'repeat' ) );
1003 }
1004
1005 /**
1006 * @param string $key
1007 *
1008 * @return int field id
1009 */
1010 public static function get_id_by_key( $key ) {
1011 $id = FrmDb::get_var( 'frm_fields', array( 'field_key' => sanitize_title( $key ) ) );
1012
1013 return (int) $id;
1014 }
1015
1016 /**
1017 * @param string $id
1018 *
1019 * @return string
1020 */
1021 public static function get_key_by_id( $id ) {
1022 return FrmDb::get_var( 'frm_fields', array( 'id' => $id ), 'field_key' );
1023 }
1024
1025 public static function is_image( $field ) {
1026 $type = self::get_field_type( $field );
1027
1028 return ( $type == 'url' && self::get_option( $field, 'show_image' ) );
1029 }
1030
1031 /**
1032 * Check if field is radio or Dynamic radio
1033 *
1034 * @since 3.0
1035 *
1036 * @param array|object $field
1037 *
1038 * @return boolean true if field type is radio or Dynamic radio
1039 */
1040 public static function is_radio( $field ) {
1041 return self::is_field_type( $field, 'radio' );
1042 }
1043
1044 /**
1045 * Check if field is checkbox or Dynamic checkbox
1046 *
1047 * @since 3.0
1048 *
1049 * @param array|object $field
1050 *
1051 * @return boolean true if field type is checkbox or Dynamic checkbox
1052 */
1053 public static function is_checkbox( $field ) {
1054 return self::is_field_type( $field, 'checkbox' );
1055 }
1056
1057 /**
1058 * Check if field is checkbox or radio
1059 *
1060 * @since 3.0
1061 *
1062 * @param array|object $field
1063 * @param string $is_type Options include radio, checkbox, text
1064 *
1065 * @return boolean true if field type is checkbox or Dynamic checkbox
1066 */
1067 public static function is_field_type( $field, $is_type ) {
1068 $field_type = self::get_original_field_type( $field );
1069 $data_type = self::get_option( $field, 'data_type' );
1070
1071 $is_field_type = (
1072 $is_type === $field_type ||
1073 ( 'data' === $field_type && $is_type === $data_type ) ||
1074 ( 'lookup' === $field_type && $is_type === $data_type ) ||
1075 ( 'product' === $field_type && $is_type === $data_type )
1076 );
1077
1078 /**
1079 * When a field type is checked, allow individual fields
1080 * to set the type.
1081 *
1082 * @since 4.04
1083 */
1084 return apply_filters( 'frm_is_field_type', $is_field_type, compact( 'field', 'is_type' ) );
1085 }
1086
1087 /**
1088 * Checks if the given field array is a combo field.
1089 *
1090 * @since 4.10.02
1091 *
1092 * @param array $field Field array.
1093 * @return bool
1094 */
1095 public static function is_combo_field( $field ) {
1096 $field_type_obj = FrmFieldFactory::get_field_factory( $field );
1097
1098 return ! empty( $field_type_obj->is_combo_field );
1099 }
1100 }
1101