PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.9
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.9
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 / controllers / FrmFieldsController.php

FrmFieldsController.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 6.9, at classes/controllers/FrmFieldsController.php

1,002 lines 27.9 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 FrmFieldsController {
7
8 public static function load_field() {
9 FrmAppHelper::permission_check( 'frm_edit_forms' );
10 check_ajax_referer( 'frm_ajax', 'nonce' );
11
12 // Javascript may be included in some field settings.
13 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
14 $fields = isset( $_POST['field'] ) ? wp_unslash( $_POST['field'] ) : array();
15 if ( empty( $fields ) ) {
16 wp_die();
17 }
18
19 $_GET['page'] = 'formidable';
20
21 $values = array(
22 'id' => FrmAppHelper::get_post_param( 'form_id', '', 'absint' ),
23 'doing_ajax' => true,
24 );
25 $field_html = array();
26
27 foreach ( $fields as $field ) {
28 $field = htmlspecialchars_decode( nl2br( $field ) );
29 $field = json_decode( $field );
30 if ( ! isset( $field->id ) || ! is_numeric( $field->id ) ) {
31 // this field may have already been loaded
32 continue;
33 }
34
35 if ( ! isset( $field->value ) ) {
36 $field->value = '';
37 }
38 $field->field_options = json_decode( json_encode( $field->field_options ), true );
39 $field->options = json_decode( json_encode( $field->options ), true );
40 $field->default_value = json_decode( json_encode( $field->default_value ), true );
41
42 ob_start();
43 self::load_single_field( $field, $values );
44 $field_html[ absint( $field->id ) ] = ob_get_contents();
45 ob_end_clean();
46 }
47
48 echo json_encode( $field_html );
49
50 wp_die();
51 }
52
53 /**
54 * Create a new field with ajax
55 */
56 public static function create() {
57 FrmAppHelper::permission_check( 'frm_edit_forms' );
58 check_ajax_referer( 'frm_ajax', 'nonce' );
59
60 $field_type = FrmAppHelper::get_post_param( 'field_type', '', 'sanitize_text_field' );
61 $form_id = FrmAppHelper::get_post_param( 'form_id', 0, 'absint' );
62
63 do_action( 'frm_before_create_field', $field_type, $form_id );
64
65 $field = self::include_new_field( $field_type, $form_id );
66
67 // this hook will allow for multiple fields to be added at once
68 do_action( 'frm_after_field_created', $field, $form_id );
69
70 wp_die();
71 }
72
73 /**
74 * Set up and create a new field
75 *
76 * @param string $field_type
77 * @param integer $form_id
78 *
79 * @return array|bool
80 */
81 public static function include_new_field( $field_type, $form_id ) {
82 $field_values = FrmFieldsHelper::setup_new_vars( $field_type, $form_id );
83
84 // When a new field is added to the form, flag it as draft and hide it from the front-end.
85 $field_values['field_options']['draft'] = 1;
86
87 /**
88 * @param array $field_values
89 */
90 $field_values = apply_filters( 'frm_before_field_created', $field_values );
91 $field_id = FrmField::create( $field_values );
92
93 if ( ! $field_id ) {
94 return false;
95 }
96
97 $field = self::get_field_array_from_id( $field_id );
98
99 $values = array();
100 if ( FrmAppHelper::pro_is_installed() ) {
101 $values['post_type'] = FrmProFormsHelper::post_type( $form_id );
102
103 $parent_form_id = FrmDb::get_var( 'frm_forms', array( 'id' => $form_id ), 'parent_form_id' );
104 if ( $parent_form_id ) {
105 $field['parent_form_id'] = $parent_form_id;
106 }
107 }
108
109 self::load_single_field( $field, $values, $form_id );
110
111 return $field;
112 }
113
114 public static function duplicate() {
115 FrmAppHelper::permission_check( 'frm_edit_forms' );
116 check_ajax_referer( 'frm_ajax', 'nonce' );
117
118 $field_id = FrmAppHelper::get_post_param( 'field_id', 0, 'absint' );
119 $form_id = FrmAppHelper::get_post_param( 'form_id', 0, 'absint' );
120
121 $new_field = FrmField::duplicate_single_field( $field_id, $form_id );
122
123 if ( is_array( $new_field ) && ! empty( $new_field['field_id'] ) ) {
124 self::load_single_field( $new_field['field_id'], $new_field['values'] );
125 }
126
127 wp_die();
128 }
129
130 /**
131 * @since 3.0
132 *
133 * @param int $field_id
134 *
135 * @return array
136 */
137 public static function get_field_array_from_id( $field_id ) {
138 $field = FrmField::getOne( $field_id );
139
140 return FrmFieldsHelper::setup_edit_vars( $field );
141 }
142
143 /**
144 * @since 3.0
145 *
146 * @param int|array|object $field_object
147 * @param array $values
148 * @param int $form_id
149 */
150 public static function load_single_field( $field_object, $values, $form_id = 0 ) {
151 global $frm_vars;
152 $frm_vars['is_admin'] = true;
153
154 if ( is_numeric( $field_object ) ) {
155 $field_object = FrmField::getOne( $field_object );
156 } elseif ( is_array( $field_object ) ) {
157 $field = $field_object;
158 $field_object = FrmField::getOne( $field['id'] );
159 }
160
161 $field_obj = FrmFieldFactory::get_field_factory( $field_object );
162 $display = self::display_field_options( array(), $field_obj );
163
164 $ajax_loading = isset( $values['ajax_load'] ) && $values['ajax_load'];
165 $ajax_this_field = isset( $values['count'] ) && $values['count'] > 10 && ! in_array( $field_object->type, array( 'divider', 'end_divider' ) );
166
167 if ( $ajax_loading && $ajax_this_field ) {
168 $li_classes = self::get_classes_for_builder_field( array(), $display, $field_obj );
169 include FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/ajax-field-placeholder.php';
170 } else {
171 if ( ! isset( $field ) && is_object( $field_object ) ) {
172 $field_object->parent_form_id = isset( $values['id'] ) ? $values['id'] : $field_object->form_id;
173
174 $field = FrmFieldsHelper::setup_edit_vars( $field_object );
175 }
176
177 $li_classes = self::get_classes_for_builder_field( $field, $display, $field_obj );
178 $li_classes .= ' ui-state-default widgets-holder-wrap';
179
180 require FrmAppHelper::plugin_path() . '/classes/views/frm-forms/add_field.php';
181 }
182 }
183
184 /**
185 * @since 3.0
186 */
187 private static function get_classes_for_builder_field( $field, $display, $field_info ) {
188 $li_classes = $field_info->form_builder_classes( $display['type'] );
189 $li_classes .= ' frm_form_field frmstart ';
190
191 if ( isset( $field['classes'] ) ) {
192 $li_classes .= trim( $field['classes'] ) . ' ';
193 }
194
195 $li_classes .= 'frmend';
196
197 if ( $field ) {
198 $li_classes = apply_filters( 'frm_build_field_class', $li_classes, $field );
199 }
200
201 return $li_classes;
202 }
203
204 public static function destroy() {
205 FrmAppHelper::permission_check( 'frm_edit_forms' );
206 check_ajax_referer( 'frm_ajax', 'nonce' );
207
208 $field_id = FrmAppHelper::get_post_param( 'field_id', 0, 'absint' );
209 FrmField::destroy( $field_id );
210 wp_die();
211 }
212
213 /**
214 * Field Options.
215 */
216 public static function import_options() {
217 FrmAppHelper::permission_check( 'frm_edit_forms' );
218 check_ajax_referer( 'frm_ajax', 'nonce' );
219
220 if ( ! is_admin() || ! current_user_can( 'frm_edit_forms' ) ) {
221 return;
222 }
223
224 $field_id = FrmAppHelper::get_param( 'field_id', '', 'post', 'absint' );
225 $field = FrmField::getOne( $field_id );
226 $bulk_edit_types = array( 'radio', 'checkbox', 'select' );
227
228 /**
229 * Filter to add new fields that will support import_options/Bulk Edit Options.
230 *
231 * @since 6.8.4
232 *
233 * @param array $bulk_edit_types
234 * @return array
235 */
236 $bulk_edit_types = apply_filters( 'frm_bulk_edit_field_types', $bulk_edit_types );
237
238 if ( ! in_array( $field->type, $bulk_edit_types, true ) ) {
239 return;
240 }
241
242 $field = FrmFieldsHelper::setup_edit_vars( $field );
243 $opts = FrmAppHelper::get_param( 'opts', '', 'post', 'wp_kses_post' );
244 $opts = explode( "\n", rtrim( $opts, "\n" ) );
245 $opts = array_map( 'trim', $opts );
246
247 $separate = FrmAppHelper::get_param( 'separate', '', 'post', 'sanitize_text_field' );
248 $field['separate_value'] = ( $separate === 'true' );
249
250 if ( $field['separate_value'] ) {
251 foreach ( $opts as $opt_key => $opt ) {
252 if ( strpos( $opt, '|' ) !== false ) {
253 $vals = explode( '|', $opt );
254 $opts[ $opt_key ] = array(
255 'label' => trim( $vals[0] ),
256 'value' => trim( $vals[1] ),
257 );
258 unset( $vals );
259 }
260 unset( $opt_key, $opt );
261 }
262 }
263
264 // Keep other options after bulk update.
265 if ( isset( $field['field_options']['other'] ) && $field['field_options']['other'] == true ) {
266 $other_array = array();
267 foreach ( $field['options'] as $opt_key => $opt ) {
268 if ( FrmFieldsHelper::is_other_opt( $opt_key ) ) {
269 $other_array[ $opt_key ] = $opt;
270 }
271 unset( $opt_key, $opt );
272 }
273 if ( ! empty( $other_array ) ) {
274 $opts = array_merge( $opts, $other_array );
275 }
276 }
277
278 $field['options'] = $opts;
279
280 FrmFieldsHelper::show_single_option( $field );
281
282 wp_die();
283 }
284
285 /**
286 * @since 4.0
287 *
288 * @param array $atts - Includes field array, field_obj, display array, values array.
289 */
290 public static function load_single_field_settings( $atts ) {
291 $field = $atts['field'];
292 $field_obj = $atts['field_obj'];
293 $values = $atts['values'];
294 $display = $atts['display'];
295 unset( $atts );
296
297 if ( ! isset( $field['unique'] ) ) {
298 $field['unique'] = false;
299 }
300
301 if ( ! isset( $field['read_only'] ) ) {
302 $field['read_only'] = false;
303 }
304
305 $field_types = FrmFieldsHelper::get_field_types( $field['type'] );
306 $pro_field_selection = FrmField::pro_field_selection();
307 $all_field_types = array_merge( $pro_field_selection, FrmField::field_selection() );
308 $disabled_fields = FrmAppHelper::pro_is_installed() ? array() : $pro_field_selection;
309 $frm_settings = FrmAppHelper::get_settings();
310
311 if ( ! isset( $all_field_types[ $field['type'] ] ) ) {
312 // Add fallback for an add-on field type that has been deactivated.
313 $all_field_types[ $field['type'] ] = array(
314 'name' => ucfirst( $field['type'] ),
315 'icon' => 'frm_icon_font frm_pencil_icon',
316 );
317 } elseif ( ! is_array( $all_field_types[ $field['type'] ] ) ) {
318 // Fallback for fields added in a more basic way.
319 FrmFormsHelper::prepare_field_type( $all_field_types[ $field['type'] ] );
320 }
321
322 $type_name = $all_field_types[ $field['type'] ]['name'];
323 if ( $field['type'] === 'divider' && FrmField::is_option_true( $field, 'repeat' ) ) {
324 $type_name = $all_field_types['divider|repeat']['name'];
325 }
326
327 if ( $display['default'] ) {
328 $default_value_types = self::default_value_types( $field, compact( 'display' ) );
329 }
330
331 if ( $display['clear_on_focus'] && is_array( $field['placeholder'] ) ) {
332 $field['placeholder'] = implode( ', ', $field['placeholder'] );
333 }
334
335 include FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/settings.php';
336 }
337
338 /**
339 * Get the list of default value types that can be toggled in the builder.
340 *
341 * @since 4.0
342 * @return array
343 */
344 private static function default_value_types( $field, $atts ) {
345 $types = array(
346 'default_value' => array(
347 'class' => '',
348 'icon' => 'frm_icon_font frm_text2_icon',
349 'title' => __( 'Default Value (Text)', 'formidable' ),
350 'data' => array(
351 'frmshow' => '#default-value-for-',
352 ),
353 ),
354 'calc' => array(
355 'class' => 'frm_show_upgrade frm_noallow',
356 'title' => __( 'Default Value (Calculation)', 'formidable' ),
357 'icon' => 'frm_icon_font frm_calculator_icon',
358 'data' => array(
359 'medium' => 'calculations',
360 'upgrade' => __( 'Calculator forms', 'formidable' ),
361 ),
362 ),
363 'get_values_field' => array(
364 'class' => 'frm_show_upgrade frm_noallow',
365 'title' => __( 'Default Value (Lookup)', 'formidable' ),
366 'icon' => 'frm_icon_font frm_search_icon',
367 'data' => array(
368 'medium' => 'lookup',
369 'upgrade' => __( 'Lookup fields', 'formidable' ),
370 ),
371 ),
372 );
373
374 $types = apply_filters( 'frm_default_value_types', $types, $atts );
375
376 if ( FrmAppHelper::pro_is_installed() && ! FrmAppHelper::meets_min_pro_version( '4.0' ) ) {
377 // Prevent settings from showing in 2 spots.
378 unset( $types['calc'], $types['get_values_field'] );
379 }
380
381 // Set active class.
382 $settings = array_keys( $types );
383 $active = 'default_value';
384
385 foreach ( $settings as $type ) {
386 if ( ! empty( $field[ $type ] ) ) {
387 $active = $type;
388 }
389 }
390
391 $types[ $active ]['class'] .= ' current';
392 $types[ $active ]['current'] = true;
393
394 return $types;
395 }
396
397 public static function change_type( $type ) {
398 $type_switch = array(
399 'scale' => 'radio',
400 'star' => 'radio',
401 '10radio' => 'radio',
402 'rte' => 'textarea',
403 'website' => 'url',
404 'image' => 'url',
405 );
406 if ( isset( $type_switch[ $type ] ) ) {
407 $type = $type_switch[ $type ];
408 }
409
410 $pro_fields = FrmField::pro_field_selection();
411 // We want to keep credit_card types as credit card types for Stripe Lite.
412 // The credit_card key is set for backward compatibility.
413 unset( $pro_fields['credit_card'] );
414
415 if ( array_key_exists( $type, $pro_fields ) ) {
416 $type = 'text';
417 }
418
419 return $type;
420 }
421
422 /**
423 * @param array $settings
424 * @param object $field_info
425 *
426 * @return array
427 */
428 public static function display_field_options( $settings, $field_info = null ) {
429 if ( $field_info ) {
430 $settings = $field_info->display_field_settings();
431 $settings['field_data'] = $field_info->field;
432 }
433
434 return apply_filters( 'frm_display_field_options', $settings );
435 }
436
437 /**
438 * Display the format option
439 *
440 * @since 3.0
441 *
442 * @param array $field
443 */
444 public static function show_format_option( $field ) {
445 $attributes = array();
446 $attributes['class'] = 'frm-has-modal';
447
448 if ( 'phone' === $field['type'] ) {
449 $attributes['id'] = 'frm-phone-field-custom-format-' . $field['id'];
450 $attributes['class'] .= ' frm_hidden';
451 }
452
453 include FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/value-format.php';
454 }
455
456 public static function input_html( $field, $echo = true ) {
457 $class = array();
458 self::add_input_classes( $field, $class );
459
460 $add_html = array();
461 self::add_html_size( $field, $add_html );
462 self::add_html_length( $field, $add_html );
463 self::add_html_placeholder( $field, $add_html, $class );
464 self::add_validation_messages( $field, $add_html );
465
466 $class = apply_filters( 'frm_field_classes', implode( ' ', $class ), $field );
467
468 FrmFormsHelper::add_html_attr( $class, 'class', $add_html );
469
470 self::add_shortcodes_to_html( $field, $add_html );
471 self::add_pattern_attribute( $field, $add_html );
472
473 $add_html = apply_filters( 'frm_field_extra_html', $add_html, $field );
474 $add_html = ' ' . implode( ' ', $add_html ) . ' ';
475
476 if ( $echo ) {
477 echo $add_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
478 }
479
480 return $add_html;
481 }
482
483 private static function add_input_classes( $field, array &$class ) {
484 if ( isset( $field['input_class'] ) && ! empty( $field['input_class'] ) ) {
485 $class[] = $field['input_class'];
486 }
487
488 if ( $field['type'] == 'hidden' || $field['type'] == 'user_id' ) {
489 return;
490 }
491
492 if ( isset( $field['size'] ) && $field['size'] > 0 ) {
493 $class[] = 'auto_width';
494 }
495 }
496
497 private static function add_html_size( $field, array &$add_html ) {
498 $size_fields = array(
499 'select',
500 'data',
501 'time',
502 'hidden',
503 'file',
504 'lookup',
505 );
506
507 if ( ! isset( $field['size'] ) || $field['size'] <= 0 || in_array( $field['type'], $size_fields ) ) {
508 return;
509 }
510
511 if ( FrmAppHelper::is_admin_page( 'formidable' ) ) {
512 return;
513 }
514
515 if ( is_numeric( $field['size'] ) ) {
516 $field['size'] .= 'px';
517 }
518
519 $important = apply_filters( 'frm_use_important_width', 1, $field );
520 // Note: This inline styling must stay since we cannot realistically set a class for every possible field size.
521 $add_html['style'] = 'style="width:' . esc_attr( $field['size'] ) . ( $important ? ' !important' : '' ) . '"';
522
523 self::add_html_cols( $field, $add_html );
524 }
525
526 private static function add_html_cols( $field, array &$add_html ) {
527 if ( ! in_array( $field['type'], array( 'textarea', 'rte' ), true ) ) {
528 return;
529 }
530
531 // Convert to cols for textareas.
532 $calc = array(
533 '' => 9,
534 'px' => 9,
535 'rem' => 0.444,
536 'em' => 0.544,
537 );
538
539 // include "col" for valid html
540 $unit = trim( preg_replace( '/[0-9]+/', '', $field['size'] ) );
541
542 if ( ! isset( $calc[ $unit ] ) ) {
543 return;
544 }
545
546 $size = (float) str_replace( $unit, '', $field['size'] ) / $calc[ $unit ];
547
548 $add_html['cols'] = 'cols="' . absint( $size ) . '"';
549 }
550
551 private static function add_html_length( $field, array &$add_html ) {
552 // Check for max setting and if this field accepts maxlength.
553 $fields = array(
554 'textarea',
555 'rte',
556 'hidden',
557 'file',
558 );
559
560 if ( FrmField::is_option_empty( $field, 'max' ) || in_array( $field['type'], $fields ) ) {
561 return;
562 }
563
564 if ( FrmAppHelper::is_admin_page( 'formidable' ) ) {
565 // Don't load on form builder page.
566 return;
567 }
568
569 $add_html['maxlength'] = 'maxlength="' . esc_attr( $field['max'] ) . '"';
570 }
571
572 private static function add_html_placeholder( $field, array &$add_html, array &$class ) {
573 if ( $field['default_value'] != '' ) {
574 if ( is_array( $field['default_value'] ) ) {
575 $add_html['data-frmval'] = 'data-frmval="' . esc_attr( json_encode( $field['default_value'] ) ) . '"';
576 } else {
577 self::add_frmval_to_input( $field, $add_html );
578 }
579 }
580
581 $field['placeholder'] = self::prepare_placeholder( $field );
582 if ( $field['placeholder'] == '' || is_array( $field['placeholder'] ) ) {
583 // don't include a json placeholder
584 return;
585 }
586
587 $frm_settings = FrmAppHelper::get_settings();
588
589 if ( $frm_settings->use_html ) {
590 self::add_placeholder_to_input( $field, $add_html );
591 } else {
592 self::add_frmval_to_input( $field, $add_html );
593
594 $class[] = 'frm_toggle_default';
595
596 if ( $field['value'] == $field['placeholder'] ) {
597 $class[] = 'frm_default';
598 }
599 }
600 }
601
602 /**
603 * Prepares field placeholder.
604 *
605 * @since 5.4 This doesn't call `FrmFieldsController::get_default_value_from_name()` anymore.
606 *
607 * @param array $field Field array.
608 * @return string
609 */
610 private static function prepare_placeholder( $field ) {
611 $placeholder = isset( $field['placeholder'] ) ? $field['placeholder'] : '';
612
613 return $placeholder;
614 }
615
616 /**
617 * If the label position is "inside",
618 * get the label to use as the placeholder
619 *
620 * @since 2.05
621 * @since 5.4 Remove the logic code for "inside" label position.
622 *
623 * @param array $field
624 *
625 * @return string
626 */
627 public static function get_default_value_from_name( $field ) {
628 return '';
629 }
630
631 /**
632 * Maybe add a blank placeholder option before any options
633 * in a dropdown.
634 *
635 * @since 4.04
636 * @return bool True if placeholder was added.
637 */
638 public static function add_placeholder_to_select( $field ) {
639 $placeholder = FrmField::get_option( $field, 'placeholder' );
640 if ( empty( $placeholder ) ) {
641 $placeholder = self::get_default_value_from_name( $field );
642 }
643
644 $use_placeholder = $placeholder;
645 $autocomplete = FrmField::get_option( $field, 'autocom' );
646
647 if ( $autocomplete ) {
648 $use_chosen = ! is_callable( 'FrmProAppHelper::use_chosen_js' ) || FrmProAppHelper::use_chosen_js();
649 if ( $use_chosen ) {
650 $use_placeholder = '';
651 }
652 }
653
654 if ( $placeholder !== '' ) {
655 $placeholder_attributes = array(
656 'class' => 'frm-select-placeholder',
657 'value' => '',
658 'data-placeholder' => 'true',
659 );
660
661 if ( $autocomplete && empty( $use_chosen ) ) {
662 // This is required for Slim Select.
663 $placeholder_attributes['data-placeholder'] = 'true';
664 }
665
666 FrmHtmlHelper::echo_dropdown_option( $use_placeholder, false, $placeholder_attributes );
667 return true;
668 }
669
670 return false;
671 }
672
673 /**
674 * Use HMTL5 placeholder with js fallback
675 *
676 * @param array $field
677 * @param array $add_html
678 */
679 private static function add_placeholder_to_input( $field, &$add_html ) {
680 if ( FrmFieldsHelper::is_placeholder_field_type( $field['type'] ) ) {
681 $add_html['placeholder'] = 'placeholder="' . esc_attr( $field['placeholder'] ) . '"';
682 }
683 }
684
685 private static function add_frmval_to_input( $field, &$add_html ) {
686 if ( $field['placeholder'] != '' ) {
687 $add_html['data-frmval'] = 'data-frmval="' . esc_attr( $field['placeholder'] ) . '"';
688
689 if ( 'select' === $field['type'] ) {
690 $add_html['data-frmplaceholder'] = 'data-frmplaceholder="' . esc_attr( $field['placeholder'] ) . '"';
691 }
692 }
693
694 if ( $field['default_value'] != '' ) {
695 $add_html['data-frmval'] = 'data-frmval="' . esc_attr( $field['default_value'] ) . '"';
696 }
697 }
698
699 private static function add_validation_messages( $field, array &$add_html ) {
700 $field_validation_messages_status = self::get_validation_data_attribute_visibility_info( $field );
701
702 if ( FrmField::is_required( $field ) && ! empty( $field_validation_messages_status['data-reqmsg'] ) ) {
703 $required_message = FrmFieldsHelper::get_error_msg( $field, 'blank' );
704 $add_html['data-reqmsg'] = 'data-reqmsg="' . esc_attr( $required_message ) . '"';
705 self::maybe_add_html_required( $field, $add_html );
706 }
707
708 if ( ! FrmField::is_option_empty( $field, 'invalid' ) && ! empty( $field_validation_messages_status['data-invmsg'] ) ) {
709 $invalid_message = FrmFieldsHelper::get_error_msg( $field, 'invalid' );
710 $add_html['data-invmsg'] = 'data-invmsg="' . esc_attr( $invalid_message ) . '"';
711 }
712
713 if ( ! empty( $add_html['data-reqmsg'] ) || ! empty( $add_html['data-invmsg'] ) ) {
714 self::maybe_add_error_html_for_js_validation( $field, $add_html );
715 }
716 }
717
718 /**
719 * Returns an array that contains field validation messages status.
720 *
721 * @since 6.9
722 *
723 * @param array|object $field
724 * @return array
725 */
726 private static function get_validation_data_attribute_visibility_info( $field ) {
727 if ( FrmField::get_field_type( $field ) === 'hidden' ) {
728 $field_validation_data_attributes = array(
729 'data-invmsg' => false,
730 'data-reqmsg' => false,
731 );
732 } else {
733 $field_validation_data_attributes = array(
734 'data-invmsg' => true,
735 'data-reqmsg' => true,
736 );
737 }
738
739 /**
740 * Allows controlling which field validation messages would be included in the field html.
741 *
742 * @since 6.9
743 *
744 * @param array $field_validation_messages_status
745 * @param array|object $field
746 */
747 return apply_filters( 'frm_field_validation_include_data_attributes', $field_validation_data_attributes, $field );
748 }
749
750 /**
751 * @since 5.0.03
752 *
753 * @param array $field
754 * @param array $add_html
755 */
756 private static function maybe_add_error_html_for_js_validation( $field, array &$add_html ) {
757 $form = self::get_form_for_js_validation( $field );
758 if ( false === $form ) {
759 return;
760 }
761
762 $error_body = self::pull_custom_error_body_from_custom_html( $form, $field );
763 if ( false !== $error_body ) {
764 $error_body = urlencode( $error_body );
765 $add_html['data-error-html'] = 'data-error-html="' . esc_attr( $error_body ) . '"';
766 }
767 }
768
769 /**
770 * @since 5.0.03
771 *
772 * @param array $field
773 * @return stdClass|false false if there is no form object found with JS validation active.
774 */
775 private static function get_form_for_js_validation( $field ) {
776 global $frm_vars;
777 if ( ! empty( $frm_vars['js_validate_forms'] ) ) {
778 if ( isset( $frm_vars['js_validate_forms'][ $field['form_id'] ] ) ) {
779 return $frm_vars['js_validate_forms'][ $field['form_id'] ];
780 }
781 if ( ! empty( $field['parent_form_id'] ) && isset( $frm_vars['js_validate_forms'][ $field['parent_form_id'] ] ) ) {
782 return $frm_vars['js_validate_forms'][ $field['parent_form_id'] ];
783 }
784 }
785 return false;
786 }
787
788 /**
789 * @param stdClass $form
790 * @param array $field
791 * @param array $errors
792 * @return string|false
793 */
794 public static function pull_custom_error_body_from_custom_html( $form, $field, $errors = array() ) {
795 if ( empty( $field['custom_html'] ) ) {
796 return false;
797 }
798
799 $custom_html = $field['custom_html'];
800 $custom_html = apply_filters( 'frm_before_replace_shortcodes', $custom_html, $field, $errors, $form );
801
802 $start = strpos( $custom_html, '[if error]' );
803 if ( false === $start ) {
804 return false;
805 }
806
807 $end = strpos( $custom_html, '[/if error]' );
808 if ( false === $end || $end < $start ) {
809 return false;
810 }
811
812 $error_body = substr( $custom_html, $start + 10, $end - $start - 10 );
813 $default_html = array(
814 '<div class="frm_error" id="frm_error_field_[key]">[error]</div>',
815 '<div class="frm_error" role="alert" id="frm_error_field_[key]">[error]</div>',
816 '<div class="frm_error">[error]</div>',
817 '<div class="frm_error" role="alert">[error]</div>',
818 );
819
820 if ( in_array( $error_body, $default_html, true ) ) {
821 return false;
822 }
823
824 return $error_body;
825 }
826
827 /**
828 * If 'required' is added to a conditionally hidden field, the form won't
829 * submit in many browsers. Check to make sure the javascript to conditionally
830 * remove it is present if needed.
831 *
832 * @since 3.06.01
833 * @param array $field
834 * @param array $add_html
835 */
836 private static function maybe_add_html_required( $field, array &$add_html ) {
837 $excluded_field_types =
838 FrmField::is_radio( $field ) ||
839 FrmField::is_checkbox( $field ) ||
840 FrmField::is_field_type( $field, 'file' ) ||
841 FrmField::is_field_type( $field, 'nps' ) ||
842 FrmField::is_field_type( $field, 'scale' );
843
844 if ( $excluded_field_types ) {
845 return;
846 }
847
848 $include_html = FrmAppHelper::meets_min_pro_version( '3.06.01' );
849 if ( $include_html ) {
850 $add_html['aria-required'] = 'aria-required="true"';
851 }
852 }
853
854 private static function add_shortcodes_to_html( $field, array &$add_html ) {
855 if ( FrmField::is_option_empty( $field, 'shortcodes' ) ) {
856 return;
857 }
858
859 if ( ! empty( $field['autocomplete'] ) ) {
860 unset( $field['shortcodes']['autocomplete'] );
861 }
862
863 foreach ( $field['shortcodes'] as $k => $v ) {
864 if ( 'opt' === $k ) {
865 continue;
866 }
867
868 if ( is_numeric( $k ) && strpos( $v, '=' ) ) {
869 $add_html[] = $v;
870 } elseif ( ! empty( $k ) && isset( $add_html[ $k ] ) ) {
871 $add_html[ $k ] = str_replace( $k . '="', $k . '="' . $v, $add_html[ $k ] );
872 } else {
873 $add_html[ $k ] = $k . '="' . esc_attr( $v ) . '"';
874 }
875
876 unset( $k, $v );
877 }
878 }
879
880 /**
881 * Add pattern attribute
882 *
883 * @since 3.0
884 *
885 * @param array $field
886 * @param array $add_html
887 */
888 private static function add_pattern_attribute( $field, array &$add_html ) {
889 $has_format = FrmField::is_option_true_in_array( $field, 'format' );
890 $format_field = FrmField::is_field_type( $field, 'text' );
891
892 if ( $field['type'] == 'phone' || ( $has_format && $format_field ) ) {
893 $frm_settings = FrmAppHelper::get_settings();
894
895 if ( $frm_settings->use_html ) {
896 $format = FrmEntryValidate::phone_format( $field );
897 $format = substr( $format, 2, - 1 );
898
899 $add_html['pattern'] = 'pattern="' . esc_attr( $format ) . '"';
900 }
901 }
902 }
903
904 public static function check_value( $opt, $opt_key, $field ) {
905 if ( is_array( $opt ) ) {
906 if ( FrmField::is_option_true( $field, 'separate_value' ) ) {
907 $opt = isset( $opt['value'] ) ? $opt['value'] : ( isset( $opt['label'] ) ? $opt['label'] : reset( $opt ) );
908 } else {
909 $opt = isset( $opt['label'] ) ? $opt['label'] : reset( $opt );
910 }
911 }
912
913 return $opt;
914 }
915
916 public static function check_label( $opt ) {
917 if ( is_array( $opt ) ) {
918 $opt = ( isset( $opt['label'] ) ? $opt['label'] : reset( $opt ) );
919 }
920
921 return $opt;
922 }
923
924 /**
925 * @deprecated 4.0
926 */
927 public static function update_ajax_option() {
928 _deprecated_function( __METHOD__, '4.0' );
929 FrmAppHelper::permission_check( 'frm_edit_forms' );
930 check_ajax_referer( 'frm_ajax', 'nonce' );
931
932 $field_id = FrmAppHelper::get_post_param( 'field', 0, 'absint' );
933 if ( ! $field_id ) {
934 wp_die();
935 }
936
937 $field = FrmField::getOne( $field_id );
938
939 if ( isset( $_POST['separate_value'] ) ) {
940 $new_val = FrmField::is_option_true( $field, 'separate_value' ) ? 0 : 1;
941
942 $field->field_options['separate_value'] = $new_val;
943 unset( $new_val );
944 }
945
946 FrmField::update(
947 $field_id,
948 array(
949 'field_options' => $field->field_options,
950 'form_id' => $field->form_id,
951 )
952 );
953 wp_die();
954 }
955
956 /**
957 * @deprecated 4.0
958 */
959 public static function import_choices() {
960 _deprecated_function( __METHOD__, '4.0' );
961 wp_die();
962 }
963
964 /**
965 * Add Single Option or Other Option.
966 *
967 * @deprecated 4.0 Moved to Pro for Other option only.
968 */
969 public static function add_option() {
970 _deprecated_function( __METHOD__, '4.0', 'FrmProFieldsController::add_other_option' );
971 }
972
973 /**
974 * @deprecated 4.0
975 */
976 public static function update_order() {
977 FrmDeprecated::update_order();
978 }
979
980 /**
981 * @deprecated 3.0
982 * @codeCoverageIgnore
983 */
984 public static function edit_name( $field = 'name', $id = '' ) {
985 FrmDeprecated::edit_name( $field, $id );
986 }
987
988 /**
989 * @deprecated 3.0
990 * @codeCoverageIgnore
991 *
992 * @param int $field_id
993 * @param array $values
994 * @param int $form_id
995 *
996 * @return array
997 */
998 public static function include_single_field( $field_id, $values, $form_id = 0 ) {
999 return FrmDeprecated::include_single_field( $field_id, $values, $form_id );
1000 }
1001 }
1002