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

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