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

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