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

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