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

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