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

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