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

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

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