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

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