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

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