PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.16.1
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.16.1
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
formidable / classes / helpers / FrmFieldsHelper.php

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

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