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

960 lines 26.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 die( 'You are not allowed to call this page directly.' );
4 }
5
6 class 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 include( FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/value-format.php' );
446 }
447
448 public static function input_html( $field, $echo = true ) {
449 $class = array();
450 self::add_input_classes( $field, $class );
451
452 $add_html = array();
453 self::add_html_size( $field, $add_html );
454 self::add_html_length( $field, $add_html );
455 self::add_html_placeholder( $field, $add_html, $class );
456 self::add_validation_messages( $field, $add_html );
457
458 $class = apply_filters( 'frm_field_classes', implode( ' ', $class ), $field );
459
460 FrmFormsHelper::add_html_attr( $class, 'class', $add_html );
461
462 self::add_shortcodes_to_html( $field, $add_html );
463 self::add_pattern_attribute( $field, $add_html );
464
465 $add_html = apply_filters( 'frm_field_extra_html', $add_html, $field );
466 $add_html = ' ' . implode( ' ', $add_html ) . ' ';
467
468 if ( $echo ) {
469 echo $add_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
470 }
471
472 return $add_html;
473 }
474
475 private static function add_input_classes( $field, array &$class ) {
476 if ( isset( $field['input_class'] ) && ! empty( $field['input_class'] ) ) {
477 $class[] = $field['input_class'];
478 }
479
480 if ( $field['type'] == 'hidden' || $field['type'] == 'user_id' ) {
481 return;
482 }
483
484 if ( isset( $field['size'] ) && $field['size'] > 0 ) {
485 $class[] = 'auto_width';
486 }
487 }
488
489 private static function add_html_size( $field, array &$add_html ) {
490 $size_fields = array(
491 'select',
492 'data',
493 'time',
494 'hidden',
495 'file',
496 'lookup',
497 );
498
499 if ( ! isset( $field['size'] ) || $field['size'] <= 0 || in_array( $field['type'], $size_fields ) ) {
500 return;
501 }
502
503 if ( FrmAppHelper::is_admin_page( 'formidable' ) ) {
504 return;
505 }
506
507 if ( is_numeric( $field['size'] ) ) {
508 $field['size'] .= 'px';
509 }
510
511 $important = apply_filters( 'frm_use_important_width', 1, $field );
512 // Note: This inline styling must stay since we cannot realistically set a class for every possible field size.
513 $add_html['style'] = 'style="width:' . esc_attr( $field['size'] ) . ( $important ? ' !important' : '' ) . '"';
514
515 self::add_html_cols( $field, $add_html );
516 }
517
518 private static function add_html_cols( $field, array &$add_html ) {
519 if ( ! in_array( $field['type'], array( 'textarea', 'rte' ), true ) ) {
520 return;
521 }
522
523 // Convert to cols for textareas.
524 $calc = array(
525 '' => 9,
526 'px' => 9,
527 'rem' => 0.444,
528 'em' => 0.544,
529 );
530
531 // include "col" for valid html
532 $unit = trim( preg_replace( '/[0-9]+/', '', $field['size'] ) );
533
534 if ( ! isset( $calc[ $unit ] ) ) {
535 return;
536 }
537
538 $size = (float) str_replace( $unit, '', $field['size'] ) / $calc[ $unit ];
539
540 $add_html['cols'] = 'cols="' . absint( $size ) . '"';
541 }
542
543 private static function add_html_length( $field, array &$add_html ) {
544 // Check for max setting and if this field accepts maxlength.
545 $fields = array(
546 'textarea',
547 'rte',
548 'hidden',
549 'file',
550 );
551
552 if ( FrmField::is_option_empty( $field, 'max' ) || in_array( $field['type'], $fields ) ) {
553 return;
554 }
555
556 if ( FrmAppHelper::is_admin_page( 'formidable' ) ) {
557 // Don't load on form builder page.
558 return;
559 }
560
561 $add_html['maxlength'] = 'maxlength="' . esc_attr( $field['max'] ) . '"';
562 }
563
564 private static function add_html_placeholder( $field, array &$add_html, array &$class ) {
565 if ( $field['default_value'] != '' ) {
566 if ( is_array( $field['default_value'] ) ) {
567 $add_html['data-frmval'] = 'data-frmval="' . esc_attr( json_encode( $field['default_value'] ) ) . '"';
568 } else {
569 self::add_frmval_to_input( $field, $add_html );
570 }
571 }
572
573 $field['placeholder'] = self::prepare_placeholder( $field );
574 if ( $field['placeholder'] == '' || is_array( $field['placeholder'] ) ) {
575 // don't include a json placeholder
576 return;
577 }
578
579 $frm_settings = FrmAppHelper::get_settings();
580
581 if ( $frm_settings->use_html ) {
582 self::add_placeholder_to_input( $field, $add_html );
583 } else {
584 self::add_frmval_to_input( $field, $add_html );
585
586 $class[] = 'frm_toggle_default';
587
588 if ( $field['value'] == $field['placeholder'] ) {
589 $class[] = 'frm_default';
590 }
591 }
592 }
593
594 /**
595 * Prepares field placeholder.
596 *
597 * @since 5.4 This doesn't call `FrmFieldsController::get_default_value_from_name()` anymore.
598 *
599 * @param array $field Field array.
600 * @return string
601 */
602 private static function prepare_placeholder( $field ) {
603 $placeholder = isset( $field['placeholder'] ) ? $field['placeholder'] : '';
604
605 return $placeholder;
606 }
607
608 /**
609 * If the label position is "inside",
610 * get the label to use as the placeholder
611 *
612 * @since 2.05
613 * @since 5.4 Remove the logic code for "inside" label position.
614 *
615 * @param array $field
616 *
617 * @return string
618 */
619 public static function get_default_value_from_name( $field ) {
620 return '';
621 }
622
623 /**
624 * Maybe add a blank placeholder option before any options
625 * in a dropdown.
626 *
627 * @since 4.04
628 * @return bool True if placeholder was added.
629 */
630 public static function add_placeholder_to_select( $field ) {
631 $placeholder = FrmField::get_option( $field, 'placeholder' );
632 if ( empty( $placeholder ) ) {
633 $placeholder = self::get_default_value_from_name( $field );
634 }
635
636 $use_placeholder = $placeholder;
637 $autocomplete = FrmField::get_option( $field, 'autocom' );
638
639 if ( $autocomplete ) {
640 $use_chosen = ! is_callable( 'FrmProAppHelper::use_chosen_js' ) || FrmProAppHelper::use_chosen_js();
641 if ( $use_chosen ) {
642 $use_placeholder = '';
643 }
644 }
645
646 if ( $placeholder !== '' ) {
647 $placeholder_attributes = array(
648 'class' => 'frm-select-placeholder',
649 'value' => '',
650 'data-placeholder' => 'true',
651 );
652
653 if ( $autocomplete && empty( $use_chosen ) ) {
654 // This is required for Slim Select.
655 $placeholder_attributes['data-placeholder'] = 'true';
656 }
657
658 FrmHtmlHelper::echo_dropdown_option( $use_placeholder, false, $placeholder_attributes );
659 return true;
660 }
661
662 return false;
663 }
664
665 /**
666 * Use HMTL5 placeholder with js fallback
667 *
668 * @param array $field
669 * @param array $add_html
670 */
671 private static function add_placeholder_to_input( $field, &$add_html ) {
672 if ( FrmFieldsHelper::is_placeholder_field_type( $field['type'] ) ) {
673 $add_html['placeholder'] = 'placeholder="' . esc_attr( $field['placeholder'] ) . '"';
674 }
675 }
676
677 private static function add_frmval_to_input( $field, &$add_html ) {
678 if ( $field['placeholder'] != '' ) {
679 $add_html['data-frmval'] = 'data-frmval="' . esc_attr( $field['placeholder'] ) . '"';
680
681 if ( 'select' === $field['type'] ) {
682 $add_html['data-frmplaceholder'] = 'data-frmplaceholder="' . esc_attr( $field['placeholder'] ) . '"';
683 }
684 }
685
686 if ( $field['default_value'] != '' ) {
687 $add_html['data-frmval'] = 'data-frmval="' . esc_attr( $field['default_value'] ) . '"';
688 }
689 }
690
691 private static function add_validation_messages( $field, array &$add_html ) {
692 if ( FrmField::is_required( $field ) ) {
693 $required_message = FrmFieldsHelper::get_error_msg( $field, 'blank' );
694 $add_html['data-reqmsg'] = 'data-reqmsg="' . esc_attr( $required_message ) . '"';
695 self::maybe_add_html_required( $field, $add_html );
696 }
697
698 if ( ! FrmField::is_option_empty( $field, 'invalid' ) ) {
699 $invalid_message = FrmFieldsHelper::get_error_msg( $field, 'invalid' );
700 $add_html['data-invmsg'] = 'data-invmsg="' . esc_attr( $invalid_message ) . '"';
701 }
702
703 if ( ! empty( $add_html['data-reqmsg'] ) || ! empty( $add_html['data-invmsg'] ) ) {
704 self::maybe_add_error_html_for_js_validation( $field, $add_html );
705 }
706 }
707
708 /**
709 * @since 5.0.03
710 *
711 * @param array $field
712 * @param array $add_html
713 */
714 private static function maybe_add_error_html_for_js_validation( $field, array &$add_html ) {
715 $form = self::get_form_for_js_validation( $field );
716 if ( false === $form ) {
717 return;
718 }
719
720 $error_body = self::pull_custom_error_body_from_custom_html( $form, $field );
721 if ( false !== $error_body ) {
722 $error_body = urlencode( $error_body );
723 $add_html['data-error-html'] = 'data-error-html="' . esc_attr( $error_body ) . '"';
724 }
725 }
726
727 /**
728 * @since 5.0.03
729 *
730 * @param array $field
731 * @return stdClass|false false if there is no form object found with JS validation active.
732 */
733 private static function get_form_for_js_validation( $field ) {
734 global $frm_vars;
735 if ( ! empty( $frm_vars['js_validate_forms'] ) ) {
736 if ( isset( $frm_vars['js_validate_forms'][ $field['form_id'] ] ) ) {
737 return $frm_vars['js_validate_forms'][ $field['form_id'] ];
738 }
739 if ( ! empty( $field['parent_form_id'] ) && isset( $frm_vars['js_validate_forms'][ $field['parent_form_id'] ] ) ) {
740 return $frm_vars['js_validate_forms'][ $field['parent_form_id'] ];
741 }
742 }
743 return false;
744 }
745
746 /**
747 * @param stdClass $form
748 * @param array $field
749 * @param array $errors
750 * @return string|false
751 */
752 public static function pull_custom_error_body_from_custom_html( $form, $field, $errors = array() ) {
753 if ( empty( $field['custom_html'] ) ) {
754 return false;
755 }
756
757 $custom_html = $field['custom_html'];
758 $custom_html = apply_filters( 'frm_before_replace_shortcodes', $custom_html, $field, $errors, $form );
759
760 $start = strpos( $custom_html, '[if error]' );
761 if ( false === $start ) {
762 return false;
763 }
764
765 $end = strpos( $custom_html, '[/if error]' );
766 if ( false === $end || $end < $start ) {
767 return false;
768 }
769
770 $error_body = substr( $custom_html, $start + 10, $end - $start - 10 );
771 $default_html = array(
772 '<div class="frm_error" id="frm_error_field_[key]">[error]</div>',
773 '<div class="frm_error" role="alert" id="frm_error_field_[key]">[error]</div>',
774 '<div class="frm_error">[error]</div>',
775 '<div class="frm_error" role="alert">[error]</div>',
776 );
777
778 if ( in_array( $error_body, $default_html, true ) ) {
779 return false;
780 }
781
782 return $error_body;
783 }
784
785 /**
786 * If 'required' is added to a conditionally hidden field, the form won't
787 * submit in many browsers. Check to make sure the javascript to conditionally
788 * remove it is present if needed.
789 *
790 * @since 3.06.01
791 * @param array $field
792 * @param array $add_html
793 */
794 private static function maybe_add_html_required( $field, array &$add_html ) {
795 $excluded_field_types =
796 FrmField::is_radio( $field ) ||
797 FrmField::is_checkbox( $field ) ||
798 FrmField::is_field_type( $field, 'file' ) ||
799 FrmField::is_field_type( $field, 'nps' ) ||
800 FrmField::is_field_type( $field, 'scale' );
801
802 if ( $excluded_field_types ) {
803 return;
804 }
805
806 $include_html = FrmAppHelper::meets_min_pro_version( '3.06.01' );
807 if ( $include_html ) {
808 $add_html['aria-required'] = 'aria-required="true"';
809 }
810 }
811
812 private static function add_shortcodes_to_html( $field, array &$add_html ) {
813 if ( FrmField::is_option_empty( $field, 'shortcodes' ) ) {
814 return;
815 }
816
817 if ( ! empty( $field['autocomplete'] ) ) {
818 unset( $field['shortcodes']['autocomplete'] );
819 }
820
821 foreach ( $field['shortcodes'] as $k => $v ) {
822 if ( 'opt' === $k ) {
823 continue;
824 }
825
826 if ( is_numeric( $k ) && strpos( $v, '=' ) ) {
827 $add_html[] = $v;
828 } elseif ( ! empty( $k ) && isset( $add_html[ $k ] ) ) {
829 $add_html[ $k ] = str_replace( $k . '="', $k . '="' . $v, $add_html[ $k ] );
830 } else {
831 $add_html[ $k ] = $k . '="' . esc_attr( $v ) . '"';
832 }
833
834 unset( $k, $v );
835 }
836 }
837
838 /**
839 * Add pattern attribute
840 *
841 * @since 3.0
842 *
843 * @param array $field
844 * @param array $add_html
845 */
846 private static function add_pattern_attribute( $field, array &$add_html ) {
847 $has_format = FrmField::is_option_true_in_array( $field, 'format' );
848 $format_field = FrmField::is_field_type( $field, 'text' );
849
850 if ( $field['type'] == 'phone' || ( $has_format && $format_field ) ) {
851 $frm_settings = FrmAppHelper::get_settings();
852
853 if ( $frm_settings->use_html ) {
854 $format = FrmEntryValidate::phone_format( $field );
855 $format = substr( $format, 2, - 1 );
856
857 $add_html['pattern'] = 'pattern="' . esc_attr( $format ) . '"';
858 }
859 }
860 }
861
862 public static function check_value( $opt, $opt_key, $field ) {
863 if ( is_array( $opt ) ) {
864 if ( FrmField::is_option_true( $field, 'separate_value' ) ) {
865 $opt = isset( $opt['value'] ) ? $opt['value'] : ( isset( $opt['label'] ) ? $opt['label'] : reset( $opt ) );
866 } else {
867 $opt = isset( $opt['label'] ) ? $opt['label'] : reset( $opt );
868 }
869 }
870
871 return $opt;
872 }
873
874 public static function check_label( $opt ) {
875 if ( is_array( $opt ) ) {
876 $opt = ( isset( $opt['label'] ) ? $opt['label'] : reset( $opt ) );
877 }
878
879 return $opt;
880 }
881
882 /**
883 * @deprecated 4.0
884 */
885 public static function update_ajax_option() {
886 _deprecated_function( __METHOD__, '4.0' );
887 FrmAppHelper::permission_check( 'frm_edit_forms' );
888 check_ajax_referer( 'frm_ajax', 'nonce' );
889
890 $field_id = FrmAppHelper::get_post_param( 'field', 0, 'absint' );
891 if ( ! $field_id ) {
892 wp_die();
893 }
894
895 $field = FrmField::getOne( $field_id );
896
897 if ( isset( $_POST['separate_value'] ) ) {
898 $new_val = FrmField::is_option_true( $field, 'separate_value' ) ? 0 : 1;
899
900 $field->field_options['separate_value'] = $new_val;
901 unset( $new_val );
902 }
903
904 FrmField::update(
905 $field_id,
906 array(
907 'field_options' => $field->field_options,
908 'form_id' => $field->form_id,
909 )
910 );
911 wp_die();
912 }
913
914 /**
915 * @deprecated 4.0
916 */
917 public static function import_choices() {
918 _deprecated_function( __METHOD__, '4.0' );
919 wp_die();
920 }
921
922 /**
923 * Add Single Option or Other Option.
924 *
925 * @deprecated 4.0 Moved to Pro for Other option only.
926 */
927 public static function add_option() {
928 _deprecated_function( __METHOD__, '4.0', 'FrmProFieldsController::add_other_option' );
929 }
930
931 /**
932 * @deprecated 4.0
933 */
934 public static function update_order() {
935 FrmDeprecated::update_order();
936 }
937
938 /**
939 * @deprecated 3.0
940 * @codeCoverageIgnore
941 */
942 public static function edit_name( $field = 'name', $id = '' ) {
943 FrmDeprecated::edit_name( $field, $id );
944 }
945
946 /**
947 * @deprecated 3.0
948 * @codeCoverageIgnore
949 *
950 * @param int $field_id
951 * @param array $values
952 * @param int $form_id
953 *
954 * @return array
955 */
956 public static function include_single_field( $field_id, $values, $form_id = 0 ) {
957 return FrmDeprecated::include_single_field( $field_id, $values, $form_id );
958 }
959 }
960