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

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