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

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