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

1,786 lines 53.4 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 * @param string $class
66 * @param string $param
67 * @param array $add_html
68 *
69 * @since 2.0.6
70 */
71 public static function add_html_attr( $class, $param, &$add_html ) {
72 if ( ! empty( $class ) ) {
73 $add_html[ $param ] = sanitize_title( $param ) . '="' . esc_attr( trim( sanitize_text_field( $class ) ) ) . '"';
74 }
75 }
76
77 /**
78 * @param string|object|false $selected - The label for the placeholder, or the form object.
79 */
80 public static function form_switcher( $selected = false ) {
81 $where = apply_filters( 'frm_forms_dropdown', array(), '' );
82 $forms = FrmForm::get_published_forms( $where );
83
84 $args = array(
85 'id' => 0,
86 'form' => 0,
87 );
88 if ( isset( $_GET['id'] ) && ! isset( $_GET['form'] ) ) {
89 unset( $args['form'] );
90 } elseif ( isset( $_GET['form'] ) && ! isset( $_GET['id'] ) ) {
91 unset( $args['id'] );
92 }
93
94 $frm_action = FrmAppHelper::simple_get( 'frm_action', 'sanitize_title' );
95 if ( FrmAppHelper::is_admin_page( 'formidable-entries' ) && in_array( $frm_action, array( 'edit', 'show', 'destroy', 'destroy_all' ) ) ) {
96 $args['frm_action'] = 'list';
97 $args['form'] = 0;
98 } elseif ( FrmAppHelper::is_admin_page( 'formidable' ) && in_array( $frm_action, array( 'new', 'duplicate' ) ) ) {
99 $args['frm_action'] = 'edit';
100 } elseif ( FrmAppHelper::is_style_editor_page() ) {
101 // Avoid passing style into form switcher on style page.
102 unset( $args['id'] );
103 $query_args = array(
104 'page' => 'formidable-styles',
105 );
106 if ( $frm_action ) {
107 $query_args['frm_action'] = $frm_action;
108 }
109 $base = add_query_arg( $query_args, admin_url( 'admin.php' ) );
110 } elseif ( isset( $_GET['post'] ) ) {
111 $args['form'] = 0;
112 $base = admin_url( 'edit.php?post_type=frm_display' );
113 }
114
115 $form_id = 0;
116 if ( is_object( $selected ) ) {
117 $form_id = $selected->id;
118 $selected = $selected->name;
119 }
120
121 $name = ( $selected === false ) ? __( 'Switch Form', 'formidable' ) : $selected;
122 $name = '' === $name ? __( '(no title)', 'formidable' ) : strip_tags( $name );
123 $truncated_name = FrmAppHelper::truncate( $name, 25 );
124
125 if ( count( $forms ) < 2 ) {
126 ?>
127 <div id="frm_bs_dropdown">
128 <h1>
129 <span class="frm_bstooltip" title="<?php echo esc_attr( $truncated_name === $name ? '' : $name ); ?>" data-placement="right">
130 <?php echo esc_html( $name ); ?>
131 </span>
132 </h1>
133 </div>
134 <?php
135 return;
136 }
137 ?>
138 <div id="frm_bs_dropdown" class="dropdown <?php echo esc_attr( is_rtl() ? 'dropdown-menu-right' : 'dropdown-menu-left' ); ?>">
139 <a href="#" id="frm-navbarDrop" class="frm-dropdown-toggle" data-toggle="dropdown">
140 <h1>
141 <span class="frm_bstooltip" title="<?php echo esc_attr( $truncated_name === $name ? '' : $name ); ?>" data-placement="right">
142 <?php echo esc_html( $name ); ?>
143 </span>
144 <?php FrmAppHelper::icon_by_class( 'frmfont frm_arrowdown6_icon', array( 'aria-hidden' => 'true' ) ); ?>
145 </h1>
146 </a>
147 <ul class="frm-dropdown-menu frm-on-top frm-inline-modal frm_code_list frm-full-hover" role="menu" aria-labelledby="frm-navbarDrop">
148 <?php if ( count( $forms ) > 8 ) { ?>
149 <li class="frm-with-search">
150 <?php
151 FrmAppHelper::show_search_box(
152 array(
153 'input_id' => 'dropform',
154 'placeholder' => __( 'Search Forms', 'formidable' ),
155 'tosearch' => 'frm-dropdown-form',
156 )
157 );
158 ?>
159 </li>
160 <?php } ?>
161 <?php
162 foreach ( $forms as $form ) {
163 if ( $form->id === $form_id ) {
164 // Don't include the selected form in the switcher since it does nothing.
165 continue;
166 }
167
168 if ( isset( $args['id'] ) ) {
169 $args['id'] = $form->id;
170 }
171 if ( isset( $args['form'] ) ) {
172 $args['form'] = $form->id;
173 }
174
175 $url = isset( $base ) ? add_query_arg( $args, $base ) : add_query_arg( $args );
176 $form_name = empty( $form->name ) ? __( '(no title)', 'formidable' ) : $form->name;
177 ?>
178 <li class="frm-dropdown-form">
179 <a href="<?php echo esc_url( $url ); ?>" tabindex="-1" class="frm-justify-between">
180 <?php echo esc_html( $form_name ); ?>
181 <span>
182 <?php
183 printf(
184 /* translators: %d: Form ID */
185 esc_html__( '(ID %d)', 'formidable' ),
186 esc_attr( $form->id )
187 );
188 ?>
189 </span>
190 <span class="frm_hidden"><?php echo esc_html( $form->form_key ); ?></span>
191 </a>
192 </li>
193 <?php
194 unset( $form );
195 }//end foreach
196 ?>
197 </ul>
198 </div>
199 <?php
200 }
201
202 /**
203 * @since 3.05
204 * @deprecated 4.0
205 *
206 * @param array $values The form array.
207 */
208 public static function builder_submit_button( $values ) {
209 FrmDeprecated::builder_submit_button( $values );
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 * @param array $options
409 * @param array $values
410 *
411 * @since 2.0.6
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]<a href="#" tabindex="0" class="frm_save_draft" [draft_hook]>[draft_label]</a>[/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 * @param array $args
587 */
588 public static function insert_opt_html( $args ) {
589 $class = isset( $args['class'] ) ? $args['class'] : '';
590 $fields = FrmField::all_field_selection();
591 $field = isset( $fields[ $args['type'] ] ) ? $fields[ $args['type'] ] : array();
592
593 self::prepare_field_type( $field );
594
595 if ( ! isset( $field['icon'] ) ) {
596 $field['icon'] = 'frmfont frm_pencil_icon';
597 }
598
599 $possible_email_field = FrmFieldFactory::field_has_property( $args['type'], 'holds_email_values' );
600 if ( $possible_email_field ) {
601 $class .= ' show_frm_not_email_to';
602 }
603
604 if ( 'url' === $args['type'] ) {
605 $class .= ' frm_insert_url';
606 }
607 ?>
608 <li class="<?php echo esc_attr( $class ); ?>">
609 <a href="javascript:void(0)" class="frmids frm_insert_code"
610 data-code="<?php echo esc_attr( $args['id'] ); ?>">
611 <?php FrmAppHelper::icon_by_class( $field['icon'], array( 'aria-hidden' => 'true' ) ); ?>
612 <?php echo esc_attr( FrmAppHelper::truncate( $args['name'], 60 ) ); ?>
613 <span>[<?php echo esc_attr( isset( $args['id_label'] ) ? $args['id_label'] : $args['id'] ); ?>]</span>
614 </a>
615 <a href="javascript:void(0)" class="frmkeys frm_insert_code frm_hidden"
616 data-code="<?php echo esc_attr( $args['key'] ); ?>">
617 <?php if ( isset( $field['icon'] ) ) { ?>
618 <?php FrmAppHelper::icon_by_class( $field['icon'], array( 'aria-hidden' => 'true' ) ); ?>
619 <?php } ?>
620 <?php echo esc_attr( FrmAppHelper::truncate( $args['name'], 60 ) ); ?>
621 <span>[<?php echo esc_attr( FrmAppHelper::truncate( isset( $args['key_label'] ) ? $args['key_label'] : $args['key'], 7 ) ); ?>]</span>
622 </a>
623 </li>
624 <?php
625 }
626
627 /**
628 * @since 4.0
629 * @param array $args
630 */
631 public static function insert_code_html( $args ) {
632 $defaults = array(
633 'class' => '',
634 'code' => '',
635 'label' => '',
636 'title' => '',
637 );
638
639 $args = array_merge( $defaults, $args );
640 $has_tooltip = ! empty( $args['title'] );
641
642 ?>
643 <li class="<?php echo esc_attr( $args['class'] ); ?>">
644 <a href="javascript:void(0)" class="frm_insert_code <?php echo $has_tooltip ? 'frm_help' : ''; ?>"
645 <?php echo $has_tooltip ? 'title="' . esc_attr( $args['title'] ) . '"' : ''; ?>
646 data-code="<?php echo esc_attr( $args['code'] ); ?>">
647 <?php echo esc_attr( FrmAppHelper::truncate( $args['label'], 60 ) ); ?>
648 <span>
649 [<?php echo esc_attr( FrmAppHelper::truncate( $args['code'], 10 ) ); ?>]
650 </span>
651 </a>
652 </li>
653 <?php
654 }
655
656 /**
657 * Some field types in add-ons may have been added with only
658 * a field type and name.
659 *
660 * @since 4.0
661 */
662 public static function prepare_field_type( &$field ) {
663 if ( ! is_array( $field ) ) {
664 $field = array(
665 'name' => $field,
666 'icon' => 'frm_icon_font frm_pencil_icon',
667 );
668 }
669 }
670
671 /**
672 * Automatically add end section fields if they don't exist (2.0 migration)
673 *
674 * @since 2.0
675 *
676 * @param object $form
677 * @param array $fields
678 * @param boolean $reset_fields
679 */
680 public static function auto_add_end_section_fields( $form, $fields, &$reset_fields ) {
681 if ( empty( $fields ) ) {
682 return;
683 }
684
685 $end_section_values = apply_filters( 'frm_before_field_created', FrmFieldsHelper::setup_new_vars( 'end_divider', $form->id ) );
686 $open = false;
687 $prev_order = false;
688 $add_order = 0;
689 $last_field = false;
690 foreach ( $fields as $field ) {
691 if ( $prev_order === $field->field_order ) {
692 $add_order ++;
693 }
694
695 if ( $add_order ) {
696 $reset_fields = true;
697 $field->field_order = $field->field_order + $add_order;
698 FrmField::update( $field->id, array( 'field_order' => $field->field_order ) );
699 }
700
701 switch ( $field->type ) {
702 case 'divider':
703 // Create an end section if open.
704 self::maybe_create_end_section( $open, $reset_fields, $add_order, $end_section_values, $field, 'move' );
705
706 // Mark it open for the next end section.
707 $open = true;
708 break;
709 case 'break':
710 self::maybe_create_end_section( $open, $reset_fields, $add_order, $end_section_values, $field, 'move' );
711 break;
712 case 'end_divider':
713 if ( ! $open ) {
714 // The section isn't open, so this is an extra field that needs to be removed.
715 FrmField::destroy( $field->id );
716 $reset_fields = true;
717 }
718
719 // There is already an end section here, so there is no need to create one.
720 $open = false;
721 }//end switch
722 $prev_order = $field->field_order;
723
724 $last_field = $field;
725 unset( $field );
726 }//end foreach
727
728 self::maybe_create_end_section( $open, $reset_fields, $add_order, $end_section_values, $last_field );
729 }
730
731 /**
732 * Create end section field if it doesn't exist. This is for migration from < 2.0
733 * Fix any ordering that may be messed up
734 */
735 public static function maybe_create_end_section( &$open, &$reset_fields, &$add_order, $end_section_values, $field, $move = 'no' ) {
736 if ( ! $open ) {
737 return;
738 }
739
740 $end_section_values['field_order'] = $field->field_order + 1;
741
742 FrmField::create( $end_section_values );
743
744 if ( $move == 'move' ) {
745 // bump the order of current field unless we're at the end of the form
746 FrmField::update( $field->id, array( 'field_order' => $field->field_order + 2 ) );
747 }
748
749 $add_order += 2;
750 $open = false;
751 $reset_fields = true;
752 }
753
754 public static function replace_shortcodes( $html, $form, $title = false, $description = false, $values = array() ) {
755 $codes = array(
756 'form_name' => $title,
757 'form_description' => $description,
758 'entry_key' => true,
759 );
760 foreach ( $codes as $code => $show ) {
761 if ( $code === 'form_name' ) {
762 $replace_with = $form->name;
763 } elseif ( $code === 'form_description' ) {
764 $replace_with = FrmAppHelper::use_wpautop( $form->description );
765 } elseif ( $code === 'entry_key' && ! empty( $_GET ) && isset( $_GET['entry'] ) ) {
766 $replace_with = FrmAppHelper::simple_get( 'entry' );
767 } else {
768 $replace_with = '';
769 }
770
771 FrmShortcodeHelper::remove_inline_conditions( ( FrmAppHelper::is_true( $show ) && $replace_with != '' ), $code, $replace_with, $html );
772 }
773
774 // Replace [form_key].
775 $html = str_replace( '[form_key]', $form->form_key, $html );
776
777 // Replace [frmurl].
778 $html = str_replace( '[frmurl]', FrmFieldsHelper::dynamic_default_values( 'frmurl' ), $html );
779
780 if ( strpos( $html, '[button_label]' ) ) {
781 add_filter( 'frm_submit_button', 'FrmFormsHelper::submit_button_label', 1 );
782 $submit_label = apply_filters( 'frm_submit_button', $title, $form );
783 $submit_label = esc_attr( do_shortcode( $submit_label ) );
784 $html = str_replace( '[button_label]', $submit_label, $html );
785 }
786
787 $html = apply_filters( 'frm_form_replace_shortcodes', $html, $form, $values );
788
789 if ( strpos( $html, '[if back_button]' ) ) {
790 $html = preg_replace( '/(\[if\s+back_button\])(.*?)(\[\/if\s+back_button\])/mis', '', $html );
791 }
792
793 if ( strpos( $html, '[if save_draft]' ) ) {
794 $html = preg_replace( '/(\[if\s+save_draft\])(.*?)(\[\/if\s+save_draft\])/mis', '', $html );
795 }
796
797 if ( strpos( $html, '[if start_over]' ) ) {
798 $html = preg_replace( '/(\[if\s+start_over\])(.*?)(\[\/if\s+start_over\])/mis', '', $html );
799 }
800
801 if ( apply_filters( 'frm_do_html_shortcodes', true ) ) {
802 $html = do_shortcode( $html );
803 }
804
805 return $html;
806 }
807
808 public static function submit_button_label( $submit ) {
809 if ( ! $submit ) {
810 $frm_settings = FrmAppHelper::get_settings();
811 $submit = $frm_settings->submit_value;
812 }
813
814 return $submit;
815 }
816
817 /**
818 * If the Formidable styling isn't being loaded,
819 * use inline styling to hide the element
820 *
821 * @since 2.03.05
822 */
823 public static function maybe_hide_inline() {
824 $frm_settings = FrmAppHelper::get_settings();
825 if ( $frm_settings->load_style == 'none' ) {
826 echo ' style="display:none;"';
827 } elseif ( $frm_settings->load_style == 'dynamic' ) {
828 FrmStylesController::enqueue_style();
829 }
830 }
831
832 public static function get_form_style_class( $form = false ) {
833 $style = self::get_form_style( $form );
834 $class = ' with_frm_style';
835
836 if ( empty( $style ) ) {
837 if ( FrmAppHelper::is_admin_page( 'formidable-entries' ) ) {
838 return $class;
839 } else {
840 return;
841 }
842 }
843
844 // If submit button needs to be inline or centered.
845 if ( is_object( $form ) ) {
846 $form = $form->options;
847 }
848
849 if ( ! empty( $form['submit_align'] ) ) {
850 $submit_align = $form['submit_align'];
851 } elseif ( self::form_should_be_inline_and_missing_class( $form ) ) {
852 $submit_align = 'inline';
853 } else {
854 $submit_align = '';
855 }
856
857 if ( 'inline' === $submit_align ) {
858 $class .= ' frm_inline_form';
859 $class .= self::maybe_align_fields_top( $form );
860 } elseif ( 'center' === $submit_align ) {
861 $class .= ' frm_center_submit';
862 }
863
864 $class = apply_filters( 'frm_add_form_style_class', $class, $style, compact( 'form' ) );
865
866 return $class;
867 }
868
869 /**
870 * 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.
871 *
872 * @since 5.0.12
873 *
874 * @param array $form
875 * @return bool
876 */
877 private static function form_should_be_inline_and_missing_class( $form ) {
878 if ( isset( $form['form_class'] ) && false !== strpos( ' ' . $form['form_class'] . ' ', ' frm_inline_form ' ) ) {
879 // not missing class, avoid adding it twice.
880 return false;
881 }
882 return ! empty( $form['submit_html'] ) && false !== strpos( $form['submit_html'], 'frm_inline_submit' );
883 }
884
885 /**
886 * Returns appropriate class if form has top labels
887 *
888 * @param array $form
889 *
890 * @return string
891 */
892 private static function maybe_align_fields_top( $form ) {
893 return self::form_has_top_labels( $form ) ? ' frm_inline_top' : '';
894 }
895
896 /**
897 * Determine if a form has fields with top labels so submit button can be aligned properly
898 *
899 * @param array $form
900 *
901 * @return bool
902 */
903 private static function form_has_top_labels( $form ) {
904 if ( ! isset( $form['fields'] ) ) {
905 return false;
906 }
907
908 $fields = $form['fields'];
909 if ( count( $fields ) <= 0 ) {
910 return false;
911 }
912
913 // Start from the fields closest to the submit button.
914 $fields = array_reverse( $fields );
915 foreach ( $fields as $field ) {
916 $type = isset( $field['original_type'] ) ? $field['original_type'] : $field['type'];
917 $has_input = FrmFieldFactory::field_has_property( $type, 'has_input' );
918 if ( $has_input ) {
919 return self::field_has_top_label( $field, $form );
920 }
921 }
922
923 return false;
924 }
925
926 /**
927 * Check if a field's label position is set to "top"
928 *
929 * @param array $field
930 * @param object|string|bool $form
931 *
932 * @return bool
933 */
934 private static function field_has_top_label( $field, $form ) {
935 $label_position = FrmFieldsHelper::label_position( $field['label'], $field, $form );
936
937 return in_array( $label_position, array( 'top', 'inside', 'hidden' ) );
938 }
939
940 /**
941 * @param object|array|string|boolean $form
942 * @return string
943 */
944 public static function get_form_style( $form ) {
945 $style = 1;
946 if ( empty( $form ) || 'default' === $form ) {
947 return $style;
948 } elseif ( is_object( $form ) && $form->parent_form_id ) {
949 // get the parent form if this is a child
950 $form = $form->parent_form_id;
951 } elseif ( is_array( $form ) && ! empty( $form['parent_form_id'] ) ) {
952 $form = $form['parent_form_id'];
953 } elseif ( is_array( $form ) && isset( $form['custom_style'] ) ) {
954 $style = $form['custom_style'];
955 }
956
957 if ( $form && is_string( $form ) ) {
958 $form = FrmForm::getOne( $form );
959 }
960
961 $style = ( $form && is_object( $form ) && isset( $form->options['custom_style'] ) ) ? $form->options['custom_style'] : $style;
962
963 return $style;
964 }
965
966 /**
967 * Display the validation error messages when an entry is submitted
968 *
969 * @param array $args Includes img, errors.
970 *
971 * @since 2.0.6
972 */
973 public static function show_errors( $args ) {
974 $invalid_msg = self::get_invalid_error_message( $args );
975
976 if ( empty( $invalid_msg ) ) {
977 $show_img = false;
978 } else {
979 echo wp_kses_post( $invalid_msg );
980 $show_img = true;
981 }
982
983 self::show_error(
984 array(
985 'img' => $args['img'],
986 'errors' => $args['errors'],
987 'show_img' => $show_img,
988 )
989 );
990 }
991
992 /**
993 * Display the error message in the front-end along with the image if set
994 * The image was removed from the styling settings, but it may still be set with a hook
995 * If the message in the global settings is empty, show every validation message in the error box
996 *
997 * @param array $args Includes img, errors, and show_img.
998 *
999 * @since 2.0.6
1000 */
1001 public static function show_error( $args ) {
1002 // remove any blank messages
1003 $args['errors'] = array_filter( (array) $args['errors'] );
1004
1005 $line_break_first = $args['show_img'];
1006 foreach ( $args['errors'] as $error_key => $error ) {
1007 if ( $line_break_first && ! is_numeric( $error_key ) && ( $error_key == 'cptch_number' || strpos( $error_key, 'field' ) === 0 ) ) {
1008 continue;
1009 }
1010
1011 $id = str_replace( 'field', 'field_', $error_key );
1012 echo '<div id="' . esc_attr( $id ) . '_error">';
1013
1014 if ( $args['show_img'] && ! empty( $args['img'] ) ) {
1015 echo '<img src="' . esc_attr( $args['img'] ) . '" alt="" />';
1016 } else {
1017 $args['show_img'] = true;
1018 }
1019
1020 echo wp_kses_post( $error );
1021
1022 echo '</div>';
1023 }
1024 }
1025
1026 public static function maybe_get_scroll_js( $id ) {
1027 $offset = apply_filters( 'frm_scroll_offset', 4, array( 'form_id' => $id ) );
1028 if ( $offset != - 1 ) {
1029 self::get_scroll_js( $id );
1030 }
1031 }
1032
1033 public static function get_scroll_js( $form_id ) {
1034 echo '<script type="text/javascript">document.addEventListener(\'DOMContentLoaded\',function(){frmFrontForm.scrollMsg(' . (int) $form_id . ');})</script>';
1035 }
1036
1037 /**
1038 * @since 3.0
1039 */
1040 public static function actions_dropdown( $atts ) {
1041 if ( FrmAppHelper::is_admin_page( 'formidable' ) ) {
1042 $status = $atts['status'];
1043 $form_id = isset( $atts['id'] ) ? $atts['id'] : FrmAppHelper::get_param( 'id', 0, 'get', 'absint' );
1044 $trash_link = self::delete_trash_info( $form_id, $status );
1045 $links = self::get_action_links( $form_id, $status );
1046 include( FrmAppHelper::plugin_path() . '/classes/views/frm-forms/actions-dropdown.php' );
1047 }
1048 }
1049
1050 /**
1051 * @since 3.0
1052 */
1053 public static function get_action_links( $form_id, $form ) {
1054 if ( ! is_object( $form ) ) {
1055 $form = FrmForm::getOne( $form_id );
1056 }
1057
1058 $actions = array();
1059 $trash_links = self::delete_trash_links( $form_id );
1060 if ( 'trash' == $form->status ) {
1061 $actions['restore'] = $trash_links['restore'];
1062
1063 if ( current_user_can( 'frm_delete_forms' ) ) {
1064 $actions['trash'] = $trash_links['delete'];
1065 }
1066 } elseif ( current_user_can( 'frm_edit_forms' ) ) {
1067 $duplicate_link = '?page=formidable&frm_action=duplicate&id=' . $form_id;
1068 if ( $form->is_template ) {
1069 $actions['frm_duplicate'] = array(
1070 'url' => wp_nonce_url( $duplicate_link ),
1071 'label' => __( 'Create Form from Template', 'formidable' ),
1072 'icon' => 'frm_icon_font frm_clone_icon',
1073 );
1074 } else {
1075 $actions['duplicate'] = array(
1076 'url' => wp_nonce_url( $duplicate_link ),
1077 'label' => __( 'Duplicate Form', 'formidable' ),
1078 'icon' => 'frm_icon_font frm_clone_icon',
1079 );
1080 }
1081
1082 $actions['trash'] = self::delete_trash_info( $form_id, $form->status );
1083 }//end if
1084
1085 return $actions;
1086 }
1087
1088 public static function edit_form_link( $data ) {
1089 $form_id = self::get_form_id_from_data( $data );
1090
1091 if ( ! $form_id ) {
1092 return '';
1093 }
1094
1095 $label = self::edit_form_link_label( $data );
1096 $link = '<a href="' . esc_url( FrmForm::get_edit_link( $form_id ) ) . '">' . esc_html( $label ) . '</a>';
1097 return $link;
1098 }
1099
1100 public static function edit_form_link_label( $data ) {
1101 $name = self::get_form_name_from_data( $data );
1102 if ( ! $name ) {
1103 return __( '(no title)', 'formidable' );
1104 }
1105 return FrmAppHelper::truncate( $name, 40 );
1106 }
1107
1108 /**
1109 * @param mixed $data
1110 * @return int
1111 */
1112 private static function get_form_id_from_data( $data ) {
1113 if ( is_object( $data ) ) {
1114 $form_id = $data->id;
1115 } else {
1116 $form_id = $data;
1117 }
1118 return $form_id;
1119 }
1120
1121 /**
1122 * @param mixed $data
1123 * @return string
1124 */
1125 private static function get_form_name_from_data( $data ) {
1126 if ( is_object( $data ) ) {
1127 $form_name = $data->name;
1128 } else {
1129 $form_id = $data;
1130 $form_name = FrmForm::getName( $form_id );
1131 }
1132 return $form_name;
1133 }
1134
1135 public static function delete_trash_link( $id, $status, $length = 'label' ) {
1136 $link_details = self::delete_trash_info( $id, $status );
1137
1138 return self::format_link_html( $link_details, $length );
1139 }
1140
1141 /**
1142 * @since 3.0
1143 */
1144 public static function format_link_html( $link_details, $length = 'label' ) {
1145 $link = '';
1146 if ( ! empty( $link_details ) ) {
1147 $link = '<a href="' . esc_url( $link_details['url'] ) . '" class="frm-trash-link"';
1148 if ( isset( $link_details['data'] ) ) {
1149 foreach ( $link_details['data'] as $data => $value ) {
1150 $link .= ' data-' . esc_attr( $data ) . '="' . esc_attr( $value ) . '"';
1151 }
1152 } elseif ( isset( $link_details['confirm'] ) ) {
1153 $link .= ' onclick="return confirm(\'' . esc_attr( $link_details['confirm'] ) . '\')"';
1154 }
1155
1156 $label = ( isset( $link_details[ $length ] ) ? $link_details[ $length ] : $link_details['label'] );
1157 if ( $length == 'icon' && isset( $link_details[ $length ] ) ) {
1158 $label = '<span class="' . $label . '" title="' . esc_attr( $link_details['label'] ) . '" aria-hidden="true"></span>';
1159 $link .= ' aria-label="' . esc_attr( $link_details['label'] ) . '"';
1160 }
1161
1162 $link .= '>' . $label . '</a>';
1163 }
1164
1165 return $link;
1166 }
1167
1168 /**
1169 * @since 3.0
1170 */
1171 public static function delete_trash_info( $id, $status ) {
1172 $labels = self::delete_trash_links( $id );
1173
1174 if ( 'trash' == $status ) {
1175 $info = $labels['restore'];
1176 } elseif ( current_user_can( 'frm_delete_forms' ) ) {
1177 if ( EMPTY_TRASH_DAYS ) {
1178 $info = $labels['trash'];
1179 } else {
1180 $info = $labels['delete'];
1181 }
1182 } else {
1183 $info = array();
1184 }
1185
1186 return $info;
1187 }
1188
1189 /**
1190 * @since 3.0
1191 */
1192 public static function delete_trash_links( $id ) {
1193 $current_page = FrmAppHelper::get_simple_request( array( 'param' => 'form_type' ) );
1194 $base_url = '?page=formidable&form_type=' . $current_page . '&id=' . $id;
1195
1196 return array(
1197 'restore' => array(
1198 'label' => __( 'Restore from Trash', 'formidable' ),
1199 'short' => __( 'Restore', 'formidable' ),
1200 'url' => wp_nonce_url( $base_url . '&frm_action=untrash', 'untrash_form_' . absint( $id ) ),
1201 ),
1202 'trash' => array(
1203 'label' => __( 'Move Form to Trash', 'formidable' ),
1204 'short' => __( 'Trash', 'formidable' ),
1205 'url' => wp_nonce_url( $base_url . '&frm_action=trash', 'trash_form_' . absint( $id ) ),
1206 'icon' => 'frm_icon_font frm_delete_icon',
1207 'data' => array(
1208 'frmverify' => __( 'Do you want to move this form to the trash?', 'formidable' ),
1209 'frmverify-btn' => 'frm-button-red',
1210 ),
1211 ),
1212 'delete' => array(
1213 'label' => __( 'Delete Permanently', 'formidable' ),
1214 'short' => __( 'Delete', 'formidable' ),
1215 'url' => wp_nonce_url( $base_url . '&frm_action=destroy', 'destroy_form_' . absint( $id ) ),
1216 'confirm' => __( 'Are you sure you want to delete this form and all its entries?', 'formidable' ),
1217 'icon' => 'frm_icon_font frm_delete_icon',
1218 'data' => array(
1219 'frmverify' => __( 'This will permanently delete the form and all its entries. This is irreversible. Are you sure you want to continue?', 'formidable' ),
1220 'frmverify-btn' => 'frm-button-red',
1221 ),
1222 ),
1223 );
1224 }
1225
1226 /**
1227 * @since 3.0
1228 */
1229 public static function css_classes() {
1230 $classes = array(
1231 'frm_total' => array(
1232 'label' => __( 'Total', 'formidable' ),
1233 'title' => __( 'Add this to a read-only field to display the text in bold without a border or background.', 'formidable' ),
1234 ),
1235 'frm_total_big' => array(
1236 'label' => __( 'Big Total', 'formidable' ),
1237 'title' => __( 'Add this to a read-only field to display the text in large, bold text without a border or background.', 'formidable' ),
1238 ),
1239 'frm_scroll_box' => array(
1240 'label' => __( 'Scroll Box', 'formidable' ),
1241 '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' ),
1242 ),
1243 'frm_first' => array(
1244 'label' => __( 'First', 'formidable' ),
1245 'title' => __( 'Add this to the first field in each row along with a width. ie frm_first frm4', 'formidable' ),
1246 ),
1247 'frm_alignright' => __( 'Right', 'formidable' ),
1248 'frm_grid_first' => __( 'First Grid Row', 'formidable' ),
1249 'frm_grid' => __( 'Even Grid Row', 'formidable' ),
1250 'frm_grid_odd' => __( 'Odd Grid Row', 'formidable' ),
1251 'frm_color_block' => array(
1252 'label' => __( 'Color Block', 'formidable' ),
1253 'title' => __( 'Add a background color to the field or section.', 'formidable' ),
1254 ),
1255 'frm_capitalize' => array(
1256 'label' => __( 'Capitalize', 'formidable' ),
1257 'title' => __( 'Automatically capitalize the first letter in each word.', 'formidable' ),
1258 ),
1259 );
1260
1261 return apply_filters( 'frm_layout_classes', $classes );
1262 }
1263
1264 public static function grid_classes() {
1265 return array(
1266 'frm_half' => '1/2',
1267 'frm_third' => '1/3',
1268 'frm_two_thirds' => '2/3',
1269 'frm_fourth' => '1/4',
1270 'frm_three_fourths' => '3/4',
1271 'frm_sixth' => '1/6',
1272 'frm10' => '5/6',
1273 'frm12' => '100%',
1274 );
1275 }
1276
1277 /**
1278 * @since 3.0
1279 */
1280 public static function style_class_label( $style, $class ) {
1281 $label = '';
1282 if ( empty( $style ) ) {
1283 $label = $class;
1284 } elseif ( ! is_array( $style ) ) {
1285 $label = $style;
1286 } elseif ( isset( $style['label'] ) ) {
1287 $label = $style['label'];
1288 }
1289
1290 return $label;
1291 }
1292
1293 public static function status_nice_name( $status ) {
1294 $nice_names = array(
1295 'draft' => __( 'Draft', 'formidable' ),
1296 'trash' => __( 'Trash', 'formidable' ),
1297 'publish' => __( 'Published', 'formidable' ),
1298 );
1299
1300 if ( ! in_array( $status, array_keys( $nice_names ) ) ) {
1301 $status = 'publish';
1302 }
1303
1304 $name = $nice_names[ $status ];
1305
1306 return $name;
1307 }
1308
1309 /**
1310 * Renders a template icon based on the given categories.
1311 *
1312 * @param array $categories The categories to render the icon for.
1313 * @param array $atts {
1314 * Optional. An array of attributes for rendering.
1315 * @type string $html 'span' or 'div'. Default 'span'.
1316 * @type boolean $bg Whether to add a background color or not. Default false.
1317 * }
1318 *
1319 * @return void
1320 */
1321 public static function template_icon( $categories, $atts = array() ) {
1322 // Define defaults.
1323 $defaults = array(
1324 'bg' => true,
1325 );
1326 $atts = array_merge( $defaults, $atts );
1327
1328 // Filter out ignored categories.
1329 $ignore = self::ignore_template_categories();
1330 $categories = array_diff( $categories, $ignore );
1331
1332 // Define icons mapping.
1333 $icons = array(
1334 'WooCommerce' => array( 'woocommerce', 'var(--purple)' ),
1335 'Post' => array( 'wordpress', 'rgb(0,160,210)' ),
1336 'User Registration' => array( 'register', 'var(--pink)' ),
1337 'Registration and Signup' => array( 'register', 'var(--pink)' ),
1338 'PayPal' => array( 'paypal' ),
1339 'Stripe' => array( 'credit_card', 'var(--green)' ),
1340 'Twilio' => array( 'sms' ),
1341 'Payment' => array( 'credit_card' ),
1342 'Order Form' => array( 'product' ),
1343 'Finance' => array( 'total' ),
1344 'Health and Wellness' => array( 'heart', 'var(--pink)' ),
1345 'Event Planning' => array( 'calendar', 'var(--orange)' ),
1346 'Real Estate' => array( 'house' ),
1347 'Nonprofit' => array( 'heart_solid' ),
1348 'Calculator' => array( 'calculator', 'var(--purple)' ),
1349 'Quiz' => array( 'percent' ),
1350 'Registrations' => array( 'address_card' ),
1351 'Customer Service' => array( 'users_solid' ),
1352 'Education' => array( 'pencil' ),
1353 'Marketing' => array( 'eye' ),
1354 'Feedback' => array( 'smile' ),
1355 'Business Operations' => array( 'case' ),
1356 'Contact Form' => array( 'email' ),
1357 'Conversational Forms' => array( 'chat_forms' ),
1358 'Survey' => array( 'chat_forms', 'var(--orange)' ),
1359 'Application' => array( 'align_right' ),
1360 'Signature' => array( 'signature' ),
1361 '' => array( 'align_right' ),
1362 );
1363
1364 // Determine the icon to be used.
1365 $icon = $icons[''];
1366 if ( count( $categories ) === 1 ) {
1367 $category = reset( $categories );
1368 $icon = isset( $icons[ $category ] ) ? $icons[ $category ] : $icon;
1369 } elseif ( ! empty( $categories ) ) {
1370 $icons = array_intersect_key( $icons, array_flip( $categories ) );
1371 $icon = reset( $icons );
1372 }
1373
1374 // Prepare variables for output.
1375 $icon_name = $icon[0];
1376 $bg_color = isset( $icon[1] ) ? $icon[1] : '';
1377
1378 // Render the icon.
1379 echo '<span class="frm-category-icon frm-icon-wrapper"';
1380 if ( $bg_color && $atts['bg'] ) {
1381 echo ' style="background-color:' . esc_attr( $bg_color ) . '"';
1382 }
1383 echo '>';
1384 FrmAppHelper::icon_by_class( 'frmfont frm_' . $icon_name . '_icon' );
1385 echo '</span>';
1386 }
1387
1388 /**
1389 * Retrieves the list of template categories to ignore.
1390 *
1391 * @since 4.03.01
1392 *
1393 * @return string[] Array of categories to ignore.
1394 */
1395 public static function ignore_template_categories() {
1396 return array( 'Business', 'Elite', 'Personal', 'Creator', 'Basic', 'free' );
1397 }
1398
1399 /**
1400 * Get template install link.
1401 *
1402 * @since 4.02
1403 *
1404 * @param array $template Template details.
1405 * @param array $args Additional arguments.
1406 * @return array The link attributes.
1407 */
1408 public static function get_template_install_link( $template, $args ) {
1409 $defaults = array(
1410 'class' => 'install-now',
1411 'href' => 'href',
1412 'atts' => true,
1413 );
1414
1415 if ( ! empty( $template['url'] ) ) {
1416 $link = array(
1417 'url' => $template['url'],
1418 'label' => __( 'Create Form', 'formidable' ),
1419 'class' => 'frm-install-template',
1420 'href' => 'rel',
1421 'atts' => '',
1422 );
1423 } elseif ( self::plan_is_allowed( $args ) ) {
1424 $link = array(
1425 'url' => FrmAppHelper::admin_upgrade_link( 'addons', 'account/downloads/' ) . '&utm_content=' . $template['slug'],
1426 'label' => __( 'Renew', 'formidable' ),
1427 );
1428 } else {
1429 $link = array(
1430 'url' => $args['pricing'],
1431 'label' => __( 'Upgrade', 'formidable' ),
1432 );
1433 }
1434
1435 return array_merge( $defaults, $link );
1436 }
1437
1438 /**
1439 * Is the template included with the license type?
1440 *
1441 * @since 4.02.02
1442 *
1443 * @param array $args
1444 * @return bool
1445 */
1446 public static function plan_is_allowed( $args ) {
1447 if ( empty( $args['license_type'] ) ) {
1448 return false;
1449 }
1450
1451 $plans = array( 'free', 'personal', 'business', 'elite' );
1452 $license_type = strtolower( $args['license_type'] );
1453 $plan_required = strtolower( $args['plan_required'] );
1454 $included = $license_type === $plan_required;
1455
1456 if ( $included || ! in_array( $plan_required, $plans, true ) ) {
1457 return $included;
1458 }
1459
1460 foreach ( $plans as $plan ) {
1461 if ( $included || $plan === $license_type ) {
1462 break;
1463 }
1464 $included = $plan === $plan_required;
1465 }
1466
1467 return $included;
1468 }
1469
1470 /**
1471 * If a template or add-on cannot be installed, show a message
1472 * about which plan is required.
1473 *
1474 * @since 4.0
1475 */
1476 public static function show_plan_required( $requires, $link ) {
1477 if ( empty( $requires ) ) {
1478 return;
1479 }
1480
1481 ?>
1482 <p class="frm_plan_required">
1483 <?php esc_html_e( 'License plan required:', 'formidable' ); ?>
1484 <a href="<?php echo esc_url( $link ); ?>" target="_blank" rel="noopener">
1485 <?php echo esc_html( $requires ); ?>
1486 </a>
1487 </p>
1488 <?php
1489 }
1490
1491 /**
1492 * @since 4.0
1493 *
1494 * @param array $item
1495 * @return string|false
1496 */
1497 public static function get_plan_required( &$item ) {
1498 if ( ! isset( $item['categories'] ) || ! is_array( $item['categories'] ) || ! empty( $item['url'] ) ) {
1499 return false;
1500 }
1501
1502 $plans = array( 'free', 'Basic', 'Personal', 'Plus', 'Creator', 'Business', 'Elite' );
1503
1504 foreach ( $item['categories'] as $k => $category ) {
1505 if ( in_array( $category, $plans, true ) ) {
1506 unset( $item['categories'][ $k ] );
1507
1508 if ( in_array( $category, array( 'Creator', 'Personal' ), true ) ) {
1509 // Show the current package name.
1510 $category = 'Plus';
1511 }
1512
1513 return $category;
1514 }
1515 }
1516
1517 return false;
1518 }
1519
1520 /**
1521 * Checks for warnings to be displayed after form settings are saved.
1522 *
1523 * @since 4.04
1524 *
1525 * @param array $values The $_POST array, which contains values submitted in a form.
1526 *
1527 * @return array An array of warnings or an empty array.
1528 */
1529 public static function check_for_warnings( $values ) {
1530 $warnings = array();
1531
1532 $redirect_warning = self::check_redirect_url_for_unsafe_params( $values );
1533
1534 if ( $redirect_warning ) {
1535 $warnings[] = $redirect_warning;
1536 }
1537
1538 return apply_filters( 'frm_check_for_warnings', $warnings, $values );
1539 }
1540
1541 /**
1542 * Checks the redirect URL for params whose names are reserved words.
1543 *
1544 * @since 4.04
1545 *
1546 * @param array $values The $_POST array, which contains the values submitted in a form.
1547 *
1548 * @return bool|string A warning message about unsafe params or false.
1549 */
1550 private static function check_redirect_url_for_unsafe_params( $values ) {
1551 if ( ! isset( $values['options'] ) ) {
1552 return false;
1553 }
1554
1555 $options = $values['options'];
1556 FrmAppHelper::sanitize_with_html( $options );
1557
1558 if ( ( ! isset( $options['success_action'] ) ) || $options['success_action'] !== 'redirect' || ! isset( $options['success_url'] ) ) {
1559 return false;
1560 }
1561
1562 $unsafe_params_in_redirect = self::get_unsafe_params( $options['success_url'] );
1563
1564 return self::create_unsafe_param_warning( $unsafe_params_in_redirect );
1565 }
1566
1567 /**
1568 * Returns an array of params whose names are reserved words in the specified URL.
1569 *
1570 * @since 4.04
1571 *
1572 * @param string $url The URL whose params are being checked.
1573 *
1574 * @return array An array of params whose names are reserved words or an empty array.
1575 */
1576 private static function get_unsafe_params( $url ) {
1577 $redirect_components = parse_url( $url );
1578 if ( empty( $redirect_components['query'] ) ) {
1579 return array();
1580 }
1581 parse_str( $redirect_components['query'], $redirect_params );
1582 $redirect_param_names = array_keys( $redirect_params );
1583 $reserved_words = self::reserved_words();
1584 $unsafe_params_in_redirect = array_intersect( $redirect_param_names, $reserved_words );
1585
1586 return array_values( $unsafe_params_in_redirect );
1587 }
1588
1589 /**
1590 * Returns a warning if reserved words have been used as param names in the redirect URL.
1591 *
1592 * @since 4.04
1593 *
1594 * @param array $unsafe_params_in_redirect Array of params from the redirect URL whose names are reserved words.
1595 *
1596 * @return bool|string A string with an unsafe param message or false.
1597 */
1598 private static function create_unsafe_param_warning( $unsafe_params_in_redirect ) {
1599 $count = count( $unsafe_params_in_redirect );
1600 $caution = esc_html__( 'Is this intentional?', 'formidable' );
1601 $reserved_words_intro = esc_html__( 'See the list of reserved words in WordPress.', 'formidable' );
1602 $reserved_words_link = '<a href="https://codex.wordpress.org/WordPress_Query_Vars" target="_blank"> ' . $reserved_words_intro . '</a>';
1603
1604 if ( $count === 0 ) {
1605 return false;
1606 }
1607
1608 if ( $count == 1 ) {
1609 /* translators: %s: the name of a single parameter in the redirect URL */
1610 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;
1611 }
1612
1613 $unsafe_params_string = implode( '", "', $unsafe_params_in_redirect );
1614
1615 /* translators: %s: the names of two or more parameters in the redirect URL, separated by commas */
1616 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;
1617 }
1618
1619 /**
1620 * Returns an array of common reserved words in WordPress.
1621 *
1622 * An edited list of reserved terms from the Codex.
1623 * https://codex.wordpress.org/Reserved_Terms
1624 *
1625 * @since 4.04
1626 *
1627 * @return array Array of WordPress reserved words.
1628 */
1629 public static function reserved_words() {
1630 return array(
1631 'id',
1632 'attachment',
1633 'author',
1634 'author_name',
1635 'calendar',
1636 'cat',
1637 'category',
1638 'category_name',
1639 'cpage',
1640 'custom',
1641 'day',
1642 'date',
1643 'error',
1644 'feed',
1645 'hour',
1646 'm',
1647 'minute',
1648 'more',
1649 'name',
1650 'order',
1651 'p',
1652 'page',
1653 'page_id',
1654 'paged',
1655 'pb',
1656 'post',
1657 'posts',
1658 'preview',
1659 's',
1660 'search',
1661 'second',
1662 'sentence',
1663 'tag',
1664 'taxonomy',
1665 'tb',
1666 'term',
1667 'terms',
1668 'theme',
1669 'title',
1670 'type',
1671 'w',
1672 'year',
1673 );
1674 }
1675
1676 /**
1677 * Make sure the field shortcodes in a url always add the sanitize_url=1 option if nothing is defined.
1678 * This is to prevent some field characters like ', @, and | from being stripped from the redirect URL.
1679 *
1680 * @since 5.0.16
1681 *
1682 * @param string $url
1683 * @param int $form_id
1684 * @return string
1685 */
1686 public static function maybe_add_sanitize_url_attr( $url, $form_id ) {
1687 if ( false === strpos( $url, '[' ) ) {
1688 // Do nothing if no shortcodes are detected.
1689 return $url;
1690 }
1691
1692 $parsed = wp_parse_url( $url );
1693 if ( empty( $parsed['query'] ) ) {
1694 // Do nothing if no query can be detected in the url string.
1695 return $url;
1696 }
1697
1698 $original_query = $parsed['query'];
1699 $query = $parsed['query'];
1700
1701 $shortcodes = FrmFieldsHelper::get_shortcodes( $query, $form_id );
1702 if ( empty( $shortcodes[0] ) ) {
1703 // No shortcodes found, do nothing.
1704 return $url;
1705 }
1706
1707 foreach ( $shortcodes[0] as $key => $shortcode ) {
1708 $options = trim( $shortcodes[3][ $key ] );
1709
1710 if ( in_array( $shortcodes[1][ $key ], array( 'if ' ), true ) ) {
1711 // Skip if shortcodes.
1712 continue;
1713 }
1714
1715 if ( false !== strpos( $options, 'sanitize_url=' ) || false !== strpos( $options, 'sanitize=' ) ) {
1716 // A sanitize option is already set so leave it alone.
1717 continue;
1718 }
1719
1720 $new_shortcode = '[' . $shortcodes[2][ $key ];
1721 if ( $options ) {
1722 $new_shortcode .= ' ' . $options;
1723 }
1724 $new_shortcode .= ' sanitize_url=1]';
1725
1726 $query = str_replace( $shortcode, $new_shortcode, $query );
1727 }//end foreach
1728
1729 if ( $query === $original_query ) {
1730 return $url;
1731 }
1732
1733 return str_replace( $original_query, $query, $url );
1734 }
1735
1736 /**
1737 * Check if Pro isn't up to date yet.
1738 * 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).
1739 *
1740 * @since 6.2
1741 *
1742 * @return bool
1743 */
1744 public static function should_use_pro_for_ajax_submit() {
1745 return is_callable( 'FrmProForm::is_ajax_on' ) && ! is_callable( 'FrmProFormsHelper::lite_supports_ajax_submit' );
1746 }
1747
1748 /**
1749 * Outputs the appropriate button text in the publish box.
1750 *
1751 * @return void
1752 */
1753 public static function publish_box_button_text() {
1754 $is_new_template = FrmAppHelper::simple_get( 'new_template' );
1755 $action = FrmAppHelper::simple_get( 'frm_action' );
1756
1757 if ( ( 'edit' === $action || 'settings' === $action ) && $is_new_template ) {
1758 esc_html_e( 'Save', 'formidable' );
1759 } else {
1760 esc_html_e( 'Update', 'formidable' );
1761 }
1762 }
1763
1764 /**
1765 * @since 4.02
1766 * @deprecated 6.7
1767 */
1768 public static function template_install_html( $link, $class = '' ) {
1769 _deprecated_function( __METHOD__, '6.7' );
1770 }
1771
1772 /**
1773 * Check an array of templates, determine how many the logged in user can use
1774 *
1775 * @deprecated 6.7
1776 *
1777 * @param array $templates
1778 * @param array $args
1779 * @return int
1780 */
1781 public static function available_count( $templates, $args ) {
1782 _deprecated_function( __METHOD__, '6.7' );
1783 return 0;
1784 }
1785 }
1786