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

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