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

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