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

1,614 lines 48.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 public static function get_default_opts() {
360 $frm_settings = FrmAppHelper::get_settings();
361
362 return array(
363 'submit_value' => $frm_settings->submit_value,
364 'success_action' => 'message',
365 'success_msg' => $frm_settings->success_msg,
366 'show_form' => 0,
367 'akismet' => '',
368 'honeypot' => 'basic',
369 'antispam' => 0,
370 'no_save' => 0,
371 'ajax_load' => 0,
372 'js_validate' => 0,
373 'form_class' => '',
374 'custom_style' => 1,
375 'before_html' => self::get_default_html( 'before' ),
376 'after_html' => '',
377 'submit_html' => self::get_default_html( 'submit' ),
378 );
379 }
380
381 /**
382 * @param array $options
383 * @param array $values
384 *
385 * @since 2.0.6
386 */
387 public static function fill_form_options( &$options, $values ) {
388 $defaults = self::get_default_opts();
389 foreach ( $defaults as $var => $default ) {
390 $options[ $var ] = isset( $values['options'][ $var ] ) ? $values['options'][ $var ] : $default;
391 unset( $var, $default );
392 }
393 }
394
395 /**
396 * @param string $loc
397 */
398 public static function get_default_html( $loc ) {
399 if ( $loc == 'submit' ) {
400 $draft_link = self::get_draft_link();
401 $default_html = <<<SUBMIT_HTML
402 <div class="frm_submit">
403 [if back_button]<button type="submit" name="frm_prev_page" formnovalidate="formnovalidate" class="frm_prev_page" [back_hook]>[back_label]</button>[/if back_button]
404 <button class="frm_button_submit" type="submit" [button_action]>[button_label]</button>
405 $draft_link
406 </div>
407 SUBMIT_HTML;
408 } elseif ( $loc == 'before' ) {
409 $default_html = <<<BEFORE_HTML
410 <legend class="frm_screen_reader">[form_name]</legend>
411 [if form_name]<h3 class="frm_form_title">[form_name]</h3>[/if form_name]
412 [if form_description]<div class="frm_description">[form_description]</div>[/if form_description]
413 BEFORE_HTML;
414 } else {
415 $default_html = '';
416 }
417
418 return $default_html;
419 }
420
421 public static function get_draft_link() {
422 $link = '[if save_draft]<a href="#" tabindex="0" class="frm_save_draft" [draft_hook]>[draft_label]</a>[/if save_draft]';
423
424 return $link;
425 }
426
427 public static function get_custom_submit( $html, $form, $submit, $form_action, $values ) {
428 $button = self::replace_shortcodes( $html, $form, $submit, $form_action, $values );
429 if ( ! strpos( $button, '[button_action]' ) ) {
430 echo FrmAppHelper::maybe_kses( $button ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
431 return;
432 }
433
434 /**
435 * @since 5.0.06
436 */
437 $button = apply_filters( 'frm_submit_button_html', $button, compact( 'form' ) );
438 if ( FrmAppHelper::should_never_allow_unfiltered_html() ) {
439 $button = FrmAppHelper::kses_submit_button( $button );
440 }
441 $button_parts = explode( '[button_action]', $button );
442
443 $classes = apply_filters( 'frm_submit_button_class', array(), $form );
444 if ( ! empty( $classes ) ) {
445 $classes = implode( ' ', $classes );
446 $button_class = ' class="frm_button_submit';
447 if ( strpos( $button_parts[0], $button_class ) !== false ) {
448 $button_parts[0] = str_replace( $button_class, $button_class . ' ' . esc_attr( $classes ), $button_parts[0] );
449 } else {
450 $button_parts[0] .= ' class="' . esc_attr( $classes ) . '"';
451 }
452 }
453
454 echo $button_parts[0]; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
455 do_action( 'frm_submit_button_action', $form, $form_action );
456 echo $button_parts[1]; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
457 }
458
459 /**
460 * @since 4.0
461 */
462 public static function html_shortcodes() {
463 $codes = array(
464 'id' => array(
465 'label' => __( 'Field ID', 'formidable' ),
466 'class' => 'show_field_custom_html',
467 ),
468 'key' => array(
469 'label' => __( 'Field Key', 'formidable' ),
470 'class' => 'show_field_custom_html',
471 ),
472 'field_name' => array(
473 'label' => __( 'Field Name', 'formidable' ),
474 'class' => 'show_field_custom_html',
475 ),
476 'description' => array(
477 'label' => __( 'Field Description', 'formidable' ),
478 'class' => 'show_field_custom_html',
479 ),
480 'label_position' => array(
481 'label' => __( 'Label Position', 'formidable' ),
482 'class' => 'show_field_custom_html',
483 ),
484 'required_label' => array(
485 'label' => __( 'Required Label', 'formidable' ),
486 'class' => 'show_field_custom_html',
487 ),
488 'input' => array(
489 'label' => __( 'Input Field', 'formidable' ),
490 'class' => 'show_field_custom_html',
491 ),
492 'input opt=1' => array(
493 'label' => __( 'Single Option', 'formidable' ),
494 'title' => __( 'Show a single radio or checkbox option by replacing 1 with the order of the option', 'formidable' ),
495 'class' => 'show_field_custom_html',
496 ),
497 'input label=0' => array(
498 'label' => __( 'Hide Option Label', 'formidable' ),
499 'class' => 'show_field_custom_html',
500 ),
501 'required_class' => array(
502 'label' => __( 'Required Class', 'formidable' ),
503 'title' => __( 'Add class name if field is required', 'formidable' ),
504 'class' => 'show_field_custom_html',
505 ),
506 'error_class' => array(
507 'label' => __( 'Error Class', 'formidable' ),
508 'title' => __( 'Add class name if field has an error on form submit', 'formidable' ),
509 'class' => 'show_field_custom_html',
510 ),
511
512 'form_name' => array(
513 'label' => __( 'Form Name', 'formidable' ),
514 'class' => 'show_before_html show_after_html',
515 ),
516 'form_description' => array(
517 'label' => __( 'Form Description', 'formidable' ),
518 'class' => 'show_before_html show_after_html',
519 ),
520 'form_key' => array(
521 'label' => __( 'Form Key', 'formidable' ),
522 'class' => 'show_before_html show_after_html',
523 ),
524 'deletelink' => array(
525 'label' => __( 'Delete Entry Link', 'formidable' ),
526 'class' => 'show_before_html show_after_html',
527 ),
528
529 'button_label' => array(
530 'label' => __( 'Button Label', 'formidable' ),
531 'class' => 'show_submit_html',
532 ),
533 'button_action' => array(
534 'label' => __( 'Button Hook', 'formidable' ),
535 'class' => 'show_submit_html',
536 ),
537 );
538
539 /**
540 * @since 4.0
541 */
542 return apply_filters( 'frm_html_codes', $codes );
543 }
544
545 /**
546 * @since 4.0
547 * @param array $args
548 */
549 public static function insert_opt_html( $args ) {
550 $class = isset( $args['class'] ) ? $args['class'] : '';
551 $fields = FrmField::all_field_selection();
552 $field = isset( $fields[ $args['type'] ] ) ? $fields[ $args['type'] ] : array();
553
554 self::prepare_field_type( $field );
555
556 if ( ! isset( $field['icon'] ) ) {
557 $field['icon'] = 'frmfont frm_pencil_icon';
558 }
559
560 $possible_email_field = FrmFieldFactory::field_has_property( $args['type'], 'holds_email_values' );
561 if ( $possible_email_field ) {
562 $class .= ' show_frm_not_email_to';
563 }
564 ?>
565 <li class="<?php echo esc_attr( $class ); ?>">
566 <a href="javascript:void(0)" class="frmids frm_insert_code"
567 data-code="<?php echo esc_attr( $args['id'] ); ?>">
568 <span>[<?php echo esc_attr( isset( $args['id_label'] ) ? $args['id_label'] : $args['id'] ); ?>]</span>
569 <?php FrmAppHelper::icon_by_class( $field['icon'], array( 'aria-hidden' => 'true' ) ); ?>
570 <?php echo esc_attr( FrmAppHelper::truncate( $args['name'], 60 ) ); ?>
571 </a>
572 <a href="javascript:void(0)" class="frmkeys frm_insert_code frm_hidden"
573 data-code="<?php echo esc_attr( $args['key'] ); ?>">
574 <span>[<?php echo esc_attr( FrmAppHelper::truncate( isset( $args['key_label'] ) ? $args['key_label'] : $args['key'], 7 ) ); ?>]</span>
575 <?php if ( isset( $field['icon'] ) ) { ?>
576 <?php FrmAppHelper::icon_by_class( $field['icon'], array( 'aria-hidden' => 'true' ) ); ?>
577 <?php } ?>
578 <?php echo esc_attr( FrmAppHelper::truncate( $args['name'], 60 ) ); ?>
579 </a>
580 </li>
581 <?php
582 }
583
584 /**
585 * @since 4.0
586 * @param array $args
587 */
588 public static function insert_code_html( $args ) {
589 $defaults = array(
590 'class' => '',
591 'code' => '',
592 'label' => '',
593 'title' => '',
594 );
595
596 $args = array_merge( $defaults, $args );
597 $has_tooltip = ! empty( $args['title'] );
598
599 ?>
600 <li class="<?php echo esc_attr( $args['class'] ); ?>">
601 <a href="javascript:void(0)" class="frm_insert_code <?php echo $has_tooltip ? 'frm_help' : ''; ?>"
602 <?php echo $has_tooltip ? 'title="' . esc_attr( $args['title'] ) . '"' : ''; ?>
603 data-code="<?php echo esc_attr( $args['code'] ); ?>">
604 <span>
605 [<?php echo esc_attr( FrmAppHelper::truncate( $args['code'], 10 ) ); ?>]
606 </span>
607 <?php echo esc_attr( FrmAppHelper::truncate( $args['label'], 60 ) ); ?>
608 </a>
609 </li>
610 <?php
611 }
612
613 /**
614 * Some field types in add-ons may have been added with only
615 * a field type and name.
616 *
617 * @since 4.0
618 */
619 public static function prepare_field_type( &$field ) {
620 if ( ! is_array( $field ) ) {
621 $field = array(
622 'name' => $field,
623 'icon' => 'frm_icon_font frm_pencil_icon',
624 );
625 }
626 }
627
628 /**
629 * Automatically add end section fields if they don't exist (2.0 migration)
630 *
631 * @since 2.0
632 *
633 * @param boolean $reset_fields
634 */
635 public static function auto_add_end_section_fields( $form, $fields, &$reset_fields ) {
636 if ( empty( $fields ) ) {
637 return;
638 }
639
640 $end_section_values = apply_filters( 'frm_before_field_created', FrmFieldsHelper::setup_new_vars( 'end_divider', $form->id ) );
641 $open = false;
642 $prev_order = false;
643 $add_order = 0;
644 $last_field = false;
645 foreach ( $fields as $field ) {
646 if ( $prev_order === $field->field_order ) {
647 $add_order ++;
648 }
649
650 if ( $add_order ) {
651 $reset_fields = true;
652 $field->field_order = $field->field_order + $add_order;
653 FrmField::update( $field->id, array( 'field_order' => $field->field_order ) );
654 }
655
656 switch ( $field->type ) {
657 case 'divider':
658 // Create an end section if open.
659 self::maybe_create_end_section( $open, $reset_fields, $add_order, $end_section_values, $field, 'move' );
660
661 // Mark it open for the next end section.
662 $open = true;
663 break;
664 case 'break':
665 self::maybe_create_end_section( $open, $reset_fields, $add_order, $end_section_values, $field, 'move' );
666 break;
667 case 'end_divider':
668 if ( ! $open ) {
669 // The section isn't open, so this is an extra field that needs to be removed.
670 FrmField::destroy( $field->id );
671 $reset_fields = true;
672 }
673
674 // There is already an end section here, so there is no need to create one.
675 $open = false;
676 }
677 $prev_order = $field->field_order;
678
679 $last_field = $field;
680 unset( $field );
681 }
682
683 self::maybe_create_end_section( $open, $reset_fields, $add_order, $end_section_values, $last_field );
684 }
685
686 /**
687 * Create end section field if it doesn't exist. This is for migration from < 2.0
688 * Fix any ordering that may be messed up
689 */
690 public static function maybe_create_end_section( &$open, &$reset_fields, &$add_order, $end_section_values, $field, $move = 'no' ) {
691 if ( ! $open ) {
692 return;
693 }
694
695 $end_section_values['field_order'] = $field->field_order + 1;
696
697 FrmField::create( $end_section_values );
698
699 if ( $move == 'move' ) {
700 // bump the order of current field unless we're at the end of the form
701 FrmField::update( $field->id, array( 'field_order' => $field->field_order + 2 ) );
702 }
703
704 $add_order += 2;
705 $open = false;
706 $reset_fields = true;
707 }
708
709 public static function replace_shortcodes( $html, $form, $title = false, $description = false, $values = array() ) {
710 $codes = array(
711 'form_name' => $title,
712 'form_description' => $description,
713 'entry_key' => true,
714 );
715 foreach ( $codes as $code => $show ) {
716 if ( $code === 'form_name' ) {
717 $replace_with = $form->name;
718 } elseif ( $code === 'form_description' ) {
719 $replace_with = FrmAppHelper::use_wpautop( $form->description );
720 } elseif ( $code === 'entry_key' && isset( $_GET ) && isset( $_GET['entry'] ) ) {
721 $replace_with = FrmAppHelper::simple_get( 'entry' );
722 } else {
723 $replace_with = '';
724 }
725
726 FrmShortcodeHelper::remove_inline_conditions( ( FrmAppHelper::is_true( $show ) && $replace_with != '' ), $code, $replace_with, $html );
727 }
728
729 //replace [form_key]
730 $html = str_replace( '[form_key]', $form->form_key, $html );
731
732 //replace [frmurl]
733 $html = str_replace( '[frmurl]', FrmFieldsHelper::dynamic_default_values( 'frmurl' ), $html );
734
735 if ( strpos( $html, '[button_label]' ) ) {
736 add_filter( 'frm_submit_button', 'FrmFormsHelper::submit_button_label', 1 );
737 $submit_label = apply_filters( 'frm_submit_button', $title, $form );
738 $submit_label = esc_attr( do_shortcode( $submit_label ) );
739 $html = str_replace( '[button_label]', $submit_label, $html );
740 }
741
742 $html = apply_filters( 'frm_form_replace_shortcodes', $html, $form, $values );
743
744 if ( strpos( $html, '[if back_button]' ) ) {
745 $html = preg_replace( '/(\[if\s+back_button\])(.*?)(\[\/if\s+back_button\])/mis', '', $html );
746 }
747
748 if ( strpos( $html, '[if save_draft]' ) ) {
749 $html = preg_replace( '/(\[if\s+save_draft\])(.*?)(\[\/if\s+save_draft\])/mis', '', $html );
750 }
751
752 if ( apply_filters( 'frm_do_html_shortcodes', true ) ) {
753 $html = do_shortcode( $html );
754 }
755
756 return $html;
757 }
758
759 public static function submit_button_label( $submit ) {
760 if ( ! $submit ) {
761 $frm_settings = FrmAppHelper::get_settings();
762 $submit = $frm_settings->submit_value;
763 }
764
765 return $submit;
766 }
767
768 /**
769 * If the Formidable styling isn't being loaded,
770 * use inline styling to hide the element
771 *
772 * @since 2.03.05
773 */
774 public static function maybe_hide_inline() {
775 $frm_settings = FrmAppHelper::get_settings();
776 if ( $frm_settings->load_style == 'none' ) {
777 echo ' style="display:none;"';
778 } elseif ( $frm_settings->load_style == 'dynamic' ) {
779 FrmStylesController::enqueue_style();
780 }
781 }
782
783 public static function get_form_style_class( $form = false ) {
784 $style = self::get_form_style( $form );
785 $class = ' with_frm_style';
786
787 if ( empty( $style ) ) {
788 if ( FrmAppHelper::is_admin_page( 'formidable-entries' ) ) {
789 return $class;
790 } else {
791 return;
792 }
793 }
794
795 // If submit button needs to be inline or centered.
796 if ( is_object( $form ) ) {
797 $form = $form->options;
798 }
799
800 if ( ! empty( $form['submit_align'] ) ) {
801 $submit_align = $form['submit_align'];
802 } elseif ( self::form_should_be_inline_and_missing_class( $form ) ) {
803 $submit_align = 'inline';
804 } else {
805 $submit_align = '';
806 }
807
808 if ( 'inline' === $submit_align ) {
809 $class .= ' frm_inline_form';
810 $class .= self::maybe_align_fields_top( $form );
811 } elseif ( 'center' === $submit_align ) {
812 $class .= ' frm_center_submit';
813 }
814
815 $class = apply_filters( 'frm_add_form_style_class', $class, $style );
816
817 return $class;
818 }
819
820 /**
821 * 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.
822 *
823 * @since 5.0.12
824 *
825 * @param array $form
826 * @return bool
827 */
828 private static function form_should_be_inline_and_missing_class( $form ) {
829 if ( isset( $form['form_class'] ) && false !== strpos( ' ' . $form['form_class'] . ' ', ' frm_inline_form ' ) ) {
830 // not missing class, avoid adding it twice.
831 return false;
832 }
833 return ! empty( $form['submit_html'] ) && false !== strpos( $form['submit_html'], 'frm_inline_submit' );
834 }
835
836 /**
837 * Returns appropriate class if form has top labels
838 *
839 * @param $form
840 *
841 * @return string
842 */
843 private static function maybe_align_fields_top( $form ) {
844 return self::form_has_top_labels( $form ) ? ' frm_inline_top' : '';
845 }
846
847 /**
848 * Determine if a form has fields with top labels so submit button can be aligned properly
849 *
850 * @param $form
851 *
852 * @return bool
853 */
854 private static function form_has_top_labels( $form ) {
855 if ( ! isset( $form['fields'] ) ) {
856 return false;
857 }
858
859 $fields = $form['fields'];
860 if ( count( $fields ) <= 0 ) {
861 return false;
862 }
863
864 $fields = array_reverse( $fields ); // start from the fields closest to the submit button
865 foreach ( $fields as $field ) {
866 $type = isset( $field['original_type'] ) ? $field['original_type'] : $field['type'];
867 $has_input = FrmFieldFactory::field_has_property( $type, 'has_input' );
868 if ( $has_input ) {
869 return self::field_has_top_label( $field, $form );
870 }
871 }
872
873 return false;
874 }
875
876 /**
877 * Check if a field's label position is set to "top"
878 *
879 * @param $field
880 * @param $form
881 *
882 * @return bool
883 */
884 private static function field_has_top_label( $field, $form ) {
885 $label_position = FrmFieldsHelper::label_position( $field['label'], $field, $form );
886
887 return in_array( $label_position, array( 'top', 'inside', 'hidden' ) );
888 }
889
890 /**
891 * @param object|string|boolean $form
892 *
893 * @return string
894 */
895 public static function get_form_style( $form ) {
896 $style = 1;
897 if ( empty( $form ) || 'default' === $form ) {
898 return $style;
899 } elseif ( is_object( $form ) && $form->parent_form_id ) {
900 // get the parent form if this is a child
901 $form = $form->parent_form_id;
902 } elseif ( is_array( $form ) && isset( $form['parent_form_id'] ) && $form['parent_form_id'] ) {
903 $form = $form['parent_form_id'];
904 } elseif ( is_array( $form ) && isset( $form['custom_style'] ) ) {
905 $style = $form['custom_style'];
906 }
907
908 if ( $form && is_string( $form ) ) {
909 $form = FrmForm::getOne( $form );
910 }
911
912 $style = ( $form && is_object( $form ) && isset( $form->options['custom_style'] ) ) ? $form->options['custom_style'] : $style;
913
914 return $style;
915 }
916
917 /**
918 * Display the validation error messages when an entry is submitted
919 *
920 * @param array $args - includes img, errors
921 *
922 * @since 2.0.6
923 */
924 public static function show_errors( $args ) {
925 $invalid_msg = self::get_invalid_error_message( $args );
926
927 if ( empty( $invalid_msg ) ) {
928 $show_img = false;
929 } else {
930 echo wp_kses_post( $invalid_msg );
931 $show_img = true;
932 }
933
934 self::show_error(
935 array(
936 'img' => $args['img'],
937 'errors' => $args['errors'],
938 'show_img' => $show_img,
939 )
940 );
941 }
942
943 /**
944 * Display the error message in the front-end along with the image if set
945 * The image was removed from the styling settings, but it may still be set with a hook
946 * If the message in the global settings is empty, show every validation message in the error box
947 *
948 * @param array $args - includes img, errors, and show_img
949 *
950 * @since 2.0.6
951 */
952 public static function show_error( $args ) {
953 // remove any blank messages
954 $args['errors'] = array_filter( (array) $args['errors'] );
955
956 $line_break_first = $args['show_img'];
957 foreach ( $args['errors'] as $error_key => $error ) {
958 if ( $line_break_first && ! is_numeric( $error_key ) && ( $error_key == 'cptch_number' || strpos( $error_key, 'field' ) === 0 ) ) {
959 continue;
960 }
961
962 $id = str_replace( 'field', 'field_', $error_key );
963 echo '<div id="' . esc_attr( $id ) . '_error">';
964
965 if ( $args['show_img'] && ! empty( $args['img'] ) ) {
966 echo '<img src="' . esc_attr( $args['img'] ) . '" alt="" />';
967 } else {
968 $args['show_img'] = true;
969 }
970
971 echo wp_kses_post( $error );
972
973 echo '</div>';
974 }
975 }
976
977 public static function maybe_get_scroll_js( $id ) {
978 $offset = apply_filters( 'frm_scroll_offset', 4, array( 'form_id' => $id ) );
979 if ( $offset != - 1 ) {
980 self::get_scroll_js( $id );
981 }
982 }
983
984 public static function get_scroll_js( $form_id ) {
985 echo '<script type="text/javascript">document.addEventListener(\'DOMContentLoaded\',function(){frmFrontForm.scrollMsg(' . (int) $form_id . ');})</script>';
986 }
987
988 /**
989 * @since 3.0
990 */
991 public static function actions_dropdown( $atts ) {
992 if ( FrmAppHelper::is_admin_page( 'formidable' ) ) {
993 $status = $atts['status'];
994 $form_id = isset( $atts['id'] ) ? $atts['id'] : FrmAppHelper::get_param( 'id', 0, 'get', 'absint' );
995 $trash_link = self::delete_trash_info( $form_id, $status );
996 $links = self::get_action_links( $form_id, $status );
997 include( FrmAppHelper::plugin_path() . '/classes/views/frm-forms/actions-dropdown.php' );
998 }
999 }
1000
1001 /**
1002 * @since 3.0
1003 */
1004 public static function get_action_links( $form_id, $form ) {
1005 if ( ! is_object( $form ) ) {
1006 $form = FrmForm::getOne( $form_id );
1007 }
1008
1009 $actions = array();
1010 $trash_links = self::delete_trash_links( $form_id );
1011 if ( 'trash' == $form->status ) {
1012 $actions['restore'] = $trash_links['restore'];
1013
1014 if ( current_user_can( 'frm_delete_forms' ) ) {
1015 $actions['trash'] = $trash_links['delete'];
1016 }
1017 } elseif ( current_user_can( 'frm_edit_forms' ) ) {
1018 $duplicate_link = '?page=formidable&frm_action=duplicate&id=' . $form_id;
1019 if ( $form->is_template ) {
1020 $actions['frm_duplicate'] = array(
1021 'url' => wp_nonce_url( $duplicate_link ),
1022 'label' => __( 'Create Form from Template', 'formidable' ),
1023 'icon' => 'frm_icon_font frm_clone_icon',
1024 );
1025 } else {
1026 $actions['duplicate'] = array(
1027 'url' => wp_nonce_url( $duplicate_link ),
1028 'label' => __( 'Duplicate Form', 'formidable' ),
1029 'icon' => 'frm_icon_font frm_clone_icon',
1030 );
1031 }
1032
1033 $actions['trash'] = self::delete_trash_info( $form_id, $form->status );
1034 }
1035
1036 return $actions;
1037 }
1038
1039 public static function edit_form_link( $data ) {
1040 $form_id = self::get_form_id_from_data( $data );
1041
1042 if ( ! $form_id ) {
1043 return '';
1044 }
1045
1046 $label = self::edit_form_link_label( $data );
1047 $link = '<a href="' . esc_url( FrmForm::get_edit_link( $form_id ) ) . '">' . esc_html( $label ) . '</a>';
1048 return $link;
1049 }
1050
1051 public static function edit_form_link_label( $data ) {
1052 $name = self::get_form_name_from_data( $data );
1053 if ( ! $name ) {
1054 return __( '(no title)', 'formidable' );
1055 }
1056 return FrmAppHelper::truncate( $name, 40 );
1057 }
1058
1059 /**
1060 * @param mixed data
1061 * @return int
1062 */
1063 private static function get_form_id_from_data( $data ) {
1064 if ( is_object( $data ) ) {
1065 $form_id = $data->id;
1066 } else {
1067 $form_id = $data;
1068 }
1069 return $form_id;
1070 }
1071
1072 /**
1073 * @param mixed $data
1074 * @return string
1075 */
1076 private static function get_form_name_from_data( $data ) {
1077 if ( is_object( $data ) ) {
1078 $form_name = $data->name;
1079 } else {
1080 $form_id = $data;
1081 $form_name = FrmForm::getName( $form_id );
1082 }
1083 return $form_name;
1084 }
1085
1086 public static function delete_trash_link( $id, $status, $length = 'label' ) {
1087 $link_details = self::delete_trash_info( $id, $status );
1088
1089 return self::format_link_html( $link_details, $length );
1090 }
1091
1092 /**
1093 * @since 3.0
1094 */
1095 public static function format_link_html( $link_details, $length = 'label' ) {
1096 $link = '';
1097 if ( ! empty( $link_details ) ) {
1098 $link = '<a href="' . esc_url( $link_details['url'] ) . '" class="frm-trash-link"';
1099 if ( isset( $link_details['data'] ) ) {
1100 foreach ( $link_details['data'] as $data => $value ) {
1101 $link .= ' data-' . esc_attr( $data ) . '="' . esc_attr( $value ) . '"';
1102 }
1103 } elseif ( isset( $link_details['confirm'] ) ) {
1104 $link .= ' onclick="return confirm(\'' . esc_attr( $link_details['confirm'] ) . '\')"';
1105 }
1106
1107 $label = ( isset( $link_details[ $length ] ) ? $link_details[ $length ] : $link_details['label'] );
1108 if ( $length == 'icon' && isset( $link_details[ $length ] ) ) {
1109 $label = '<span class="' . $label . '" title="' . esc_attr( $link_details['label'] ) . '" aria-hidden="true"></span>';
1110 $link .= ' aria-label="' . esc_attr( $link_details['label'] ) . '"';
1111 }
1112
1113 $link .= '>' . $label . '</a>';
1114 }
1115
1116 return $link;
1117 }
1118
1119 /**
1120 * @since 3.0
1121 */
1122 public static function delete_trash_info( $id, $status ) {
1123 $labels = self::delete_trash_links( $id );
1124
1125 if ( 'trash' == $status ) {
1126 $info = $labels['restore'];
1127 } elseif ( current_user_can( 'frm_delete_forms' ) ) {
1128 if ( EMPTY_TRASH_DAYS ) {
1129 $info = $labels['trash'];
1130 } else {
1131 $info = $labels['delete'];
1132 }
1133 } else {
1134 $info = array();
1135 }
1136
1137 return $info;
1138 }
1139
1140 /**
1141 * @since 3.0
1142 */
1143 private static function delete_trash_links( $id ) {
1144 $current_page = FrmAppHelper::get_simple_request( array( 'param' => 'form_type' ) );
1145 $base_url = '?page=formidable&form_type=' . $current_page . '&id=' . $id;
1146
1147 return array(
1148 'restore' => array(
1149 'label' => __( 'Restore from Trash', 'formidable' ),
1150 'short' => __( 'Restore', 'formidable' ),
1151 'url' => wp_nonce_url( $base_url . '&frm_action=untrash', 'untrash_form_' . absint( $id ) ),
1152 ),
1153 'trash' => array(
1154 'label' => __( 'Move Form to Trash', 'formidable' ),
1155 'short' => __( 'Trash', 'formidable' ),
1156 'url' => wp_nonce_url( $base_url . '&frm_action=trash', 'trash_form_' . absint( $id ) ),
1157 'icon' => 'frm_icon_font frm_delete_icon',
1158 'data' => array( 'frmverify' => __( 'Do you want to move this form to the trash?', 'formidable' ) ),
1159 ),
1160 'delete' => array(
1161 'label' => __( 'Delete Permanently', 'formidable' ),
1162 'short' => __( 'Delete', 'formidable' ),
1163 'url' => wp_nonce_url( $base_url . '&frm_action=destroy', 'destroy_form_' . absint( $id ) ),
1164 'confirm' => __( 'Are you sure you want to delete this form and all its entries?', 'formidable' ),
1165 'icon' => 'frm_icon_font frm_delete_icon',
1166 'data' => array( 'frmverify' => __( 'This will permanently delete the form and all its entries. This is irreversible. Are you sure you want to continue?', 'formidable' ) ),
1167 ),
1168 );
1169 }
1170
1171 /**
1172 * @since 3.0
1173 */
1174 public static function css_classes() {
1175 $classes = array(
1176 'frm_total' => array(
1177 'label' => __( 'Total', 'formidable' ),
1178 'title' => __( 'Add this to a read-only field to display the text in bold without a border or background.', 'formidable' ),
1179 ),
1180 'frm_total_big' => array(
1181 'label' => __( 'Big Total', 'formidable' ),
1182 'title' => __( 'Add this to a read-only field to display the text in large, bold text without a border or background.', 'formidable' ),
1183 ),
1184 'frm_scroll_box' => array(
1185 'label' => __( 'Scroll Box', 'formidable' ),
1186 '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' ),
1187 ),
1188 'frm_first' => array(
1189 'label' => __( 'First', 'formidable' ),
1190 'title' => __( 'Add this to the first field in each row along with a width. ie frm_first frm4', 'formidable' ),
1191 ),
1192 'frm_alignright' => __( 'Right', 'formidable' ),
1193 'frm_grid_first' => __( 'First Grid Row', 'formidable' ),
1194 'frm_grid' => __( 'Even Grid Row', 'formidable' ),
1195 'frm_grid_odd' => __( 'Odd Grid Row', 'formidable' ),
1196 'frm_color_block' => array(
1197 'label' => __( 'Color Block', 'formidable' ),
1198 'title' => __( 'Add a background color to the field or section.', 'formidable' ),
1199 ),
1200 'frm_capitalize' => array(
1201 'label' => __( 'Capitalize', 'formidable' ),
1202 'title' => __( 'Automatically capitalize the first letter in each word.', 'formidable' ),
1203 ),
1204 );
1205
1206 return apply_filters( 'frm_layout_classes', $classes );
1207 }
1208
1209 public static function grid_classes() {
1210 return array(
1211 'frm_half' => '1/2',
1212 'frm_third' => '1/3',
1213 'frm_two_thirds' => '2/3',
1214 'frm_fourth' => '1/4',
1215 'frm_three_fourths' => '3/4',
1216 'frm_sixth' => '1/6',
1217 'frm10' => '5/6',
1218 'frm12' => '100%',
1219 );
1220 }
1221
1222 /**
1223 * @since 3.0
1224 */
1225 public static function style_class_label( $style, $class ) {
1226 $label = '';
1227 if ( empty( $style ) ) {
1228 $label = $class;
1229 } elseif ( ! is_array( $style ) ) {
1230 $label = $style;
1231 } elseif ( isset( $style['label'] ) ) {
1232 $label = $style['label'];
1233 }
1234
1235 return $label;
1236 }
1237
1238 public static function status_nice_name( $status ) {
1239 $nice_names = array(
1240 'draft' => __( 'Draft', 'formidable' ),
1241 'trash' => __( 'Trash', 'formidable' ),
1242 'publish' => __( 'Published', 'formidable' ),
1243 );
1244
1245 if ( ! in_array( $status, array_keys( $nice_names ) ) ) {
1246 $status = 'publish';
1247 }
1248
1249 $name = $nice_names[ $status ];
1250
1251 return $name;
1252 }
1253
1254 public static function template_icon( $categories ) {
1255 $ignore = self::ignore_template_categories();
1256 $categories = array_diff( $categories, $ignore );
1257
1258 $icons = array(
1259 'WooCommerce' => array( 'woocommerce', 'var(--purple)' ),
1260 'Post' => array( 'wordpress', 'rgb(0,160,210)' ),
1261 'User Registration' => array( 'register', 'var(--pink)' ),
1262 'PayPal' => array( 'paypal' ),
1263 'Stripe' => array( 'credit_card', 'var(--green)' ),
1264 'Twilio' => array( 'sms', 'rgb(0,160,210)' ),
1265 'Payment' => array( 'credit_card', 'var(--green)' ),
1266 'Health and Wellness' => array( 'heart', 'var(--pink)' ),
1267 'Event Planning' => array( 'calendar', 'var(--orange)' ),
1268 'Real Estate' => array( 'house', 'var(--purple)' ),
1269 'Calculator' => array( 'calculator', 'var(--purple)' ),
1270 'Registrations' => array( 'address_card' ),
1271 'Customer Service' => array( 'users_solid', 'var(--pink)' ),
1272 'Education' => array( 'pencil', 'var(--primary-color)' ),
1273 'Marketing' => array( 'eye', 'rgb(0,160,210)' ),
1274 'Feedback' => array( 'smile', 'var(--green)' ),
1275 'Business Operations' => array( 'case' ),
1276 'Contact Form' => array( 'email' ),
1277 'Survey' => array( 'comment', 'var(--primary-color)' ),
1278 'Application Form' => array( 'align_right', 'rgb(0,160,210)' ),
1279 '' => array( 'align_right' ),
1280 );
1281
1282 $icons[ __( 'My Templates', 'formidable' ) ] = array( 'user', 'var(--orange)' );
1283
1284 $icon = $icons[''];
1285
1286 if ( count( $categories ) === 1 ) {
1287 $category = reset( $categories );
1288 $icon = isset( $icons[ $category ] ) ? $icons[ $category ] : $icon;
1289 } elseif ( ! empty( $categories ) ) {
1290 foreach ( $icons as $cat => $icon ) {
1291 if ( ! in_array( $cat, $categories ) ) {
1292 unset( $icons[ $cat ] );
1293 }
1294 }
1295 $icon = reset( $icons );
1296 }
1297
1298 echo '<span class="frm-inner-circle" ' . ( isset( $icon[1] ) ? 'style="background-color:' . esc_attr( $icon[1] ) : '' ) . '">';
1299 FrmAppHelper::icon_by_class( 'frmfont frm_' . $icon[0] . '_icon' );
1300 echo '<span class="frm_hidden">';
1301 FrmAppHelper::icon_by_class( 'frmfont frm_lock_icon' );
1302 echo '</span>';
1303 echo '</span>';
1304 }
1305
1306 /**
1307 * @since 4.03.01
1308 */
1309 public static function ignore_template_categories() {
1310 return array( 'Business', 'Elite', 'Personal', 'Creator', 'Basic', 'free' );
1311 }
1312
1313 /**
1314 * @since 4.02
1315 */
1316 public static function get_template_install_link( $template, $args ) {
1317 $defaults = array(
1318 'class' => 'install-now',
1319 'href' => 'href',
1320 'atts' => true,
1321 );
1322
1323 if ( ! empty( $template['url'] ) ) {
1324 $link = array(
1325 'url' => $template['url'],
1326 'label' => __( 'Create Form', 'formidable' ),
1327 'class' => 'frm-install-template',
1328 'href' => 'rel',
1329 'atts' => '',
1330 );
1331 } elseif ( self::plan_is_allowed( $args ) ) {
1332 $link = array(
1333 'url' => FrmAppHelper::admin_upgrade_link( 'addons', 'account/downloads/' ) . '&utm_content=' . $template['slug'],
1334 'label' => __( 'Renew', 'formidable' ),
1335 );
1336 } else {
1337 $link = array(
1338 'url' => $args['pricing'],
1339 'label' => __( 'Upgrade', 'formidable' ),
1340 );
1341 }
1342
1343 return array_merge( $defaults, $link );
1344 }
1345
1346 /**
1347 * Is the template included with the license type?
1348 *
1349 * @since 4.02.02
1350 *
1351 * @param array $args
1352 *
1353 * @return bool
1354 */
1355 public static function plan_is_allowed( $args ) {
1356 if ( empty( $args['license_type'] ) ) {
1357 return false;
1358 }
1359
1360 $included = $args['license_type'] === strtolower( $args['plan_required'] );
1361
1362 $plans = array( 'free', 'personal', 'business', 'elite' );
1363 if ( $included || ! in_array( strtolower( $args['plan_required'] ), $plans, true ) ) {
1364 return $included;
1365 }
1366
1367 foreach ( $plans as $plan ) {
1368 if ( $included || $plan === $args['license_type'] ) {
1369 break;
1370 }
1371 $included = $plan === strtolower( $args['plan_required'] );
1372 }
1373
1374 return $included;
1375 }
1376
1377 /**
1378 * @since 4.02
1379 */
1380 public static function template_install_html( $link, $class = '' ) {
1381 $link['class'] .= ' ' . $class;
1382 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"' : '' ) . '>';
1383 }
1384
1385 /**
1386 * If a template or add-on cannot be installed, show a message
1387 * about which plan is required.
1388 *
1389 * @since 4.0
1390 */
1391 public static function show_plan_required( $requires, $link ) {
1392 if ( empty( $requires ) ) {
1393 return;
1394 }
1395
1396 ?>
1397 <p class="frm_plan_required">
1398 <?php esc_html_e( 'License plan required:', 'formidable' ); ?>
1399 <a href="<?php echo esc_url( $link ); ?>" target="_blank" rel="noopener">
1400 <?php echo esc_html( $requires ); ?>
1401 </a>
1402 </p>
1403 <?php
1404 }
1405
1406 /**
1407 * @since 4.0
1408 */
1409 public static function get_plan_required( &$item ) {
1410 if ( ! isset( $item['categories'] ) || ! empty( $item['url'] ) ) {
1411 return false;
1412 }
1413
1414 $plans = array( 'free', 'Basic', 'Personal', 'Business', 'Elite' );
1415
1416 foreach ( $item['categories'] as $k => $category ) {
1417 if ( in_array( $category, $plans, true ) ) {
1418 unset( $item['categories'][ $k ] );
1419
1420 if ( $category === 'Personal' ) {
1421 // Show the current package name.
1422 $category = 'Plus';
1423 }
1424
1425 return $category;
1426 }
1427 }
1428
1429 return false;
1430 }
1431
1432 /**
1433 * Checks for warnings to be displayed after form settings are saved.
1434 *
1435 * @since 4.04
1436 *
1437 * @param array $values The $_POST array, which contains values submitted in a form.
1438 *
1439 * @return array An array of warnings or an empty array.
1440 */
1441 public static function check_for_warnings( $values ) {
1442 $warnings = array();
1443
1444 $redirect_warning = self::check_redirect_url_for_unsafe_params( $values );
1445
1446 if ( $redirect_warning ) {
1447 $warnings[] = $redirect_warning;
1448 }
1449
1450 return apply_filters( 'frm_check_for_warnings', $warnings, $values );
1451 }
1452
1453 /**
1454 * Checks the redirect URL for params whose names are reserved words.
1455 *
1456 * @since 4.04
1457 *
1458 * @param array $values The $_POST array, which contains the values submitted in a form.
1459 *
1460 * @return bool|string A warning message about unsafe params or false.
1461 */
1462 private static function check_redirect_url_for_unsafe_params( $values ) {
1463 if ( ! isset( $values['options'] ) ) {
1464 return false;
1465 }
1466
1467 $options = $values['options'];
1468 FrmAppHelper::sanitize_with_html( $options );
1469
1470 if ( ( ! isset( $options['success_action'] ) ) || $options['success_action'] !== 'redirect' || ! isset( $options['success_url'] ) ) {
1471 return false;
1472 }
1473
1474 $unsafe_params_in_redirect = self::get_unsafe_params( $options['success_url'] );
1475
1476 return self::create_unsafe_param_warning( $unsafe_params_in_redirect );
1477 }
1478
1479 /**
1480 * Returns an array of params whose names are reserved words in the specified URL.
1481 *
1482 * @since 4.04
1483 *
1484 * @param string $url The URL whose params are being checked.
1485 *
1486 * @return array An array of params whose names are reserved words or an empty array.
1487 */
1488 private static function get_unsafe_params( $url ) {
1489 $redirect_components = parse_url( $url );
1490 if ( empty( $redirect_components['query'] ) ) {
1491 return array();
1492 }
1493 parse_str( $redirect_components['query'], $redirect_params );
1494 $redirect_param_names = array_keys( $redirect_params );
1495 $reserved_words = self::reserved_words();
1496 $unsafe_params_in_redirect = array_intersect( $redirect_param_names, $reserved_words );
1497
1498 return array_values( $unsafe_params_in_redirect );
1499 }
1500
1501 /**
1502 * Returns a warning if reserved words have been used as param names in the redirect URL.
1503 *
1504 * @since 4.04
1505 *
1506 * @param array $unsafe_params_in_redirect Array of params from the redirect URL whose names are reserved words.
1507 *
1508 * @return bool|string A string with an unsafe param message or false.
1509 */
1510 private static function create_unsafe_param_warning( $unsafe_params_in_redirect ) {
1511 $count = count( $unsafe_params_in_redirect );
1512 $caution = esc_html__( 'Is this intentional?', 'formidable' );
1513 $reserved_words_intro = esc_html__( 'See the list of reserved words in WordPress.', 'formidable' );
1514 $reserved_words_link = '<a href="https://codex.wordpress.org/WordPress_Query_Vars" target="_blank"> ' . $reserved_words_intro . '</a>';
1515
1516 if ( $count === 0 ) {
1517 return false;
1518 }
1519
1520 if ( $count == 1 ) {
1521 /* translators: %s: the name of a single parameter in the redirect URL */
1522 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;
1523 }
1524
1525 $unsafe_params_string = implode( '", "', $unsafe_params_in_redirect );
1526
1527 /* translators: %s: the names of two or more parameters in the redirect URL, separated by commas */
1528 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;
1529 }
1530
1531 /**
1532 * Returns an array of common reserved words in WordPress.
1533 *
1534 * An edited list of reserved terms from the Codex.
1535 * https://codex.wordpress.org/Reserved_Terms
1536 *
1537 * @since 4.04
1538 *
1539 * @return array Array of WordPress reserved words.
1540 */
1541 public static function reserved_words() {
1542 return array(
1543 'id',
1544 'attachment',
1545 'author',
1546 'author_name',
1547 'calendar',
1548 'cat',
1549 'category',
1550 'category_name',
1551 'cpage',
1552 'custom',
1553 'day',
1554 'date',
1555 'error',
1556 'feed',
1557 'hour',
1558 'm',
1559 'minute',
1560 'more',
1561 'name',
1562 'order',
1563 'p',
1564 'page',
1565 'page_id',
1566 'paged',
1567 'pb',
1568 'post',
1569 'posts',
1570 'preview',
1571 's',
1572 'search',
1573 'second',
1574 'sentence',
1575 'tag',
1576 'taxonomy',
1577 'tb',
1578 'term',
1579 'terms',
1580 'theme',
1581 'title',
1582 'type',
1583 'w',
1584 'year',
1585 );
1586 }
1587
1588 /**
1589 * Check an array of templates, determine how many the logged in user can use
1590 *
1591 * @param array $templates
1592 * @param array $args
1593 * @return int
1594 */
1595 public static function available_count( $templates, $args ) {
1596 return array_reduce(
1597 $templates,
1598 function( $total, $template ) use ( $args ) {
1599 if ( ! empty( $template['url'] ) ) {
1600 return $total + 1;
1601 }
1602
1603 $args['plan_required'] = self::get_plan_required( $template );
1604 if ( self::plan_is_allowed( $args ) ) {
1605 return $total + 1;
1606 }
1607
1608 return $total;
1609 },
1610 0
1611 );
1612 }
1613 }
1614