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

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