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

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