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

2,509 lines 74.2 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 $options_count = count( $field['options'] );
606
607 foreach ( $field['options'] as $opt_key => $opt ) {
608 $field_val = self::get_value_from_array( $opt, $opt_key, $field );
609 $opt = self::get_label_from_array( $opt, $opt_key, $field );
610
611 $field_name = $base_name . ( $default_type === 'checkbox' ? '[' . $opt_key . ']' : '' );
612
613 $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'] ) ) ) );
614
615 // If this is an "Other" option, get the HTML for it.
616 if ( self::is_other_opt( $opt_key ) ) {
617 if ( FrmAppHelper::pro_is_installed() ) {
618 require FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/other-option.php';
619 }
620 } else {
621 require FrmAppHelper::plugin_path() . '/classes/views/frm-fields/single-option.php';
622 }
623
624 unset( $checked );
625 }
626 }
627
628 /**
629 * Include hidden row for javascript to duplicate.
630 *
631 * @since 4.0
632 * @param array $field
633 */
634 private static function hidden_field_option( $field ) {
635 // Don't duplicate during an ajax add option.
636 $ajax_action = FrmAppHelper::get_param( 'action', '', 'post', 'sanitize_text_field' );
637 if ( $ajax_action === 'frm_add_field_option' ) {
638 return;
639 }
640
641 $opt_key = '000';
642 $field_val = __( 'New Option', 'formidable' );
643 $opt = __( 'New Option', 'formidable' );
644 $checked = false;
645 $field_name = 'default_value_' . $field['id'];
646 $html_id = isset( $field['html_id'] ) ? $field['html_id'] : self::get_html_id( $field );
647
648 $default_type = self::get_default_value_type( $field );
649 $field_name .= ( $default_type === 'checkbox' ? '[' . $opt_key . ']' : '' );
650
651 require FrmAppHelper::plugin_path() . '/classes/views/frm-fields/single-option.php';
652 }
653
654 /**
655 * @since 4.0
656 *
657 * @param array $field
658 * @return string radio or checkbox
659 */
660 private static function get_default_value_type( $field ) {
661 $default_type = $field['type'];
662 if ( $field['type'] === 'select' ) {
663 $default_type = FrmField::is_multiple_select( $field ) ? 'checkbox' : 'radio';
664 }
665 return $default_type;
666 }
667
668 public static function get_value_from_array( $opt, $opt_key, $field ) {
669 $opt = apply_filters( 'frm_field_value_saved', $opt, $opt_key, $field );
670
671 return FrmFieldsController::check_value( $opt, $opt_key, $field );
672 }
673
674 public static function get_label_from_array( $opt, $opt_key, $field ) {
675 $opt = apply_filters( 'frm_field_label_seen', $opt, $opt_key, $field );
676
677 return FrmFieldsController::check_label( $opt );
678 }
679
680 /**
681 * Shows the inline modal.
682 *
683 * @since 4.0
684 * @since 6.4.1 Added `inside_class` in the arguments.
685 *
686 * @param array $args The arguments.
687 */
688 public static function inline_modal( $args ) {
689 $defaults = array(
690 'id' => '',
691 'class' => '',
692 'show' => 0,
693 'callback' => array(),
694 'args' => array(),
695 'title' => '',
696 'inside_class' => 'inside',
697 'dismiss-icon' => true,
698 );
699 $args = array_merge( $defaults, $args );
700
701 include FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/inline-modal.php';
702 }
703
704 /**
705 * @since 4.0
706 */
707 public static function smart_values() {
708 $continue = apply_filters( 'frm_smart_values_box', true );
709 if ( $continue === true ) {
710 $upgrade_link = array(
711 'medium' => 'builder',
712 'content' => 'smart-tags',
713 );
714 include FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/smart-values.php';
715 }
716 }
717
718 /**
719 * @since 4.0
720 */
721 public static function input_mask() {
722 include FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/input-mask-info.php';
723 }
724
725 /**
726 * @since 4.0
727 */
728 public static function layout_classes() {
729 include FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/layout-classes.php';
730 }
731
732 /**
733 * @param int $tax_id
734 *
735 * @return string
736 */
737 public static function get_term_link( $tax_id ) {
738 $tax = get_taxonomy( $tax_id );
739 if ( ! $tax ) {
740 return '';
741 }
742
743 $link = sprintf(
744 /* translators: %1$s: Start HTML link, %2$s: Content type label, %3$s: Content type, %4$s: End HTML link */
745 esc_html__( 'Options are dynamically created from your %1$s%2$s: %3$s%4$s', 'formidable' ),
746 '<a href="' . esc_url( admin_url( 'edit-tags.php?taxonomy=' . $tax->name ) ) . '" target="_blank">',
747 esc_html__( 'taxonomy', 'formidable' ),
748 empty( $tax->labels->name ) ? esc_html__( 'Categories', 'formidable' ) : $tax->labels->name,
749 '</a>'
750 );
751 unset( $tax );
752
753 return $link;
754 }
755
756 public static function value_meets_condition( $observed_value, $cond, $hide_opt ) {
757 $hide_opt = self::get_value_for_comparison( $hide_opt );
758 $observed_value = self::get_value_for_comparison( $observed_value );
759
760 if ( is_array( $observed_value ) ) {
761 return self::array_value_condition( $observed_value, $cond, $hide_opt );
762 }
763
764 $m = false;
765 if ( $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 === '<=' ) {
776 $m = $observed_value <= $hide_opt;
777 } elseif ( $cond === 'LIKE' || $cond === 'not LIKE' ) {
778 $m = stripos( $observed_value, $hide_opt );
779 if ( $cond === 'not LIKE' ) {
780 $m = $m === false;
781 } else {
782 $m = $m !== false;
783 }
784 } elseif ( $cond === '%LIKE' ) {
785 // ends with
786 $length = strlen( $hide_opt );
787 $substr = substr( $observed_value, strlen( $observed_value ) - $length );
788 $m = 0 === strcasecmp( $substr, $hide_opt );
789 } elseif ( 'LIKE%' === $cond ) {
790 // starts with
791 $length = strlen( $hide_opt );
792 $substr = substr( $observed_value, 0, $length );
793 $m = 0 === strcasecmp( $substr, $hide_opt );
794 }//end if
795
796 return $m;
797 }
798
799 /**
800 * Trim and sanitize the values
801 *
802 * @since 2.05
803 */
804 private static function get_value_for_comparison( $value ) {
805 // Remove white space from hide_opt
806 if ( ! is_array( $value ) ) {
807 $value = trim( $value );
808 }
809
810 FrmAppHelper::sanitize_value( 'wp_kses_post', $value );
811
812 return $value;
813 }
814
815 public static function array_value_condition( $observed_value, $cond, $hide_opt ) {
816 $m = false;
817 if ( $cond === '==' ) {
818 if ( is_array( $hide_opt ) ) {
819 $m = array_intersect( $hide_opt, $observed_value );
820 $m = ! empty( $m );
821 } else {
822 $m = in_array( $hide_opt, $observed_value );
823 }
824 } elseif ( $cond === '!=' ) {
825 $m = ! in_array( $hide_opt, $observed_value );
826 } elseif ( $cond === '>' ) {
827 $min = min( $observed_value );
828 $m = $min > $hide_opt;
829 } elseif ( $cond === '<' ) {
830 $max = max( $observed_value );
831 $m = $max < $hide_opt;
832 } elseif ( $cond === 'LIKE' || $cond === 'not LIKE' ) {
833 foreach ( $observed_value as $ob ) {
834 $m = strpos( $ob, $hide_opt );
835 if ( $m !== false ) {
836 $m = true;
837 break;
838 }
839 }
840
841 if ( $cond === 'not LIKE' ) {
842 $m = $m === false;
843 }
844 } elseif ( $cond === '%LIKE' ) {
845 // ends with
846 foreach ( $observed_value as $ob ) {
847 if ( $hide_opt === substr( $ob, strlen( $ob ) - strlen( $hide_opt ) ) ) {
848 $m = true;
849 break;
850 }
851 }
852 } elseif ( $cond === 'LIKE%' ) {
853 // starts with
854 foreach ( $observed_value as $ob ) {
855 if ( $hide_opt === substr( $ob, 0, strlen( $hide_opt ) ) ) {
856 $m = true;
857 break;
858 }
859 }
860 }//end if
861
862 return $m;
863 }
864
865 /**
866 * Replace a few basic shortcodes and field ids
867 *
868 * @since 2.0
869 * @return string
870 */
871 public static function basic_replace_shortcodes( $value, $form, $entry ) {
872 if ( strpos( $value, '[sitename]' ) !== false ) {
873 $new_value = wp_specialchars_decode( FrmAppHelper::site_name(), ENT_QUOTES );
874 $value = str_replace( '[sitename]', $new_value, $value );
875 }
876
877 $value = apply_filters( 'frm_content', $value, $form, $entry );
878 $value = do_shortcode( $value );
879
880 return $value;
881 }
882
883 public static function get_shortcodes( $content, $form_id ) {
884 if ( FrmAppHelper::pro_is_installed() ) {
885 return FrmProDisplaysHelper::get_shortcodes( $content, $form_id );
886 }
887
888 $fields = FrmField::getAll(
889 array(
890 'fi.form_id' => (int) $form_id,
891 'fi.type not' => FrmField::no_save_fields(),
892 )
893 );
894
895 $tagregexp = self::allowed_shortcodes( $fields );
896
897 preg_match_all( "/\[(if )?($tagregexp)\b(.*?)(?:(\/))?\](?:(.+?)\[\/\2\])?/s", $content, $matches, PREG_PATTERN_ORDER );
898
899 return $matches;
900 }
901
902 public static function allowed_shortcodes( $fields = array() ) {
903 $tagregexp = array(
904 'editlink',
905 'id',
906 'key',
907 'ip',
908 'siteurl',
909 'sitename',
910 'admin_email',
911 'default-email',
912 'default-from-email',
913 'post[-|_]id',
914 'created[-|_]at',
915 'updated[-|_]at',
916 'updated[-|_]by',
917 'parent[-|_]id',
918 'form_name',
919 );
920
921 foreach ( $fields as $field ) {
922 $tagregexp[] = $field->id;
923 $tagregexp[] = $field->field_key;
924 }
925
926 $tagregexp = implode( '|', $tagregexp );
927
928 return $tagregexp;
929 }
930
931 public static function replace_content_shortcodes( $content, $entry, $shortcodes ) {
932 foreach ( $shortcodes[0] as $short_key => $tag ) {
933 if ( empty( $tag ) ) {
934 continue;
935 }
936
937 $atts = FrmShortcodeHelper::get_shortcode_attribute_array( $shortcodes[3][ $short_key ] );
938 $tag = FrmShortcodeHelper::get_shortcode_tag( $shortcodes, $short_key );
939
940 $atts['entry'] = $entry;
941 $atts['tag'] = $tag;
942 $replace_with = self::get_value_for_shortcode( $atts );
943
944 if ( $replace_with !== null ) {
945 $replace_with = self::trigger_shortcode_atts( $replace_with, $atts );
946 self::sanitize_embedded_shortcodes( compact( 'entry' ), $replace_with );
947 $content = str_replace( $shortcodes[0][ $short_key ], $replace_with, $content );
948 }
949
950 unset( $atts, $replace_with );
951 }
952
953 return $content;
954 }
955
956 /**
957 * @param string $replace_with
958 * @param array $atts
959 * @return string
960 */
961 private static function trigger_shortcode_atts( $replace_with, $atts ) {
962 $supported_atts = array( 'remove_accents', 'sanitize', 'sanitize_url' );
963 $included_atts = array_intersect( $supported_atts, array_keys( $atts ) );
964 foreach ( $included_atts as $included_att ) {
965 if ( '0' === $atts[ $included_att ] ) {
966 // Skip any option that uses 0 so sanitize_url=0 does not encode.
967 continue;
968 }
969 $function = 'atts_' . $included_att;
970 $replace_with = self::$function( $replace_with, $atts );
971 }
972 return $replace_with;
973 }
974
975 /**
976 * Converts all accent characters to ASCII characters.
977 *
978 * @since 6.3.1
979 *
980 * @param string $replace_with The text to remove accents from.
981 *
982 * @return string
983 */
984 public static function atts_remove_accents( $replace_with ) {
985 return remove_accents( $replace_with );
986 }
987
988 /**
989 * @param string $replace_with
990 * @return string
991 */
992 private static function atts_sanitize( $replace_with ) {
993 return sanitize_title_with_dashes( $replace_with );
994 }
995
996 /**
997 * @param string $replace_with
998 * @return string
999 */
1000 private static function atts_sanitize_url( $replace_with ) {
1001 return urlencode( $replace_with );
1002 }
1003
1004 /**
1005 * Prevent shortcodes in fields from being processed
1006 *
1007 * @since 3.01.02
1008 *
1009 * @param array $atts Includes entry object.
1010 * @param string|null $value
1011 * @return void
1012 */
1013 public static function sanitize_embedded_shortcodes( $atts, &$value ) {
1014 if ( is_null( $value ) ) {
1015 return;
1016 }
1017
1018 $atts['value'] = $value;
1019 $should_sanitize = apply_filters( 'frm_sanitize_shortcodes', true, $atts );
1020 if ( $should_sanitize ) {
1021 $value = str_replace( '[', '&#91;', $value );
1022 }
1023 }
1024
1025 /**
1026 * @since 3.0
1027 *
1028 * @param array $atts
1029 *
1030 * @return string
1031 */
1032 private static function get_value_for_shortcode( $atts ) {
1033 $clean_tag = str_replace( '-', '_', $atts['tag'] );
1034
1035 $shortcode_values = array(
1036 'id' => $atts['entry']->id,
1037 'key' => $atts['entry']->item_key,
1038 'ip' => $atts['entry']->ip,
1039 );
1040
1041 $dynamic_default = array( 'admin_email', 'siteurl', 'frmurl', 'sitename', 'get', 'default-email', 'default-from-email' );
1042
1043 if ( isset( $shortcode_values[ $atts['tag'] ] ) ) {
1044 $replace_with = $shortcode_values[ $atts['tag'] ];
1045 } elseif ( in_array( $atts['tag'], $dynamic_default, true ) ) {
1046 $replace_with = self::dynamic_default_values( $atts['tag'], $atts );
1047 } elseif ( $clean_tag === 'user_agent' ) {
1048 $description = $atts['entry']->description;
1049 $replace_with = FrmEntriesHelper::get_browser( $description['browser'] );
1050 } elseif ( $clean_tag === 'created_at' || $clean_tag === 'updated_at' ) {
1051 $atts['tag'] = $clean_tag;
1052 $replace_with = self::get_entry_timestamp( $atts );
1053 } elseif ( $clean_tag === 'created_by' || $clean_tag === 'updated_by' ) {
1054 $replace_with = self::get_display_value( $atts['entry']->{$clean_tag}, (object) array( 'type' => 'user_id' ), $atts );
1055 } else {
1056 $replace_with = self::get_field_shortcode_value( $atts );
1057 }
1058
1059 return $replace_with;
1060 }
1061
1062 /**
1063 * @since 3.0
1064 *
1065 * @param array $atts
1066 *
1067 * @return string
1068 */
1069 private static function get_entry_timestamp( $atts ) {
1070 if ( isset( $atts['format'] ) ) {
1071 $time_format = ' ';
1072 } else {
1073 $atts['format'] = get_option( 'date_format' );
1074 $time_format = '';
1075 }
1076
1077 return FrmAppHelper::get_formatted_time( $atts['entry']->{$atts['tag']}, $atts['format'], $time_format );
1078 }
1079
1080 /**
1081 * @since 3.0
1082 *
1083 * @param array $atts
1084 *
1085 * @return string|null
1086 */
1087 private static function get_field_shortcode_value( $atts ) {
1088 $field = FrmField::getOne( $atts['tag'] );
1089 if ( empty( $field ) ) {
1090 return null;
1091 }
1092
1093 if ( isset( $atts['show'] ) && $atts['show'] === 'field_label' ) {
1094 $replace_with = $field->name;
1095 } elseif ( isset( $atts['show'] ) && $atts['show'] === 'description' ) {
1096 $replace_with = $field->description;
1097 } else {
1098 $replace_with = FrmEntryMeta::get_meta_value( $atts['entry'], $field->id );
1099 $string_value = $replace_with;
1100 if ( is_array( $replace_with ) ) {
1101 $sep = isset( $atts['sep'] ) ? $atts['sep'] : ', ';
1102 $string_value = FrmAppHelper::safe_implode( $sep, $replace_with );
1103 }
1104
1105 if ( empty( $string_value ) && $string_value != '0' ) {
1106 $replace_with = '';
1107 } else {
1108 $atts['entry_id'] = $atts['entry']->id;
1109 $atts['entry_key'] = $atts['entry']->item_key;
1110 $replace_with = self::get_display_value( $replace_with, $field, $atts );
1111 }
1112 }
1113
1114 return $replace_with;
1115 }
1116
1117 /**
1118 * Get the value to replace a few standard shortcodes
1119 *
1120 * @since 2.0
1121 * @return string
1122 */
1123 public static function dynamic_default_values( $tag, $atts = array(), $return_array = false ) {
1124 $new_value = '';
1125 switch ( $tag ) {
1126 case 'admin_email':
1127 $new_value = get_option( 'admin_email' );
1128 break;
1129 case 'default-email':
1130 $frm_settings = FrmAppHelper::get_settings();
1131 $new_value = ! empty( $frm_settings->default_email ) && is_email( $frm_settings->default_email ) ? $frm_settings->default_email : get_option( 'admin_email' );
1132 break;
1133 case 'default-from-email':
1134 $frm_settings = FrmAppHelper::get_settings();
1135 $new_value = ! empty( $frm_settings->from_email ) && is_email( $frm_settings->from_email ) ? $frm_settings->from_email : get_option( 'admin_email' );
1136 break;
1137 case 'siteurl':
1138 $new_value = FrmAppHelper::site_url();
1139 break;
1140 case 'frmurl':
1141 $new_value = FrmAppHelper::plugin_url();
1142 break;
1143 case 'sitename':
1144 $new_value = FrmAppHelper::site_name();
1145 break;
1146 case 'get':
1147 $new_value = self::process_get_shortcode( $atts, $return_array );
1148 }//end switch
1149
1150 return $new_value;
1151 }
1152
1153 /**
1154 * Process the [get] shortcode
1155 *
1156 * @since 2.0
1157 * @return array|string
1158 */
1159 public static function process_get_shortcode( $atts, $return_array = false ) {
1160 if ( ! isset( $atts['param'] ) ) {
1161 return '';
1162 }
1163
1164 if ( strpos( $atts['param'], '&#91;' ) ) {
1165 $atts['param'] = str_replace( '&#91;', '[', $atts['param'] );
1166 $atts['param'] = str_replace( '&#93;', ']', $atts['param'] );
1167 }
1168
1169 $new_value = FrmAppHelper::get_param( $atts['param'], '', 'get', 'sanitize_text_field' );
1170 $new_value = FrmAppHelper::get_query_var( $new_value, $atts['param'] );
1171
1172 if ( $new_value == '' ) {
1173 if ( ! isset( $atts['prev_val'] ) ) {
1174 $atts['prev_val'] = '';
1175 }
1176
1177 $new_value = isset( $atts['default'] ) ? $atts['default'] : $atts['prev_val'];
1178 }
1179
1180 if ( is_array( $new_value ) && ! $return_array ) {
1181 $new_value = implode( ', ', $new_value );
1182 }
1183
1184 return $new_value;
1185 }
1186
1187 public static function get_display_value( $value, $field, $atts = array() ) {
1188
1189 $value = apply_filters( 'frm_get_' . $field->type . '_display_value', $value, $field, $atts );
1190 $value = apply_filters( 'frm_get_display_value', $value, $field, $atts );
1191
1192 $value = self::get_unfiltered_display_value( compact( 'value', 'field', 'atts' ) );
1193
1194 return apply_filters( 'frm_display_value', $value, $field, $atts );
1195 }
1196
1197 /**
1198 * @param array $atts Includes value, field, and atts.
1199 */
1200 public static function get_unfiltered_display_value( $atts ) {
1201 $value = $atts['value'];
1202 $field = $atts['field'];
1203 $atts = isset( $atts['atts'] ) ? $atts['atts'] : $atts;
1204
1205 if ( is_array( $field ) ) {
1206 $field = $field['id'];
1207 }
1208
1209 $field_obj = FrmFieldFactory::get_field_object( $field );
1210
1211 return $field_obj->get_display_value( $value, $atts );
1212 }
1213
1214 /**
1215 * Get a value from the user profile from the user ID
1216 *
1217 * @since 3.0
1218 *
1219 * @return string
1220 */
1221 public static function get_user_display_name( $user_id, $user_info = 'display_name', $args = array() ) {
1222 $defaults = array(
1223 'blank' => false,
1224 'link' => false,
1225 'size' => 96,
1226 );
1227
1228 $args = wp_parse_args( $args, $defaults );
1229
1230 $user = get_userdata( $user_id );
1231 $info = '';
1232
1233 if ( $user ) {
1234 if ( $user_info === 'avatar' ) {
1235 $info = get_avatar( $user_id, $args['size'] );
1236 } elseif ( $user_info === 'author_link' ) {
1237 $info = get_author_posts_url( $user_id );
1238 } else {
1239 $info = isset( $user->$user_info ) ? $user->$user_info : '';
1240 }
1241
1242 if ( 'display_name' === $user_info && empty( $info ) && ! $args['blank'] ) {
1243 $info = $user->user_login;
1244 }
1245 }
1246
1247 if ( $args['link'] ) {
1248 $info = '<a href="' . esc_url( admin_url( 'user-edit.php?user_id=' . $user_id ) ) . '">' . $info . '</a>';
1249 }
1250
1251 return $info;
1252 }
1253
1254 /**
1255 * @param string $type
1256 * @return array
1257 */
1258 public static function get_field_types( $type ) {
1259 $single_input = self::single_input_fields();
1260 $multiple_input = array( 'radio', 'checkbox', 'select', 'scale', 'star', 'lookup' );
1261 $field_selection = FrmField::all_field_selection();
1262 $field_types = array();
1263
1264 if ( in_array( $type, $single_input, true ) ) {
1265 self::field_types_for_input( $single_input, $field_selection, $field_types );
1266 } elseif ( in_array( $type, $multiple_input, true ) ) {
1267 self::field_types_for_input( $multiple_input, $field_selection, $field_types );
1268 } elseif ( isset( $field_selection[ $type ] ) ) {
1269 $field_types[ $type ] = $field_selection[ $type ];
1270 }
1271
1272 $field_types = apply_filters( 'frm_switch_field_types', $field_types, compact( 'type', 'field_selection' ) );
1273
1274 return $field_types;
1275 }
1276
1277 /**
1278 * Get a list of all fields that use a single value input.
1279 *
1280 * @since 4.0
1281 */
1282 public static function single_input_fields() {
1283 $fields = array(
1284 'text',
1285 'textarea',
1286 'rte',
1287 'number',
1288 'email',
1289 'url',
1290 'date',
1291 'phone',
1292 'hidden',
1293 'time',
1294 'tag',
1295 'password',
1296 'gdpr',
1297 );
1298 return apply_filters( 'frm_single_input_fields', $fields );
1299 }
1300
1301 /**
1302 * @param string[] $inputs
1303 * @param array $fields
1304 * @param array $field_types
1305 * @return void
1306 */
1307 private static function field_types_for_input( $inputs, $fields, &$field_types ) {
1308 foreach ( $inputs as $input ) {
1309 // This may not be set if a field type was removed using the frm_available_fields or frm_pro_available_fields filters.
1310 if ( array_key_exists( $input, $fields ) ) {
1311 $field_types[ $input ] = $fields[ $input ];
1312 }
1313 unset( $input );
1314 }
1315 }
1316
1317 /**
1318 * Check if current field option is an "other" option
1319 *
1320 * @since 2.0.6
1321 *
1322 * @param string $opt_key
1323 *
1324 * @return bool Returns true if current field option is an "Other" option
1325 */
1326 public static function is_other_opt( $opt_key ) {
1327 return $opt_key && strpos( $opt_key, 'other_' ) === 0;
1328 }
1329
1330 /**
1331 * Get value that belongs in "Other" text box
1332 *
1333 * @since 2.0.6
1334 *
1335 * @param array $args
1336 */
1337 public static function get_other_val( $args ) {
1338 $defaults = array(
1339 'opt_key' => 0,
1340 'field' => array(),
1341 'parent' => false,
1342 'pointer' => false,
1343 );
1344 $args = wp_parse_args( $args, $defaults );
1345
1346 $opt_key = $args['opt_key'];
1347 $field = $args['field'];
1348 $parent = $args['parent'];
1349 $pointer = $args['pointer'];
1350 $other_val = '';
1351
1352 // If option is an "other" option and there is a value set for this field,
1353 // check if the value belongs in the current "Other" option text field
1354 if ( ! self::is_other_opt( $opt_key ) || ! FrmField::is_option_true( $field, 'value' ) ) {
1355 return $other_val;
1356 }
1357
1358 // Check posted vals before checking saved values
1359 // For fields inside repeating sections - note, don't check if $pointer is true because it will often be zero
1360 if ( $parent && isset( $_POST['item_meta'][ $parent ][ $pointer ]['other'][ $field['id'] ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
1361 if ( FrmField::is_field_with_multiple_values( $field ) ) {
1362 // phpcs:ignore WordPress.Security.NonceVerification.Missing
1363 $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 ] ) ) : '';
1364 } else {
1365 $other_val = sanitize_text_field( wp_unslash( $_POST['item_meta'][ $parent ][ $pointer ]['other'][ $field['id'] ] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
1366 }
1367
1368 return $other_val;
1369 }
1370
1371 if ( isset( $field['id'] ) && isset( $_POST['item_meta']['other'][ $field['id'] ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
1372 // For normal fields
1373
1374 if ( FrmField::is_field_with_multiple_values( $field ) ) {
1375 // phpcs:ignore WordPress.Security.NonceVerification.Missing
1376 $other_val = isset( $_POST['item_meta']['other'][ $field['id'] ][ $opt_key ] ) ? sanitize_text_field( wp_unslash( $_POST['item_meta']['other'][ $field['id'] ][ $opt_key ] ) ) : '';
1377 } else {
1378 $other_val = sanitize_text_field( wp_unslash( $_POST['item_meta']['other'][ $field['id'] ] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
1379 }
1380
1381 return $other_val;
1382 }//end if
1383
1384 // For checkboxes
1385 if ( $field['type'] === 'checkbox' && is_array( $field['value'] ) ) {
1386 // Check if there is an "other" val in saved value and make sure the
1387 // "other" val is not equal to the Other checkbox option
1388 if ( isset( $field['value'][ $opt_key ] ) && $field['options'][ $opt_key ] != $field['value'][ $opt_key ] ) {
1389 $other_val = $field['value'][ $opt_key ];
1390 }
1391 } else {
1392 /**
1393 * For radio buttons and dropdowns
1394 * Check if saved value equals any of the options. If not, set it as the other value.
1395 */
1396 foreach ( $field['options'] as $opt_val ) {
1397 $temp_val = is_array( $opt_val ) ? $opt_val['value'] : $opt_val;
1398 // Multi-select dropdowns - key is not preserved
1399 if ( is_array( $field['value'] ) ) {
1400 $o_key = array_search( $temp_val, $field['value'] );
1401 if ( isset( $field['value'][ $o_key ] ) ) {
1402 unset( $field['value'][ $o_key ], $o_key );
1403 }
1404 } elseif ( $temp_val == $field['value'] ) {
1405 // For radio and regular dropdowns
1406 return '';
1407 } else {
1408 $other_val = $field['value'];
1409 }
1410 unset( $opt_val, $temp_val );
1411 }
1412 // For multi-select dropdowns only
1413 if ( is_array( $field['value'] ) && ! empty( $field['value'] ) ) {
1414 $other_val = reset( $field['value'] );
1415 }
1416 }//end if
1417
1418 return $other_val;
1419 }
1420
1421 /**
1422 * Check if there is a saved value for the "Other" text field. If so, set it as the $other_val.
1423 * Intended for front-end use
1424 *
1425 * @since 2.0.6
1426 *
1427 * @param array $args Should include field, opt_key and field name.
1428 * @param bool $other_opt
1429 * @param string $checked
1430 *
1431 * @return array $other_args
1432 */
1433 public static function prepare_other_input( $args, &$other_opt, &$checked ) {
1434 $other_args = array(
1435 'name' => '',
1436 'value' => '',
1437 );
1438
1439 // Check if this is an "Other" option.
1440 if ( ! self::is_other_opt( $args['opt_key'] ) ) {
1441 return $other_args;
1442 }
1443
1444 $other_opt = true;
1445
1446 self::set_other_name( $args, $other_args );
1447 self::set_other_value( $args, $other_args );
1448
1449 if ( '' !== $other_args['value'] ) {
1450 $checked = 'checked="checked" ';
1451 }
1452
1453 // If 'other' is selected as one of the default values for a checkbox field, 'checked' attribute should be on.
1454 if ( ! $checked &&
1455 $args['field']['type'] === 'checkbox' &&
1456 is_array( $args['field']['value'] ) &&
1457 isset( $args['field_val'] ) &&
1458 in_array( $args['field_val'], $args['field']['value'], true )
1459 ) {
1460 $checked = 'checked="checked" ';
1461 }
1462
1463 return $other_args;
1464 }
1465
1466 /**
1467 * @since 2.0.6
1468 * @param array $args
1469 * @param array $other_args
1470 */
1471 private static function set_other_name( $args, &$other_args ) {
1472 // Set up name for other field
1473 $other_args['name'] = str_replace( '[]', '', $args['field_name'] );
1474 $other_args['name'] = preg_replace( '/\[' . $args['field']['id'] . '\]$/', '', $other_args['name'] );
1475 $other_args['name'] = $other_args['name'] . '[other][' . $args['field']['id'] . ']';
1476
1477 // Converts item_meta[field_id] => item_meta[other][field_id] and
1478 // item_meta[parent][0][field_id] => item_meta[parent][0][other][field_id]
1479 if ( FrmField::is_field_with_multiple_values( $args['field'] ) ) {
1480 $other_args['name'] .= '[' . $args['opt_key'] . ']';
1481 }
1482 }
1483
1484 /**
1485 * Find the parent and pointer, and get text for "other" text field
1486 *
1487 * @since 2.0.6
1488 * @param array $args
1489 * @param array $other_args
1490 */
1491 private static function set_other_value( $args, &$other_args ) {
1492 $parent = '';
1493 $pointer = '';
1494
1495 // Check for parent ID and pointer
1496 $temp_array = explode( '[', $args['field_name'] );
1497
1498 // Count should only be greater than 3 if inside of a repeating section
1499 if ( count( $temp_array ) > 3 ) {
1500 $parent = str_replace( ']', '', $temp_array[1] );
1501 $pointer = str_replace( ']', '', $temp_array[2] );
1502 }
1503
1504 // Get text for "other" text field
1505 $other_args['value'] = self::get_other_val(
1506 array(
1507 'opt_key' => $args['opt_key'],
1508 'field' => $args['field'],
1509 'parent' => $parent,
1510 'pointer' => $pointer,
1511 )
1512 );
1513 }
1514
1515 /**
1516 * If this field includes an other option, show it
1517 *
1518 * @since 2.0.6
1519 * @param array $args
1520 */
1521 public static function include_other_input( $args ) {
1522 if ( ! $args['other_opt'] ) {
1523 return;
1524 }
1525
1526 $classes = array( 'frm_other_input' );
1527 if ( ! $args['checked'] || trim( $args['checked'] ) == '' ) {
1528 // hide the field if the other option is not selected
1529 $classes[] = 'frm_pos_none';
1530 }
1531 if ( $args['field']['type'] === 'select' && ! empty( $args['field']['multiple'] ) ) {
1532 $classes[] = 'frm_other_full';
1533 }
1534
1535 // Set up HTML ID for Other field
1536 $other_id = self::get_other_field_html_id( $args['field']['type'], $args['html_id'], $args['opt_key'] );
1537
1538 $label = isset( $args['opt_label'] ) ? $args['opt_label'] : $args['field']['name'];
1539
1540 echo '<label for="' . esc_attr( $other_id ) . '" class="frm_screen_reader frm_hidden">' .
1541 esc_html( $label ) .
1542 '</label>' .
1543 '<input type="text" id="' . esc_attr( $other_id ) . '" class="' . esc_attr( implode( ' ', $classes ) ) . '" ' .
1544 ( $args['read_only'] ? ' readonly="readonly" disabled="disabled"' : '' ) .
1545 ' name="' . esc_attr( $args['name'] ) . '" value="' . esc_attr( $args['value'] ) . '" />';
1546 }
1547
1548 /**
1549 * Get the HTML id for an "Other" text field
1550 * Note: This does not affect fields in repeating sections
1551 *
1552 * @since 2.0.08
1553 *
1554 * @param string $type Field type.
1555 * @param string $html_id
1556 * @param bool|string $opt_key
1557 *
1558 * @return string $other_id
1559 */
1560 public static function get_other_field_html_id( $type, $html_id, $opt_key = false ) {
1561 $other_id = $html_id;
1562
1563 // If hidden radio field, add an opt key of 0
1564 if ( $type === 'radio' && $opt_key === false ) {
1565 $opt_key = 0;
1566 }
1567
1568 if ( $opt_key !== false ) {
1569 $other_id .= '-' . $opt_key;
1570 }
1571
1572 $other_id .= '-otext';
1573
1574 return $other_id;
1575 }
1576
1577 public static function switch_field_ids( $val ) {
1578 global $frm_duplicate_ids;
1579 $replace = array();
1580 $replace_with = array();
1581 foreach ( (array) $frm_duplicate_ids as $old => $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[] = '[\/if ' . $old . ']';
1589 $replace_with[] = '[\/if ' . $new . ']';
1590 $replace[] = '[foreach ' . $old . ']';
1591 $replace_with[] = '[foreach ' . $new . ']';
1592 $replace[] = '[/foreach ' . $old . ']';
1593 $replace_with[] = '[/foreach ' . $new . ']';
1594 $replace[] = '[\/foreach ' . $old . ']';
1595 $replace_with[] = '[\/foreach ' . $new . ']';
1596 $replace[] = '[' . $old . ']';
1597 $replace_with[] = '[' . $new . ']';
1598 $replace[] = '[' . $old . ' ';
1599 $replace_with[] = '[' . $new . ' ';
1600 $replace[] = 'field_id="' . $old . '"';
1601 $replace_with[] = 'field_id="' . $new . '"';
1602 $replace[] = 'field_id=\"' . $old . '\"';
1603 $replace_with[] = 'field_id=\"' . $new . '\"';
1604 // This covers conditional logic.
1605 $replace[] = '_field":"' . $old . '","';
1606 $replace_with[] = '_field":"' . $new . '","';
1607 unset( $old, $new );
1608 }//end foreach
1609 if ( is_array( $val ) ) {
1610 foreach ( $val as $k => $v ) {
1611 if ( is_string( $v ) ) {
1612 if ( 'custom_html' === $k ) {
1613 $val[ $k ] = self::switch_ids_except_strings( $replace, $replace_with, array( '[if description]', '[description]', '[/if description]' ), $v );
1614 unset( $k, $v );
1615 continue;
1616 }
1617 $val[ $k ] = str_replace( $replace, $replace_with, $v );
1618 unset( $k, $v );
1619 }
1620 }
1621 } else {
1622 $val = str_replace( $replace, $replace_with, $val );
1623 }
1624
1625 return $val;
1626 }
1627
1628 /**
1629 * Removes exception strings from replacement arrays and replaces the rest in the provided value string.
1630 *
1631 * @since 6.14
1632 *
1633 * @param array $replace Values to be replaced.
1634 * @param array $replace_with Replacement values.
1635 * @param array $exceptions Array of strings to skip.
1636 * @param string $value Value to be updated.
1637 *
1638 * @return string
1639 */
1640 private static function switch_ids_except_strings( $replace, $replace_with, $exceptions, $value ) {
1641 foreach ( $exceptions as $exception ) {
1642 $index = array_search( $exception, $replace, true );
1643 if ( false === $index ) {
1644 continue;
1645 }
1646 unset( $replace[ $index ] );
1647 unset( $replace_with[ $index ] );
1648 }
1649 $value = str_replace( $replace, $replace_with, $value );
1650 return $value;
1651 }
1652
1653 /**
1654 * @since 4.0
1655 */
1656 public static function bulk_options_overlay() {
1657 $prepop = array();
1658 self::get_bulk_prefilled_opts( $prepop, true );
1659
1660 include FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/bulk-options-overlay.php';
1661 }
1662
1663 public static function get_us_states() {
1664 $states = array(
1665 'AL' => 'Alabama',
1666 'AK' => 'Alaska',
1667 'AR' => 'Arkansas',
1668 'AZ' => 'Arizona',
1669 'CA' => 'California',
1670 'CO' => 'Colorado',
1671 'CT' => 'Connecticut',
1672 'DE' => 'Delaware',
1673 'DC' => 'District of Columbia',
1674 'FL' => 'Florida',
1675 'GA' => 'Georgia',
1676 'HI' => 'Hawaii',
1677 'ID' => 'Idaho',
1678 'IL' => 'Illinois',
1679 'IN' => 'Indiana',
1680 'IA' => 'Iowa',
1681 'KS' => 'Kansas',
1682 'KY' => 'Kentucky',
1683 'LA' => 'Louisiana',
1684 'ME' => 'Maine',
1685 'MD' => 'Maryland',
1686 'MA' => 'Massachusetts',
1687 'MI' => 'Michigan',
1688 'MN' => 'Minnesota',
1689 'MS' => 'Mississippi',
1690 'MO' => 'Missouri',
1691 'MT' => 'Montana',
1692 'NE' => 'Nebraska',
1693 'NV' => 'Nevada',
1694 'NH' => 'New Hampshire',
1695 'NJ' => 'New Jersey',
1696 'NM' => 'New Mexico',
1697 'NY' => 'New York',
1698 'NC' => 'North Carolina',
1699 'ND' => 'North Dakota',
1700 'OH' => 'Ohio',
1701 'OK' => 'Oklahoma',
1702 'OR' => 'Oregon',
1703 'PA' => 'Pennsylvania',
1704 'RI' => 'Rhode Island',
1705 'SC' => 'South Carolina',
1706 'SD' => 'South Dakota',
1707 'TN' => 'Tennessee',
1708 'TX' => 'Texas',
1709 'UT' => 'Utah',
1710 'VT' => 'Vermont',
1711 'VA' => 'Virginia',
1712 'WA' => 'Washington',
1713 'WV' => 'West Virginia',
1714 'WI' => 'Wisconsin',
1715 'WY' => 'Wyoming',
1716 );
1717
1718 return apply_filters( 'frm_us_states', $states );
1719 }
1720
1721 public static function get_countries() {
1722 $countries = array(
1723 __( 'Afghanistan', 'formidable' ),
1724 __( 'Aland Islands', 'formidable' ),
1725 __( 'Albania', 'formidable' ),
1726 __( 'Algeria', 'formidable' ),
1727 __( 'American Samoa', 'formidable' ),
1728 __( 'Andorra', 'formidable' ),
1729 __( 'Angola', 'formidable' ),
1730 __( 'Anguilla', 'formidable' ),
1731 __( 'Antarctica', 'formidable' ),
1732 __( 'Antigua and Barbuda', 'formidable' ),
1733 __( 'Argentina', 'formidable' ),
1734 __( 'Armenia', 'formidable' ),
1735 __( 'Aruba', 'formidable' ),
1736 __( 'Australia', 'formidable' ),
1737 __( 'Austria', 'formidable' ),
1738 __( 'Azerbaijan', 'formidable' ),
1739 __( 'Bahamas', 'formidable' ),
1740 __( 'Bahrain', 'formidable' ),
1741 __( 'Bangladesh', 'formidable' ),
1742 __( 'Barbados', 'formidable' ),
1743 __( 'Belarus', 'formidable' ),
1744 __( 'Belgium', 'formidable' ),
1745 __( 'Belize', 'formidable' ),
1746 __( 'Benin', 'formidable' ),
1747 __( 'Bermuda', 'formidable' ),
1748 __( 'Bhutan', 'formidable' ),
1749 __( 'Bolivia', 'formidable' ),
1750 __( 'Bonaire, Sint Eustatius and Saba', 'formidable' ),
1751 __( 'Bosnia and Herzegovina', 'formidable' ),
1752 __( 'Botswana', 'formidable' ),
1753 __( 'Bouvet Island', 'formidable' ),
1754 __( 'Brazil', 'formidable' ),
1755 __( 'British Indian Ocean Territory', 'formidable' ),
1756 __( 'Brunei', 'formidable' ),
1757 __( 'Bulgaria', 'formidable' ),
1758 __( 'Burkina Faso', 'formidable' ),
1759 __( 'Burundi', 'formidable' ),
1760 __( 'Cambodia', 'formidable' ),
1761 __( 'Cameroon', 'formidable' ),
1762 __( 'Canada', 'formidable' ),
1763 __( 'Cape Verde', 'formidable' ),
1764 __( 'Cayman Islands', 'formidable' ),
1765 __( 'Central African Republic', 'formidable' ),
1766 __( 'Chad', 'formidable' ),
1767 __( 'Chile', 'formidable' ),
1768 __( 'China', 'formidable' ),
1769 __( 'Christmas Island', 'formidable' ),
1770 __( 'Cocos (Keeling) Islands', 'formidable' ),
1771 __( 'Colombia', 'formidable' ),
1772 __( 'Comoros', 'formidable' ),
1773 __( 'Congo', 'formidable' ),
1774 __( 'Cook Islands', 'formidable' ),
1775 __( 'Costa Rica', 'formidable' ),
1776 __( 'C&ocirc;te d\'Ivoire', 'formidable' ),
1777 __( 'Croatia', 'formidable' ),
1778 __( 'Cuba', 'formidable' ),
1779 __( 'Curacao', 'formidable' ),
1780 __( 'Cyprus', 'formidable' ),
1781 __( 'Czech Republic', 'formidable' ),
1782 __( 'Denmark', 'formidable' ),
1783 __( 'Djibouti', 'formidable' ),
1784 __( 'Dominica', 'formidable' ),
1785 __( 'Dominican Republic', 'formidable' ),
1786 __( 'East Timor', 'formidable' ),
1787 __( 'Ecuador', 'formidable' ),
1788 __( 'Egypt', 'formidable' ),
1789 __( 'El Salvador', 'formidable' ),
1790 __( 'Equatorial Guinea', 'formidable' ),
1791 __( 'Eritrea', 'formidable' ),
1792 __( 'Estonia', 'formidable' ),
1793 __( 'Ethiopia', 'formidable' ),
1794 __( 'Falkland Islands (Malvinas)', 'formidable' ),
1795 __( 'Faroe Islands', 'formidable' ),
1796 __( 'Fiji', 'formidable' ),
1797 __( 'Finland', 'formidable' ),
1798 __( 'France', 'formidable' ),
1799 __( 'French Guiana', 'formidable' ),
1800 __( 'French Polynesia', 'formidable' ),
1801 __( 'French Southern Territories', 'formidable' ),
1802 __( 'Gabon', 'formidable' ),
1803 __( 'Gambia', 'formidable' ),
1804 __( 'Georgia', 'formidable' ),
1805 __( 'Germany', 'formidable' ),
1806 __( 'Ghana', 'formidable' ),
1807 __( 'Gibraltar', 'formidable' ),
1808 __( 'Greece', 'formidable' ),
1809 __( 'Greenland', 'formidable' ),
1810 __( 'Grenada', 'formidable' ),
1811 __( 'Guadeloupe', 'formidable' ),
1812 __( 'Guam', 'formidable' ),
1813 __( 'Guatemala', 'formidable' ),
1814 __( 'Guernsey', 'formidable' ),
1815 __( 'Guinea', 'formidable' ),
1816 __( 'Guinea-Bissau', 'formidable' ),
1817 __( 'Guyana', 'formidable' ),
1818 __( 'Haiti', 'formidable' ),
1819 __( 'Heard Island and McDonald Islands', 'formidable' ),
1820 __( 'Holy See', 'formidable' ),
1821 __( 'Honduras', 'formidable' ),
1822 __( 'Hong Kong', 'formidable' ),
1823 __( 'Hungary', 'formidable' ),
1824 __( 'Iceland', 'formidable' ),
1825 __( 'India', 'formidable' ),
1826 __( 'Indonesia', 'formidable' ),
1827 __( 'Iran', 'formidable' ),
1828 __( 'Iraq', 'formidable' ),
1829 __( 'Ireland', 'formidable' ),
1830 __( 'Israel', 'formidable' ),
1831 __( 'Isle of Man', 'formidable' ),
1832 __( 'Italy', 'formidable' ),
1833 __( 'Jamaica', 'formidable' ),
1834 __( 'Japan', 'formidable' ),
1835 __( 'Jersey', 'formidable' ),
1836 __( 'Jordan', 'formidable' ),
1837 __( 'Kazakhstan', 'formidable' ),
1838 __( 'Kenya', 'formidable' ),
1839 __( 'Kiribati', 'formidable' ),
1840 __( 'North Korea', 'formidable' ),
1841 __( 'South Korea', 'formidable' ),
1842 __( 'Kosovo', 'formidable' ),
1843 __( 'Kuwait', 'formidable' ),
1844 __( 'Kyrgyzstan', 'formidable' ),
1845 __( 'Laos', 'formidable' ),
1846 __( 'Latvia', 'formidable' ),
1847 __( 'Lebanon', 'formidable' ),
1848 __( 'Lesotho', 'formidable' ),
1849 __( 'Liberia', 'formidable' ),
1850 __( 'Libya', 'formidable' ),
1851 __( 'Liechtenstein', 'formidable' ),
1852 __( 'Lithuania', 'formidable' ),
1853 __( 'Luxembourg', 'formidable' ),
1854 __( 'Macao', 'formidable' ),
1855 __( 'Macedonia', 'formidable' ),
1856 __( 'Madagascar', 'formidable' ),
1857 __( 'Malawi', 'formidable' ),
1858 __( 'Malaysia', 'formidable' ),
1859 __( 'Maldives', 'formidable' ),
1860 __( 'Mali', 'formidable' ),
1861 __( 'Malta', 'formidable' ),
1862 __( 'Marshall Islands', 'formidable' ),
1863 __( 'Martinique', 'formidable' ),
1864 __( 'Mauritania', 'formidable' ),
1865 __( 'Mauritius', 'formidable' ),
1866 __( 'Mayotte', 'formidable' ),
1867 __( 'Mexico', 'formidable' ),
1868 __( 'Micronesia', 'formidable' ),
1869 __( 'Moldova', 'formidable' ),
1870 __( 'Monaco', 'formidable' ),
1871 __( 'Mongolia', 'formidable' ),
1872 __( 'Montenegro', 'formidable' ),
1873 __( 'Montserrat', 'formidable' ),
1874 __( 'Morocco', 'formidable' ),
1875 __( 'Mozambique', 'formidable' ),
1876 __( 'Myanmar', 'formidable' ),
1877 __( 'Namibia', 'formidable' ),
1878 __( 'Nauru', 'formidable' ),
1879 __( 'Nepal', 'formidable' ),
1880 __( 'Netherlands', 'formidable' ),
1881 __( 'New Caledonia', 'formidable' ),
1882 __( 'New Zealand', 'formidable' ),
1883 __( 'Nicaragua', 'formidable' ),
1884 __( 'Niger', 'formidable' ),
1885 __( 'Nigeria', 'formidable' ),
1886 __( 'Niue', 'formidable' ),
1887 __( 'Norfolk Island', 'formidable' ),
1888 __( 'Northern Mariana Islands', 'formidable' ),
1889 __( 'Norway', 'formidable' ),
1890 __( 'Oman', 'formidable' ),
1891 __( 'Pakistan', 'formidable' ),
1892 __( 'Palau', 'formidable' ),
1893 __( 'Palestine', 'formidable' ),
1894 __( 'Panama', 'formidable' ),
1895 __( 'Papua New Guinea', 'formidable' ),
1896 __( 'Paraguay', 'formidable' ),
1897 __( 'Peru', 'formidable' ),
1898 __( 'Philippines', 'formidable' ),
1899 __( 'Pitcairn', 'formidable' ),
1900 __( 'Poland', 'formidable' ),
1901 __( 'Portugal', 'formidable' ),
1902 __( 'Puerto Rico', 'formidable' ),
1903 __( 'Qatar', 'formidable' ),
1904 __( 'Reunion', 'formidable' ),
1905 __( 'Romania', 'formidable' ),
1906 __( 'Russia', 'formidable' ),
1907 __( 'Rwanda', 'formidable' ),
1908 __( 'Saint Barthelemy', 'formidable' ),
1909 __( 'Saint Helena, Ascension and Tristan da Cunha', 'formidable' ),
1910 __( 'Saint Kitts and Nevis', 'formidable' ),
1911 __( 'Saint Lucia', 'formidable' ),
1912 __( 'Saint Martin (French part)', 'formidable' ),
1913 __( 'Saint Pierre and Miquelon', 'formidable' ),
1914 __( 'Saint Vincent and the Grenadines', 'formidable' ),
1915 __( 'Samoa', 'formidable' ),
1916 __( 'San Marino', 'formidable' ),
1917 __( 'Sao Tome and Principe', 'formidable' ),
1918 __( 'Saudi Arabia', 'formidable' ),
1919 __( 'Senegal', 'formidable' ),
1920 __( 'Serbia', 'formidable' ),
1921 __( 'Seychelles', 'formidable' ),
1922 __( 'Sierra Leone', 'formidable' ),
1923 __( 'Singapore', 'formidable' ),
1924 __( 'Sint Maarten (Dutch part)', 'formidable' ),
1925 __( 'Slovakia', 'formidable' ),
1926 __( 'Slovenia', 'formidable' ),
1927 __( 'Solomon Islands', 'formidable' ),
1928 __( 'Somalia', 'formidable' ),
1929 __( 'South Africa', 'formidable' ),
1930 __( 'South Georgia and the South Sandwich Islands', 'formidable' ),
1931 __( 'South Sudan', 'formidable' ),
1932 __( 'Spain', 'formidable' ),
1933 __( 'Sri Lanka', 'formidable' ),
1934 __( 'Sudan', 'formidable' ),
1935 __( 'Suriname', 'formidable' ),
1936 __( 'Svalbard and Jan Mayen', 'formidable' ),
1937 __( 'Swaziland', 'formidable' ),
1938 __( 'Sweden', 'formidable' ),
1939 __( 'Switzerland', 'formidable' ),
1940 __( 'Syria', 'formidable' ),
1941 __( 'Taiwan', 'formidable' ),
1942 __( 'Tajikistan', 'formidable' ),
1943 __( 'Tanzania', 'formidable' ),
1944 __( 'Thailand', 'formidable' ),
1945 __( 'Timor-Leste', 'formidable' ),
1946 __( 'Togo', 'formidable' ),
1947 __( 'Tokelau', 'formidable' ),
1948 __( 'Tonga', 'formidable' ),
1949 __( 'Trinidad and Tobago', 'formidable' ),
1950 __( 'Tunisia', 'formidable' ),
1951 __( 'Turkey', 'formidable' ),
1952 __( 'Turkmenistan', 'formidable' ),
1953 __( 'Turks and Caicos Islands', 'formidable' ),
1954 __( 'Tuvalu', 'formidable' ),
1955 __( 'Uganda', 'formidable' ),
1956 __( 'Ukraine', 'formidable' ),
1957 __( 'United Arab Emirates', 'formidable' ),
1958 __( 'United Kingdom', 'formidable' ),
1959 __( 'United States', 'formidable' ),
1960 __( 'United States Minor Outlying Islands', 'formidable' ),
1961 __( 'Uruguay', 'formidable' ),
1962 __( 'Uzbekistan', 'formidable' ),
1963 __( 'Vanuatu', 'formidable' ),
1964 __( 'Vatican City', 'formidable' ),
1965 __( 'Venezuela', 'formidable' ),
1966 __( 'Vietnam', 'formidable' ),
1967 __( 'Virgin Islands, British', 'formidable' ),
1968 __( 'Virgin Islands, U.S.', 'formidable' ),
1969 __( 'Wallis and Futuna', 'formidable' ),
1970 __( 'Western Sahara', 'formidable' ),
1971 __( 'Yemen', 'formidable' ),
1972 __( 'Zambia', 'formidable' ),
1973 __( 'Zimbabwe', 'formidable' ),
1974 );
1975
1976 sort( $countries, SORT_LOCALE_STRING );
1977 return apply_filters( 'frm_countries', $countries );
1978 }
1979
1980 /**
1981 * Gets bulk prefilled options.
1982 *
1983 * @since 5.0.04 Add `$include_class` param.
1984 *
1985 * @param array $prepop Bulk options.
1986 * @param array|false $include_class Include the class in the bulk options.
1987 */
1988 public static function get_bulk_prefilled_opts( array &$prepop, $include_class = false ) {
1989 // Countries.
1990 $countries = self::get_countries();
1991 if ( $include_class ) {
1992 $countries['class'] = 'frm-countries-opts';
1993 }
1994
1995 $prepop[ __( 'Countries', 'formidable' ) ] = $countries;
1996
1997 // State abv.
1998 $states = self::get_us_states();
1999 $state_abv = array_keys( $states );
2000 sort( $state_abv );
2001 if ( $include_class ) {
2002 $state_abv['class'] = 'frm-state-abv-opts';
2003 }
2004
2005 $prepop[ __( 'U.S. State Abbreviations', 'formidable' ) ] = $state_abv;
2006
2007 // States.
2008 $states = array_values( $states );
2009 sort( $states );
2010 if ( $include_class ) {
2011 $states['class'] = 'frm-states-opts';
2012 }
2013
2014 $prepop[ __( 'U.S. States', 'formidable' ) ] = $states;
2015 unset( $state_abv, $states );
2016
2017 // Age.
2018 $ages = array(
2019 __( 'Under 18', 'formidable' ),
2020 __( '18-24', 'formidable' ),
2021 __( '25-34', 'formidable' ),
2022 __( '35-44', 'formidable' ),
2023 __( '45-54', 'formidable' ),
2024 __( '55-64', 'formidable' ),
2025 __( '65 or Above', 'formidable' ),
2026 __( 'Prefer Not to Answer', 'formidable' ),
2027 );
2028 if ( $include_class ) {
2029 $ages['class'] = 'frm-age-opts';
2030 }
2031
2032 $prepop[ __( 'Age', 'formidable' ) ] = $ages;
2033
2034 // Satisfaction.
2035 $satisfaction = array(
2036 __( 'Very Unsatisfied', 'formidable' ),
2037 __( 'Unsatisfied', 'formidable' ),
2038 __( 'Neutral', 'formidable' ),
2039 __( 'Satisfied', 'formidable' ),
2040 __( 'Very Satisfied', 'formidable' ),
2041 __( 'N/A', 'formidable' ),
2042 );
2043 if ( $include_class ) {
2044 $satisfaction['class'] = 'frm-satisfaction-opts';
2045 }
2046
2047 $prepop[ __( 'Satisfaction', 'formidable' ) ] = $satisfaction;
2048
2049 // Importance.
2050 $importance = array(
2051 __( 'Not at all Important', 'formidable' ),
2052 __( 'Somewhat Important', 'formidable' ),
2053 __( 'Neutral', 'formidable' ),
2054 __( 'Important', 'formidable' ),
2055 __( 'Very Important', 'formidable' ),
2056 __( 'N/A', 'formidable' ),
2057 );
2058 if ( $include_class ) {
2059 $importance['class'] = 'frm-importance-opts';
2060 }
2061
2062 $prepop[ __( 'Importance', 'formidable' ) ] = $importance;
2063
2064 // Agreement.
2065 $agreement = array(
2066 __( 'Strongly Disagree', 'formidable' ),
2067 __( 'Disagree', 'formidable' ),
2068 __( 'Neutral', 'formidable' ),
2069 __( 'Agree', 'formidable' ),
2070 __( 'Strongly Agree', 'formidable' ),
2071 __( 'N/A', 'formidable' ),
2072 );
2073 if ( $include_class ) {
2074 $agreement['class'] = 'frm-agreement-opts';
2075 }
2076
2077 $prepop[ __( 'Agreement', 'formidable' ) ] = $agreement;
2078
2079 // Likely.
2080 $likely = array(
2081 __( 'Extremely Unlikely', 'formidable' ),
2082 __( 'Unlikely', 'formidable' ),
2083 __( 'Neutral', 'formidable' ),
2084 __( 'Likely', 'formidable' ),
2085 __( 'Extremely Likely', 'formidable' ),
2086 __( 'N/A', 'formidable' ),
2087 );
2088 if ( $include_class ) {
2089 $likely['class'] = 'frm-likely-opts';
2090 }
2091
2092 $prepop[ __( 'Likely', 'formidable' ) ] = $likely;
2093
2094 $prepop = apply_filters( 'frm_bulk_field_choices', $prepop );
2095 }
2096
2097 /**
2098 * Display a field value selector
2099 *
2100 * @since 2.03.05
2101 *
2102 * @param int $selector_field_id
2103 * @param array $selector_args
2104 */
2105 public static function display_field_value_selector( $selector_field_id, $selector_args ) {
2106 $field_value_selector = FrmFieldFactory::create_field_value_selector( $selector_field_id, $selector_args );
2107 $field_value_selector->display();
2108 }
2109
2110 /**
2111 * Convert a field object to a flat array
2112 *
2113 * @since 2.03.05
2114 *
2115 * @param object $field
2116 *
2117 * @return array
2118 */
2119 public static function convert_field_object_to_flat_array( $field ) {
2120 $field_options = is_array( $field->field_options ) ? $field->field_options : array();
2121 $field_array = get_object_vars( $field );
2122 unset( $field_array['field_options'] );
2123
2124 return $field_array + $field_options;
2125 }
2126
2127 /**
2128 * @since 4.04
2129 *
2130 * @param array $args
2131 * @return void
2132 */
2133 public static function show_add_field_buttons( $args ) {
2134 $field_key = $args['field_key'];
2135 $field_type = $args['field_type'];
2136 $field_label = FrmAppHelper::icon_by_class( FrmFormsHelper::get_field_link_icon( $field_type ), array( 'echo' => false ) );
2137 $field_name = FrmFormsHelper::get_field_link_name( $field_type );
2138 $field_label .= ' <span>' . $field_name . '</span>';
2139
2140 if ( ! empty( $field_type['is_new'] ) ) {
2141 ob_start();
2142 FrmAppHelper::show_pill_text();
2143 $field_label .= ob_get_clean();
2144 }
2145
2146 // If the individual field isn't allowed, disable it.
2147 $run_filter = true;
2148 $single_no_allow = ' ';
2149 $install_data = '';
2150 $requires = '';
2151 $link = isset( $field_type['link'] ) ? esc_url_raw( $field_type['link'] ) : '';
2152 $has_show_upgrade_class = isset( $field_type['icon'] ) && strpos( $field_type['icon'], ' frm_show_upgrade' );
2153 $show_upgrade = $has_show_upgrade_class || false !== strpos( $args['no_allow_class'], 'frm_show_upgrade' );
2154
2155 if ( $has_show_upgrade_class ) {
2156 $single_no_allow .= 'frm_show_upgrade';
2157 $field_type['icon'] = str_replace( ' frm_show_upgrade', '', $field_type['icon'] );
2158 $run_filter = false;
2159 if ( isset( $field_type['addon'] ) ) {
2160 $upgrading = FrmAddonsController::install_link( $field_type['addon'] );
2161 if ( isset( $upgrading['url'] ) ) {
2162 $install_data = json_encode( $upgrading );
2163 }
2164 $requires = FrmFormsHelper::get_plan_required( $upgrading );
2165 } elseif ( isset( $field_type['require'] ) ) {
2166 $requires = $field_type['require'];
2167 }
2168 }
2169
2170 $upgrade_label = '';
2171 $upgrade_message = '';
2172 if ( $show_upgrade ) {
2173 /* translators: %s: Field name */
2174 $upgrade_label = sprintf( esc_html__( '%s fields', 'formidable' ), $field_name );
2175
2176 if ( isset( $field_type['message'] ) ) {
2177 $upgrade_message = FrmAppHelper::kses( $field_type['message'], array( 'a', 'img' ) );
2178 }
2179 }
2180
2181 $li_params = array(
2182 'class' => 'frmbutton frm6 ' . $args['no_allow_class'] . $single_no_allow . ' frm_t' . str_replace( '|', '-', $field_key ),
2183 'id' => $field_key,
2184 'data-upgrade' => $upgrade_label,
2185 'data-link' => $link,
2186 'data-medium' => 'builder',
2187 'data-oneclick' => $install_data,
2188 'data-content' => $field_key,
2189 'data-requires' => $requires,
2190 );
2191
2192 if ( ! empty( $field_type['hide'] ) ) {
2193 $li_params['class'] .= ' frm_hidden';
2194 }
2195
2196 if ( ! empty( $field_type['upsell_image'] ) ) {
2197 $li_params['data-upsell-image'] = $field_type['upsell_image'];
2198 }
2199
2200 if ( $upgrade_message ) {
2201 $li_params['data-message'] = $upgrade_message;
2202 }
2203 ?>
2204 <li <?php FrmAppHelper::array_to_html_params( $li_params, true ); ?>>
2205 <?php
2206 if ( $run_filter ) {
2207 $field_label = apply_filters( 'frmpro_field_links', $field_label, $args['id'], $field_key );
2208 }
2209 FrmAppHelper::kses_echo( $field_label, array( 'a', 'i', 'span', 'use', 'svg' ) );
2210 ?>
2211 </li>
2212 <?php
2213 }
2214
2215 /**
2216 * Shows Display format option.
2217 *
2218 * @since 5.0.04
2219 *
2220 * @param array $field Field data.
2221 */
2222 public static function show_radio_display_format( $field ) {
2223 $options = self::get_display_format_options( $field );
2224
2225 $args = self::get_display_format_args( $field, $options );
2226
2227 include FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/radio-display-format.php';
2228 }
2229
2230 /**
2231 * Creates an array that contains variables used for display format options setting.
2232 *
2233 * @since 6.3.2
2234 *
2235 * @param array $field The field.
2236 *
2237 * @return array
2238 */
2239 public static function get_display_format_options( $field ) {
2240 $options = array(
2241 '0' => array(
2242 'text' => __( 'Simple', 'formidable' ),
2243 'svg' => 'frm_simple_radio',
2244 ),
2245 '1' => array(
2246 'text' => __( 'Images', 'formidable' ),
2247 'svg' => 'frm_image_as_option',
2248 'addon' => 'pro',
2249 'upgrade' => __( 'Image Options', 'formidable' ),
2250 '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" />',
2251 'content' => 'image-options',
2252 ),
2253 'buttons' => array(
2254 'text' => __( 'Buttons', 'formidable' ),
2255 'svg' => 'frm_button_as_option',
2256 'addon' => 'surveys',
2257 'upgrade' => __( 'Button Options', 'formidable' ),
2258 'message' => __( 'Show buttons for radio buttons or check boxes. This is ideal for polls, surveys, segmenting questionnaires and more.', 'formidable' ),
2259 'content' => 'button-options',
2260 ),
2261 );
2262
2263 /**
2264 * Allows modifying the options of Display format setting of Radio field.
2265 *
2266 * @since 5.0.04
2267 *
2268 * @param array $options Options.
2269 * @param array $field
2270 */
2271 $options = apply_filters( 'frm_' . $field['type'] . '_display_format_options', $options, $field );
2272
2273 return $options;
2274 }
2275
2276 /**
2277 * Gets display format arguments to pass to the images_dropdown() method.
2278 *
2279 * @since 5.0.04
2280 *
2281 * @param array $field Field data.
2282 * @param array $options Options array.
2283 * @return array
2284 */
2285 public static function get_display_format_args( $field, $options ) {
2286 $args = array(
2287 'selected' => '0',
2288 'options' => array(),
2289 'name' => 'field_options[image_options_' . $field['id'] . ']',
2290 'input_attrs' => array(
2291 'class' => 'frm_toggle_image_options',
2292 ),
2293 );
2294
2295 self::fill_image_setting_options( $options, $args );
2296
2297 /**
2298 * Allows modifying the arguments of Display format setting of Radio field.
2299 *
2300 * @since 5.0.04
2301 *
2302 * @param array $args Arguments.
2303 * @param array $method_args The arguments from the method. Contains `field`, `options`.
2304 */
2305 return apply_filters( 'frm_' . $field['type'] . '_display_format_args', $args, compact( 'field', 'options' ) );
2306 }
2307
2308 /**
2309 * @since 5.0.04
2310 */
2311 private static function fill_image_setting_options( $options, &$args ) {
2312 foreach ( $options as $key => $option ) {
2313 $args['options'][ $key ] = $option;
2314
2315 if ( ! empty( $option['addon'] ) ) {
2316 $args['options'][ $key ]['custom_attrs'] = self::fill_image_setting_addon_link( $option );
2317 }
2318
2319 unset( $args['options'][ $key ]['addon'] );
2320 $fill = array( 'upgrade', 'message', 'content' );
2321 foreach ( $fill as $f ) {
2322 unset( $args['options'][ $key ][ $f ], $f );
2323 }
2324 }
2325 }
2326
2327 /**
2328 * @since 5.0.04
2329 *
2330 * @return array
2331 */
2332 private static function fill_image_setting_addon_link( $option ) {
2333 $custom_attrs = array(
2334 'class' => 'frm_noallow frm_show_upgrade',
2335 'data-medium' => 'builder',
2336 );
2337
2338 // translators: Add-on name.
2339 $custom_attrs['data-upgrade'] = sprintf( __( 'Formidable %s', 'formidable' ), ucwords( $option['addon'] ) );
2340
2341 $fill = array( 'upgrade', 'message', 'content' );
2342 foreach ( $fill as $f ) {
2343 if ( isset( $option[ $f ] ) ) {
2344 $custom_attrs[ 'data-' . $f ] = $option[ $f ];
2345 }
2346 }
2347
2348 if ( 'pro' === $option['addon'] ) {
2349 return $custom_attrs;
2350 }
2351
2352 $upgrading = FrmAddonsController::install_link( $option['addon'] );
2353 if ( isset( $upgrading['url'] ) ) {
2354 $install_data = wp_json_encode( $upgrading );
2355 } else {
2356 $install_data = '';
2357 }
2358
2359 $custom_attrs['data-oneclick'] = $install_data;
2360 $custom_attrs['data-requires'] = FrmFormsHelper::get_plan_required( $upgrading );
2361
2362 return $custom_attrs;
2363 }
2364
2365 /**
2366 * Maybe adjust a field value based on type.
2367 * Some types require unserializing an array.
2368 * These types are defined by a array_allowed property on their field model class.
2369 *
2370 * @since 6.2
2371 *
2372 * @param mixed $value
2373 * @param string $field_type
2374 * @return void
2375 */
2376 public static function prepare_field_value( &$value, $field_type ) {
2377 $field_object = FrmFieldFactory::get_field_type( $field_type );
2378 $value = $field_object->maybe_decode_value( $value );
2379 }
2380
2381 /**
2382 * @since 6.8
2383 *
2384 * @param int|string $form_id
2385 * @param array $field_ids If this is not empty, the results will be filtered by field id.
2386 * @return array
2387 */
2388 public static function get_draft_field_results( $form_id, $field_ids = array() ) {
2389 if ( FrmAppHelper::pro_is_installed() ) {
2390 $child_form_ids = FrmDb::get_col( 'frm_forms', array( 'parent_form_id' => $form_id ) );
2391 $form_ids = array_merge( array( $form_id ), $child_form_ids );
2392 } else {
2393 $form_ids = array( $form_id );
2394 }
2395
2396 $where = array(
2397 'form_id' => $form_ids,
2398 // Do a soft check for fields that look like drafts only.
2399 'field_options LIKE' => 's:5:"draft";i:1;',
2400 );
2401
2402 if ( $field_ids ) {
2403 $where['id'] = $field_ids;
2404 }
2405
2406 $rows = FrmDb::get_results( 'frm_fields', $where, 'id, field_options' );
2407
2408 return array_filter(
2409 $rows,
2410 function ( $row ) {
2411 FrmAppHelper::unserialize_or_decode( $row->field_options );
2412 return is_array( $row->field_options ) && ! empty( $row->field_options['draft'] );
2413 }
2414 );
2415 }
2416
2417 /**
2418 * This is called when loading the form builder.
2419 * Any unsaved draft fields get added to a hidden draft_fields input on load.
2420 *
2421 * @since 6.8
2422 *
2423 * @param int|string $form_id
2424 * @return array
2425 */
2426 public static function get_all_draft_field_ids( $form_id ) {
2427 $draft_field_rows = self::get_draft_field_results( $form_id );
2428 return wp_list_pluck( $draft_field_rows, 'id' );
2429 }
2430
2431 /**
2432 * Render AI generate options button.
2433 *
2434 * @since 6.24
2435 *
2436 * @param array $args Field arguments.
2437 * @param bool $should_hide_bulk_edit Whether to hide bulk edit.
2438 */
2439 public static function render_ai_generate_options_button( $args, $should_hide_bulk_edit = false ) {
2440 $attributes = array( 'class' => self::get_ai_generate_options_button_class() );
2441
2442 if ( ! empty( $should_hide_bulk_edit ) ) {
2443 $attributes['class'] .= ' frm-force-hidden';
2444 }
2445
2446 $data = FrmAppHelper::get_upgrade_data_params(
2447 'ai',
2448 array(
2449 'requires' => 'Business',
2450 'upgrade' => __( 'Generate options with AI', 'formidable' ),
2451 'medium' => 'builder',
2452 'content' => 'generate-options-with-ai',
2453 ),
2454 true
2455 );
2456
2457 if ( in_array( FrmAddonsController::license_type(), array( 'elite', 'business' ), true ) && 'active' === $data['plugin-status'] ) {
2458 // Backwards compatibility "@since 6.24".
2459 if ( ! method_exists( 'FrmAIAppController', 'get_ai_generated_options_summary' ) ) {
2460 $data = array(
2461 'modal-title' => __( 'Generate options with AI', 'formidable' ),
2462 'modal-content' => __( 'Update the Formidable AI add-on to the last version to use this feature.', 'formidable' ),
2463 );
2464 } else {
2465 $attributes['class'] .= ' frm-ai-generate-options-modal-trigger';
2466 $attributes['data-fid'] = $args['likert_id'] ?? $args['field']['id'];
2467 }
2468 }
2469
2470 if ( empty( $attributes['data-fid'] ) ) {
2471 unset( $data['plugin-status'] );
2472 foreach ( $data as $key => $value ) {
2473 $attributes[ 'data-' . $key ] = $value;
2474 }
2475 }
2476
2477 include FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/generate-options-with-ai.php';
2478 }
2479
2480 /**
2481 * Get AI generate options button class.
2482 *
2483 * @since 6.24
2484 *
2485 * @return string Button class.
2486 */
2487 private static function get_ai_generate_options_button_class() {
2488 return implode(
2489 ' ',
2490 array(
2491 'frm_form_field',
2492 'frm6',
2493 'frm6_followed',
2494 'frm-h-stack',
2495 'button',
2496 'frm-button-secondary',
2497 'frm-button-gradient',
2498 'frm-rounded-6',
2499 'frm-max-w-fit',
2500 'frm-font-normal',
2501 'frm-py-2xs',
2502 'frm-px-xs',
2503 'frm-mt-xs',
2504 'frm-mb-12',
2505 )
2506 );
2507 }
2508 }
2509