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

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