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

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