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

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

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