PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.26.1
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.26.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 / FrmFormsHelper.php

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

2,228 lines 60.4 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 FrmFormsHelper {
7
8 /**
9 * Store and re-use field type data for the insert_opt_html function (to avoid multiple calls to FrmField::all_field_selection).
10 *
11 * @since 6.10
12 *
13 * @var array|null
14 */
15 private static $field_type_data_for_insert_opt_html;
16
17 /**
18 * @since 2.2.10
19 *
20 * @return string
21 */
22 public static function form_error_class() {
23 return apply_filters( 'frm_form_error_class', 'frm_error_style' );
24 }
25
26 /**
27 * @param string $key
28 * @param false|object $form
29 *
30 * @return string
31 */
32 public static function get_direct_link( $key, $form = false ) {
33 $target_url = esc_url( admin_url( 'admin-ajax.php?action=frm_forms_preview&form=' . $key ) );
34 $target_url = apply_filters( 'frm_direct_link', $target_url, $key, $form );
35
36 return $target_url;
37 }
38
39 /**
40 * @param string $field_name
41 * @param int|string $field_value
42 * @param array $args
43 *
44 * @return void
45 */
46 public static function forms_dropdown( $field_name, $field_value = '', $args = array() ) {
47 $defaults = array(
48 'blank' => true,
49 'field_id' => false,
50 'onchange' => false,
51 'exclude' => false,
52 'class' => '',
53 'inc_children' => 'exclude',
54 );
55 $args = wp_parse_args( $args, $defaults );
56
57 if ( ! $args['field_id'] ) {
58 $args['field_id'] = $field_name;
59 }
60
61 $query = array();
62
63 if ( $args['exclude'] ) {
64 $query['id !'] = $args['exclude'];
65 }
66
67 $where = apply_filters( 'frm_forms_dropdown', $query, $field_name );
68 $forms = FrmForm::get_published_forms( $where, 999, $args['inc_children'] );
69 $add_html = array();
70 self::add_html_attr( $args['onchange'], 'onchange', $add_html );
71 self::add_html_attr( $args['class'], 'class', $add_html );
72
73 ?>
74 <select name="<?php echo esc_attr( $field_name ); ?>"
75 id="<?php echo esc_attr( $args['field_id'] ); ?>"
76 <?php echo wp_strip_all_tags( implode( ' ', $add_html ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
77 <?php if ( $args['blank'] ) { ?>
78 <option value=""><?php echo $args['blank'] == 1 ? ' ' : '- ' . esc_attr( $args['blank'] ) . ' -'; ?></option>
79 <?php } ?>
80 <?php foreach ( $forms as $form ) { ?>
81 <option value="<?php echo esc_attr( $form->id ); ?>" <?php selected( $field_value, $form->id ); ?>>
82 <?php echo esc_html( '' === $form->name ? self::get_no_title_text() : FrmAppHelper::truncate( $form->name, 50 ) . ( $form->parent_form_id ? __( ' (child)', 'formidable' ) : '' ) ); ?>
83 </option>
84 <?php } ?>
85 </select>
86 <?php
87 }
88
89 /**
90 * @since 2.0.6
91 *
92 * @param string $class
93 * @param string $param
94 * @param array $add_html
95 *
96 * @return void
97 */
98 public static function add_html_attr( $class, $param, &$add_html ) {
99 if ( ! empty( $class ) ) {
100 $add_html[ $param ] = sanitize_title( $param ) . '="' . esc_attr( trim( sanitize_text_field( $class ) ) ) . '"';
101 }
102 }
103
104 /**
105 * @param false|object|string $selected - The label for the placeholder, or the form object.
106 *
107 * @return void
108 */
109 public static function form_switcher( $selected = false ) {
110 $where = apply_filters( 'frm_forms_dropdown', array(), '' );
111 $forms = FrmForm::get_published_forms( $where );
112
113 $args = array(
114 'id' => 0,
115 'form' => 0,
116 );
117
118 if ( isset( $_GET['id'] ) && ! isset( $_GET['form'] ) ) {
119 unset( $args['form'] );
120 } elseif ( isset( $_GET['form'] ) && ! isset( $_GET['id'] ) ) {
121 unset( $args['id'] );
122 }
123
124 $frm_action = FrmAppHelper::simple_get( 'frm_action', 'sanitize_title' );
125
126 if ( FrmAppHelper::is_admin_page( 'formidable-entries' ) && in_array( $frm_action, array( 'edit', 'show', 'destroy', 'destroy_all' ), true ) ) {
127 $args['frm_action'] = 'list';
128 $args['form'] = 0;
129 } elseif ( FrmAppHelper::is_admin_page( 'formidable' ) && in_array( $frm_action, array( 'new', 'duplicate' ), true ) ) {
130 $args['frm_action'] = 'edit';
131 } elseif ( FrmAppHelper::is_style_editor_page() ) {
132 // Avoid passing style into form switcher on style page.
133 unset( $args['id'] );
134 $query_args = array(
135 'page' => 'formidable-styles',
136 );
137
138 if ( $frm_action ) {
139 $query_args['frm_action'] = $frm_action;
140 }
141
142 $base = add_query_arg( $query_args, admin_url( 'admin.php' ) );
143 } elseif ( isset( $_GET['post'] ) ) {
144 $args['form'] = 0;
145 $base = admin_url( 'edit.php?post_type=frm_display' );
146 }//end if
147
148 $form_id = 0;
149
150 if ( is_object( $selected ) ) {
151 $form_id = $selected->id;
152 $selected = $selected->name;
153 }
154
155 $name = $selected === false ? __( 'Switch Form', 'formidable' ) : $selected;
156 $name = '' === $name || is_null( $name ) ? self::get_no_title_text() : strip_tags( $name );
157 $truncated_name = FrmAppHelper::truncate( $name, 25 );
158
159 if ( count( $forms ) < 2 ) {
160 ?>
161 <div id="frm_bs_dropdown">
162 <h1>
163 <span class="frm_bstooltip" title="<?php echo esc_attr( $truncated_name === $name ? '' : $name ); ?>" data-placement="right">
164 <?php echo esc_html( $name ); ?>
165 </span>
166 </h1>
167 </div>
168 <?php
169 return;
170 }
171 ?>
172 <div id="frm_bs_dropdown" class="dropdown <?php echo esc_attr( is_rtl() ? 'dropdown-menu-right' : 'dropdown-menu-left' ); ?>">
173 <a href="#" id="frm-navbarDrop" class="frm-dropdown-toggle" data-toggle="dropdown">
174 <h1>
175 <span class="frm_bstooltip" title="<?php echo esc_attr( $truncated_name === $name ? '' : $name ); ?>" data-placement="right">
176 <?php echo esc_html( $name ); ?>
177 </span>
178 <?php FrmAppHelper::icon_by_class( 'frmfont frm_arrowdown6_icon', array( 'aria-hidden' => 'true' ) ); ?>
179 </h1>
180 </a>
181 <ul class="frm-dropdown-menu frm-on-top frm-inline-modal frm_code_list frm-full-hover" role="menu" aria-labelledby="frm-navbarDrop">
182 <?php if ( count( $forms ) > 8 ) { ?>
183 <li class="frm-with-search">
184 <?php
185 FrmAppHelper::show_search_box(
186 array(
187 'input_id' => 'dropform',
188 'placeholder' => __( 'Search Forms', 'formidable' ),
189 'tosearch' => 'frm-dropdown-form',
190 // Specify a value to avoid the $_REQUEST['s'] default value.
191 'value' => '',
192 )
193 );
194 ?>
195 </li>
196 <?php } ?>
197 <?php
198 foreach ( $forms as $form ) {
199 if ( $form->id === $form_id ) {
200 // Don't include the selected form in the switcher since it does nothing.
201 continue;
202 }
203
204 if ( isset( $args['id'] ) ) {
205 $args['id'] = $form->id;
206 }
207
208 if ( isset( $args['form'] ) ) {
209 $args['form'] = $form->id;
210 }
211
212 $url = isset( $base ) ? add_query_arg( $args, $base ) : add_query_arg( $args );
213 $form_name = empty( $form->name ) ? self::get_no_title_text() : $form->name;
214 ?>
215 <li class="frm-dropdown-form">
216 <a href="<?php echo esc_url( $url ); ?>" tabindex="-1" class="frm-justify-between">
217 <?php echo esc_html( $form_name ); ?>
218 <span>
219 <?php
220 printf(
221 /* translators: %d: Form ID */
222 esc_html__( '(ID %d)', 'formidable' ),
223 esc_attr( $form->id )
224 );
225 ?>
226 </span>
227 <span class="frm_hidden"><?php echo esc_html( $form->form_key ); ?></span>
228 </a>
229 </li>
230 <?php
231 unset( $form );
232 }//end foreach
233 ?>
234 </ul>
235 </div>
236 <?php
237 }
238
239 /**
240 * @param string $col
241 * @param string $sort_col
242 * @param string $sort_dir
243 *
244 * @return void
245 */
246 public static function get_sortable_classes( $col, $sort_col, $sort_dir ) {
247 echo $sort_col == $col ? 'sorted' : 'sortable';
248 echo $sort_col == $col && $sort_dir === 'desc' ? ' asc' : ' desc';
249 }
250
251 /**
252 * @since 3.0
253 *
254 * @param array|string $field_type
255 *
256 * @return string
257 */
258 public static function get_field_link_name( $field_type ) {
259 if ( is_array( $field_type ) ) {
260 $field_label = $field_type['name'];
261 } else {
262 $field_label = $field_type;
263 }
264
265 return $field_label;
266 }
267
268 /**
269 * @since 3.0
270 *
271 * @param array|string $field_type
272 *
273 * @return string
274 */
275 public static function get_field_link_icon( $field_type ) {
276 if ( is_array( $field_type ) && isset( $field_type['icon'] ) ) {
277 $icon = $field_type['icon'];
278 } else {
279 $icon = 'frm_icon_font frm_pencil_icon';
280 }
281
282 return $icon;
283 }
284
285 /**
286 * Get the invalid form error message
287 *
288 * @since 2.02.07
289 *
290 * @param array $args
291 *
292 * @return string
293 */
294 public static function get_invalid_error_message( $args ) {
295 $settings_args = $args;
296
297 if ( isset( $args['form'] ) ) {
298 $settings_args['current_form'] = $args['form']->id;
299 }
300
301 $frm_settings = FrmAppHelper::get_settings( $settings_args );
302 $invalid_msg = do_shortcode( $frm_settings->invalid_msg );
303 return apply_filters( 'frm_invalid_error_message', $invalid_msg, $args );
304 }
305
306 /**
307 * @param array $atts {
308 * The success message details.
309 *
310 * @type string $message
311 * @type stdClass $form
312 * @type int $entry_id
313 * @type string $class
314 * }
315 *
316 * @return string
317 */
318 public static function get_success_message( $atts ) {
319 $message = apply_filters( 'frm_content', $atts['message'], $atts['form'], $atts['entry_id'] );
320
321 // Only autop if the message includes line breaks.
322 $autop = strpos( $message, "\n" ) !== false;
323
324 /**
325 * Filters whether to autop the success message.
326 * This is false by default if the message does not include line breaks.
327 *
328 * @since 6.23
329 *
330 * @param bool $autop
331 * @param string $message
332 * @param object $form
333 */
334 $autop = (bool) apply_filters( 'frm_wpautop_success_message', $autop, $message, $atts['form'] );
335
336 if ( $autop ) {
337 $message = FrmAppHelper::use_wpautop( $message );
338 }
339
340 $message = do_shortcode( $message );
341 $message = '<div class="' . esc_attr( $atts['class'] ) . '" role="status">' . $message . '</div>';
342 return $message;
343 }
344
345 /**
346 * Used when a form is created
347 *
348 * @param array $values
349 *
350 * @return array
351 */
352 public static function setup_new_vars( $values = array() ) {
353 global $wpdb;
354
355 if ( ! empty( $values ) ) {
356 $post_values = $values;
357 } else {
358 $values = array();
359 $post_values = ! empty( $_POST ) ? $_POST : array(); // phpcs:ignore WordPress.Security.NonceVerification.Missing
360 }
361
362 $defaults = array(
363 'name' => '',
364 'description' => '',
365 );
366
367 foreach ( $defaults as $var => $default ) {
368 if ( ! isset( $values[ $var ] ) ) {
369 $values[ $var ] = FrmAppHelper::get_param( $var, $default, 'get', 'sanitize_text_field' );
370 }
371 }
372
373 $values['description'] = FrmAppHelper::use_wpautop( $values['description'] );
374
375 $defaults = array(
376 'form_id' => '',
377 'logged_in' => '',
378 'editable' => '',
379 'is_template' => 0,
380 'status' => 'published',
381 'parent_form_id' => 0,
382 );
383
384 foreach ( $defaults as $var => $default ) {
385 if ( ! isset( $values[ $var ] ) ) {
386 $values[ $var ] = FrmAppHelper::get_param( $var, $default, 'get', 'sanitize_text_field' );
387 }
388 }
389 unset( $defaults );
390
391 if ( ! isset( $values['form_key'] ) ) {
392 $values['form_key'] = $post_values && isset( $post_values['form_key'] ) ? $post_values['form_key'] : FrmAppHelper::get_unique_key( '', $wpdb->prefix . 'frm_forms', 'form_key' );
393 }
394
395 $values = self::fill_default_opts( $values, false, $post_values );
396 $values['custom_style'] = FrmAppHelper::custom_style_value( $post_values );
397
398 return apply_filters( 'frm_setup_new_form_vars', $values );
399 }
400
401 /**
402 * Used when editing a form
403 *
404 * @param array $values
405 * @param object $record
406 * @param array $post_values
407 *
408 * @return array
409 */
410 public static function setup_edit_vars( $values, $record, $post_values = array() ) {
411 if ( empty( $post_values ) ) {
412 $post_values = wp_unslash( $_POST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
413 }
414
415 $values['form_key'] = $post_values['form_key'] ?? $record->form_key;
416 $values['is_template'] = $post_values['is_template'] ?? $record->is_template;
417 $values['status'] = $record->status;
418
419 $values = self::fill_default_opts( $values, $record, $post_values );
420
421 return apply_filters( 'frm_setup_edit_form_vars', $values );
422 }
423
424 /**
425 * @param array $values
426 * @param false|object $record
427 * @param array|false $post_values
428 *
429 * @return array
430 */
431 public static function fill_default_opts( $values, $record, $post_values ) {
432
433 $defaults = self::get_default_opts();
434
435 foreach ( $defaults as $var => $default ) {
436 if ( is_array( $default ) ) {
437 if ( ! isset( $values[ $var ] ) ) {
438 $values[ $var ] = $record && isset( $record->options[ $var ] ) ? $record->options[ $var ] : array();
439 }
440
441 foreach ( $default as $k => $v ) {
442 $values[ $var ][ $k ] = $post_values && isset( $post_values[ $var ][ $k ] ) ? $post_values[ $var ][ $k ] : ( $record && isset( $record->options[ $var ] ) && isset( $record->options[ $var ][ $k ] ) ? $record->options[ $var ][ $k ] : $v );
443
444 if ( is_array( $v ) ) {
445 foreach ( $v as $k1 => $v1 ) {
446 $values[ $var ][ $k ][ $k1 ] = $post_values && isset( $post_values[ $var ][ $k ][ $k1 ] ) ? $post_values[ $var ][ $k ][ $k1 ] : ( $record && isset( $record->options[ $var ] ) && isset( $record->options[ $var ][ $k ] ) && isset( $record->options[ $var ][ $k ][ $k1 ] ) ? $record->options[ $var ][ $k ][ $k1 ] : $v1 );
447 unset( $k1, $v1 );
448 }
449 }
450
451 unset( $k, $v );
452 }
453 } else {
454 $values[ $var ] = $post_values && isset( $post_values['options'][ $var ] ) ? $post_values['options'][ $var ] : ( $record && isset( $record->options[ $var ] ) ? $record->options[ $var ] : $default );
455 }
456
457 unset( $var, $default );
458 }//end foreach
459
460 return $values;
461 }
462
463 /**
464 * @return array
465 */
466 public static function get_default_opts() {
467 $frm_settings = FrmAppHelper::get_settings();
468
469 return array(
470 'submit_value' => $frm_settings->submit_value,
471 'success_action' => 'message',
472 'success_msg' => $frm_settings->success_msg,
473 'show_form' => 0,
474 'akismet' => '',
475 'stopforumspam' => 0,
476 'antispam' => 0,
477 'no_save' => 0,
478 'ajax_load' => 0,
479 'js_validate' => 0,
480 'form_class' => '',
481 'custom_style' => 1,
482 'before_html' => self::get_default_html( 'before' ),
483 'after_html' => '',
484 'submit_html' => self::get_default_html( 'submit' ),
485 'show_title' => 0,
486 'show_description' => 0,
487 'ajax_submit' => 0,
488 );
489 }
490
491 /**
492 * @since 2.0.6
493 *
494 * @param array $options
495 * @param array $values
496 *
497 * @return void
498 */
499 public static function fill_form_options( &$options, $values ) {
500 $defaults = self::get_default_opts();
501
502 foreach ( $defaults as $var => $default ) {
503 $options[ $var ] = $values['options'][ $var ] ?? $default;
504 unset( $var, $default );
505 }
506 }
507
508 /**
509 * @param string $loc
510 *
511 * @return string
512 */
513 public static function get_default_html( $loc ) {
514 if ( $loc === 'submit' ) {
515 $draft_link = self::get_draft_link();
516 $start_over = self::get_start_over_shortcode();
517 $default_html = <<<SUBMIT_HTML
518 <div class="frm_submit frm_flex">
519 <button class="frm_button_submit" type="submit" [button_action]>[button_label]</button>
520 [if back_button]<button type="submit" name="frm_prev_page" formnovalidate="formnovalidate" class="frm_prev_page" [back_hook]>[back_label]</button>[/if back_button]
521 $draft_link
522 $start_over
523 </div>
524 SUBMIT_HTML;
525 } elseif ( $loc === 'before' ) {
526 $default_html = <<<BEFORE_HTML
527 <legend class="frm_screen_reader">[form_name]</legend>
528 [if form_name]<h3 class="frm_form_title">[form_name]</h3>[/if form_name]
529 [if form_description]<div class="frm_description">[form_description]</div>[/if form_description]
530 BEFORE_HTML;
531 } else {
532 $default_html = '';
533 }
534
535 return $default_html;
536 }
537
538 /**
539 * @return string
540 */
541 public static function get_draft_link() {
542 $link = '[if save_draft]<button class="frm_save_draft" [draft_hook]>[draft_label]</button>[/if save_draft]';
543
544 return $link;
545 }
546
547 /**
548 * Gets start over button shortcode.
549 *
550 * @since 5.4
551 *
552 * @return string
553 */
554 public static function get_start_over_shortcode() {
555 return '[if start_over]<a href="#" tabindex="0" class="frm_start_over" [start_over_hook]>[start_over_label]</a>[/if start_over]';
556 }
557
558 /**
559 * @param string $html
560 * @param object $form
561 * @param string $submit
562 * @param object|null $form_action
563 * @param array $values
564 *
565 * @return void
566 */
567 public static function get_custom_submit( $html, $form, $submit, $form_action, $values ) {
568 $button = self::replace_shortcodes( $html, $form, $submit, $form_action, $values );
569
570 if ( ! strpos( $button, '[button_action]' ) ) {
571 echo FrmAppHelper::maybe_kses( $button ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
572 return;
573 }
574
575 /**
576 * @since 5.0.06
577 */
578 $button = apply_filters( 'frm_submit_button_html', $button, compact( 'form' ) );
579
580 if ( FrmAppHelper::should_never_allow_unfiltered_html() ) {
581 $button = FrmAppHelper::kses_submit_button( $button );
582 }
583
584 $button_parts = explode( '[button_action]', $button );
585
586 $classes = apply_filters( 'frm_submit_button_class', array(), $form );
587
588 if ( ! empty( $classes ) ) {
589 $classes = implode( ' ', $classes );
590 $button_class = 'frm_button_submit';
591
592 if ( preg_match( '/\bclass="[^"]*?\b' . preg_quote( $button_class, '/' ) . '\b[^"]*?"/', $button_parts[0] ) ) {
593 $button_parts[0] = str_replace( $button_class, $button_class . ' ' . esc_attr( $classes ), $button_parts[0] );
594 } else {
595 $button_parts[0] .= ' class="' . esc_attr( $classes ) . '"';
596 }
597 }
598
599 echo $button_parts[0]; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
600 do_action( 'frm_submit_button_action', $form, $form_action );
601 echo $button_parts[1]; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
602 }
603
604 /**
605 * @since 4.0
606 *
607 * @return array
608 */
609 public static function html_shortcodes() {
610 $codes = array(
611 'id' => array(
612 'label' => __( 'Field ID', 'formidable' ),
613 'class' => 'show_field_custom_html',
614 ),
615 'key' => array(
616 'label' => __( 'Field Key', 'formidable' ),
617 'class' => 'show_field_custom_html',
618 ),
619 'field_name' => array(
620 'label' => __( 'Field Name', 'formidable' ),
621 'class' => 'show_field_custom_html',
622 ),
623 'description' => array(
624 'label' => __( 'Field Description', 'formidable' ),
625 'class' => 'show_field_custom_html',
626 ),
627 'label_position' => array(
628 'label' => __( 'Label Position', 'formidable' ),
629 'class' => 'show_field_custom_html',
630 ),
631 'required_label' => array(
632 'label' => __( 'Required Label', 'formidable' ),
633 'class' => 'show_field_custom_html',
634 ),
635 'input' => array(
636 'label' => __( 'Input Field', 'formidable' ),
637 'class' => 'show_field_custom_html',
638 ),
639 'input opt=1' => array(
640 'label' => __( 'Single Option', 'formidable' ),
641 'title' => __( 'Show a single radio or checkbox option by replacing 1 with the order of the option', 'formidable' ),
642 'class' => 'show_field_custom_html',
643 ),
644 'input label=0' => array(
645 'label' => __( 'Hide Option Label', 'formidable' ),
646 'class' => 'show_field_custom_html',
647 ),
648 'required_class' => array(
649 'label' => __( 'Required Class', 'formidable' ),
650 'title' => __( 'Add class name if field is required', 'formidable' ),
651 'class' => 'show_field_custom_html',
652 ),
653 'error_class' => array(
654 'label' => __( 'Error Class', 'formidable' ),
655 'title' => __( 'Add class name if field has an error on form submit', 'formidable' ),
656 'class' => 'show_field_custom_html',
657 ),
658
659 'form_name' => array(
660 'label' => __( 'Form Name', 'formidable' ),
661 'class' => 'show_before_html show_after_html',
662 ),
663 'form_description' => array(
664 'label' => __( 'Form Description', 'formidable' ),
665 'class' => 'show_before_html show_after_html',
666 ),
667 'form_key' => array(
668 'label' => __( 'Form Key', 'formidable' ),
669 'class' => 'show_before_html show_after_html',
670 ),
671 'deletelink' => array(
672 'label' => __( 'Delete Entry Link', 'formidable' ),
673 'class' => 'show_before_html show_after_html',
674 ),
675
676 'button_label' => array(
677 'label' => __( 'Button Label', 'formidable' ),
678 'class' => 'show_submit_html',
679 ),
680 'button_action' => array(
681 'label' => __( 'Button Hook', 'formidable' ),
682 'class' => 'show_submit_html',
683 ),
684 );
685
686 /**
687 * @since 4.0
688 */
689 return apply_filters( 'frm_html_codes', $codes );
690 }
691
692 /**
693 * @since 4.0
694 *
695 * @param array $args
696 *
697 * @return void
698 */
699 public static function insert_opt_html( $args ) {
700 $class = $args['class'] ?? '';
701 $fields = self::get_field_type_data_for_insert_opt_html();
702 $field = $fields[ $args['type'] ] ?? array();
703
704 self::prepare_field_type( $field );
705
706 if ( ! isset( $field['icon'] ) ) {
707 $field['icon'] = 'frmfont frm_pencil_icon';
708 }
709
710 $possible_email_field = FrmFieldFactory::field_has_property( $args['type'], 'holds_email_values' );
711
712 if ( $possible_email_field ) {
713 $class .= ' show_frm_not_email_to';
714 }
715
716 if ( 'url' === $args['type'] ) {
717 $class .= ' frm_insert_url';
718 }
719
720 $truncated_name = FrmAppHelper::truncate( $args['name'], 60 );
721
722 if ( isset( $field['icon'] ) ) {
723 $icon = FrmAppHelper::icon_by_class(
724 $field['icon'],
725 array(
726 'aria-hidden' => 'true',
727 'echo' => false,
728 )
729 );
730 } else {
731 $icon = '';
732 }
733 ?>
734 <li class="<?php echo esc_attr( $class ); ?>">
735 <a href="javascript:void(0)" class="frmids frm_insert_code" data-code="<?php echo esc_attr( $args['id'] ); ?>">
736 <?php
737 echo FrmAppHelper::kses_icon( $icon ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
738 echo esc_html( $truncated_name );
739 ?>
740 <span>[<?php echo esc_attr( $args['id_label'] ?? $args['id'] ); ?>]</span>
741 </a>
742 <a href="javascript:void(0)" class="frmkeys frm_insert_code frm_hidden" data-code="<?php echo esc_attr( $args['key'] ); ?>">
743 <?php
744 echo FrmAppHelper::kses_icon( $icon ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
745 echo esc_html( $truncated_name );
746 ?>
747 <span>[<?php echo esc_attr( FrmAppHelper::truncate( $args['key_label'] ?? $args['key'], 7 ) ); ?>]</span>
748 </a>
749 </li>
750 <?php
751 }
752
753 /**
754 * Store and re-use field selection data for use when outputting shortcodes options in shortcode pop up.
755 * This significantly improves performance by avoiding repeat calls to FrmField::all_field_selection.
756 *
757 * @since 6.10
758 *
759 * @return array
760 */
761 private static function get_field_type_data_for_insert_opt_html() {
762 if ( ! isset( self::$field_type_data_for_insert_opt_html ) ) {
763 self::$field_type_data_for_insert_opt_html = FrmField::all_field_selection();
764 }
765 return self::$field_type_data_for_insert_opt_html;
766 }
767
768 /**
769 * @since 4.0
770 *
771 * @param array $args
772 *
773 * @return void
774 */
775 public static function insert_code_html( $args ) {
776 $defaults = array(
777 'class' => '',
778 'code' => '',
779 'label' => '',
780 'title' => '',
781 );
782
783 $args = array_merge( $defaults, $args );
784 $has_tooltip = ! empty( $args['title'] );
785
786 ?>
787 <li class="<?php echo esc_attr( $args['class'] ); ?>">
788 <a href="javascript:void(0)" class="frm_insert_code <?php echo $has_tooltip ? 'frm_help' : ''; ?>"
789 <?php echo $has_tooltip ? 'title="' . esc_attr( $args['title'] ) . '"' : ''; ?>
790 data-code="<?php echo esc_attr( $args['code'] ); ?>">
791 <?php echo esc_attr( FrmAppHelper::truncate( $args['label'], 60 ) ); ?>
792 <span>
793 [<?php echo esc_attr( FrmAppHelper::truncate( $args['code'], 10 ) ); ?>]
794 </span>
795 </a>
796 </li>
797 <?php
798 }
799
800 /**
801 * Some field types in add-ons may have been added with only
802 * a field type and name.
803 *
804 * @since 4.0
805 *
806 * @param array|string $field
807 *
808 * @return void
809 */
810 public static function prepare_field_type( &$field ) {
811 if ( ! is_array( $field ) ) {
812 $field = array(
813 'name' => $field,
814 'icon' => 'frm_icon_font frm_pencil_icon',
815 );
816 }
817 }
818
819 /**
820 * Automatically add end section fields if they don't exist (2.0 migration)
821 *
822 * @since 2.0
823 *
824 * @param object $form
825 * @param array $fields
826 * @param bool $reset_fields
827 *
828 * @return void
829 */
830 public static function auto_add_end_section_fields( $form, $fields, &$reset_fields ) {
831 if ( empty( $fields ) ) {
832 return;
833 }
834
835 $end_section_values = apply_filters( 'frm_before_field_created', FrmFieldsHelper::setup_new_vars( 'end_divider', $form->id ) );
836 $open = false;
837 $prev_order = false;
838 $add_order = 0;
839 $last_field = false;
840
841 foreach ( $fields as $field ) {
842 if ( $prev_order === $field->field_order ) {
843 ++$add_order;
844 }
845
846 if ( $add_order ) {
847 $reset_fields = true;
848 $field->field_order = $field->field_order + $add_order;
849 FrmField::update( $field->id, array( 'field_order' => $field->field_order ) );
850 }
851
852 switch ( $field->type ) {
853 case 'divider':
854 // Create an end section if open.
855 self::maybe_create_end_section( $open, $reset_fields, $add_order, $end_section_values, $field, 'move' );
856
857 // Mark it open for the next end section.
858 $open = true;
859 break;
860 case 'break':
861 self::maybe_create_end_section( $open, $reset_fields, $add_order, $end_section_values, $field, 'move' );
862 break;
863 case 'end_divider':
864 if ( ! $open ) {
865 // The section isn't open, so this is an extra field that needs to be removed.
866 FrmField::destroy( $field->id );
867 $reset_fields = true;
868 }
869
870 // There is already an end section here, so there is no need to create one.
871 $open = false;
872 }//end switch
873 $prev_order = $field->field_order;
874
875 $last_field = $field;
876 unset( $field );
877 }//end foreach
878
879 self::maybe_create_end_section( $open, $reset_fields, $add_order, $end_section_values, $last_field );
880 }
881
882 /**
883 * Create end section field if it doesn't exist. This is for migration from < 2.0
884 * Fix any ordering that may be messed up
885 *
886 * @param bool $open
887 * @param bool $reset_fields
888 * @param int $add_order
889 * @param array $end_section_values
890 * @param object $field
891 * @param string $move
892 *
893 * @return void
894 */
895 public static function maybe_create_end_section( &$open, &$reset_fields, &$add_order, $end_section_values, $field, $move = 'no' ) {
896 if ( ! $open ) {
897 return;
898 }
899
900 $end_section_values['field_order'] = $field->field_order + 1;
901
902 FrmField::create( $end_section_values );
903
904 if ( $move === 'move' ) {
905 // bump the order of current field unless we're at the end of the form
906 FrmField::update( $field->id, array( 'field_order' => $field->field_order + 2 ) );
907 }
908
909 $add_order += 2;
910 $open = false;
911 $reset_fields = true;
912 }
913
914 /**
915 * @param string $html
916 * @param object $form
917 * @param bool $title
918 * @param bool $description
919 * @param array $values
920 *
921 * @return string
922 */
923 public static function replace_shortcodes( $html, $form, $title = false, $description = false, $values = array() ) {
924 $codes = array(
925 'form_name' => $title,
926 'form_description' => $description,
927 'entry_key' => true,
928 );
929
930 foreach ( $codes as $code => $show ) {
931 if ( $code === 'form_name' ) {
932 $replace_with = $form->name;
933 } elseif ( $code === 'form_description' ) {
934 $replace_with = FrmAppHelper::use_wpautop( $form->description );
935 } elseif ( $code === 'entry_key' && ! empty( $_GET ) && isset( $_GET['entry'] ) ) {
936 $replace_with = FrmAppHelper::simple_get( 'entry' );
937 } else {
938 $replace_with = '';
939 }
940
941 FrmShortcodeHelper::remove_inline_conditions( ( FrmAppHelper::is_true( $show ) && $replace_with != '' ), $code, $replace_with, $html );
942 }
943
944 // Replace [form_key].
945 $html = str_replace( '[form_key]', $form->form_key, $html );
946
947 // Replace [frmurl].
948 $html = str_replace( '[frmurl]', FrmFieldsHelper::dynamic_default_values( 'frmurl' ), $html );
949
950 if ( strpos( $html, '[button_label]' ) ) {
951 add_filter( 'frm_submit_button', 'FrmFormsHelper::submit_button_label', 1 );
952 $submit_label = apply_filters( 'frm_submit_button', $title, $form );
953 $submit_label = esc_attr( do_shortcode( $submit_label ) );
954 $html = str_replace( '[button_label]', $submit_label, $html );
955 }
956
957 $html = apply_filters( 'frm_form_replace_shortcodes', $html, $form, $values );
958
959 if ( strpos( $html, '[if back_button]' ) ) {
960 $html = preg_replace( '/(\[if\s+back_button\])(.*?)(\[\/if\s+back_button\])/mis', '', $html );
961 }
962
963 if ( strpos( $html, '[if save_draft]' ) ) {
964 $html = preg_replace( '/(\[if\s+save_draft\])(.*?)(\[\/if\s+save_draft\])/mis', '', $html );
965 }
966
967 if ( strpos( $html, '[if start_over]' ) ) {
968 $html = preg_replace( '/(\[if\s+start_over\])(.*?)(\[\/if\s+start_over\])/mis', '', $html );
969 }
970
971 if ( apply_filters( 'frm_do_html_shortcodes', true ) ) {
972 $html = do_shortcode( $html );
973 }
974
975 return $html;
976 }
977
978 /**
979 * @param string $submit
980 *
981 * @return string
982 */
983 public static function submit_button_label( $submit ) {
984 if ( ! $submit ) {
985 $frm_settings = FrmAppHelper::get_settings();
986 $submit = $frm_settings->submit_value;
987 }
988
989 return $submit;
990 }
991
992 /**
993 * If the Formidable styling isn't being loaded,
994 * use inline styling to hide the element
995 *
996 * @since 2.03.05
997 *
998 * @return void
999 */
1000 public static function maybe_hide_inline() {
1001 $frm_settings = FrmAppHelper::get_settings();
1002
1003 if ( $frm_settings->load_style === 'none' ) {
1004 echo ' style="display:none;"';
1005 } elseif ( $frm_settings->load_style === 'dynamic' ) {
1006 FrmStylesController::enqueue_style();
1007 }
1008 }
1009
1010 /**
1011 * @param array|bool|int|object|string $form
1012 *
1013 * @return string|null
1014 */
1015 public static function get_form_style_class( $form = false ) {
1016 $style = self::get_form_style( $form );
1017 $class = ' with_frm_style';
1018
1019 if ( empty( $style ) ) {
1020 if ( FrmAppHelper::is_admin_page( 'formidable-entries' ) ) {
1021 return $class;
1022 }
1023 return;
1024 }
1025
1026 // If submit button needs to be inline or centered.
1027 if ( is_object( $form ) ) {
1028 $form = $form->options;
1029 }
1030
1031 if ( ! empty( $form['submit_align'] ) ) {
1032 $submit_align = $form['submit_align'];
1033 } elseif ( self::form_should_be_inline_and_missing_class( $form ) ) {
1034 $submit_align = 'inline';
1035 } else {
1036 $submit_align = '';
1037 }
1038
1039 if ( 'inline' === $submit_align ) {
1040 $class .= ' frm_inline_form';
1041 $class .= self::maybe_align_fields_top( $form );
1042 } elseif ( 'center' === $submit_align ) {
1043 $class .= ' frm_center_submit';
1044 }
1045
1046 $class = apply_filters( 'frm_add_form_style_class', $class, $style, compact( 'form' ) );
1047
1048 return $class;
1049 }
1050
1051 /**
1052 * In order for frm_inline_submit to inline the submit button the form must also have the frm_inline_form that adds the grid-column style rules required for it to work.
1053 *
1054 * @since 5.0.12
1055 *
1056 * @param array $form
1057 *
1058 * @return bool
1059 */
1060 private static function form_should_be_inline_and_missing_class( $form ) {
1061 if ( isset( $form['form_class'] ) && false !== strpos( ' ' . $form['form_class'] . ' ', ' frm_inline_form ' ) ) {
1062 // not missing class, avoid adding it twice.
1063 return false;
1064 }
1065 return ! empty( $form['submit_html'] ) && false !== strpos( $form['submit_html'], 'frm_inline_submit' );
1066 }
1067
1068 /**
1069 * Returns appropriate class if form has top labels
1070 *
1071 * @param array $form
1072 *
1073 * @return string
1074 */
1075 private static function maybe_align_fields_top( $form ) {
1076 return self::form_has_top_labels( $form ) ? ' frm_inline_top' : '';
1077 }
1078
1079 /**
1080 * Determine if a form has fields with top labels so submit button can be aligned properly
1081 *
1082 * @param array $form
1083 *
1084 * @return bool
1085 */
1086 private static function form_has_top_labels( $form ) {
1087 if ( ! isset( $form['fields'] ) ) {
1088 return false;
1089 }
1090
1091 $fields = $form['fields'];
1092
1093 if ( count( $fields ) <= 0 ) {
1094 return false;
1095 }
1096
1097 // Start from the fields closest to the submit button.
1098 $fields = array_reverse( $fields );
1099
1100 foreach ( $fields as $field ) {
1101 $type = $field['original_type'] ?? $field['type'];
1102 $has_input = FrmFieldFactory::field_has_property( $type, 'has_input' );
1103
1104 if ( $has_input ) {
1105 return self::field_has_top_label( $field, $form );
1106 }
1107 }
1108
1109 return false;
1110 }
1111
1112 /**
1113 * Check if a field's label position is set to "top"
1114 *
1115 * @param array $field
1116 * @param bool|object|string $form
1117 *
1118 * @return bool
1119 */
1120 private static function field_has_top_label( $field, $form ) {
1121 $label_position = FrmFieldsHelper::label_position( $field['label'], $field, $form );
1122
1123 return in_array( $label_position, array( 'top', 'inside', 'hidden' ) );
1124 }
1125
1126 /**
1127 * @param array|bool|int|object|string $form
1128 *
1129 * @return string
1130 */
1131 public static function get_form_style( $form ) {
1132 $style = 1;
1133
1134 if ( empty( $form ) || 'default' === $form ) {
1135 return $style;
1136 }
1137
1138 if ( is_object( $form ) && $form->parent_form_id ) {
1139 // get the parent form if this is a child
1140 $form = $form->parent_form_id;
1141 } elseif ( is_array( $form ) && ! empty( $form['parent_form_id'] ) ) {
1142 $form = $form['parent_form_id'];
1143 } elseif ( is_array( $form ) && isset( $form['custom_style'] ) ) {
1144 $style = $form['custom_style'];
1145 }
1146
1147 if ( $form && ( is_string( $form ) || is_int( $form ) ) ) {
1148 $form = FrmForm::getOne( $form );
1149 }
1150
1151 $style = $form && is_object( $form ) && isset( $form->options['custom_style'] ) ? $form->options['custom_style'] : $style;
1152
1153 return $style;
1154 }
1155
1156 /**
1157 * Display the validation error messages when an entry is submitted
1158 *
1159 * @since 2.0.6
1160 *
1161 * @param array $args Includes img, errors.
1162 *
1163 * @return void
1164 */
1165 public static function show_errors( $args ) {
1166 $invalid_msg = self::get_invalid_error_message( $args );
1167
1168 if ( empty( $invalid_msg ) ) {
1169 $show_img = false;
1170 } else {
1171 echo wp_kses_post( $invalid_msg );
1172 $show_img = true;
1173 }
1174
1175 self::show_error(
1176 array(
1177 'img' => $args['img'],
1178 'errors' => $args['errors'],
1179 'show_img' => $show_img,
1180 )
1181 );
1182 }
1183
1184 /**
1185 * Display the error message in the front-end along with the image if set
1186 * The image was removed from the styling settings, but it may still be set with a hook
1187 * If the message in the global settings is empty, show every validation message in the error box
1188 *
1189 * @since 2.0.6
1190 *
1191 * @param array $args Includes img, errors, and show_img.
1192 *
1193 * @return void
1194 */
1195 public static function show_error( $args ) {
1196 // remove any blank messages
1197 $args['errors'] = array_filter( (array) $args['errors'] );
1198
1199 $line_break_first = $args['show_img'];
1200
1201 foreach ( $args['errors'] as $error_key => $error ) {
1202 if ( $line_break_first && ! is_numeric( $error_key ) && ( $error_key === 'cptch_number' || strpos( $error_key, 'field' ) === 0 ) ) {
1203 continue;
1204 }
1205
1206 $id = str_replace( 'field', 'field_', $error_key );
1207 echo '<div id="' . esc_attr( $id ) . '_error">';
1208
1209 if ( $args['show_img'] && ! empty( $args['img'] ) ) {
1210 echo '<img src="' . esc_url( $args['img'] ) . '" alt="" />';
1211 } else {
1212 $args['show_img'] = true;
1213 }
1214
1215 echo wp_kses_post( $error );
1216
1217 echo '</div>';
1218 }
1219 }
1220
1221 /**
1222 * @param int|string $id
1223 *
1224 * @return void
1225 */
1226 public static function maybe_get_scroll_js( $id ) {
1227 $offset = apply_filters( 'frm_scroll_offset', 4, array( 'form_id' => $id ) );
1228
1229 if ( $offset != - 1 ) {
1230 self::get_scroll_js( $id );
1231 }
1232 }
1233
1234 /**
1235 * @param int|string $form_id
1236 *
1237 * @return void
1238 */
1239 public static function get_scroll_js( $form_id ) {
1240 echo '<script type="text/javascript">document.addEventListener(\'DOMContentLoaded\',function(){frmFrontForm.scrollMsg(' . (int) $form_id . ');})</script>';
1241 }
1242
1243 /**
1244 * @since 3.0
1245 *
1246 * @param int|object|string $form_id
1247 * @param mixed $form
1248 *
1249 * @return array
1250 */
1251 public static function get_action_links( $form_id, $form ) {
1252 if ( ! is_object( $form ) ) {
1253 $form = FrmForm::getOne( $form_id );
1254 }
1255
1256 $actions = array();
1257 $trash_links = self::delete_trash_links( $form_id );
1258
1259 if ( 'trash' == $form->status ) {
1260 $actions['restore'] = $trash_links['restore'];
1261
1262 if ( current_user_can( 'frm_delete_forms' ) ) {
1263 $actions['trash'] = $trash_links['delete'];
1264 }
1265 } elseif ( current_user_can( 'frm_edit_forms' ) ) {
1266 $duplicate_link = '?page=formidable&frm_action=duplicate&id=' . $form_id;
1267
1268 if ( $form->is_template ) {
1269 $actions['frm_duplicate'] = array(
1270 'url' => wp_nonce_url( $duplicate_link ),
1271 'label' => __( 'Create Form from Template', 'formidable' ),
1272 'icon' => 'frm_icon_font frm_clone_icon',
1273 );
1274 } else {
1275 $actions['duplicate'] = array(
1276 'url' => wp_nonce_url( $duplicate_link ),
1277 'label' => __( 'Duplicate Form', 'formidable' ),
1278 'icon' => 'frm_icon_font frm_clone_icon',
1279 );
1280 }
1281
1282 $actions['trash'] = self::delete_trash_info( $form_id, $form->status );
1283 }//end if
1284
1285 return $actions;
1286 }
1287
1288 /**
1289 * @param int|object|string $data
1290 *
1291 * @return string
1292 */
1293 public static function edit_form_link( $data ) {
1294 $form_id = self::get_form_id_from_data( $data );
1295
1296 if ( ! $form_id ) {
1297 return '';
1298 }
1299
1300 $label = self::edit_form_link_label( $data );
1301 $link = '<a href="' . esc_url( FrmForm::get_edit_link( $form_id ) ) . '">' . esc_html( $label ) . '</a>';
1302 return $link;
1303 }
1304
1305 /**
1306 * Returns a text used when no title is set.
1307 *
1308 * @since 6.16.1
1309 *
1310 * @return string
1311 */
1312 public static function get_no_title_text() {
1313 return __( '(no title)', 'formidable' );
1314 }
1315
1316 /**
1317 * @param int|object|string $data
1318 *
1319 * @return string
1320 */
1321 public static function edit_form_link_label( $data ) {
1322 $name = self::get_form_name_from_data( $data );
1323
1324 if ( ! $name ) {
1325 return self::get_no_title_text();
1326 }
1327 return FrmAppHelper::truncate( $name, 40 );
1328 }
1329
1330 /**
1331 * @param int|object|string $data
1332 *
1333 * @return int|string
1334 */
1335 private static function get_form_id_from_data( $data ) {
1336 if ( is_object( $data ) ) {
1337 $form_id = $data->id;
1338 } else {
1339 $form_id = $data;
1340 }
1341 return $form_id;
1342 }
1343
1344 /**
1345 * @param mixed $data
1346 *
1347 * @return string
1348 */
1349 private static function get_form_name_from_data( $data ) {
1350 if ( is_object( $data ) ) {
1351 $form_name = $data->name;
1352 } else {
1353 $form_id = $data;
1354 $form_name = FrmForm::getName( $form_id );
1355 }
1356 return $form_name;
1357 }
1358
1359 /**
1360 * @param int $id
1361 * @param string $status
1362 * @param string $length
1363 *
1364 * @return string
1365 */
1366 public static function delete_trash_link( $id, $status, $length = 'label' ) {
1367 $link_details = self::delete_trash_info( $id, $status );
1368
1369 return self::format_link_html( $link_details, $length );
1370 }
1371
1372 /**
1373 * @since 3.0
1374 *
1375 * @param array $link_details
1376 * @param string $length
1377 *
1378 * @return string
1379 */
1380 public static function format_link_html( $link_details, $length = 'label' ) {
1381 $link = '';
1382
1383 if ( ! empty( $link_details ) ) {
1384 $link = '<a href="' . esc_url( $link_details['url'] ) . '" class="frm-trash-link"';
1385
1386 if ( isset( $link_details['data'] ) ) {
1387 foreach ( $link_details['data'] as $data => $value ) {
1388 $link .= ' data-' . esc_attr( $data ) . '="' . esc_attr( $value ) . '"';
1389 }
1390 } elseif ( isset( $link_details['confirm'] ) ) {
1391 $link .= ' onclick="return confirm(\'' . esc_attr( $link_details['confirm'] ) . '\')"';
1392 }
1393
1394 $label = ( $link_details[ $length ] ?? $link_details['label'] );
1395
1396 if ( $length === 'icon' && isset( $link_details[ $length ] ) ) {
1397 $label = '<span class="' . $label . '" title="' . esc_attr( $link_details['label'] ) . '" aria-hidden="true"></span>';
1398 $link .= ' aria-label="' . esc_attr( $link_details['label'] ) . '"';
1399 }
1400
1401 $link .= '>' . $label . '</a>';
1402 }
1403
1404 return $link;
1405 }
1406
1407 /**
1408 * @since 3.0
1409 *
1410 * @param int $id
1411 * @param string $status
1412 *
1413 * @return array
1414 */
1415 public static function delete_trash_info( $id, $status ) {
1416 $labels = self::delete_trash_links( $id );
1417
1418 if ( 'trash' === $status ) {
1419 $info = $labels['restore'];
1420 } elseif ( current_user_can( 'frm_delete_forms' ) ) {
1421 if ( EMPTY_TRASH_DAYS ) {
1422 $info = $labels['trash'];
1423 } else {
1424 $info = $labels['delete'];
1425 }
1426 } else {
1427 $info = array();
1428 }
1429
1430 return $info;
1431 }
1432
1433 /**
1434 * @since 3.0
1435 *
1436 * @param int $id
1437 *
1438 * @return array
1439 */
1440 public static function delete_trash_links( $id ) {
1441 $current_page = FrmAppHelper::get_simple_request( array( 'param' => 'form_type' ) );
1442 $base_url = '?page=formidable&form_type=' . $current_page . '&id=' . $id;
1443
1444 return array(
1445 'restore' => array(
1446 'label' => __( 'Restore from Trash', 'formidable' ),
1447 'short' => __( 'Restore', 'formidable' ),
1448 'url' => wp_nonce_url( $base_url . '&frm_action=untrash', 'untrash_form_' . absint( $id ) ),
1449 ),
1450 'trash' => array(
1451 'label' => __( 'Move Form to Trash', 'formidable' ),
1452 'short' => __( 'Trash', 'formidable' ),
1453 'url' => wp_nonce_url( $base_url . '&frm_action=trash', 'trash_form_' . absint( $id ) ),
1454 'icon' => 'frm_icon_font frm_delete_icon',
1455 'data' => array(
1456 'frmverify' => __( 'Do you want to move this form to the trash?', 'formidable' ),
1457 'frmverify-btn' => 'frm-button-red',
1458 ),
1459 ),
1460 'delete' => array(
1461 'label' => __( 'Delete Permanently', 'formidable' ),
1462 'short' => __( 'Delete', 'formidable' ),
1463 'url' => wp_nonce_url( $base_url . '&frm_action=destroy', 'destroy_form_' . absint( $id ) ),
1464 'confirm' => __( 'Are you sure you want to delete this form and all its entries?', 'formidable' ),
1465 'icon' => 'frm_icon_font frm_delete_icon',
1466 'data' => array(
1467 'frmverify' => __( 'This will permanently delete the form and all its entries. This is irreversible. Are you sure you want to continue?', 'formidable' ),
1468 'frmverify-btn' => 'frm-button-red',
1469 ),
1470 ),
1471 );
1472 }
1473
1474 /**
1475 * @since 3.0
1476 *
1477 * @return array
1478 */
1479 public static function css_classes() {
1480 $classes = array(
1481 'frm_total' => array(
1482 'label' => __( 'Total', 'formidable' ),
1483 'title' => __( 'Add this to a read-only field to display the text in bold without a border or background.', 'formidable' ),
1484 ),
1485 'frm_total_big' => array(
1486 'label' => __( 'Big Total', 'formidable' ),
1487 'title' => __( 'Add this to a read-only field to display the text in large, bold text without a border or background.', 'formidable' ),
1488 ),
1489 'frm_scroll_box' => array(
1490 'label' => __( 'Scroll Box', 'formidable' ),
1491 'title' => __( 'If you have many checkbox or radio button options, you may add this class to allow your user to easily scroll through the options. Or add a scrolling area around content in an HTML field.', 'formidable' ),
1492 ),
1493 'frm_first' => array(
1494 'label' => __( 'First', 'formidable' ),
1495 'title' => __( 'Add this to the first field in each row along with a width. ie frm_first frm4', 'formidable' ),
1496 ),
1497 'frm_alignright' => __( 'Right', 'formidable' ),
1498 'frm_grid_first' => __( 'First Grid Row', 'formidable' ),
1499 'frm_grid' => __( 'Even Grid Row', 'formidable' ),
1500 'frm_grid_odd' => __( 'Odd Grid Row', 'formidable' ),
1501 'frm_color_block' => array(
1502 'label' => __( 'Color Block', 'formidable' ),
1503 'title' => __( 'Add a background color to the field or section.', 'formidable' ),
1504 ),
1505 'frm_capitalize' => array(
1506 'label' => __( 'Capitalize', 'formidable' ),
1507 'title' => __( 'Automatically capitalize the first letter in each word.', 'formidable' ),
1508 ),
1509 );
1510
1511 return apply_filters( 'frm_layout_classes', $classes );
1512 }
1513
1514 /**
1515 * @return array
1516 */
1517 public static function grid_classes() {
1518 return array(
1519 'frm_half' => '1/2',
1520 'frm_third' => '1/3',
1521 'frm_two_thirds' => '2/3',
1522 'frm_fourth' => '1/4',
1523 'frm_three_fourths' => '3/4',
1524 'frm_sixth' => '1/6',
1525 'frm10' => '5/6',
1526 'frm12' => '100%',
1527 );
1528 }
1529
1530 /**
1531 * @since 3.0
1532 *
1533 * @param mixed $style
1534 * @param string $class
1535 *
1536 * @return string
1537 */
1538 public static function style_class_label( $style, $class ) {
1539 $label = '';
1540
1541 if ( empty( $style ) ) {
1542 $label = $class;
1543 } elseif ( ! is_array( $style ) ) {
1544 $label = $style;
1545 } elseif ( isset( $style['label'] ) ) {
1546 $label = $style['label'];
1547 }
1548
1549 return $label;
1550 }
1551
1552 /**
1553 * @param string $status
1554 *
1555 * @return string
1556 */
1557 public static function status_nice_name( $status ) {
1558 $nice_names = array(
1559 'draft' => __( 'Draft', 'formidable' ),
1560 'trash' => __( 'Trash', 'formidable' ),
1561 'publish' => __( 'Published', 'formidable' ),
1562 );
1563
1564 if ( ! in_array( $status, array_keys( $nice_names ), true ) ) {
1565 $status = 'publish';
1566 }
1567
1568 $name = $nice_names[ $status ];
1569
1570 return $name;
1571 }
1572
1573 /**
1574 * Renders a template icon based on the given categories.
1575 *
1576 * @param array $categories The categories to render the icon for.
1577 * @param array $atts {
1578 * Optional. An array of attributes for rendering.
1579 *
1580 * @type string $html 'span' or 'div'. Default 'span'.
1581 * @type bool $bg Whether to add a background color or not. Default false.
1582 * }
1583 *
1584 * @return void
1585 */
1586 public static function template_icon( $categories, $atts = array() ) {
1587 // Define defaults.
1588 $defaults = array(
1589 'bg' => true,
1590 );
1591 $atts = array_merge( $defaults, $atts );
1592
1593 // Filter out ignored categories.
1594 $ignore = self::get_license_types();
1595 $categories = array_diff( $categories, $ignore );
1596
1597 // Define icons mapping.
1598 $icons = array(
1599 'WooCommerce' => array( 'woocommerce', 'var(--purple)' ),
1600 'Post' => array( 'wordpress', 'rgb(0,160,210)' ),
1601 'User Registration' => array( 'register', 'var(--pink)' ),
1602 'Registration and Signup' => array( 'register', 'var(--pink)' ),
1603 'PayPal' => array( 'paypal' ),
1604 'Stripe' => array( 'credit_card', 'var(--green)' ),
1605 'Twilio' => array( 'sms' ),
1606 'Payment' => array( 'credit_card' ),
1607 'Order Form' => array( 'product' ),
1608 'Finance' => array( 'total' ),
1609 'Health and Wellness' => array( 'heart', 'var(--pink)' ),
1610 'Event Planning' => array( 'calendar', 'var(--orange)' ),
1611 'Real Estate' => array( 'house' ),
1612 'Nonprofit' => array( 'heart_solid' ),
1613 'Calculator' => array( 'calculator', 'var(--purple)' ),
1614 'Quiz' => array( 'percent' ),
1615 'Registrations' => array( 'address_card' ),
1616 'Customer Service' => array( 'users_solid' ),
1617 'Education' => array( 'pencil' ),
1618 'Marketing' => array( 'eye' ),
1619 'Feedback' => array( 'smile' ),
1620 'Business Operations' => array( 'case' ),
1621 'Contact Form' => array( 'email' ),
1622 'Conversational Forms' => array( 'chat_forms' ),
1623 'Survey' => array( 'chat_forms', 'var(--orange)' ),
1624 'Application' => array( 'align_right' ),
1625 'Signature' => array( 'signature' ),
1626 '' => array( 'align_right' ),
1627 );
1628
1629 // Determine the icon to be used.
1630 $icon = $icons[''];
1631
1632 if ( count( $categories ) === 1 ) {
1633 $category = reset( $categories );
1634 $icon = $icons[ $category ] ?? $icon;
1635 } elseif ( ! empty( $categories ) ) {
1636 $icons = array_intersect_key( $icons, array_flip( $categories ) );
1637 $icon = reset( $icons );
1638 }
1639
1640 // Prepare variables for output.
1641 $icon_name = $icon[0];
1642 $bg_color = $icon[1] ?? '';
1643
1644 // Render the icon.
1645 echo '<span class="frm-category-icon frm-icon-wrapper"';
1646
1647 if ( $bg_color && $atts['bg'] ) {
1648 echo ' style="background-color:' . esc_attr( $bg_color ) . '"';
1649 }
1650 echo '>';
1651 FrmAppHelper::icon_by_class( 'frmfont frm_' . $icon_name . '_icon' );
1652 echo '</span>';
1653 }
1654
1655 /**
1656 * Get template install link.
1657 *
1658 * @since 4.02
1659 *
1660 * @param array $template Template details.
1661 * @param array $args Additional arguments.
1662 *
1663 * @return array The link attributes.
1664 */
1665 public static function get_template_install_link( $template, $args ) {
1666 $defaults = array(
1667 'class' => 'install-now',
1668 'href' => 'href',
1669 'atts' => true,
1670 );
1671
1672 if ( ! empty( $template['url'] ) ) {
1673 $link = array(
1674 'url' => $template['url'],
1675 'label' => __( 'Create Form', 'formidable' ),
1676 'class' => 'frm-install-template',
1677 'href' => 'rel',
1678 'atts' => '',
1679 );
1680 } else {
1681 $link = array(
1682 'url' => $args['pricing'],
1683 'label' => __( 'Upgrade', 'formidable' ),
1684 );
1685 }
1686
1687 return array_merge( $defaults, $link );
1688 }
1689
1690 /**
1691 * Is the template included with the license type?
1692 *
1693 * @since 4.02.02
1694 *
1695 * @param array $args
1696 *
1697 * @return bool
1698 */
1699 public static function plan_is_allowed( $args ) {
1700 if ( empty( $args['license_type'] ) ) {
1701 return false;
1702 }
1703
1704 $plans = array( 'free', 'personal', 'business', 'elite' );
1705 $license_type = strtolower( $args['license_type'] );
1706 $plan_required = strtolower( $args['plan_required'] );
1707 $included = $license_type === $plan_required;
1708
1709 if ( $included || ! in_array( $plan_required, $plans, true ) ) {
1710 return $included;
1711 }
1712
1713 foreach ( $plans as $plan ) {
1714 if ( $included || $plan === $license_type ) {
1715 break;
1716 }
1717
1718 $included = $plan === $plan_required;
1719 }
1720
1721 return $included;
1722 }
1723
1724 /**
1725 * If a template or add-on cannot be installed, show a message
1726 * about which plan is required.
1727 *
1728 * @since 4.0
1729 *
1730 * @param string $requires
1731 * @param string $link
1732 *
1733 * @return void
1734 */
1735 public static function show_plan_required( $requires, $link ) {
1736 if ( empty( $requires ) ) {
1737 return;
1738 }
1739
1740 ?>
1741 <p class="frm_plan_required">
1742 <?php esc_html_e( 'Plan required:', 'formidable' ); ?>
1743 <a href="<?php echo esc_url( $link ); ?>" target="_blank" rel="noopener">
1744 <?php echo esc_html( $requires ); ?>
1745 </a>
1746 </p>
1747 <?php
1748 }
1749
1750 /**
1751 * @since 4.0
1752 *
1753 * @param array $item
1754 *
1755 * @return false|string
1756 */
1757 public static function get_plan_required( &$item ) {
1758 if ( ! isset( $item['categories'] ) || ! is_array( $item['categories'] ) || ! empty( $item['url'] ) ) {
1759 return false;
1760 }
1761
1762 $plans = self::get_license_types();
1763
1764 foreach ( $item['categories'] as $k => $category ) {
1765 if ( in_array( $category, $plans, true ) ) {
1766 unset( $item['categories'][ $k ] );
1767
1768 $category = self::convert_legacy_package_names( $category );
1769
1770 return $category;
1771 }
1772 }
1773
1774 return false;
1775 }
1776
1777 /**
1778 * Converts legacy package names to the current standard package name.
1779 *
1780 * @since 6.15
1781 *
1782 * @param string $package_name
1783 *
1784 * @return string The updated package name.
1785 */
1786 public static function convert_legacy_package_names( $package_name ) {
1787 if ( in_array( $package_name, array( 'Creator', 'Personal' ), true ) ) {
1788 $package_name = 'Plus';
1789 }
1790
1791 return $package_name;
1792 }
1793
1794 /**
1795 * Get the license types.
1796 *
1797 * @since 6.15
1798 *
1799 * @param array $args
1800 *
1801 * @return array
1802 */
1803 public static function get_license_types( $args = array() ) {
1804 $defaults = array(
1805 'include_all' => true,
1806 'case_lower' => false,
1807 );
1808 $args = wp_parse_args( $args, $defaults );
1809
1810 $license_types = array( 'Basic', 'Plus', 'Business', 'Elite' );
1811
1812 if ( $args['include_all'] ) {
1813 $license_types = array_merge( array( 'free', 'Personal', 'Creator' ), $license_types );
1814 }
1815
1816 if ( $args['case_lower'] ) {
1817 $license_types = array_map( 'strtolower', $license_types );
1818 }
1819
1820 return $license_types;
1821 }
1822
1823 /**
1824 * Checks for warnings to be displayed after form settings are saved.
1825 *
1826 * @since 4.04
1827 *
1828 * @param array $values The $_POST array, which contains values submitted in a form.
1829 *
1830 * @return array An array of warnings or an empty array.
1831 */
1832 public static function check_for_warnings( $values ) {
1833 $warnings = array();
1834
1835 $redirect_warning = self::check_redirect_url_for_unsafe_params( $values );
1836
1837 if ( $redirect_warning ) {
1838 $warnings[] = $redirect_warning;
1839 }
1840
1841 return apply_filters( 'frm_check_for_warnings', $warnings, $values );
1842 }
1843
1844 /**
1845 * Checks the redirect URL for params whose names are reserved words.
1846 *
1847 * @since 4.04
1848 *
1849 * @param array $values The $_POST array, which contains the values submitted in a form.
1850 *
1851 * @return bool|string A warning message about unsafe params or false.
1852 */
1853 private static function check_redirect_url_for_unsafe_params( $values ) {
1854 if ( ! isset( $values['options'] ) ) {
1855 return false;
1856 }
1857
1858 $options = $values['options'];
1859 FrmAppHelper::sanitize_with_html( $options );
1860
1861 if ( ! isset( $options['success_action'] ) || $options['success_action'] !== 'redirect' || ! isset( $options['success_url'] ) ) {
1862 return false;
1863 }
1864
1865 $unsafe_params_in_redirect = self::get_unsafe_params( $options['success_url'] );
1866
1867 return self::create_unsafe_param_warning( $unsafe_params_in_redirect );
1868 }
1869
1870 /**
1871 * Returns an array of params whose names are reserved words in the specified URL.
1872 *
1873 * @since 4.04
1874 *
1875 * @param string $url The URL whose params are being checked.
1876 *
1877 * @return array An array of params whose names are reserved words or an empty array.
1878 */
1879 private static function get_unsafe_params( $url ) {
1880 $redirect_components = parse_url( $url );
1881
1882 if ( empty( $redirect_components['query'] ) ) {
1883 return array();
1884 }
1885 parse_str( $redirect_components['query'], $redirect_params );
1886 $redirect_param_names = array_keys( $redirect_params );
1887 $reserved_words = self::reserved_words();
1888 $unsafe_params_in_redirect = array_intersect( $redirect_param_names, $reserved_words );
1889
1890 return array_values( $unsafe_params_in_redirect );
1891 }
1892
1893 /**
1894 * Returns a warning if reserved words have been used as param names in the redirect URL.
1895 *
1896 * @since 4.04
1897 *
1898 * @param array $unsafe_params_in_redirect Array of params from the redirect URL whose names are reserved words.
1899 *
1900 * @return bool|string A string with an unsafe param message or false.
1901 */
1902 private static function create_unsafe_param_warning( $unsafe_params_in_redirect ) {
1903 $count = count( $unsafe_params_in_redirect );
1904 $caution = esc_html__( 'Is this intentional?', 'formidable' );
1905 $reserved_words_intro = esc_html__( 'See the list of reserved words in WordPress.', 'formidable' );
1906 $reserved_words_link = '<a href="https://codex.wordpress.org/WordPress_Query_Vars" target="_blank"> ' . $reserved_words_intro . '</a>';
1907
1908 if ( $count === 0 ) {
1909 return false;
1910 }
1911
1912 if ( $count == 1 ) {
1913 /* translators: %s: the name of a single parameter in the redirect URL */
1914 return sprintf( esc_html__( 'The redirect URL is using the parameter "%s", which is reserved by WordPress. ', 'formidable' ), $unsafe_params_in_redirect[0] ) . $caution . $reserved_words_link;
1915 }
1916
1917 $unsafe_params_string = implode( '", "', $unsafe_params_in_redirect );
1918
1919 /* translators: %s: the names of two or more parameters in the redirect URL, separated by commas */
1920 return sprintf( esc_html__( 'The redirect URL is using the parameters "%s", which are reserved by WordPress. ', 'formidable' ), $unsafe_params_string ) . $caution . $reserved_words_link;
1921 }
1922
1923 /**
1924 * Returns an array of common reserved words in WordPress.
1925 *
1926 * An edited list of reserved terms from the Codex.
1927 * https://codex.wordpress.org/Reserved_Terms
1928 *
1929 * @since 4.04
1930 *
1931 * @return array Array of WordPress reserved words.
1932 */
1933 public static function reserved_words() {
1934 return array(
1935 'id',
1936 'attachment',
1937 'author',
1938 'author_name',
1939 'calendar',
1940 'cat',
1941 'category',
1942 'category_name',
1943 'cpage',
1944 'custom',
1945 'day',
1946 'date',
1947 'error',
1948 'feed',
1949 'hour',
1950 'm',
1951 'minute',
1952 'more',
1953 'name',
1954 'order',
1955 'p',
1956 'page',
1957 'page_id',
1958 'paged',
1959 'pb',
1960 'post',
1961 'posts',
1962 'preview',
1963 's',
1964 'search',
1965 'second',
1966 'sentence',
1967 'tag',
1968 'taxonomy',
1969 'tb',
1970 'term',
1971 'terms',
1972 'theme',
1973 'title',
1974 'type',
1975 'w',
1976 'year',
1977 );
1978 }
1979
1980 /**
1981 * Make sure the field shortcodes in a url always add the sanitize_url=1 option if nothing is defined.
1982 * This is to prevent some field characters like ', @, and | from being stripped from the redirect URL.
1983 *
1984 * @since 5.0.16
1985 *
1986 * @param string $url
1987 * @param int $form_id
1988 *
1989 * @return string
1990 */
1991 public static function maybe_add_sanitize_url_attr( $url, $form_id ) {
1992 if ( false === strpos( $url, '[' ) ) {
1993 // Do nothing if no shortcodes are detected.
1994 return $url;
1995 }
1996
1997 $parsed = wp_parse_url( $url );
1998
1999 if ( empty( $parsed['query'] ) ) {
2000 // Do nothing if no query can be detected in the url string.
2001 return $url;
2002 }
2003
2004 $original_query = $parsed['query'];
2005 $query = $parsed['query'];
2006
2007 $shortcodes = FrmFieldsHelper::get_shortcodes( $query, $form_id );
2008
2009 if ( empty( $shortcodes[0] ) ) {
2010 // No shortcodes found, do nothing.
2011 return $url;
2012 }
2013
2014 foreach ( $shortcodes[0] as $key => $shortcode ) {
2015 $options = trim( $shortcodes[3][ $key ] );
2016
2017 if ( in_array( $shortcodes[1][ $key ], array( 'if ' ), true ) ) {
2018 // Skip if shortcodes.
2019 continue;
2020 }
2021
2022 if ( false !== strpos( $options, 'sanitize_url=' ) || false !== strpos( $options, 'sanitize=' ) ) {
2023 // A sanitize option is already set so leave it alone.
2024 continue;
2025 }
2026
2027 $new_shortcode = '[' . $shortcodes[2][ $key ];
2028
2029 if ( $options ) {
2030 $new_shortcode .= ' ' . $options;
2031 }
2032
2033 $new_shortcode .= ' sanitize_url=1]';
2034
2035 $query = str_replace( $shortcode, $new_shortcode, $query );
2036 }//end foreach
2037
2038 if ( $query === $original_query ) {
2039 return $url;
2040 }
2041
2042 return str_replace( $original_query, $query, $url );
2043 }
2044
2045 /**
2046 * Outputs the appropriate button text in the publish box.
2047 *
2048 * @return void
2049 */
2050 public static function publish_box_button_text() {
2051 $is_new_template = FrmAppHelper::simple_get( 'new_template' );
2052 $action = FrmAppHelper::simple_get( 'frm_action' );
2053
2054 if ( ( 'edit' === $action || 'settings' === $action ) && $is_new_template ) {
2055 esc_html_e( 'Save', 'formidable' );
2056 } else {
2057 esc_html_e( 'Update', 'formidable' );
2058 }
2059 }
2060
2061 /**
2062 * Strip characters similar to the WordPress sanitize_html_class function, but allow for [ and ].
2063 * This allows shortcodes inside of the layout classes setting.
2064 *
2065 * @since 6.16
2066 *
2067 * @param string $classname
2068 *
2069 * @return string
2070 */
2071 public static function sanitize_layout_class( $classname ) {
2072 // Strip out any percent-encoded characters.
2073 $sanitized = preg_replace( '|%[a-fA-F0-9][a-fA-F0-9]|', '', $classname );
2074
2075 // Limit to A-Z, a-z, 0-9, '_', '-', '[', ']'.
2076 $sanitized = preg_replace( '/[^A-Za-z0-9_\-\[\]]/', '', $sanitized );
2077
2078 return $sanitized;
2079 }
2080
2081 /**
2082 * Returns true if the preview should be blocked.
2083 *
2084 * @since 6.20
2085 *
2086 * @param string $form_key
2087 *
2088 * @return bool
2089 */
2090 public static function should_block_preview( $form_key ) {
2091 $should_block = in_array( $form_key, array( 'contact-form', 'contact-us' ), true ) && ! current_user_can( 'frm_view_forms' );
2092 /**
2093 * Filters whether the form preview should be blocked.
2094 *
2095 * @since 6.20
2096 *
2097 * @param bool $should_block
2098 * @param string $form_key
2099 */
2100 $should_block = (bool) apply_filters( 'frm_block_preview', $should_block, $form_key );
2101 return $should_block;
2102 }
2103
2104 /**
2105 * Checks if the form is loaded by API.
2106 *
2107 * @since 6.21
2108 *
2109 * @return bool
2110 */
2111 public static function form_is_loaded_by_api() {
2112 global $frm_vars;
2113
2114 if ( ! empty( $frm_vars['inplace_edit'] ) ) {
2115 return true;
2116 }
2117 return self::is_formidable_api_form() || self::is_gutenberg_editor() || self::is_elementor_ajax() || self::is_visual_views_preview();
2118 }
2119
2120 /**
2121 * @since 6.21
2122 *
2123 * @return bool
2124 */
2125 private static function is_visual_views_preview() {
2126 return 'frm_views_process_box_preview' === FrmAppHelper::get_post_param( 'action' );
2127 }
2128
2129 /**
2130 * @since 6.21
2131 *
2132 * @return bool
2133 */
2134 private static function is_elementor_ajax() {
2135 return 'elementor_ajax' === FrmAppHelper::get_post_param( 'action' );
2136 }
2137
2138 /**
2139 * @since 6.21
2140 *
2141 * @return bool
2142 */
2143 private static function is_gutenberg_editor() {
2144 $url = FrmAppHelper::get_server_value( 'REQUEST_URI' );
2145
2146 if ( false !== strpos( $url, '/wp-json/wp/v2/block-renderer/formidable/simple-form' ) ) {
2147 return true;
2148 }
2149
2150 if ( false !== strpos( urldecode( $url ), 'rest_route=/wp/v2/block-renderer/formidable/' ) ) {
2151 return true;
2152 }
2153
2154 global $pagenow;
2155
2156 if ( 'post.php' === $pagenow ) {
2157 return true;
2158 }
2159
2160 return false;
2161 }
2162
2163 /**
2164 * @since 6.21
2165 *
2166 * @return bool
2167 */
2168 private static function is_formidable_api_form() {
2169 if ( ! class_exists( 'FrmAPIAppController' ) ) {
2170 return false;
2171 }
2172
2173 $url = FrmAppHelper::get_server_value( 'REQUEST_URI' );
2174
2175 if ( false !== strpos( $url, '/wp-json/frm/v2/forms/' ) ) {
2176 // Prevent the honeypot from appearing for an API loaded form.
2177 // This is to prevent conflicts where the script is not working.
2178 return true;
2179 }
2180
2181 if ( is_callable( 'FrmProFormState::get_from_request' ) ) {
2182 $api = FrmProFormState::get_from_request( 'a', 0 );
2183
2184 if ( $api ) {
2185 return true;
2186 }
2187 }
2188
2189 return false;
2190 }
2191
2192 /**
2193 * @since 3.0
2194 * @deprecated 6.11
2195 *
2196 * @param array $atts
2197 *
2198 * @return void
2199 */
2200 public static function actions_dropdown( $atts ) {
2201 _deprecated_function( __METHOD__, '6.11' );
2202
2203 if ( ! FrmAppHelper::is_admin_page( 'formidable' ) ) {
2204 return;
2205 }
2206
2207 $status = $atts['status'];
2208 $form_id = $atts['id'] ?? FrmAppHelper::get_param( 'id', 0, 'get', 'absint' );
2209 $trash_link = self::delete_trash_info( $form_id, $status );
2210 $links = self::get_action_links( $form_id, $status );
2211 include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/actions-dropdown.php';
2212 }
2213
2214 /**
2215 * Check if Pro isn't up to date yet.
2216 * If Pro is active but using a version earlier than v6.2 fallback to Pro for AJAX submit (so things don't all happen twice).
2217 *
2218 * @since 6.2
2219 * @deprecated 6.20
2220 *
2221 * @return bool
2222 */
2223 public static function should_use_pro_for_ajax_submit() {
2224 _deprecated_function( __METHOD__, '6.20' );
2225 return false;
2226 }
2227 }
2228