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

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