PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.21
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.21
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 / helpers / FrmFieldsHelper.php

FrmFieldsHelper.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 6.21, at classes/helpers/FrmFieldsHelper.php

2,426 lines 71.9 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 FrmFieldsHelper {
7
8 /**
9 * The context is memoized for re-use as the context is checked for each field.
10 *
11 * @var bool|null
12 */
13 private static $context_is_safe_to_load_field_options_from_request_data;
14
15 public static function setup_new_vars( $type = '', $form_id = '' ) {
16
17 if ( strpos( $type, '|' ) ) {
18 list( $type, $setting ) = explode( '|', $type );
19 }
20
21 $values = self::get_default_field( $type );
22
23 global $wpdb;
24 $field_count = FrmDb::get_var(
25 'frm_fields',
26 array( 'form_id' => $form_id ),
27 'field_order',
28 array( 'order_by' => 'field_order DESC' )
29 );
30
31 $values['field_key'] = FrmAppHelper::get_unique_key( '', $wpdb->prefix . 'frm_fields', 'field_key' );
32 $values['form_id'] = $form_id;
33 $values['field_order'] = $field_count + 1;
34
35 $values['field_options']['custom_html'] = self::get_default_html( $type );
36
37 if ( ! empty( $setting ) ) {
38 if ( in_array( $type, array( 'data', 'lookup' ), true ) ) {
39 $values['field_options']['data_type'] = $setting;
40 } else {
41 $values['field_options'][ $setting ] = 1;
42 }
43 }
44
45 // Increase the field order of submit field and fields in the same row.
46 $last_row_field_ids = FrmAppHelper::get_post_param( 'last_row_field_ids', array() );
47 if ( $last_row_field_ids ) {
48 foreach ( $last_row_field_ids as $index => $last_row_field_id ) {
49 FrmField::update(
50 $last_row_field_id,
51 array( 'field_order' => $field_count + $index + 2 )
52 );
53 }
54 }
55
56 return $values;
57 }
58
59 public static function get_html_id( $field, $plus = '' ) {
60 return apply_filters( 'frm_field_html_id', 'field_' . $field['field_key'] . $plus, $field );
61 }
62
63 public static function setup_edit_vars( $field, $doing_ajax = false ) {
64 $values = self::field_object_to_array( $field );
65
66 return apply_filters( 'frm_setup_edit_field_vars', $values, array( 'doing_ajax' => $doing_ajax ) );
67 }
68
69 public static function field_object_to_array( $field ) {
70 $values = (array) $field;
71
72 self::fill_field_array( $field, $values );
73
74 $values['custom_html'] = isset( $field->field_options['custom_html'] ) ? $field->field_options['custom_html'] : self::get_default_html( $field->type );
75
76 return $values;
77 }
78
79 private static function fill_field_array( $field, array &$field_array ) {
80 $field_array['options'] = $field->options;
81 $field_array['value'] = $field->default_value;
82
83 self::prepare_edit_front_field( $field_array, $field );
84
85 $field_array = array_merge( (array) $field->field_options, $field_array );
86 }
87
88 /**
89 * Prepare field while creating a new entry
90 *
91 * @since 3.0
92 */
93 public static function prepare_new_front_field( &$field_array, $field, $args = array() ) {
94 $args['action'] = 'new';
95 self::prepare_front_field( $field_array, $field, $args );
96 }
97
98 /**
99 * Prepare field while editing an entry
100 *
101 * @since 3.0
102 */
103 public static function prepare_edit_front_field( &$field_array, $field, $entry_id = 0, $args = array() ) {
104 $args['entry_id'] = $entry_id;
105 $args['action'] = 'edit';
106 self::prepare_front_field( $field_array, $field, $args );
107 }
108
109 /**
110 * Prepare field while creating a new entry
111 *
112 * @since 3.0
113 *
114 * @param array $field_array
115 * @param stdClass $field
116 * @param array $args
117 * @return void
118 */
119 private static function prepare_front_field( &$field_array, $field, $args ) {
120 self::fill_default_field_opts( $field, $field_array );
121 self::fill_cleared_strings( $field, $field_array );
122
123 // Track the original field's type
124 $field_array['original_type'] = isset( $field->field_options['original_type'] ) ? $field->field_options['original_type'] : $field->type;
125
126 self::prepare_field_options_for_display( $field_array, $field, $args );
127
128 if ( $args['action'] === 'edit' ) {
129 /**
130 * @param array $field_array
131 * @param stdClass $field
132 * @param int|string $entry_id
133 * @param array $args
134 */
135 $field_array = apply_filters( 'frm_setup_edit_fields_vars', $field_array, $field, $args['entry_id'], $args );
136 } else {
137 /**
138 * @param array $field_array
139 * @param stdClass $field
140 * @param array $args
141 */
142 $field_array = apply_filters( 'frm_setup_new_fields_vars', $field_array, $field, $args );
143 }
144 }
145
146 /**
147 * @since 3.0
148 *
149 * @param string $type
150 *
151 * @return array
152 */
153 public static function get_default_field_options( $type ) {
154 $field_type = FrmFieldFactory::get_field_type( $type );
155
156 return $field_type->get_default_field_options();
157 }
158
159 /**
160 * @since 3.0
161 *
162 * @param object $field
163 * @param array $values
164 */
165 private static function fill_default_field_opts( $field, array &$values ) {
166 $check_post = self::context_is_safe_to_load_field_options_from_request_data();
167 $defaults = self::get_default_field_options_from_field( $field, $values );
168
169 if ( ! $check_post ) {
170 $defaults['required_indicator'] = '';
171 $defaults['original_type'] = $field->type;
172 }
173
174 foreach ( $defaults as $opt => $default ) {
175 $values[ $opt ] = isset( $field->field_options[ $opt ] ) ? $field->field_options[ $opt ] : $default;
176
177 if ( $check_post ) {
178 self::get_posted_field_setting( $opt . '_' . $field->id, $values[ $opt ] );
179 }
180 }
181 }
182
183 /**
184 * The fill_default_field_opts method is called when loading a field.
185 * This is used to preserve the $_POST data after updating settings for a field.
186 * To prevent this from happening when creating an entry, we need to check the context.
187 *
188 * @return bool
189 */
190 private static function context_is_safe_to_load_field_options_from_request_data() {
191 if ( isset( self::$context_is_safe_to_load_field_options_from_request_data ) ) {
192 return self::$context_is_safe_to_load_field_options_from_request_data;
193 }
194
195 $function = function () {
196 if ( ! FrmAppHelper::is_admin_page() ) {
197 return false;
198 }
199
200 if ( ! $_POST || ! isset( $_POST['field_options'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
201 return false;
202 }
203
204 if ( ! current_user_can( 'frm_edit_forms' ) ) {
205 return false;
206 }
207
208 $action = FrmAppHelper::get_post_param( 'action', '', 'sanitize_title' );
209 if ( 'frm_forms_preview' === $action ) {
210 // Never trigger when previewing.
211 return false;
212 }
213
214 // Confirm an allowed action is being used, and that the correct nonce is being used.
215 if ( 'update' === $action ) {
216 $nonce = FrmAppHelper::get_post_param( 'frm_save_form', '', 'sanitize_text_field' );
217 return wp_verify_nonce( $nonce, 'frm_save_form_nonce' );
218 }
219
220 $action = FrmAppHelper::get_post_param( 'frm_action', '', 'sanitize_title' );
221 if ( 'update_settings' === $action ) {
222 $nonce = FrmAppHelper::get_post_param( 'process_form', '', 'sanitize_text_field' );
223 return wp_verify_nonce( $nonce, 'process_form_nonce' );
224 }
225 };
226
227 self::$context_is_safe_to_load_field_options_from_request_data = $function();
228
229 return self::$context_is_safe_to_load_field_options_from_request_data;
230 }
231
232 /**
233 * Fill the required message, invalid message,
234 * and refill the HTML when cleared
235 *
236 * @since 3.0
237 *
238 * @param object $field
239 * @param array $field_array
240 */
241 private static function fill_cleared_strings( $field, array &$field_array ) {
242 if ( '' == $field_array['blank'] && '1' === $field_array['required'] ) {
243 $field_array['blank'] = self::default_blank_msg();
244 }
245
246 if ( '' === $field_array['invalid'] ) {
247 if ( 'captcha' === $field->type ) {
248 $frm_settings = FrmAppHelper::get_settings();
249 $field_array['invalid'] = $frm_settings->re_msg;
250 } else {
251 $field_array['invalid'] = self::default_invalid_msg();
252 }
253 }
254
255 if ( '' == $field_array['custom_html'] ) {
256 $field_array['custom_html'] = self::get_default_html( $field->type );
257 }
258 }
259
260 /**
261 * @since 6.8.3
262 *
263 * @return string
264 */
265 public static function default_invalid_msg() {
266 /* translators: %s: [field_name] shortcode (Which gets replaced by a Field Name) */
267 return sprintf( __( '%s is invalid', 'formidable' ), '[field_name]' );
268 }
269
270 /**
271 * @since 6.8.3
272 *
273 * @return string
274 */
275 public static function default_unique_msg() {
276 $frm_settings = FrmAppHelper::get_settings();
277 $unique_message = $frm_settings->unique_msg;
278 $unique_message = str_replace( 'This value', '[field_name]', $unique_message );
279 return $unique_message;
280 }
281
282 /**
283 * @since 6.8.3
284 *
285 * @return string
286 */
287 public static function default_blank_msg() {
288 $frm_settings = FrmAppHelper::get_settings();
289 $blank_message = $frm_settings->blank_msg;
290 $blank_message = str_replace( 'This field', '[field_name]', $blank_message );
291 return $blank_message;
292 }
293
294 /**
295 * When loading settings for a field, check the $_POST data and possibly use that instead of the DB value.
296 *
297 * @since 3.0
298 *
299 * @param string $setting
300 * @param mixed $value
301 */
302 private static function get_posted_field_setting( $setting, &$value ) {
303 if ( ! isset( $_POST['field_options'][ $setting ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
304 return;
305 }
306
307 if ( strpos( $setting, 'html' ) !== false ) {
308 $value = wp_unslash( $_POST['field_options'][ $setting ] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing
309
310 // Conditionally strip script tags if the user sending $_POST data is not allowed to use unfiltered HTML.
311 if ( ! FrmAppHelper::allow_unfiltered_html() ) {
312 $value = FrmAppHelper::kses( $value, 'all' );
313 }
314 } elseif ( strpos( $setting, 'format_' ) === 0 ) {
315 // TODO: Remove stripslashes on output, and use on input only.
316 $value = sanitize_text_field( $_POST['field_options'][ $setting ] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.NonceVerification.Missing
317 } else {
318 $value = wp_unslash( $_POST['field_options'][ $setting ] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing
319 FrmAppHelper::sanitize_value( 'wp_kses_post', $value );
320 }
321 }
322
323 /**
324 * @since 3.0
325 *
326 * @param object $field
327 * @param array $values The field array is needed for hooks.
328 *
329 * @return array
330 */
331 public static function get_default_field_options_from_field( $field, $values = array() ) {
332 $field_type = self::get_original_field( $field );
333 $opts = $field_type->get_default_field_options();
334
335 $opts = apply_filters( 'frm_default_field_opts', $opts, $values, $field );
336 $opts = apply_filters( 'frm_default_' . $field->type . '_field_opts', $opts, $values, $field );
337
338 return $opts;
339 }
340
341 /**
342 * @since 3.0
343 *
344 * @param object $field
345 *
346 * @return FrmFieldType
347 */
348 private static function get_original_field( $field ) {
349 $original_type = FrmField::get_option( $field, 'original_type' );
350 if ( ! empty( $original_type ) && $field->type != $original_type ) {
351 $field->type = $original_type;
352 }
353
354 return FrmFieldFactory::get_field_object( $field );
355 }
356
357 /**
358 * @since 3.0
359 *
360 * @param array $field_array
361 * @param object $field
362 * @param array $atts
363 */
364 private static function prepare_field_options_for_display( &$field_array, $field, $atts ) {
365 $field_obj = FrmFieldFactory::get_field_object( $field );
366 $field_array = $field_obj->prepare_front_field( $field_array, $atts );
367 }
368
369 /**
370 * @since 3.0
371 *
372 * @param string $type
373 *
374 * @return array
375 */
376 public static function get_default_field( $type ) {
377 $field_type = FrmFieldFactory::get_field_type( $type );
378
379 return $field_type->get_new_field_defaults();
380 }
381
382 public static function fill_field( &$values, $field, $form_id, $new_key = '' ) {
383 global $wpdb;
384
385 $values['field_key'] = FrmAppHelper::get_unique_key( $new_key, $wpdb->prefix . 'frm_fields', 'field_key' );
386 $values['form_id'] = $form_id;
387 $values['options'] = maybe_serialize( $field->options );
388 $values['default_value'] = FrmAppHelper::maybe_json_encode( $field->default_value );
389
390 foreach ( array( 'name', 'description', 'type', 'field_order', 'field_options', 'required' ) as $col ) {
391 $values[ $col ] = $field->{$col};
392 }
393 }
394
395 /**
396 * @since 2.0
397 *
398 * @param array|object $field
399 * @param string $error
400 *
401 * @return string
402 */
403 public static function get_error_msg( $field, $error ) {
404 $frm_settings = FrmAppHelper::get_settings();
405
406 $conf_msg = __( 'The entered values do not match', 'formidable' );
407 $defaults = array(
408 'unique_msg' => array(
409 'full' => self::default_unique_msg(),
410 /* translators: %s: Field name */
411 'part' => sprintf( __( '%s must be unique', 'formidable' ), '[field_name]' ),
412 ),
413 'invalid' => array(
414 'full' => __( 'This field is invalid', 'formidable' ),
415 /* translators: %s: Field name */
416 'part' => sprintf( __( '%s is invalid', 'formidable' ), '[field_name]' ),
417 ),
418 'blank' => array(
419 'full' => $frm_settings->blank_msg,
420 'part' => $frm_settings->blank_msg,
421 ),
422 'conf_msg' => array(
423 'full' => $conf_msg,
424 'part' => $conf_msg,
425 ),
426 );
427
428 $msg = FrmField::get_option( $field, $error );
429 $msg = empty( $msg ) ? $defaults[ $error ]['part'] : $msg;
430 $msg = do_shortcode( $msg );
431
432 $msg = self::maybe_replace_substrings_with_field_name( $msg, $error, $field );
433
434 return $msg;
435 }
436
437 /**
438 * @since 6.8.3
439 *
440 * @param string $msg
441 * @param string $error
442 * @param array|object $field
443 */
444 private static function maybe_replace_substrings_with_field_name( $msg, $error, $field ) {
445 $field_name = is_array( $field ) ? $field['name'] : $field->name;
446 $field_name = FrmAppHelper::maybe_kses( $field_name );
447 $substrings = self::get_substrings_to_replace_with_field_name( $field_name, compact( 'msg', 'error', 'field' ) );
448
449 // Use the "This value"/"This field" placeholder strings if field name is empty.
450 if ( ! $field_name ) {
451 if ( 'unique_msg' === $error ) {
452 $field_name = __( 'This value', 'formidable' );
453 } else {
454 $field_name = __( 'This field', 'formidable' );
455 }
456 }
457
458 $msg = str_replace( $substrings, $field_name, $msg );
459 return $msg;
460 }
461
462 /**
463 * @since 6.8.3
464 *
465 * @param string $field_name
466 * @param array $filter_args {
467 * Filter arguments.
468 *
469 * @type string $msg The current error message before the substrings are replaced.
470 * @type string $error A key including 'unique_msg', 'invalid', 'blank', or 'conf_msg'.
471 * @type array|object $field The field with the error.
472 * }
473 * @return array
474 */
475 private static function get_substrings_to_replace_with_field_name( $field_name, $filter_args ) {
476 $substrings = array( '[field_name]' );
477 if ( $field_name ) {
478 array_push( $substrings, 'This value', 'This field' );
479 }
480
481 /**
482 * @since 6.8.3
483 *
484 * @param array<string> $substrings
485 * @param array $filter_args
486 */
487 $filtered_substrings = apply_filters( 'frm_error_substrings_to_replace_with_field_name', $substrings, $filter_args );
488
489 if ( is_array( $filtered_substrings ) ) {
490 $substrings = $filtered_substrings;
491 } else {
492 _doing_it_wrong( __METHOD__, 'Only arrays should be returned when using the frm_error_substrings_to_replace_with_field_name filter.', '6.8.3' );
493 }
494
495 return $substrings;
496 }
497
498 public static function get_form_fields( $form_id, $error = array() ) {
499 $fields = FrmField::get_all_for_form( $form_id );
500
501 return apply_filters( 'frm_get_paged_fields', $fields, $form_id, $error );
502 }
503
504 public static function get_default_html( $type = 'text' ) {
505 $field = FrmFieldFactory::get_field_type( $type );
506 $default_html = $field->default_html();
507
508 // these hooks are here for reverse compatibility since 3.0
509 if ( ! apply_filters( 'frm_normal_field_type_html', true, $type ) ) {
510 $default_html = apply_filters( 'frm_other_custom_html', '', $type );
511 }
512
513 return apply_filters( 'frm_custom_html', $default_html, $type );
514 }
515
516 /**
517 * @param array $fields
518 * @param array $errors
519 * @param object $form
520 * @param object $form_action
521 */
522 public static function show_fields( $fields, $errors, $form, $form_action ) {
523 foreach ( $fields as $field ) {
524 $field_obj = FrmFieldFactory::get_field_type( $field['type'], $field );
525 $field_obj->show_field( compact( 'errors', 'form', 'form_action' ) );
526 }
527 }
528
529 /**
530 * @since 3.0
531 *
532 * @param array $atts
533 * @param array|string $value
534 */
535 public static function run_wpautop( $atts, &$value ) {
536 $autop = isset( $atts['wpautop'] ) ? $atts['wpautop'] : true;
537 if ( apply_filters( 'frm_use_wpautop', $autop ) ) {
538 if ( is_array( $value ) ) {
539 $value = implode( "\n", $value );
540 }
541 $value = wpautop( $value );
542 }
543 }
544
545 /**
546 * Get the class to use for the label position
547 *
548 * @since 2.05
549 */
550 public static function &label_position( $position, $field, $form ) {
551 if ( $position && $position != '' ) {
552 if ( $position === 'inside' && ! self::is_placeholder_field_type( $field['type'] ) ) {
553 $position = 'top';
554 }
555
556 return $position;
557 }
558
559 $position = FrmStylesController::get_style_val( 'position', $form );
560 if ( $position === 'none' ) {
561 $position = 'top';
562 } elseif ( $position === 'no_label' ) {
563 $position = 'none';
564 } elseif ( $position === 'inside' && ! self::is_placeholder_field_type( $field['type'] ) ) {
565 $position = 'top';
566 }
567
568 $position = apply_filters( 'frm_html_label_position', $position, $field, $form );
569 $position = ! empty( $position ) ? $position : 'top';
570
571 return $position;
572 }
573
574 /**
575 * Check if this field type allows placeholders
576 *
577 * @since 2.05
578 * @param string $type
579 * @return bool
580 */
581 public static function is_placeholder_field_type( $type ) {
582 return ! in_array( $type, array( 'radio', 'checkbox', 'hidden', 'file' ), true );
583 }
584
585 public static function get_checkbox_id( $field, $opt_key, $type = 'checkbox' ) {
586 $id = $field['id'];
587 if ( isset( $field['in_section'] ) && $field['in_section'] && ! FrmAppHelper::is_admin_page( 'formidable' ) ) {
588 $id .= '-' . $field['in_section'];
589 }
590
591 return 'frm_' . $type . '_' . $id . '-' . $opt_key;
592 }
593
594 public static function show_single_option( $field ) {
595 self::hidden_field_option( $field );
596
597 if ( ! is_array( $field['options'] ) ) {
598 return;
599 }
600
601 $base_name = 'default_value_' . $field['id'];
602 $html_id = isset( $field['html_id'] ) ? $field['html_id'] : self::get_html_id( $field );
603
604 $default_type = self::get_default_value_type( $field );
605
606 foreach ( $field['options'] as $opt_key => $opt ) {
607 $field_val = self::get_value_from_array( $opt, $opt_key, $field );
608 $opt = self::get_label_from_array( $opt, $opt_key, $field );
609
610 $field_name = $base_name . ( $default_type === 'checkbox' ? '[' . $opt_key . ']' : '' );
611
612 $checked = ( isset( $field['default_value'] ) && ( ( ! is_array( $field['default_value'] ) && $field['default_value'] == $field_val ) || ( is_array( $field['default_value'] ) && in_array( $field_val, $field['default_value'] ) ) ) );
613
614 // If this is an "Other" option, get the HTML for it.
615 if ( self::is_other_opt( $opt_key ) ) {
616 if ( FrmAppHelper::pro_is_installed() ) {
617 require FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/other-option.php';
618 }
619 } else {
620 require FrmAppHelper::plugin_path() . '/classes/views/frm-fields/single-option.php';
621 }
622
623 unset( $checked );
624 }
625 }
626
627 /**
628 * Include hidden row for javascript to duplicate.
629 *
630 * @since 4.0
631 * @param array $field
632 */
633 private static function hidden_field_option( $field ) {
634 // Don't duplicate during an ajax add option.
635 $ajax_action = FrmAppHelper::get_param( 'action', '', 'post', 'sanitize_text_field' );
636 if ( $ajax_action === 'frm_add_field_option' ) {
637 return;
638 }
639
640 $opt_key = '000';
641 $field_val = __( 'New Option', 'formidable' );
642 $opt = __( 'New Option', 'formidable' );
643 $checked = false;
644 $field_name = 'default_value_' . $field['id'];
645 $html_id = isset( $field['html_id'] ) ? $field['html_id'] : self::get_html_id( $field );
646
647 $default_type = self::get_default_value_type( $field );
648 $field_name .= ( $default_type === 'checkbox' ? '[' . $opt_key . ']' : '' );
649
650 require FrmAppHelper::plugin_path() . '/classes/views/frm-fields/single-option.php';
651 }
652
653 /**
654 * @since 4.0
655 *
656 * @param array $field
657 * @return string radio or checkbox
658 */
659 private static function get_default_value_type( $field ) {
660 $default_type = $field['type'];
661 if ( $field['type'] === 'select' ) {
662 $default_type = FrmField::is_multiple_select( $field ) ? 'checkbox' : 'radio';
663 }
664 return $default_type;
665 }
666
667 public static function get_value_from_array( $opt, $opt_key, $field ) {
668 $opt = apply_filters( 'frm_field_value_saved', $opt, $opt_key, $field );
669
670 return FrmFieldsController::check_value( $opt, $opt_key, $field );
671 }
672
673 public static function get_label_from_array( $opt, $opt_key, $field ) {
674 $opt = apply_filters( 'frm_field_label_seen', $opt, $opt_key, $field );
675
676 return FrmFieldsController::check_label( $opt );
677 }
678
679 /**
680 * Shows the inline modal.
681 *
682 * @since 4.0
683 * @since 6.4.1 Added `inside_class` in the arguments.
684 *
685 * @param array $args The arguments.
686 */
687 public static function inline_modal( $args ) {
688 $defaults = array(
689 'id' => '',
690 'class' => '',
691 'show' => 0,
692 'callback' => array(),
693 'args' => array(),
694 'title' => '',
695 'inside_class' => 'inside',
696 );
697 $args = array_merge( $defaults, $args );
698
699 include FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/inline-modal.php';
700 }
701
702 /**
703 * @since 4.0
704 */
705 public static function smart_values() {
706 $continue = apply_filters( 'frm_smart_values_box', true );
707 if ( $continue === true ) {
708 $upgrade_link = array(
709 'medium' => 'builder',
710 'content' => 'smart-tags',
711 );
712 include FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/smart-values.php';
713 }
714 }
715
716 /**
717 * @since 4.0
718 */
719 public static function input_mask() {
720 include FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/input-mask-info.php';
721 }
722
723 /**
724 * @since 4.0
725 */
726 public static function layout_classes() {
727 include FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/layout-classes.php';
728 }
729
730 /**
731 * @param int $tax_id
732 *
733 * @return string
734 */
735 public static function get_term_link( $tax_id ) {
736 $tax = get_taxonomy( $tax_id );
737 if ( ! $tax ) {
738 return '';
739 }
740
741 $link = sprintf(
742 /* translators: %1$s: Start HTML link, %2$s: Content type label, %3$s: Content type, %4$s: End HTML link */
743 esc_html__( 'Options are dynamically created from your %1$s%2$s: %3$s%4$s', 'formidable' ),
744 '<a href="' . esc_url( admin_url( 'edit-tags.php?taxonomy=' . $tax->name ) ) . '" target="_blank">',
745 esc_html__( 'taxonomy', 'formidable' ),
746 empty( $tax->labels->name ) ? esc_html__( 'Categories', 'formidable' ) : $tax->labels->name,
747 '</a>'
748 );
749 unset( $tax );
750
751 return $link;
752 }
753
754 public static function value_meets_condition( $observed_value, $cond, $hide_opt ) {
755 $hide_opt = self::get_value_for_comparison( $hide_opt );
756 $observed_value = self::get_value_for_comparison( $observed_value );
757
758 if ( is_array( $observed_value ) ) {
759 return self::array_value_condition( $observed_value, $cond, $hide_opt );
760 }
761
762 $m = false;
763 if ( $cond === '==' ) {
764 $m = $observed_value == $hide_opt;
765 } elseif ( $cond === '!=' ) {
766 $m = $observed_value != $hide_opt;
767 } elseif ( $cond === '>' ) {
768 $m = $observed_value > $hide_opt;
769 } elseif ( $cond === '>=' ) {
770 $m = $observed_value >= $hide_opt;
771 } elseif ( $cond === '<' ) {
772 $m = $observed_value < $hide_opt;
773 } elseif ( $cond === '<=' ) {
774 $m = $observed_value <= $hide_opt;
775 } elseif ( $cond === 'LIKE' || $cond === 'not LIKE' ) {
776 $m = stripos( $observed_value, $hide_opt );
777 if ( $cond === 'not LIKE' ) {
778 $m = $m === false;
779 } else {
780 $m = $m !== false;
781 }
782 } elseif ( $cond === '%LIKE' ) {
783 // ends with
784 $length = strlen( $hide_opt );
785 $substr = substr( $observed_value, strlen( $observed_value ) - $length );
786 $m = 0 === strcasecmp( $substr, $hide_opt );
787 } elseif ( 'LIKE%' === $cond ) {
788 // starts with
789 $length = strlen( $hide_opt );
790 $substr = substr( $observed_value, 0, $length );
791 $m = 0 === strcasecmp( $substr, $hide_opt );
792 }//end if
793
794 return $m;
795 }
796
797 /**
798 * Trim and sanitize the values
799 *
800 * @since 2.05
801 */
802 private static function get_value_for_comparison( $value ) {
803 // Remove white space from hide_opt
804 if ( ! is_array( $value ) ) {
805 $value = trim( $value );
806 }
807
808 FrmAppHelper::sanitize_value( 'wp_kses_post', $value );
809
810 return $value;
811 }
812
813 public static function array_value_condition( $observed_value, $cond, $hide_opt ) {
814 $m = false;
815 if ( $cond === '==' ) {
816 if ( is_array( $hide_opt ) ) {
817 $m = array_intersect( $hide_opt, $observed_value );
818 $m = ! empty( $m );
819 } else {
820 $m = in_array( $hide_opt, $observed_value );
821 }
822 } elseif ( $cond === '!=' ) {
823 $m = ! in_array( $hide_opt, $observed_value );
824 } elseif ( $cond === '>' ) {
825 $min = min( $observed_value );
826 $m = $min > $hide_opt;
827 } elseif ( $cond === '<' ) {
828 $max = max( $observed_value );
829 $m = $max < $hide_opt;
830 } elseif ( $cond === 'LIKE' || $cond === 'not LIKE' ) {
831 foreach ( $observed_value as $ob ) {
832 $m = strpos( $ob, $hide_opt );
833 if ( $m !== false ) {
834 $m = true;
835 break;
836 }
837 }
838
839 if ( $cond === 'not LIKE' ) {
840 $m = $m === false;
841 }
842 } elseif ( $cond === '%LIKE' ) {
843 // ends with
844 foreach ( $observed_value as $ob ) {
845 if ( $hide_opt === substr( $ob, strlen( $ob ) - strlen( $hide_opt ) ) ) {
846 $m = true;
847 break;
848 }
849 }
850 } elseif ( $cond === 'LIKE%' ) {
851 // starts with
852 foreach ( $observed_value as $ob ) {
853 if ( $hide_opt === substr( $ob, 0, strlen( $hide_opt ) ) ) {
854 $m = true;
855 break;
856 }
857 }
858 }//end if
859
860 return $m;
861 }
862
863 /**
864 * Replace a few basic shortcodes and field ids
865 *
866 * @since 2.0
867 * @return string
868 */
869 public static function basic_replace_shortcodes( $value, $form, $entry ) {
870 if ( strpos( $value, '[sitename]' ) !== false ) {
871 $new_value = wp_specialchars_decode( FrmAppHelper::site_name(), ENT_QUOTES );
872 $value = str_replace( '[sitename]', $new_value, $value );
873 }
874
875 $value = apply_filters( 'frm_content', $value, $form, $entry );
876 $value = do_shortcode( $value );
877
878 return $value;
879 }
880
881 public static function get_shortcodes( $content, $form_id ) {
882 if ( FrmAppHelper::pro_is_installed() ) {
883 return FrmProDisplaysHelper::get_shortcodes( $content, $form_id );
884 }
885
886 $fields = FrmField::getAll(
887 array(
888 'fi.form_id' => (int) $form_id,
889 'fi.type not' => FrmField::no_save_fields(),
890 )
891 );
892
893 $tagregexp = self::allowed_shortcodes( $fields );
894
895 preg_match_all( "/\[(if )?($tagregexp)\b(.*?)(?:(\/))?\](?:(.+?)\[\/\2\])?/s", $content, $matches, PREG_PATTERN_ORDER );
896
897 return $matches;
898 }
899
900 public static function allowed_shortcodes( $fields = array() ) {
901 $tagregexp = array(
902 'editlink',
903 'id',
904 'key',
905 'ip',
906 'siteurl',
907 'sitename',
908 'admin_email',
909 'default-email',
910 'default-from-email',
911 'post[-|_]id',
912 'created[-|_]at',
913 'updated[-|_]at',
914 'updated[-|_]by',
915 'parent[-|_]id',
916 'form_name',
917 );
918
919 foreach ( $fields as $field ) {
920 $tagregexp[] = $field->id;
921 $tagregexp[] = $field->field_key;
922 }
923
924 $tagregexp = implode( '|', $tagregexp );
925
926 return $tagregexp;
927 }
928
929 public static function replace_content_shortcodes( $content, $entry, $shortcodes ) {
930 foreach ( $shortcodes[0] as $short_key => $tag ) {
931 if ( empty( $tag ) ) {
932 continue;
933 }
934
935 $atts = FrmShortcodeHelper::get_shortcode_attribute_array( $shortcodes[3][ $short_key ] );
936 $tag = FrmShortcodeHelper::get_shortcode_tag( $shortcodes, $short_key );
937
938 $atts['entry'] = $entry;
939 $atts['tag'] = $tag;
940 $replace_with = self::get_value_for_shortcode( $atts );
941
942 if ( $replace_with !== null ) {
943 $replace_with = self::trigger_shortcode_atts( $replace_with, $atts );
944 self::sanitize_embedded_shortcodes( compact( 'entry' ), $replace_with );
945 $content = str_replace( $shortcodes[0][ $short_key ], $replace_with, $content );
946 }
947
948 unset( $atts, $replace_with );
949 }
950
951 return $content;
952 }
953
954 /**
955 * @param string $replace_with
956 * @param array $atts
957 * @return string
958 */
959 private static function trigger_shortcode_atts( $replace_with, $atts ) {
960 $supported_atts = array( 'remove_accents', 'sanitize', 'sanitize_url' );
961 $included_atts = array_intersect( $supported_atts, array_keys( $atts ) );
962 foreach ( $included_atts as $included_att ) {
963 if ( '0' === $atts[ $included_att ] ) {
964 // Skip any option that uses 0 so sanitize_url=0 does not encode.
965 continue;
966 }
967 $function = 'atts_' . $included_att;
968 $replace_with = self::$function( $replace_with, $atts );
969 }
970 return $replace_with;
971 }
972
973 /**
974 * Converts all accent characters to ASCII characters.
975 *
976 * @since 6.3.1
977 *
978 * @param string $replace_with The text to remove accents from.
979 *
980 * @return string
981 */
982 public static function atts_remove_accents( $replace_with ) {
983 return remove_accents( $replace_with );
984 }
985
986 /**
987 * @param string $replace_with
988 * @return string
989 */
990 private static function atts_sanitize( $replace_with ) {
991 return sanitize_title_with_dashes( $replace_with );
992 }
993
994 /**
995 * @param string $replace_with
996 * @return string
997 */
998 private static function atts_sanitize_url( $replace_with ) {
999 return urlencode( $replace_with );
1000 }
1001
1002 /**
1003 * Prevent shortcodes in fields from being processed
1004 *
1005 * @since 3.01.02
1006 *
1007 * @param array $atts Includes entry object.
1008 * @param string|null $value
1009 * @return void
1010 */
1011 public static function sanitize_embedded_shortcodes( $atts, &$value ) {
1012 if ( is_null( $value ) ) {
1013 return;
1014 }
1015
1016 $atts['value'] = $value;
1017 $should_sanitize = apply_filters( 'frm_sanitize_shortcodes', true, $atts );
1018 if ( $should_sanitize ) {
1019 $value = str_replace( '[', '&#91;', $value );
1020 }
1021 }
1022
1023 /**
1024 * @since 3.0
1025 *
1026 * @param array $atts
1027 *
1028 * @return string
1029 */
1030 private static function get_value_for_shortcode( $atts ) {
1031 $clean_tag = str_replace( '-', '_', $atts['tag'] );
1032
1033 $shortcode_values = array(
1034 'id' => $atts['entry']->id,
1035 'key' => $atts['entry']->item_key,
1036 'ip' => $atts['entry']->ip,
1037 );
1038
1039 $dynamic_default = array( 'admin_email', 'siteurl', 'frmurl', 'sitename', 'get', 'default-email', 'default-from-email' );
1040
1041 if ( isset( $shortcode_values[ $atts['tag'] ] ) ) {
1042 $replace_with = $shortcode_values[ $atts['tag'] ];
1043 } elseif ( in_array( $atts['tag'], $dynamic_default, true ) ) {
1044 $replace_with = self::dynamic_default_values( $atts['tag'], $atts );
1045 } elseif ( $clean_tag === 'user_agent' ) {
1046 $description = $atts['entry']->description;
1047 $replace_with = FrmEntriesHelper::get_browser( $description['browser'] );
1048 } elseif ( $clean_tag === 'created_at' || $clean_tag === 'updated_at' ) {
1049 $atts['tag'] = $clean_tag;
1050 $replace_with = self::get_entry_timestamp( $atts );
1051 } elseif ( $clean_tag === 'created_by' || $clean_tag === 'updated_by' ) {
1052 $replace_with = self::get_display_value( $atts['entry']->{$clean_tag}, (object) array( 'type' => 'user_id' ), $atts );
1053 } else {
1054 $replace_with = self::get_field_shortcode_value( $atts );
1055 }
1056
1057 return $replace_with;
1058 }
1059
1060 /**
1061 * @since 3.0
1062 *
1063 * @param array $atts
1064 *
1065 * @return string
1066 */
1067 private static function get_entry_timestamp( $atts ) {
1068 if ( isset( $atts['format'] ) ) {
1069 $time_format = ' ';
1070 } else {
1071 $atts['format'] = get_option( 'date_format' );
1072 $time_format = '';
1073 }
1074
1075 return FrmAppHelper::get_formatted_time( $atts['entry']->{$atts['tag']}, $atts['format'], $time_format );
1076 }
1077
1078 /**
1079 * @since 3.0
1080 *
1081 * @param array $atts
1082 *
1083 * @return string|null
1084 */
1085 private static function get_field_shortcode_value( $atts ) {
1086 $field = FrmField::getOne( $atts['tag'] );
1087 if ( empty( $field ) ) {
1088 return null;
1089 }
1090
1091 if ( isset( $atts['show'] ) && $atts['show'] === 'field_label' ) {
1092 $replace_with = $field->name;
1093 } elseif ( isset( $atts['show'] ) && $atts['show'] === 'description' ) {
1094 $replace_with = $field->description;
1095 } else {
1096 $replace_with = FrmEntryMeta::get_meta_value( $atts['entry'], $field->id );
1097 $string_value = $replace_with;
1098 if ( is_array( $replace_with ) ) {
1099 $sep = isset( $atts['sep'] ) ? $atts['sep'] : ', ';
1100 $string_value = FrmAppHelper::safe_implode( $sep, $replace_with );
1101 }
1102
1103 if ( empty( $string_value ) && $string_value != '0' ) {
1104 $replace_with = '';
1105 } else {
1106 $atts['entry_id'] = $atts['entry']->id;
1107 $atts['entry_key'] = $atts['entry']->item_key;
1108 $replace_with = self::get_display_value( $replace_with, $field, $atts );
1109 }
1110 }
1111
1112 return $replace_with;
1113 }
1114
1115 /**
1116 * Get the value to replace a few standard shortcodes
1117 *
1118 * @since 2.0
1119 * @return string
1120 */
1121 public static function dynamic_default_values( $tag, $atts = array(), $return_array = false ) {
1122 $new_value = '';
1123 switch ( $tag ) {
1124 case 'admin_email':
1125 $new_value = get_option( 'admin_email' );
1126 break;
1127 case 'default-email':
1128 $frm_settings = FrmAppHelper::get_settings();
1129 $new_value = ! empty( $frm_settings->default_email ) && is_email( $frm_settings->default_email ) ? $frm_settings->default_email : get_option( 'admin_email' );
1130 break;
1131 case 'default-from-email':
1132 $frm_settings = FrmAppHelper::get_settings();
1133 $new_value = ! empty( $frm_settings->from_email ) && is_email( $frm_settings->from_email ) ? $frm_settings->from_email : get_option( 'admin_email' );
1134 break;
1135 case 'siteurl':
1136 $new_value = FrmAppHelper::site_url();
1137 break;
1138 case 'frmurl':
1139 $new_value = FrmAppHelper::plugin_url();
1140 break;
1141 case 'sitename':
1142 $new_value = FrmAppHelper::site_name();
1143 break;
1144 case 'get':
1145 $new_value = self::process_get_shortcode( $atts, $return_array );
1146 }//end switch
1147
1148 return $new_value;
1149 }
1150
1151 /**
1152 * Process the [get] shortcode
1153 *
1154 * @since 2.0
1155 * @return array|string
1156 */
1157 public static function process_get_shortcode( $atts, $return_array = false ) {
1158 if ( ! isset( $atts['param'] ) ) {
1159 return '';
1160 }
1161
1162 if ( strpos( $atts['param'], '&#91;' ) ) {
1163 $atts['param'] = str_replace( '&#91;', '[', $atts['param'] );
1164 $atts['param'] = str_replace( '&#93;', ']', $atts['param'] );
1165 }
1166
1167 $new_value = FrmAppHelper::get_param( $atts['param'], '', 'get', 'sanitize_text_field' );
1168 $new_value = FrmAppHelper::get_query_var( $new_value, $atts['param'] );
1169
1170 if ( $new_value == '' ) {
1171 if ( ! isset( $atts['prev_val'] ) ) {
1172 $atts['prev_val'] = '';
1173 }
1174
1175 $new_value = isset( $atts['default'] ) ? $atts['default'] : $atts['prev_val'];
1176 }
1177
1178 if ( is_array( $new_value ) && ! $return_array ) {
1179 $new_value = implode( ', ', $new_value );
1180 }
1181
1182 return $new_value;
1183 }
1184
1185 public static function get_display_value( $value, $field, $atts = array() ) {
1186
1187 $value = apply_filters( 'frm_get_' . $field->type . '_display_value', $value, $field, $atts );
1188 $value = apply_filters( 'frm_get_display_value', $value, $field, $atts );
1189
1190 $value = self::get_unfiltered_display_value( compact( 'value', 'field', 'atts' ) );
1191
1192 return apply_filters( 'frm_display_value', $value, $field, $atts );
1193 }
1194
1195 /**
1196 * @param array $atts Includes value, field, and atts.
1197 */
1198 public static function get_unfiltered_display_value( $atts ) {
1199 $value = $atts['value'];
1200 $field = $atts['field'];
1201 $atts = isset( $atts['atts'] ) ? $atts['atts'] : $atts;
1202
1203 if ( is_array( $field ) ) {
1204 $field = $field['id'];
1205 }
1206
1207 $field_obj = FrmFieldFactory::get_field_object( $field );
1208
1209 return $field_obj->get_display_value( $value, $atts );
1210 }
1211
1212 /**
1213 * Get a value from the user profile from the user ID
1214 *
1215 * @since 3.0
1216 *
1217 * @return string
1218 */
1219 public static function get_user_display_name( $user_id, $user_info = 'display_name', $args = array() ) {
1220 $defaults = array(
1221 'blank' => false,
1222 'link' => false,
1223 'size' => 96,
1224 );
1225
1226 $args = wp_parse_args( $args, $defaults );
1227
1228 $user = get_userdata( $user_id );
1229 $info = '';
1230
1231 if ( $user ) {
1232 if ( $user_info === 'avatar' ) {
1233 $info = get_avatar( $user_id, $args['size'] );
1234 } elseif ( $user_info === 'author_link' ) {
1235 $info = get_author_posts_url( $user_id );
1236 } else {
1237 $info = isset( $user->$user_info ) ? $user->$user_info : '';
1238 }
1239
1240 if ( 'display_name' === $user_info && empty( $info ) && ! $args['blank'] ) {
1241 $info = $user->user_login;
1242 }
1243 }
1244
1245 if ( $args['link'] ) {
1246 $info = '<a href="' . esc_url( admin_url( 'user-edit.php?user_id=' . $user_id ) ) . '">' . $info . '</a>';
1247 }
1248
1249 return $info;
1250 }
1251
1252 /**
1253 * @param string $type
1254 * @return array
1255 */
1256 public static function get_field_types( $type ) {
1257 $single_input = self::single_input_fields();
1258 $multiple_input = array( 'radio', 'checkbox', 'select', 'scale', 'star', 'lookup' );
1259 $field_selection = FrmField::all_field_selection();
1260 $field_types = array();
1261
1262 if ( in_array( $type, $single_input, true ) ) {
1263 self::field_types_for_input( $single_input, $field_selection, $field_types );
1264 } elseif ( in_array( $type, $multiple_input, true ) ) {
1265 self::field_types_for_input( $multiple_input, $field_selection, $field_types );
1266 } elseif ( isset( $field_selection[ $type ] ) ) {
1267 $field_types[ $type ] = $field_selection[ $type ];
1268 }
1269
1270 $field_types = apply_filters( 'frm_switch_field_types', $field_types, compact( 'type', 'field_selection' ) );
1271
1272 return $field_types;
1273 }
1274
1275 /**
1276 * Get a list of all fields that use a single value input.
1277 *
1278 * @since 4.0
1279 */
1280 public static function single_input_fields() {
1281 $fields = array(
1282 'text',
1283 'textarea',
1284 'rte',
1285 'number',
1286 'email',
1287 'url',
1288 'date',
1289 'phone',
1290 'hidden',
1291 'time',
1292 'tag',
1293 'password',
1294 'gdpr',
1295 );
1296 return apply_filters( 'frm_single_input_fields', $fields );
1297 }
1298
1299 /**
1300 * @param string[] $inputs
1301 * @param array $fields
1302 * @param array $field_types
1303 * @return void
1304 */
1305 private static function field_types_for_input( $inputs, $fields, &$field_types ) {
1306 foreach ( $inputs as $input ) {
1307 // This may not be set if a field type was removed using the frm_available_fields or frm_pro_available_fields filters.
1308 if ( array_key_exists( $input, $fields ) ) {
1309 $field_types[ $input ] = $fields[ $input ];
1310 }
1311 unset( $input );
1312 }
1313 }
1314
1315 /**
1316 * Check if current field option is an "other" option
1317 *
1318 * @since 2.0.6
1319 *
1320 * @param string $opt_key
1321 *
1322 * @return bool Returns true if current field option is an "Other" option
1323 */
1324 public static function is_other_opt( $opt_key ) {
1325 return $opt_key && strpos( $opt_key, 'other_' ) === 0;
1326 }
1327
1328 /**
1329 * Get value that belongs in "Other" text box
1330 *
1331 * @since 2.0.6
1332 *
1333 * @param array $args
1334 */
1335 public static function get_other_val( $args ) {
1336 $defaults = array(
1337 'opt_key' => 0,
1338 'field' => array(),
1339 'parent' => false,
1340 'pointer' => false,
1341 );
1342 $args = wp_parse_args( $args, $defaults );
1343
1344 $opt_key = $args['opt_key'];
1345 $field = $args['field'];
1346 $parent = $args['parent'];
1347 $pointer = $args['pointer'];
1348 $other_val = '';
1349
1350 // If option is an "other" option and there is a value set for this field,
1351 // check if the value belongs in the current "Other" option text field
1352 if ( ! self::is_other_opt( $opt_key ) || ! FrmField::is_option_true( $field, 'value' ) ) {
1353 return $other_val;
1354 }
1355
1356 // Check posted vals before checking saved values
1357 // For fields inside repeating sections - note, don't check if $pointer is true because it will often be zero
1358 if ( $parent && isset( $_POST['item_meta'][ $parent ][ $pointer ]['other'][ $field['id'] ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
1359 if ( FrmField::is_field_with_multiple_values( $field ) ) {
1360 // phpcs:ignore WordPress.Security.NonceVerification.Missing
1361 $other_val = isset( $_POST['item_meta'][ $parent ][ $pointer ]['other'][ $field['id'] ][ $opt_key ] ) ? sanitize_text_field( wp_unslash( $_POST['item_meta'][ $parent ][ $pointer ]['other'][ $field['id'] ][ $opt_key ] ) ) : '';
1362 } else {
1363 $other_val = sanitize_text_field( wp_unslash( $_POST['item_meta'][ $parent ][ $pointer ]['other'][ $field['id'] ] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
1364 }
1365
1366 return $other_val;
1367 }
1368
1369 if ( isset( $field['id'] ) && isset( $_POST['item_meta']['other'][ $field['id'] ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
1370 // For normal fields
1371
1372 if ( FrmField::is_field_with_multiple_values( $field ) ) {
1373 // phpcs:ignore WordPress.Security.NonceVerification.Missing
1374 $other_val = isset( $_POST['item_meta']['other'][ $field['id'] ][ $opt_key ] ) ? sanitize_text_field( wp_unslash( $_POST['item_meta']['other'][ $field['id'] ][ $opt_key ] ) ) : '';
1375 } else {
1376 $other_val = sanitize_text_field( wp_unslash( $_POST['item_meta']['other'][ $field['id'] ] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
1377 }
1378
1379 return $other_val;
1380 }//end if
1381
1382 // For checkboxes
1383 if ( $field['type'] === 'checkbox' && is_array( $field['value'] ) ) {
1384 // Check if there is an "other" val in saved value and make sure the
1385 // "other" val is not equal to the Other checkbox option
1386 if ( isset( $field['value'][ $opt_key ] ) && $field['options'][ $opt_key ] != $field['value'][ $opt_key ] ) {
1387 $other_val = $field['value'][ $opt_key ];
1388 }
1389 } else {
1390 /**
1391 * For radio buttons and dropdowns
1392 * Check if saved value equals any of the options. If not, set it as the other value.
1393 */
1394 foreach ( $field['options'] as $opt_val ) {
1395 $temp_val = is_array( $opt_val ) ? $opt_val['value'] : $opt_val;
1396 // Multi-select dropdowns - key is not preserved
1397 if ( is_array( $field['value'] ) ) {
1398 $o_key = array_search( $temp_val, $field['value'] );
1399 if ( isset( $field['value'][ $o_key ] ) ) {
1400 unset( $field['value'][ $o_key ], $o_key );
1401 }
1402 } elseif ( $temp_val == $field['value'] ) {
1403 // For radio and regular dropdowns
1404 return '';
1405 } else {
1406 $other_val = $field['value'];
1407 }
1408 unset( $opt_val, $temp_val );
1409 }
1410 // For multi-select dropdowns only
1411 if ( is_array( $field['value'] ) && ! empty( $field['value'] ) ) {
1412 $other_val = reset( $field['value'] );
1413 }
1414 }//end if
1415
1416 return $other_val;
1417 }
1418
1419 /**
1420 * Check if there is a saved value for the "Other" text field. If so, set it as the $other_val.
1421 * Intended for front-end use
1422 *
1423 * @since 2.0.6
1424 *
1425 * @param array $args Should include field, opt_key and field name.
1426 * @param bool $other_opt
1427 * @param string $checked
1428 *
1429 * @return array $other_args
1430 */
1431 public static function prepare_other_input( $args, &$other_opt, &$checked ) {
1432 $other_args = array(
1433 'name' => '',
1434 'value' => '',
1435 );
1436
1437 // Check if this is an "Other" option.
1438 if ( ! self::is_other_opt( $args['opt_key'] ) ) {
1439 return $other_args;
1440 }
1441
1442 $other_opt = true;
1443
1444 self::set_other_name( $args, $other_args );
1445 self::set_other_value( $args, $other_args );
1446
1447 if ( '' !== $other_args['value'] ) {
1448 $checked = 'checked="checked" ';
1449 }
1450
1451 // If 'other' is selected as one of the default values for a checkbox field, 'checked' attribute should be on.
1452 if ( ! $checked &&
1453 $args['field']['type'] === 'checkbox' &&
1454 is_array( $args['field']['value'] ) &&
1455 isset( $args['field_val'] ) &&
1456 in_array( $args['field_val'], $args['field']['value'], true )
1457 ) {
1458 $checked = 'checked="checked" ';
1459 }
1460
1461 return $other_args;
1462 }
1463
1464 /**
1465 * @since 2.0.6
1466 * @param array $args
1467 * @param array $other_args
1468 */
1469 private static function set_other_name( $args, &$other_args ) {
1470 // Set up name for other field
1471 $other_args['name'] = str_replace( '[]', '', $args['field_name'] );
1472 $other_args['name'] = preg_replace( '/\[' . $args['field']['id'] . '\]$/', '', $other_args['name'] );
1473 $other_args['name'] = $other_args['name'] . '[other][' . $args['field']['id'] . ']';
1474
1475 // Converts item_meta[field_id] => item_meta[other][field_id] and
1476 // item_meta[parent][0][field_id] => item_meta[parent][0][other][field_id]
1477 if ( FrmField::is_field_with_multiple_values( $args['field'] ) ) {
1478 $other_args['name'] .= '[' . $args['opt_key'] . ']';
1479 }
1480 }
1481
1482 /**
1483 * Find the parent and pointer, and get text for "other" text field
1484 *
1485 * @since 2.0.6
1486 * @param array $args
1487 * @param array $other_args
1488 */
1489 private static function set_other_value( $args, &$other_args ) {
1490 $parent = '';
1491 $pointer = '';
1492
1493 // Check for parent ID and pointer
1494 $temp_array = explode( '[', $args['field_name'] );
1495
1496 // Count should only be greater than 3 if inside of a repeating section
1497 if ( count( $temp_array ) > 3 ) {
1498 $parent = str_replace( ']', '', $temp_array[1] );
1499 $pointer = str_replace( ']', '', $temp_array[2] );
1500 }
1501
1502 // Get text for "other" text field
1503 $other_args['value'] = self::get_other_val(
1504 array(
1505 'opt_key' => $args['opt_key'],
1506 'field' => $args['field'],
1507 'parent' => $parent,
1508 'pointer' => $pointer,
1509 )
1510 );
1511 }
1512
1513 /**
1514 * If this field includes an other option, show it
1515 *
1516 * @since 2.0.6
1517 * @param array $args
1518 */
1519 public static function include_other_input( $args ) {
1520 if ( ! $args['other_opt'] ) {
1521 return;
1522 }
1523
1524 $classes = array( 'frm_other_input' );
1525 if ( ! $args['checked'] || trim( $args['checked'] ) == '' ) {
1526 // hide the field if the other option is not selected
1527 $classes[] = 'frm_pos_none';
1528 }
1529 if ( $args['field']['type'] === 'select' && ! empty( $args['field']['multiple'] ) ) {
1530 $classes[] = 'frm_other_full';
1531 }
1532
1533 // Set up HTML ID for Other field
1534 $other_id = self::get_other_field_html_id( $args['field']['type'], $args['html_id'], $args['opt_key'] );
1535
1536 $label = isset( $args['opt_label'] ) ? $args['opt_label'] : $args['field']['name'];
1537
1538 echo '<label for="' . esc_attr( $other_id ) . '" class="frm_screen_reader frm_hidden">' .
1539 esc_html( $label ) .
1540 '</label>' .
1541 '<input type="text" id="' . esc_attr( $other_id ) . '" class="' . esc_attr( implode( ' ', $classes ) ) . '" ' .
1542 ( $args['read_only'] ? ' readonly="readonly" disabled="disabled"' : '' ) .
1543 ' name="' . esc_attr( $args['name'] ) . '" value="' . esc_attr( $args['value'] ) . '" />';
1544 }
1545
1546 /**
1547 * Get the HTML id for an "Other" text field
1548 * Note: This does not affect fields in repeating sections
1549 *
1550 * @since 2.0.08
1551 *
1552 * @param string $type Field type.
1553 * @param string $html_id
1554 * @param bool|string $opt_key
1555 *
1556 * @return string $other_id
1557 */
1558 public static function get_other_field_html_id( $type, $html_id, $opt_key = false ) {
1559 $other_id = $html_id;
1560
1561 // If hidden radio field, add an opt key of 0
1562 if ( $type === 'radio' && $opt_key === false ) {
1563 $opt_key = 0;
1564 }
1565
1566 if ( $opt_key !== false ) {
1567 $other_id .= '-' . $opt_key;
1568 }
1569
1570 $other_id .= '-otext';
1571
1572 return $other_id;
1573 }
1574
1575 public static function switch_field_ids( $val ) {
1576 global $frm_duplicate_ids;
1577 $replace = array();
1578 $replace_with = array();
1579 foreach ( (array) $frm_duplicate_ids as $old => $new ) {
1580 $replace[] = '[if ' . $old . ']';
1581 $replace_with[] = '[if ' . $new . ']';
1582 $replace[] = '[if ' . $old . ' ';
1583 $replace_with[] = '[if ' . $new . ' ';
1584 $replace[] = '[/if ' . $old . ']';
1585 $replace_with[] = '[/if ' . $new . ']';
1586 $replace[] = '[\/if ' . $old . ']';
1587 $replace_with[] = '[\/if ' . $new . ']';
1588 $replace[] = '[foreach ' . $old . ']';
1589 $replace_with[] = '[foreach ' . $new . ']';
1590 $replace[] = '[/foreach ' . $old . ']';
1591 $replace_with[] = '[/foreach ' . $new . ']';
1592 $replace[] = '[\/foreach ' . $old . ']';
1593 $replace_with[] = '[\/foreach ' . $new . ']';
1594 $replace[] = '[' . $old . ']';
1595 $replace_with[] = '[' . $new . ']';
1596 $replace[] = '[' . $old . ' ';
1597 $replace_with[] = '[' . $new . ' ';
1598 $replace[] = 'field_id="' . $old . '"';
1599 $replace_with[] = 'field_id="' . $new . '"';
1600 $replace[] = 'field_id=\"' . $old . '\"';
1601 $replace_with[] = 'field_id=\"' . $new . '\"';
1602 unset( $old, $new );
1603 }//end foreach
1604 if ( is_array( $val ) ) {
1605 foreach ( $val as $k => $v ) {
1606 if ( is_string( $v ) ) {
1607 if ( 'custom_html' === $k ) {
1608 $val[ $k ] = self::switch_ids_except_strings( $replace, $replace_with, array( '[if description]', '[description]', '[/if description]' ), $v );
1609 unset( $k, $v );
1610 continue;
1611 }
1612 $val[ $k ] = str_replace( $replace, $replace_with, $v );
1613 unset( $k, $v );
1614 }
1615 }
1616 } else {
1617 $val = str_replace( $replace, $replace_with, $val );
1618 }
1619
1620 return $val;
1621 }
1622
1623 /**
1624 * Removes exception strings from replacement arrays and replaces the rest in the provided value string.
1625 *
1626 * @since 6.14
1627 *
1628 * @param array $replace Values to be replaced.
1629 * @param array $replace_with Replacement values.
1630 * @param array $exceptions Array of strings to skip.
1631 * @param string $value Value to be updated.
1632 *
1633 * @return string
1634 */
1635 private static function switch_ids_except_strings( $replace, $replace_with, $exceptions, $value ) {
1636 foreach ( $exceptions as $exception ) {
1637 $index = array_search( $exception, $replace, true );
1638 if ( false === $index ) {
1639 continue;
1640 }
1641 unset( $replace[ $index ] );
1642 unset( $replace_with[ $index ] );
1643 }
1644 $value = str_replace( $replace, $replace_with, $value );
1645 return $value;
1646 }
1647
1648 /**
1649 * @since 4.0
1650 */
1651 public static function bulk_options_overlay() {
1652 $prepop = array();
1653 self::get_bulk_prefilled_opts( $prepop, true );
1654
1655 include FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/bulk-options-overlay.php';
1656 }
1657
1658 public static function get_us_states() {
1659 $states = array(
1660 'AL' => 'Alabama',
1661 'AK' => 'Alaska',
1662 'AR' => 'Arkansas',
1663 'AZ' => 'Arizona',
1664 'CA' => 'California',
1665 'CO' => 'Colorado',
1666 'CT' => 'Connecticut',
1667 'DE' => 'Delaware',
1668 'DC' => 'District of Columbia',
1669 'FL' => 'Florida',
1670 'GA' => 'Georgia',
1671 'HI' => 'Hawaii',
1672 'ID' => 'Idaho',
1673 'IL' => 'Illinois',
1674 'IN' => 'Indiana',
1675 'IA' => 'Iowa',
1676 'KS' => 'Kansas',
1677 'KY' => 'Kentucky',
1678 'LA' => 'Louisiana',
1679 'ME' => 'Maine',
1680 'MD' => 'Maryland',
1681 'MA' => 'Massachusetts',
1682 'MI' => 'Michigan',
1683 'MN' => 'Minnesota',
1684 'MS' => 'Mississippi',
1685 'MO' => 'Missouri',
1686 'MT' => 'Montana',
1687 'NE' => 'Nebraska',
1688 'NV' => 'Nevada',
1689 'NH' => 'New Hampshire',
1690 'NJ' => 'New Jersey',
1691 'NM' => 'New Mexico',
1692 'NY' => 'New York',
1693 'NC' => 'North Carolina',
1694 'ND' => 'North Dakota',
1695 'OH' => 'Ohio',
1696 'OK' => 'Oklahoma',
1697 'OR' => 'Oregon',
1698 'PA' => 'Pennsylvania',
1699 'RI' => 'Rhode Island',
1700 'SC' => 'South Carolina',
1701 'SD' => 'South Dakota',
1702 'TN' => 'Tennessee',
1703 'TX' => 'Texas',
1704 'UT' => 'Utah',
1705 'VT' => 'Vermont',
1706 'VA' => 'Virginia',
1707 'WA' => 'Washington',
1708 'WV' => 'West Virginia',
1709 'WI' => 'Wisconsin',
1710 'WY' => 'Wyoming',
1711 );
1712
1713 return apply_filters( 'frm_us_states', $states );
1714 }
1715
1716 public static function get_countries() {
1717 $countries = array(
1718 __( 'Afghanistan', 'formidable' ),
1719 __( 'Aland Islands', 'formidable' ),
1720 __( 'Albania', 'formidable' ),
1721 __( 'Algeria', 'formidable' ),
1722 __( 'American Samoa', 'formidable' ),
1723 __( 'Andorra', 'formidable' ),
1724 __( 'Angola', 'formidable' ),
1725 __( 'Anguilla', 'formidable' ),
1726 __( 'Antarctica', 'formidable' ),
1727 __( 'Antigua and Barbuda', 'formidable' ),
1728 __( 'Argentina', 'formidable' ),
1729 __( 'Armenia', 'formidable' ),
1730 __( 'Aruba', 'formidable' ),
1731 __( 'Australia', 'formidable' ),
1732 __( 'Austria', 'formidable' ),
1733 __( 'Azerbaijan', 'formidable' ),
1734 __( 'Bahamas', 'formidable' ),
1735 __( 'Bahrain', 'formidable' ),
1736 __( 'Bangladesh', 'formidable' ),
1737 __( 'Barbados', 'formidable' ),
1738 __( 'Belarus', 'formidable' ),
1739 __( 'Belgium', 'formidable' ),
1740 __( 'Belize', 'formidable' ),
1741 __( 'Benin', 'formidable' ),
1742 __( 'Bermuda', 'formidable' ),
1743 __( 'Bhutan', 'formidable' ),
1744 __( 'Bolivia', 'formidable' ),
1745 __( 'Bonaire, Sint Eustatius and Saba', 'formidable' ),
1746 __( 'Bosnia and Herzegovina', 'formidable' ),
1747 __( 'Botswana', 'formidable' ),
1748 __( 'Bouvet Island', 'formidable' ),
1749 __( 'Brazil', 'formidable' ),
1750 __( 'British Indian Ocean Territory', 'formidable' ),
1751 __( 'Brunei', 'formidable' ),
1752 __( 'Bulgaria', 'formidable' ),
1753 __( 'Burkina Faso', 'formidable' ),
1754 __( 'Burundi', 'formidable' ),
1755 __( 'Cambodia', 'formidable' ),
1756 __( 'Cameroon', 'formidable' ),
1757 __( 'Canada', 'formidable' ),
1758 __( 'Cape Verde', 'formidable' ),
1759 __( 'Cayman Islands', 'formidable' ),
1760 __( 'Central African Republic', 'formidable' ),
1761 __( 'Chad', 'formidable' ),
1762 __( 'Chile', 'formidable' ),
1763 __( 'China', 'formidable' ),
1764 __( 'Christmas Island', 'formidable' ),
1765 __( 'Cocos (Keeling) Islands', 'formidable' ),
1766 __( 'Colombia', 'formidable' ),
1767 __( 'Comoros', 'formidable' ),
1768 __( 'Congo', 'formidable' ),
1769 __( 'Cook Islands', 'formidable' ),
1770 __( 'Costa Rica', 'formidable' ),
1771 __( 'C&ocirc;te d\'Ivoire', 'formidable' ),
1772 __( 'Croatia', 'formidable' ),
1773 __( 'Cuba', 'formidable' ),
1774 __( 'Curacao', 'formidable' ),
1775 __( 'Cyprus', 'formidable' ),
1776 __( 'Czech Republic', 'formidable' ),
1777 __( 'Denmark', 'formidable' ),
1778 __( 'Djibouti', 'formidable' ),
1779 __( 'Dominica', 'formidable' ),
1780 __( 'Dominican Republic', 'formidable' ),
1781 __( 'East Timor', 'formidable' ),
1782 __( 'Ecuador', 'formidable' ),
1783 __( 'Egypt', 'formidable' ),
1784 __( 'El Salvador', 'formidable' ),
1785 __( 'Equatorial Guinea', 'formidable' ),
1786 __( 'Eritrea', 'formidable' ),
1787 __( 'Estonia', 'formidable' ),
1788 __( 'Ethiopia', 'formidable' ),
1789 __( 'Falkland Islands (Malvinas)', 'formidable' ),
1790 __( 'Faroe Islands', 'formidable' ),
1791 __( 'Fiji', 'formidable' ),
1792 __( 'Finland', 'formidable' ),
1793 __( 'France', 'formidable' ),
1794 __( 'French Guiana', 'formidable' ),
1795 __( 'French Polynesia', 'formidable' ),
1796 __( 'French Southern Territories', 'formidable' ),
1797 __( 'Gabon', 'formidable' ),
1798 __( 'Gambia', 'formidable' ),
1799 __( 'Georgia', 'formidable' ),
1800 __( 'Germany', 'formidable' ),
1801 __( 'Ghana', 'formidable' ),
1802 __( 'Gibraltar', 'formidable' ),
1803 __( 'Greece', 'formidable' ),
1804 __( 'Greenland', 'formidable' ),
1805 __( 'Grenada', 'formidable' ),
1806 __( 'Guadeloupe', 'formidable' ),
1807 __( 'Guam', 'formidable' ),
1808 __( 'Guatemala', 'formidable' ),
1809 __( 'Guernsey', 'formidable' ),
1810 __( 'Guinea', 'formidable' ),
1811 __( 'Guinea-Bissau', 'formidable' ),
1812 __( 'Guyana', 'formidable' ),
1813 __( 'Haiti', 'formidable' ),
1814 __( 'Heard Island and McDonald Islands', 'formidable' ),
1815 __( 'Holy See', 'formidable' ),
1816 __( 'Honduras', 'formidable' ),
1817 __( 'Hong Kong', 'formidable' ),
1818 __( 'Hungary', 'formidable' ),
1819 __( 'Iceland', 'formidable' ),
1820 __( 'India', 'formidable' ),
1821 __( 'Indonesia', 'formidable' ),
1822 __( 'Iran', 'formidable' ),
1823 __( 'Iraq', 'formidable' ),
1824 __( 'Ireland', 'formidable' ),
1825 __( 'Israel', 'formidable' ),
1826 __( 'Isle of Man', 'formidable' ),
1827 __( 'Italy', 'formidable' ),
1828 __( 'Jamaica', 'formidable' ),
1829 __( 'Japan', 'formidable' ),
1830 __( 'Jersey', 'formidable' ),
1831 __( 'Jordan', 'formidable' ),
1832 __( 'Kazakhstan', 'formidable' ),
1833 __( 'Kenya', 'formidable' ),
1834 __( 'Kiribati', 'formidable' ),
1835 __( 'North Korea', 'formidable' ),
1836 __( 'South Korea', 'formidable' ),
1837 __( 'Kosovo', 'formidable' ),
1838 __( 'Kuwait', 'formidable' ),
1839 __( 'Kyrgyzstan', 'formidable' ),
1840 __( 'Laos', 'formidable' ),
1841 __( 'Latvia', 'formidable' ),
1842 __( 'Lebanon', 'formidable' ),
1843 __( 'Lesotho', 'formidable' ),
1844 __( 'Liberia', 'formidable' ),
1845 __( 'Libya', 'formidable' ),
1846 __( 'Liechtenstein', 'formidable' ),
1847 __( 'Lithuania', 'formidable' ),
1848 __( 'Luxembourg', 'formidable' ),
1849 __( 'Macao', 'formidable' ),
1850 __( 'Macedonia', 'formidable' ),
1851 __( 'Madagascar', 'formidable' ),
1852 __( 'Malawi', 'formidable' ),
1853 __( 'Malaysia', 'formidable' ),
1854 __( 'Maldives', 'formidable' ),
1855 __( 'Mali', 'formidable' ),
1856 __( 'Malta', 'formidable' ),
1857 __( 'Marshall Islands', 'formidable' ),
1858 __( 'Martinique', 'formidable' ),
1859 __( 'Mauritania', 'formidable' ),
1860 __( 'Mauritius', 'formidable' ),
1861 __( 'Mayotte', 'formidable' ),
1862 __( 'Mexico', 'formidable' ),
1863 __( 'Micronesia', 'formidable' ),
1864 __( 'Moldova', 'formidable' ),
1865 __( 'Monaco', 'formidable' ),
1866 __( 'Mongolia', 'formidable' ),
1867 __( 'Montenegro', 'formidable' ),
1868 __( 'Montserrat', 'formidable' ),
1869 __( 'Morocco', 'formidable' ),
1870 __( 'Mozambique', 'formidable' ),
1871 __( 'Myanmar', 'formidable' ),
1872 __( 'Namibia', 'formidable' ),
1873 __( 'Nauru', 'formidable' ),
1874 __( 'Nepal', 'formidable' ),
1875 __( 'Netherlands', 'formidable' ),
1876 __( 'New Caledonia', 'formidable' ),
1877 __( 'New Zealand', 'formidable' ),
1878 __( 'Nicaragua', 'formidable' ),
1879 __( 'Niger', 'formidable' ),
1880 __( 'Nigeria', 'formidable' ),
1881 __( 'Niue', 'formidable' ),
1882 __( 'Norfolk Island', 'formidable' ),
1883 __( 'Northern Mariana Islands', 'formidable' ),
1884 __( 'Norway', 'formidable' ),
1885 __( 'Oman', 'formidable' ),
1886 __( 'Pakistan', 'formidable' ),
1887 __( 'Palau', 'formidable' ),
1888 __( 'Palestine', 'formidable' ),
1889 __( 'Panama', 'formidable' ),
1890 __( 'Papua New Guinea', 'formidable' ),
1891 __( 'Paraguay', 'formidable' ),
1892 __( 'Peru', 'formidable' ),
1893 __( 'Philippines', 'formidable' ),
1894 __( 'Pitcairn', 'formidable' ),
1895 __( 'Poland', 'formidable' ),
1896 __( 'Portugal', 'formidable' ),
1897 __( 'Puerto Rico', 'formidable' ),
1898 __( 'Qatar', 'formidable' ),
1899 __( 'Reunion', 'formidable' ),
1900 __( 'Romania', 'formidable' ),
1901 __( 'Russia', 'formidable' ),
1902 __( 'Rwanda', 'formidable' ),
1903 __( 'Saint Barthelemy', 'formidable' ),
1904 __( 'Saint Helena, Ascension and Tristan da Cunha', 'formidable' ),
1905 __( 'Saint Kitts and Nevis', 'formidable' ),
1906 __( 'Saint Lucia', 'formidable' ),
1907 __( 'Saint Martin (French part)', 'formidable' ),
1908 __( 'Saint Pierre and Miquelon', 'formidable' ),
1909 __( 'Saint Vincent and the Grenadines', 'formidable' ),
1910 __( 'Samoa', 'formidable' ),
1911 __( 'San Marino', 'formidable' ),
1912 __( 'Sao Tome and Principe', 'formidable' ),
1913 __( 'Saudi Arabia', 'formidable' ),
1914 __( 'Senegal', 'formidable' ),
1915 __( 'Serbia', 'formidable' ),
1916 __( 'Seychelles', 'formidable' ),
1917 __( 'Sierra Leone', 'formidable' ),
1918 __( 'Singapore', 'formidable' ),
1919 __( 'Sint Maarten (Dutch part)', 'formidable' ),
1920 __( 'Slovakia', 'formidable' ),
1921 __( 'Slovenia', 'formidable' ),
1922 __( 'Solomon Islands', 'formidable' ),
1923 __( 'Somalia', 'formidable' ),
1924 __( 'South Africa', 'formidable' ),
1925 __( 'South Georgia and the South Sandwich Islands', 'formidable' ),
1926 __( 'South Sudan', 'formidable' ),
1927 __( 'Spain', 'formidable' ),
1928 __( 'Sri Lanka', 'formidable' ),
1929 __( 'Sudan', 'formidable' ),
1930 __( 'Suriname', 'formidable' ),
1931 __( 'Svalbard and Jan Mayen', 'formidable' ),
1932 __( 'Swaziland', 'formidable' ),
1933 __( 'Sweden', 'formidable' ),
1934 __( 'Switzerland', 'formidable' ),
1935 __( 'Syria', 'formidable' ),
1936 __( 'Taiwan', 'formidable' ),
1937 __( 'Tajikistan', 'formidable' ),
1938 __( 'Tanzania', 'formidable' ),
1939 __( 'Thailand', 'formidable' ),
1940 __( 'Timor-Leste', 'formidable' ),
1941 __( 'Togo', 'formidable' ),
1942 __( 'Tokelau', 'formidable' ),
1943 __( 'Tonga', 'formidable' ),
1944 __( 'Trinidad and Tobago', 'formidable' ),
1945 __( 'Tunisia', 'formidable' ),
1946 __( 'Turkey', 'formidable' ),
1947 __( 'Turkmenistan', 'formidable' ),
1948 __( 'Turks and Caicos Islands', 'formidable' ),
1949 __( 'Tuvalu', 'formidable' ),
1950 __( 'Uganda', 'formidable' ),
1951 __( 'Ukraine', 'formidable' ),
1952 __( 'United Arab Emirates', 'formidable' ),
1953 __( 'United Kingdom', 'formidable' ),
1954 __( 'United States', 'formidable' ),
1955 __( 'United States Minor Outlying Islands', 'formidable' ),
1956 __( 'Uruguay', 'formidable' ),
1957 __( 'Uzbekistan', 'formidable' ),
1958 __( 'Vanuatu', 'formidable' ),
1959 __( 'Vatican City', 'formidable' ),
1960 __( 'Venezuela', 'formidable' ),
1961 __( 'Vietnam', 'formidable' ),
1962 __( 'Virgin Islands, British', 'formidable' ),
1963 __( 'Virgin Islands, U.S.', 'formidable' ),
1964 __( 'Wallis and Futuna', 'formidable' ),
1965 __( 'Western Sahara', 'formidable' ),
1966 __( 'Yemen', 'formidable' ),
1967 __( 'Zambia', 'formidable' ),
1968 __( 'Zimbabwe', 'formidable' ),
1969 );
1970
1971 sort( $countries, SORT_LOCALE_STRING );
1972 return apply_filters( 'frm_countries', $countries );
1973 }
1974
1975 /**
1976 * Gets bulk prefilled options.
1977 *
1978 * @since 5.0.04 Add `$include_class` param.
1979 *
1980 * @param array $prepop Bulk options.
1981 * @param array|false $include_class Include the class in the bulk options.
1982 */
1983 public static function get_bulk_prefilled_opts( array &$prepop, $include_class = false ) {
1984 // Countries.
1985 $countries = self::get_countries();
1986 if ( $include_class ) {
1987 $countries['class'] = 'frm-countries-opts';
1988 }
1989
1990 $prepop[ __( 'Countries', 'formidable' ) ] = $countries;
1991
1992 // State abv.
1993 $states = self::get_us_states();
1994 $state_abv = array_keys( $states );
1995 sort( $state_abv );
1996 if ( $include_class ) {
1997 $state_abv['class'] = 'frm-state-abv-opts';
1998 }
1999
2000 $prepop[ __( 'U.S. State Abbreviations', 'formidable' ) ] = $state_abv;
2001
2002 // States.
2003 $states = array_values( $states );
2004 sort( $states );
2005 if ( $include_class ) {
2006 $states['class'] = 'frm-states-opts';
2007 }
2008
2009 $prepop[ __( 'U.S. States', 'formidable' ) ] = $states;
2010 unset( $state_abv, $states );
2011
2012 // Age.
2013 $ages = array(
2014 __( 'Under 18', 'formidable' ),
2015 __( '18-24', 'formidable' ),
2016 __( '25-34', 'formidable' ),
2017 __( '35-44', 'formidable' ),
2018 __( '45-54', 'formidable' ),
2019 __( '55-64', 'formidable' ),
2020 __( '65 or Above', 'formidable' ),
2021 __( 'Prefer Not to Answer', 'formidable' ),
2022 );
2023 if ( $include_class ) {
2024 $ages['class'] = 'frm-age-opts';
2025 }
2026
2027 $prepop[ __( 'Age', 'formidable' ) ] = $ages;
2028
2029 // Satisfaction.
2030 $satisfaction = array(
2031 __( 'Very Unsatisfied', 'formidable' ),
2032 __( 'Unsatisfied', 'formidable' ),
2033 __( 'Neutral', 'formidable' ),
2034 __( 'Satisfied', 'formidable' ),
2035 __( 'Very Satisfied', 'formidable' ),
2036 __( 'N/A', 'formidable' ),
2037 );
2038 if ( $include_class ) {
2039 $satisfaction['class'] = 'frm-satisfaction-opts';
2040 }
2041
2042 $prepop[ __( 'Satisfaction', 'formidable' ) ] = $satisfaction;
2043
2044 // Importance.
2045 $importance = array(
2046 __( 'Not at all Important', 'formidable' ),
2047 __( 'Somewhat Important', 'formidable' ),
2048 __( 'Neutral', 'formidable' ),
2049 __( 'Important', 'formidable' ),
2050 __( 'Very Important', 'formidable' ),
2051 __( 'N/A', 'formidable' ),
2052 );
2053 if ( $include_class ) {
2054 $importance['class'] = 'frm-importance-opts';
2055 }
2056
2057 $prepop[ __( 'Importance', 'formidable' ) ] = $importance;
2058
2059 // Agreement.
2060 $agreement = array(
2061 __( 'Strongly Disagree', 'formidable' ),
2062 __( 'Disagree', 'formidable' ),
2063 __( 'Neutral', 'formidable' ),
2064 __( 'Agree', 'formidable' ),
2065 __( 'Strongly Agree', 'formidable' ),
2066 __( 'N/A', 'formidable' ),
2067 );
2068 if ( $include_class ) {
2069 $agreement['class'] = 'frm-agreement-opts';
2070 }
2071
2072 $prepop[ __( 'Agreement', 'formidable' ) ] = $agreement;
2073
2074 // Likely.
2075 $likely = array(
2076 __( 'Extremely Unlikely', 'formidable' ),
2077 __( 'Unlikely', 'formidable' ),
2078 __( 'Neutral', 'formidable' ),
2079 __( 'Likely', 'formidable' ),
2080 __( 'Extremely Likely', 'formidable' ),
2081 __( 'N/A', 'formidable' ),
2082 );
2083 if ( $include_class ) {
2084 $likely['class'] = 'frm-likely-opts';
2085 }
2086
2087 $prepop[ __( 'Likely', 'formidable' ) ] = $likely;
2088
2089 $prepop = apply_filters( 'frm_bulk_field_choices', $prepop );
2090 }
2091
2092 /**
2093 * Display a field value selector
2094 *
2095 * @since 2.03.05
2096 *
2097 * @param int $selector_field_id
2098 * @param array $selector_args
2099 */
2100 public static function display_field_value_selector( $selector_field_id, $selector_args ) {
2101 $field_value_selector = FrmFieldFactory::create_field_value_selector( $selector_field_id, $selector_args );
2102 $field_value_selector->display();
2103 }
2104
2105 /**
2106 * Convert a field object to a flat array
2107 *
2108 * @since 2.03.05
2109 *
2110 * @param object $field
2111 *
2112 * @return array
2113 */
2114 public static function convert_field_object_to_flat_array( $field ) {
2115 $field_options = is_array( $field->field_options ) ? $field->field_options : array();
2116 $field_array = get_object_vars( $field );
2117 unset( $field_array['field_options'] );
2118
2119 return $field_array + $field_options;
2120 }
2121
2122 /**
2123 * @since 4.04
2124 *
2125 * @param array $args
2126 * @return void
2127 */
2128 public static function show_add_field_buttons( $args ) {
2129 $field_key = $args['field_key'];
2130 $field_type = $args['field_type'];
2131 $field_label = FrmAppHelper::icon_by_class( FrmFormsHelper::get_field_link_icon( $field_type ), array( 'echo' => false ) );
2132 $field_name = FrmFormsHelper::get_field_link_name( $field_type );
2133 $field_label .= ' <span>' . $field_name . '</span>';
2134
2135 if ( ! empty( $field_type['is_new'] ) ) {
2136 ob_start();
2137 FrmAppHelper::show_pill_text();
2138 $field_label .= ob_get_clean();
2139 }
2140
2141 // If the individual field isn't allowed, disable it.
2142 $run_filter = true;
2143 $single_no_allow = ' ';
2144 $install_data = '';
2145 $requires = '';
2146 $link = isset( $field_type['link'] ) ? esc_url_raw( $field_type['link'] ) : '';
2147 $has_show_upgrade_class = isset( $field_type['icon'] ) && strpos( $field_type['icon'], ' frm_show_upgrade' );
2148 $show_upgrade = $has_show_upgrade_class || false !== strpos( $args['no_allow_class'], 'frm_show_upgrade' );
2149
2150 if ( $has_show_upgrade_class ) {
2151 $single_no_allow .= 'frm_show_upgrade';
2152 $field_type['icon'] = str_replace( ' frm_show_upgrade', '', $field_type['icon'] );
2153 $run_filter = false;
2154 if ( isset( $field_type['addon'] ) ) {
2155 $upgrading = FrmAddonsController::install_link( $field_type['addon'] );
2156 if ( isset( $upgrading['url'] ) ) {
2157 $install_data = json_encode( $upgrading );
2158 }
2159 $requires = FrmFormsHelper::get_plan_required( $upgrading );
2160 } elseif ( isset( $field_type['require'] ) ) {
2161 $requires = $field_type['require'];
2162 }
2163 }
2164
2165 $upgrade_label = '';
2166 $upgrade_message = '';
2167 if ( $show_upgrade ) {
2168 /* translators: %s: Field name */
2169 $upgrade_label = sprintf( esc_html__( '%s fields', 'formidable' ), $field_name );
2170
2171 if ( isset( $field_type['message'] ) ) {
2172 $upgrade_message = FrmAppHelper::kses( $field_type['message'], array( 'a', 'img' ) );
2173 }
2174 }
2175
2176 $li_params = array(
2177 'class' => 'frmbutton frm6 ' . $args['no_allow_class'] . $single_no_allow . ' frm_t' . str_replace( '|', '-', $field_key ),
2178 'id' => $field_key,
2179 'data-upgrade' => $upgrade_label,
2180 'data-link' => $link,
2181 'data-medium' => 'builder',
2182 'data-oneclick' => $install_data,
2183 'data-content' => $field_key,
2184 'data-requires' => $requires,
2185 );
2186
2187 if ( ! empty( $field_type['hide'] ) ) {
2188 $li_params['class'] .= ' frm_hidden';
2189 }
2190
2191 if ( ! empty( $field_type['upsell_image'] ) ) {
2192 $li_params['data-upsell-image'] = $field_type['upsell_image'];
2193 }
2194
2195 if ( $upgrade_message ) {
2196 $li_params['data-message'] = $upgrade_message;
2197 }
2198 ?>
2199 <li <?php FrmAppHelper::array_to_html_params( $li_params, true ); ?>>
2200 <?php
2201 if ( $run_filter ) {
2202 $field_label = apply_filters( 'frmpro_field_links', $field_label, $args['id'], $field_key );
2203 }
2204 FrmAppHelper::kses_echo( $field_label, array( 'a', 'i', 'span', 'use', 'svg' ) );
2205 ?>
2206 </li>
2207 <?php
2208 }
2209
2210 /**
2211 * Shows Display format option.
2212 *
2213 * @since 5.0.04
2214 *
2215 * @param array $field Field data.
2216 */
2217 public static function show_radio_display_format( $field ) {
2218 $options = self::get_display_format_options( $field );
2219
2220 $args = self::get_display_format_args( $field, $options );
2221
2222 include FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/radio-display-format.php';
2223 }
2224
2225 /**
2226 * Creates an array that contains variables used for display format options setting.
2227 *
2228 * @since 6.3.2
2229 *
2230 * @param array $field The field.
2231 *
2232 * @return array
2233 */
2234 public static function get_display_format_options( $field ) {
2235 $options = array(
2236 '0' => array(
2237 'text' => __( 'Simple', 'formidable' ),
2238 'svg' => 'frm_simple_radio',
2239 ),
2240 '1' => array(
2241 'text' => __( 'Images', 'formidable' ),
2242 'svg' => 'frm_image_as_option',
2243 'addon' => 'pro',
2244 'upgrade' => __( 'Image Options', 'formidable' ),
2245 'message' => __( 'Show images instead of radio buttons or check boxes. This is ideal for polls, surveys, segmenting questionnaires and more.', 'formidable' ) . '<img src="' . esc_url( FrmAppHelper::plugin_url() ) . '/images/image-options.png" />',
2246 'content' => 'image-options',
2247 ),
2248 'buttons' => array(
2249 'text' => __( 'Buttons', 'formidable' ),
2250 'svg' => 'frm_button_as_option',
2251 'addon' => 'surveys',
2252 'upgrade' => __( 'Button Options', 'formidable' ),
2253 'message' => __( 'Show buttons for radio buttons or check boxes. This is ideal for polls, surveys, segmenting questionnaires and more.', 'formidable' ),
2254 'content' => 'button-options',
2255 ),
2256 );
2257
2258 /**
2259 * Allows modifying the options of Display format setting of Radio field.
2260 *
2261 * @since 5.0.04
2262 *
2263 * @param array $options Options.
2264 * @param array $field
2265 */
2266 $options = apply_filters( 'frm_' . $field['type'] . '_display_format_options', $options, $field );
2267
2268 return $options;
2269 }
2270
2271 /**
2272 * Gets display format arguments to pass to the images_dropdown() method.
2273 *
2274 * @since 5.0.04
2275 *
2276 * @param array $field Field data.
2277 * @param array $options Options array.
2278 * @return array
2279 */
2280 public static function get_display_format_args( $field, $options ) {
2281 $args = array(
2282 'selected' => '0',
2283 'options' => array(),
2284 'name' => 'field_options[image_options_' . $field['id'] . ']',
2285 'input_attrs' => array(
2286 'class' => 'frm_toggle_image_options',
2287 ),
2288 );
2289
2290 self::fill_image_setting_options( $options, $args );
2291
2292 /**
2293 * Allows modifying the arguments of Display format setting of Radio field.
2294 *
2295 * @since 5.0.04
2296 *
2297 * @param array $args Arguments.
2298 * @param array $method_args The arguments from the method. Contains `field`, `options`.
2299 */
2300 return apply_filters( 'frm_' . $field['type'] . '_display_format_args', $args, compact( 'field', 'options' ) );
2301 }
2302
2303 /**
2304 * @since 5.0.04
2305 */
2306 private static function fill_image_setting_options( $options, &$args ) {
2307 foreach ( $options as $key => $option ) {
2308 $args['options'][ $key ] = $option;
2309
2310 if ( ! empty( $option['addon'] ) ) {
2311 $args['options'][ $key ]['custom_attrs'] = self::fill_image_setting_addon_link( $option );
2312 }
2313
2314 unset( $args['options'][ $key ]['addon'] );
2315 $fill = array( 'upgrade', 'message', 'content' );
2316 foreach ( $fill as $f ) {
2317 unset( $args['options'][ $key ][ $f ], $f );
2318 }
2319 }
2320 }
2321
2322 /**
2323 * @since 5.0.04
2324 *
2325 * @return array
2326 */
2327 private static function fill_image_setting_addon_link( $option ) {
2328 $custom_attrs = array(
2329 'class' => 'frm_noallow frm_show_upgrade',
2330 'data-medium' => 'builder',
2331 );
2332
2333 // translators: Add-on name.
2334 $custom_attrs['data-upgrade'] = sprintf( __( 'Formidable %s', 'formidable' ), ucwords( $option['addon'] ) );
2335
2336 $fill = array( 'upgrade', 'message', 'content' );
2337 foreach ( $fill as $f ) {
2338 if ( isset( $option[ $f ] ) ) {
2339 $custom_attrs[ 'data-' . $f ] = $option[ $f ];
2340 }
2341 }
2342
2343 if ( 'pro' === $option['addon'] ) {
2344 return $custom_attrs;
2345 }
2346
2347 $upgrading = FrmAddonsController::install_link( $option['addon'] );
2348 if ( isset( $upgrading['url'] ) ) {
2349 $install_data = wp_json_encode( $upgrading );
2350 } else {
2351 $install_data = '';
2352 }
2353
2354 $custom_attrs['data-oneclick'] = $install_data;
2355 $custom_attrs['data-requires'] = FrmFormsHelper::get_plan_required( $upgrading );
2356
2357 return $custom_attrs;
2358 }
2359
2360 /**
2361 * Maybe adjust a field value based on type.
2362 * Some types require unserializing an array.
2363 * These types are defined by a array_allowed property on their field model class.
2364 *
2365 * @since 6.2
2366 *
2367 * @param mixed $value
2368 * @param string $field_type
2369 * @return void
2370 */
2371 public static function prepare_field_value( &$value, $field_type ) {
2372 $field_object = FrmFieldFactory::get_field_type( $field_type );
2373 $value = $field_object->maybe_decode_value( $value );
2374 }
2375
2376 /**
2377 * @since 6.8
2378 *
2379 * @param int|string $form_id
2380 * @param array $field_ids If this is not empty, the results will be filtered by field id.
2381 * @return array
2382 */
2383 public static function get_draft_field_results( $form_id, $field_ids = array() ) {
2384 if ( FrmAppHelper::pro_is_installed() ) {
2385 $child_form_ids = FrmDb::get_col( 'frm_forms', array( 'parent_form_id' => $form_id ) );
2386 $form_ids = array_merge( array( $form_id ), $child_form_ids );
2387 } else {
2388 $form_ids = array( $form_id );
2389 }
2390
2391 $where = array(
2392 'form_id' => $form_ids,
2393 // Do a soft check for fields that look like drafts only.
2394 'field_options LIKE' => 's:5:"draft";i:1;',
2395 );
2396
2397 if ( $field_ids ) {
2398 $where['id'] = $field_ids;
2399 }
2400
2401 $rows = FrmDb::get_results( 'frm_fields', $where, 'id, field_options' );
2402
2403 return array_filter(
2404 $rows,
2405 function ( $row ) {
2406 FrmAppHelper::unserialize_or_decode( $row->field_options );
2407 return is_array( $row->field_options ) && ! empty( $row->field_options['draft'] );
2408 }
2409 );
2410 }
2411
2412 /**
2413 * This is called when loading the form builder.
2414 * Any unsaved draft fields get added to a hidden draft_fields input on load.
2415 *
2416 * @since 6.8
2417 *
2418 * @param int|string $form_id
2419 * @return array
2420 */
2421 public static function get_all_draft_field_ids( $form_id ) {
2422 $draft_field_rows = self::get_draft_field_results( $form_id );
2423 return wp_list_pluck( $draft_field_rows, 'id' );
2424 }
2425 }
2426