PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.9
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.9
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
formidable / classes / helpers / FrmFormsHelper.php

FrmFormsHelper.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 6.9, at classes/helpers/FrmFormsHelper.php

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