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

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