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

1,686 lines 50.6 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 '' => array( 'align_right' ),
1289 );
1290
1291 $icons[ __( 'My Templates', 'formidable' ) ] = array( 'user', 'var(--orange)' );
1292
1293 $icon = $icons[''];
1294
1295 if ( count( $categories ) === 1 ) {
1296 $category = reset( $categories );
1297 $icon = isset( $icons[ $category ] ) ? $icons[ $category ] : $icon;
1298 } elseif ( ! empty( $categories ) ) {
1299 foreach ( $icons as $cat => $icon ) {
1300 if ( ! in_array( $cat, $categories ) ) {
1301 unset( $icons[ $cat ] );
1302 }
1303 }
1304 $icon = reset( $icons );
1305 }
1306
1307 echo '<span class="frm-inner-circle" ' . ( isset( $icon[1] ) ? 'style="background-color:' . esc_attr( $icon[1] ) : '' ) . '">';
1308 FrmAppHelper::icon_by_class( 'frmfont frm_' . $icon[0] . '_icon' );
1309 echo '<span class="frm_hidden">';
1310 FrmAppHelper::icon_by_class( 'frmfont frm_lock_icon' );
1311 echo '</span>';
1312 echo '</span>';
1313 }
1314
1315 /**
1316 * @since 4.03.01
1317 */
1318 public static function ignore_template_categories() {
1319 return array( 'Business', 'Elite', 'Personal', 'Creator', 'Basic', 'free' );
1320 }
1321
1322 /**
1323 * @since 4.02
1324 */
1325 public static function get_template_install_link( $template, $args ) {
1326 $defaults = array(
1327 'class' => 'install-now',
1328 'href' => 'href',
1329 'atts' => true,
1330 );
1331
1332 if ( ! empty( $template['url'] ) ) {
1333 $link = array(
1334 'url' => $template['url'],
1335 'label' => __( 'Create Form', 'formidable' ),
1336 'class' => 'frm-install-template',
1337 'href' => 'rel',
1338 'atts' => '',
1339 );
1340 } elseif ( self::plan_is_allowed( $args ) ) {
1341 $link = array(
1342 'url' => FrmAppHelper::admin_upgrade_link( 'addons', 'account/downloads/' ) . '&utm_content=' . $template['slug'],
1343 'label' => __( 'Renew', 'formidable' ),
1344 );
1345 } else {
1346 $link = array(
1347 'url' => $args['pricing'],
1348 'label' => __( 'Upgrade', 'formidable' ),
1349 );
1350 }
1351
1352 return array_merge( $defaults, $link );
1353 }
1354
1355 /**
1356 * Is the template included with the license type?
1357 *
1358 * @since 4.02.02
1359 *
1360 * @param array $args
1361 *
1362 * @return bool
1363 */
1364 public static function plan_is_allowed( $args ) {
1365 if ( empty( $args['license_type'] ) ) {
1366 return false;
1367 }
1368
1369 $included = $args['license_type'] === strtolower( $args['plan_required'] );
1370
1371 $plans = array( 'free', 'personal', 'business', 'elite' );
1372 if ( $included || ! in_array( strtolower( $args['plan_required'] ), $plans, true ) ) {
1373 return $included;
1374 }
1375
1376 foreach ( $plans as $plan ) {
1377 if ( $included || $plan === $args['license_type'] ) {
1378 break;
1379 }
1380 $included = $plan === strtolower( $args['plan_required'] );
1381 }
1382
1383 return $included;
1384 }
1385
1386 /**
1387 * @since 4.02
1388 */
1389 public static function template_install_html( $link, $class = '' ) {
1390 $link['class'] .= ' ' . $class;
1391 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"' : '' ) . '>';
1392 }
1393
1394 /**
1395 * If a template or add-on cannot be installed, show a message
1396 * about which plan is required.
1397 *
1398 * @since 4.0
1399 */
1400 public static function show_plan_required( $requires, $link ) {
1401 if ( empty( $requires ) ) {
1402 return;
1403 }
1404
1405 ?>
1406 <p class="frm_plan_required">
1407 <?php esc_html_e( 'License plan required:', 'formidable' ); ?>
1408 <a href="<?php echo esc_url( $link ); ?>" target="_blank" rel="noopener">
1409 <?php echo esc_html( $requires ); ?>
1410 </a>
1411 </p>
1412 <?php
1413 }
1414
1415 /**
1416 * @since 4.0
1417 *
1418 * @param array $item
1419 * @return string|false
1420 */
1421 public static function get_plan_required( &$item ) {
1422 if ( ! isset( $item['categories'] ) || ! is_array( $item['categories'] ) || ! empty( $item['url'] ) ) {
1423 return false;
1424 }
1425
1426 $plans = array( 'free', 'Basic', 'Personal', 'Plus', 'Creator', 'Business', 'Elite' );
1427
1428 foreach ( $item['categories'] as $k => $category ) {
1429 if ( in_array( $category, $plans, true ) ) {
1430 unset( $item['categories'][ $k ] );
1431
1432 if ( in_array( $category, array( 'Creator', 'Personal' ), true ) ) {
1433 // Show the current package name.
1434 $category = 'Plus';
1435 }
1436
1437 return $category;
1438 }
1439 }
1440
1441 return false;
1442 }
1443
1444 /**
1445 * Checks for warnings to be displayed after form settings are saved.
1446 *
1447 * @since 4.04
1448 *
1449 * @param array $values The $_POST array, which contains values submitted in a form.
1450 *
1451 * @return array An array of warnings or an empty array.
1452 */
1453 public static function check_for_warnings( $values ) {
1454 $warnings = array();
1455
1456 $redirect_warning = self::check_redirect_url_for_unsafe_params( $values );
1457
1458 if ( $redirect_warning ) {
1459 $warnings[] = $redirect_warning;
1460 }
1461
1462 return apply_filters( 'frm_check_for_warnings', $warnings, $values );
1463 }
1464
1465 /**
1466 * Checks the redirect URL for params whose names are reserved words.
1467 *
1468 * @since 4.04
1469 *
1470 * @param array $values The $_POST array, which contains the values submitted in a form.
1471 *
1472 * @return bool|string A warning message about unsafe params or false.
1473 */
1474 private static function check_redirect_url_for_unsafe_params( $values ) {
1475 if ( ! isset( $values['options'] ) ) {
1476 return false;
1477 }
1478
1479 $options = $values['options'];
1480 FrmAppHelper::sanitize_with_html( $options );
1481
1482 if ( ( ! isset( $options['success_action'] ) ) || $options['success_action'] !== 'redirect' || ! isset( $options['success_url'] ) ) {
1483 return false;
1484 }
1485
1486 $unsafe_params_in_redirect = self::get_unsafe_params( $options['success_url'] );
1487
1488 return self::create_unsafe_param_warning( $unsafe_params_in_redirect );
1489 }
1490
1491 /**
1492 * Returns an array of params whose names are reserved words in the specified URL.
1493 *
1494 * @since 4.04
1495 *
1496 * @param string $url The URL whose params are being checked.
1497 *
1498 * @return array An array of params whose names are reserved words or an empty array.
1499 */
1500 private static function get_unsafe_params( $url ) {
1501 $redirect_components = parse_url( $url );
1502 if ( empty( $redirect_components['query'] ) ) {
1503 return array();
1504 }
1505 parse_str( $redirect_components['query'], $redirect_params );
1506 $redirect_param_names = array_keys( $redirect_params );
1507 $reserved_words = self::reserved_words();
1508 $unsafe_params_in_redirect = array_intersect( $redirect_param_names, $reserved_words );
1509
1510 return array_values( $unsafe_params_in_redirect );
1511 }
1512
1513 /**
1514 * Returns a warning if reserved words have been used as param names in the redirect URL.
1515 *
1516 * @since 4.04
1517 *
1518 * @param array $unsafe_params_in_redirect Array of params from the redirect URL whose names are reserved words.
1519 *
1520 * @return bool|string A string with an unsafe param message or false.
1521 */
1522 private static function create_unsafe_param_warning( $unsafe_params_in_redirect ) {
1523 $count = count( $unsafe_params_in_redirect );
1524 $caution = esc_html__( 'Is this intentional?', 'formidable' );
1525 $reserved_words_intro = esc_html__( 'See the list of reserved words in WordPress.', 'formidable' );
1526 $reserved_words_link = '<a href="https://codex.wordpress.org/WordPress_Query_Vars" target="_blank"> ' . $reserved_words_intro . '</a>';
1527
1528 if ( $count === 0 ) {
1529 return false;
1530 }
1531
1532 if ( $count == 1 ) {
1533 /* translators: %s: the name of a single parameter in the redirect URL */
1534 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;
1535 }
1536
1537 $unsafe_params_string = implode( '", "', $unsafe_params_in_redirect );
1538
1539 /* translators: %s: the names of two or more parameters in the redirect URL, separated by commas */
1540 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;
1541 }
1542
1543 /**
1544 * Returns an array of common reserved words in WordPress.
1545 *
1546 * An edited list of reserved terms from the Codex.
1547 * https://codex.wordpress.org/Reserved_Terms
1548 *
1549 * @since 4.04
1550 *
1551 * @return array Array of WordPress reserved words.
1552 */
1553 public static function reserved_words() {
1554 return array(
1555 'id',
1556 'attachment',
1557 'author',
1558 'author_name',
1559 'calendar',
1560 'cat',
1561 'category',
1562 'category_name',
1563 'cpage',
1564 'custom',
1565 'day',
1566 'date',
1567 'error',
1568 'feed',
1569 'hour',
1570 'm',
1571 'minute',
1572 'more',
1573 'name',
1574 'order',
1575 'p',
1576 'page',
1577 'page_id',
1578 'paged',
1579 'pb',
1580 'post',
1581 'posts',
1582 'preview',
1583 's',
1584 'search',
1585 'second',
1586 'sentence',
1587 'tag',
1588 'taxonomy',
1589 'tb',
1590 'term',
1591 'terms',
1592 'theme',
1593 'title',
1594 'type',
1595 'w',
1596 'year',
1597 );
1598 }
1599
1600 /**
1601 * Check an array of templates, determine how many the logged in user can use
1602 *
1603 * @param array $templates
1604 * @param array $args
1605 * @return int
1606 */
1607 public static function available_count( $templates, $args ) {
1608 return array_reduce(
1609 $templates,
1610 function( $total, $template ) use ( $args ) {
1611 if ( ! empty( $template['url'] ) ) {
1612 return $total + 1;
1613 }
1614
1615 $args['plan_required'] = self::get_plan_required( $template );
1616 if ( self::plan_is_allowed( $args ) ) {
1617 return $total + 1;
1618 }
1619
1620 return $total;
1621 },
1622 0
1623 );
1624 }
1625
1626 /**
1627 * Make sure the field shortcodes in a url always add the sanitize_url=1 option if nothing is defined.
1628 * This is to prevent some field characters like ', @, and | from being stripped from the redirect URL.
1629 *
1630 * @since 5.0.16
1631 *
1632 * @param string $url
1633 * @param int $form_id
1634 * @return string
1635 */
1636 public static function maybe_add_sanitize_url_attr( $url, $form_id ) {
1637 if ( false === strpos( $url, '[' ) ) {
1638 // Do nothing if no shortcodes are detected.
1639 return $url;
1640 }
1641
1642 $parsed = wp_parse_url( $url );
1643 if ( empty( $parsed['query'] ) ) {
1644 // Do nothing if no query can be detected in the url string.
1645 return $url;
1646 }
1647
1648 $original_query = $parsed['query'];
1649 $query = $parsed['query'];
1650
1651 $shortcodes = FrmFieldsHelper::get_shortcodes( $query, $form_id );
1652 if ( empty( $shortcodes[0] ) ) {
1653 // No shortcodes found, do nothing.
1654 return $url;
1655 }
1656
1657 foreach ( $shortcodes[0] as $key => $shortcode ) {
1658 $options = trim( $shortcodes[3][ $key ] );
1659
1660 if ( in_array( $shortcodes[1][ $key ], array( 'if ' ), true ) ) {
1661 // Skip if shortcodes.
1662 continue;
1663 }
1664
1665 if ( false !== strpos( $options, 'sanitize_url=' ) || false !== strpos( $options, 'sanitize=' ) ) {
1666 // A sanitize option is already set so leave it alone.
1667 continue;
1668 }
1669
1670 $new_shortcode = '[' . $shortcodes[2][ $key ];
1671 if ( $options ) {
1672 $new_shortcode .= ' ' . $options;
1673 }
1674 $new_shortcode .= ' sanitize_url=1]';
1675
1676 $query = str_replace( $shortcode, $new_shortcode, $query );
1677 }
1678
1679 if ( $query === $original_query ) {
1680 return $url;
1681 }
1682
1683 return str_replace( $original_query, $query, $url );
1684 }
1685 }
1686