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

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