PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 5.3
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v5.3
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 / FrmAppHelper.php

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

3,657 lines 101.3 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 FrmAppHelper {
7 public static $db_version = 98; // Version of the database we are moving to.
8 public static $pro_db_version = 37; //deprecated
9 public static $font_version = 7;
10
11 /**
12 * @since 2.0
13 */
14 public static $plug_version = '5.3';
15
16 /**
17 * @since 1.07.02
18 *
19 * @param none
20 *
21 * @return string The version of this plugin
22 */
23 public static function plugin_version() {
24 return self::$plug_version;
25 }
26
27 public static function plugin_folder() {
28 return basename( self::plugin_path() );
29 }
30
31 public static function plugin_path() {
32 return dirname( dirname( dirname( __FILE__ ) ) );
33 }
34
35 public static function plugin_url() {
36 // Prevously FRM_URL constant.
37 return plugins_url( '', self::plugin_path() . '/formidable.php' );
38 }
39
40 public static function relative_plugin_url() {
41 return str_replace( array( 'https:', 'http:' ), '', self::plugin_url() );
42 }
43
44 /**
45 * @return string Site URL
46 */
47 public static function site_url() {
48 return site_url();
49 }
50
51 /**
52 * Get the name of this site
53 * Used for [sitename] shortcode
54 *
55 * @since 2.0
56 * @return string
57 */
58 public static function site_name() {
59 return get_option( 'blogname' );
60 }
61
62 public static function make_affiliate_url( $url ) {
63 $affiliate_id = self::get_affiliate();
64 if ( ! empty( $affiliate_id ) ) {
65 $url = str_replace( array( 'http://', 'https://' ), '', $url );
66 $url = 'http://www.shareasale.com/r.cfm?u=' . absint( $affiliate_id ) . '&b=841990&m=64739&afftrack=plugin&urllink=' . urlencode( $url );
67 }
68
69 return $url;
70 }
71
72 public static function get_affiliate() {
73 return absint( apply_filters( 'frm_affiliate_id', 0 ) );
74 }
75
76 /**
77 * @since 3.04.02
78 * @param array|string $args
79 * @param string $page
80 */
81 public static function admin_upgrade_link( $args, $page = '' ) {
82 if ( empty( $page ) ) {
83 $page = 'https://formidableforms.com/lite-upgrade/';
84 } else {
85 $page = str_replace( 'https://formidableforms.com/', '', $page );
86 $page = 'https://formidableforms.com/' . $page;
87 }
88
89 $anchor = '';
90 if ( is_array( $args ) ) {
91 $medium = $args['medium'];
92 if ( isset( $args['content'] ) ) {
93 $content = $args['content'];
94 }
95 if ( isset( $args['anchor'] ) ) {
96 $anchor = '#' . $args['anchor'];
97 }
98 } else {
99 $medium = $args;
100 }
101
102 $query_args = array(
103 'utm_source' => 'WordPress',
104 'utm_medium' => $medium,
105 'utm_campaign' => 'liteplugin',
106 );
107
108 if ( isset( $content ) ) {
109 $query_args['utm_content'] = $content;
110 }
111
112 if ( is_array( $args ) && isset( $args['param'] ) ) {
113 $query_args['f'] = $args['param'];
114 }
115
116 if ( is_array( $args ) && ! empty( $args['plan'] ) ) {
117 $query_args['plan'] = $args['plan'];
118 }
119
120 $link = add_query_arg( $query_args, $page ) . $anchor;
121 return self::make_affiliate_url( $link );
122 }
123
124 /**
125 * Get the Formidable settings
126 *
127 * @since 2.0
128 *
129 * @param array $args - May include the form id when values need translation.
130 * @return FrmSettings $frm_setings
131 */
132 public static function get_settings( $args = array() ) {
133 global $frm_settings;
134 if ( empty( $frm_settings ) ) {
135 $frm_settings = new FrmSettings( $args );
136 } elseif ( isset( $args['current_form'] ) ) {
137 // If the global has already been set, allow strings to be filtered.
138 $frm_settings->maybe_filter_for_form( $args );
139 }
140
141 return $frm_settings;
142 }
143
144 public static function get_menu_name() {
145 $frm_settings = self::get_settings();
146
147 return $frm_settings->menu;
148 }
149
150 /**
151 * @since 3.05
152 */
153 public static function svg_logo( $atts = array() ) {
154 $defaults = array(
155 'height' => 18,
156 'width' => 18,
157 'fill' => '#4d4d4d',
158 'orange' => '#f05a24',
159 );
160 $atts = array_merge( $defaults, $atts );
161
162 return '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 599.68 601.37" width="' . esc_attr( $atts['width'] ) . '" height="' . esc_attr( $atts['height'] ) . '">
163 <path fill="' . esc_attr( $atts['orange'] ) . '" d="M289.6 384h140v76h-140z"/>
164 <path fill="' . esc_attr( $atts['fill'] ) . '" d="M400.2 147h-200c-17 0-30.6 12.2-30.6 29.3V218h260v-71zM397.9 264H169.6v196h75V340H398a32.2 32.2 0 0 0 30.1-21.4 24.3 24.3 0 0 0 1.7-8.7V264zM299.8 601.4A300.3 300.3 0 0 1 0 300.7a299.8 299.8 0 1 1 511.9 212.6 297.4 297.4 0 0 1-212 88zm0-563A262 262 0 0 0 38.3 300.7a261.6 261.6 0 1 0 446.5-185.5 259.5 259.5 0 0 0-185-76.8z"/>
165 </svg>';
166 }
167
168 /**
169 * @since 4.0
170 */
171 public static function show_logo( $atts = array() ) {
172 echo self::kses( self::svg_logo( $atts ), 'all' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
173 }
174
175 /**
176 * @since 4.03.02
177 */
178 public static function show_header_logo() {
179 $icon = self::svg_logo(
180 array(
181 'height' => 35,
182 'width' => 35,
183 )
184 );
185
186 $new_icon = apply_filters( 'frm_icon', $icon, true );
187 if ( $new_icon !== $icon ) {
188 if ( strpos( $new_icon, '<svg' ) === 0 ) {
189 $icon = str_replace( 'viewBox="0 0 20', 'width="30" height="35" style="color:#929699" viewBox="0 0 20', $new_icon );
190 } else {
191 // Show nothing if it isn't an SVG.
192 $icon = '<div style="height:39px"></div>';
193 }
194 }
195 echo self::kses( $icon, 'all' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
196 }
197
198 /**
199 * @since 2.02.04
200 */
201 public static function ips_saved() {
202 $frm_settings = self::get_settings();
203
204 return ! $frm_settings->no_ips;
205 }
206
207 public static function pro_is_installed() {
208 return apply_filters( 'frm_pro_installed', false );
209 }
210
211 /**
212 * @since 4.06.02
213 */
214 public static function pro_is_connected() {
215 global $frm_vars;
216 return self::pro_is_installed() && $frm_vars['pro_is_authorized'];
217 }
218
219 /**
220 * @since 4.06
221 */
222 public static function is_form_builder_page() {
223 $action = self::simple_get( 'frm_action', 'sanitize_title' );
224 return self::is_admin_page( 'formidable' ) && ( $action === 'edit' || $action === 'settings' || $action === 'duplicate' );
225 }
226
227 public static function is_formidable_admin() {
228 $page = self::simple_get( 'page', 'sanitize_title' );
229 $is_formidable = strpos( $page, 'formidable' ) !== false;
230 if ( empty( $page ) ) {
231 $is_formidable = self::is_view_builder_page();
232 }
233
234 return $is_formidable;
235 }
236
237 /**
238 * Check for certain page in Formidable settings
239 *
240 * @since 2.0
241 *
242 * @param string $page The name of the page to check
243 *
244 * @return boolean
245 */
246 public static function is_admin_page( $page = 'formidable' ) {
247 global $pagenow;
248 $get_page = self::simple_get( 'page', 'sanitize_title' );
249 if ( $pagenow ) {
250 // allow this to be true during ajax load i.e. ajax form builder loading
251 $is_page = ( $pagenow == 'admin.php' || $pagenow == 'admin-ajax.php' ) && $get_page == $page;
252 if ( $is_page ) {
253 return true;
254 }
255 }
256
257 return is_admin() && $get_page == $page;
258 }
259
260 /**
261 * If the current page is for editing or creating a view.
262 * Returns false for the views listing page.
263 *
264 * @since 4.0
265 */
266 public static function is_view_builder_page() {
267 global $pagenow;
268
269 if ( $pagenow !== 'post.php' && $pagenow !== 'post-new.php' && $pagenow !== 'edit.php' ) {
270 return false;
271 }
272
273 $post_type = self::simple_get( 'post_type', 'sanitize_title' );
274
275 if ( empty( $post_type ) ) {
276 $post_id = self::simple_get( 'post', 'absint' );
277 $post = get_post( $post_id );
278 $post_type = $post ? $post->post_type : '';
279 }
280
281 return $post_type === 'frm_display';
282 }
283
284 /**
285 * Check for the form preview page
286 *
287 * @since 2.0
288 *
289 * @param None
290 *
291 * @return boolean
292 */
293 public static function is_preview_page() {
294 global $pagenow;
295 $action = self::simple_get( 'action', 'sanitize_title' );
296
297 return $pagenow && $pagenow == 'admin-ajax.php' && $action == 'frm_forms_preview';
298 }
299
300 /**
301 * Check for ajax except the form preview page
302 *
303 * @since 2.0
304 *
305 * @param None
306 *
307 * @return boolean
308 */
309 public static function doing_ajax() {
310 return wp_doing_ajax() && ! self::is_preview_page();
311 }
312
313 public static function js_suffix() {
314 return defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
315 }
316
317 /**
318 * @since 2.0.8
319 */
320 public static function prevent_caching() {
321 global $frm_vars;
322
323 return isset( $frm_vars['prevent_caching'] ) && $frm_vars['prevent_caching'];
324 }
325
326 /**
327 * Check if on an admin page
328 *
329 * @since 2.0
330 *
331 * @param None
332 *
333 * @return boolean
334 */
335 public static function is_admin() {
336 return is_admin() && ! wp_doing_ajax();
337 }
338
339 /**
340 * Check if value contains blank value or empty array
341 *
342 * @since 2.0
343 *
344 * @param mixed $value - value to check
345 * @param string
346 *
347 * @return boolean
348 */
349 public static function is_empty_value( $value, $empty = '' ) {
350 return ( is_array( $value ) && empty( $value ) ) || $value === $empty;
351 }
352
353 public static function is_not_empty_value( $value, $empty = '' ) {
354 return ! self::is_empty_value( $value, $empty );
355 }
356
357 /**
358 * Get any value from the $_SERVER
359 *
360 * @since 2.0
361 *
362 * @param string $value
363 *
364 * @return string
365 */
366 public static function get_server_value( $value ) {
367 return isset( $_SERVER[ $value ] ) ? wp_strip_all_tags( wp_unslash( $_SERVER[ $value ] ) ) : '';
368 }
369
370 /**
371 * Check for the IP address in several places
372 * Used by [ip] shortcode
373 *
374 * @return string The IP address of the current user
375 */
376 public static function get_ip_address() {
377 $ip_options = array(
378 'HTTP_CLIENT_IP',
379 'HTTP_CF_CONNECTING_IP',
380 'HTTP_X_FORWARDED_FOR',
381 'HTTP_X_FORWARDED',
382 'HTTP_X_CLUSTER_CLIENT_IP',
383 'HTTP_X_REAL_IP',
384 'HTTP_FORWARDED_FOR',
385 'HTTP_FORWARDED',
386 'REMOTE_ADDR',
387 );
388 $ip = '';
389 foreach ( $ip_options as $key ) {
390 if ( ! isset( $_SERVER[ $key ] ) ) {
391 continue;
392 }
393
394 $key = self::get_server_value( $key );
395 foreach ( explode( ',', $key ) as $ip ) {
396 $ip = trim( $ip ); // just to be safe.
397
398 if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) !== false ) {
399 return sanitize_text_field( $ip );
400 }
401 }
402 }
403
404 return sanitize_text_field( $ip );
405 }
406
407 public static function get_param( $param, $default = '', $src = 'get', $sanitize = '' ) {
408 if ( strpos( $param, '[' ) ) {
409 $params = explode( '[', $param );
410 $param = $params[0];
411 }
412
413 if ( $src === 'get' ) {
414 $value = isset( $_POST[ $param ] ) ? wp_unslash( $_POST[ $param ] ) : ( isset( $_GET[ $param ] ) ? wp_unslash( $_GET[ $param ] ) : $default ); // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
415 if ( ! isset( $_POST[ $param ] ) && isset( $_GET[ $param ] ) && ! is_array( $value ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
416 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
417 $value = htmlspecialchars_decode( wp_unslash( $_GET[ $param ] ) );
418 }
419 self::sanitize_value( $sanitize, $value );
420 } else {
421 $value = self::get_simple_request(
422 array(
423 'type' => $src,
424 'param' => $param,
425 'default' => $default,
426 'sanitize' => $sanitize,
427 )
428 );
429 }
430
431 if ( isset( $params ) && is_array( $value ) && ! empty( $value ) ) {
432 foreach ( $params as $k => $p ) {
433 if ( ! $k || ! is_array( $value ) ) {
434 continue;
435 }
436
437 $p = trim( $p, ']' );
438 $value = isset( $value[ $p ] ) ? $value[ $p ] : $default;
439 }
440 }
441
442 return $value;
443 }
444
445 public static function get_post_param( $param, $default = '', $sanitize = '', $serialized = false ) {
446 return self::get_simple_request(
447 array(
448 'type' => 'post',
449 'param' => $param,
450 'default' => $default,
451 'sanitize' => $sanitize,
452 'serialized' => $serialized,
453 )
454 );
455 }
456
457 /**
458 * @since 2.0
459 *
460 * @param string $param
461 * @param string $sanitize
462 * @param string $default
463 *
464 * @return string|array
465 */
466 public static function simple_get( $param, $sanitize = 'sanitize_text_field', $default = '' ) {
467 return self::get_simple_request(
468 array(
469 'type' => 'get',
470 'param' => $param,
471 'default' => $default,
472 'sanitize' => $sanitize,
473 )
474 );
475 }
476
477 /**
478 * Get a GET/POST/REQUEST value and sanitize it
479 *
480 * @since 2.0.6
481 *
482 * @param array $args
483 *
484 * @return string|array
485 */
486 public static function get_simple_request( $args ) {
487 $defaults = array(
488 'param' => '',
489 'default' => '',
490 'type' => 'get',
491 'sanitize' => 'sanitize_text_field',
492 'serialized' => false,
493 );
494 $args = wp_parse_args( $args, $defaults );
495
496 $value = $args['default'];
497 if ( $args['type'] == 'get' ) {
498 if ( $_GET && isset( $_GET[ $args['param'] ] ) ) {
499 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing
500 $value = wp_unslash( $_GET[ $args['param'] ] );
501 }
502 } elseif ( $args['type'] == 'post' ) {
503 if ( isset( $_POST[ $args['param'] ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
504 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing
505 $value = wp_unslash( $_POST[ $args['param'] ] );
506 if ( $args['serialized'] === true && is_serialized_string( $value ) && is_serialized( $value ) ) {
507 self::unserialize_or_decode( $value );
508 }
509 }
510 } else {
511 if ( isset( $_REQUEST[ $args['param'] ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
512 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
513 $value = wp_unslash( $_REQUEST[ $args['param'] ] );
514 }
515 }
516
517 self::sanitize_value( $args['sanitize'], $value );
518
519 return $value;
520 }
521
522 /**
523 * Preserve backslashes in a value, but make sure value doesn't get compounding slashes
524 *
525 * @since 2.0.8
526 *
527 * @param string $value
528 *
529 * @return string $value
530 */
531 public static function preserve_backslashes( $value ) {
532 // If backslashes have already been added, don't add them again
533 if ( strpos( $value, '\\\\' ) === false ) {
534 $value = addslashes( $value );
535 }
536
537 return $value;
538 }
539
540 public static function sanitize_value( $sanitize, &$value ) {
541 if ( ! empty( $sanitize ) ) {
542 if ( is_array( $value ) ) {
543 $temp_values = $value;
544 foreach ( $temp_values as $k => $v ) {
545 self::sanitize_value( $sanitize, $value[ $k ] );
546 }
547 } else {
548 $value = call_user_func( $sanitize, $value );
549 }
550 }
551 }
552
553 public static function sanitize_request( $sanitize_method, &$values ) {
554 $temp_values = $values;
555 foreach ( $temp_values as $k => $val ) {
556 if ( isset( $sanitize_method[ $k ] ) ) {
557 $values[ $k ] = call_user_func( $sanitize_method[ $k ], $val );
558 }
559 }
560 }
561
562 /**
563 * @since 4.0.04
564 */
565 public static function sanitize_with_html( &$value ) {
566 self::sanitize_value( 'wp_kses_post', $value );
567 self::decode_specialchars( $value );
568 }
569
570 /**
571 * Do wp_specialchars_decode to get back '&' that wp_kses_post might have turned to '&amp;'
572 * this MUST be done, else we'll be back to the '& entity' problem.
573 *
574 * @since 4.0.04
575 */
576 public static function decode_specialchars( &$value ) {
577 if ( is_array( $value ) ) {
578 $temp_values = $value;
579 foreach ( $temp_values as $k => $v ) {
580 self::decode_specialchars( $value[ $k ] );
581 }
582 } else {
583 self::decode_amp( $value );
584 }
585 }
586
587 /**
588 * The wp_specialchars_decode function changes too much.
589 * This will leave HTML as is, but still convert &.
590 * Adapted from wp_specialchars_decode().
591 *
592 * @since 4.03.01
593 *
594 * @param string $string The string to prep.
595 */
596 private static function decode_amp( &$string ) {
597 // Don't bother if there are no entities - saves a lot of processing
598 if ( empty( $string ) || strpos( $string, '&' ) === false ) {
599 return;
600 }
601
602 $translation = array(
603 '&quot;' => '"',
604 '&#034;' => '"',
605 '&#x22;' => '"',
606 '&lt; ' => '< ', // The space preserves the HTML.
607 '&#060; ' => '< ', // The space preserves the HTML.
608 '&gt;' => '>',
609 '&#062;' => '>',
610 '&amp;' => '&',
611 '&#038;' => '&',
612 '&#x26;' => '&',
613 );
614
615 $translation_preg = array(
616 '/&#0*34;/' => '&#034;',
617 '/&#x0*22;/i' => '&#x22;',
618 '/&#0*60;/' => '&#060;',
619 '/&#0*62;/' => '&#062;',
620 '/&#0*38;/' => '&#038;',
621 '/&#x0*26;/i' => '&#x26;',
622 );
623
624 // Remove zero padding on numeric entities
625 $string = preg_replace( array_keys( $translation_preg ), array_values( $translation_preg ), $string );
626
627 // Replace characters according to translation table
628 $string = strtr( $string, $translation );
629 }
630
631 /**
632 * Sanitize the value, and allow some HTML
633 *
634 * @since 2.0
635 *
636 * @param string $value
637 * @param array|string $allowed 'all' for everything included as defaults
638 *
639 * @return string
640 */
641 public static function kses( $value, $allowed = array() ) {
642 $allowed_html = self::allowed_html( $allowed );
643
644 return wp_kses( $value, $allowed_html );
645 }
646
647 /**
648 * The regular kses function strips [button_action] from submit button HTML.
649 *
650 * @since 5.0.13
651 *
652 * @param string $html
653 * @return string
654 */
655 public static function kses_submit_button( $html ) {
656 $included_button_action = false !== strpos( $html, '[button_action]' );
657 $included_back_hook = false !== strpos( $html, '[back_hook]' );
658 $included_draft_hook = false !== strpos( $html, '[draft_hook]' );
659 add_filter( 'safe_style_css', 'FrmAppHelper::allow_visibility_style' );
660 add_filter( 'frm_striphtml_allowed_tags', 'FrmAppHelper::add_allowed_submit_button_tags' );
661 $html = self::kses( $html, 'all' );
662 remove_filter( 'safe_style_css', 'FrmAppHelper::allow_visibility_style' );
663 remove_filter( 'frm_striphtml_allowed_tags', 'FrmAppHelper::add_allowed_submit_button_tags' );
664 if ( $included_button_action ) {
665 if ( false !== strpos( $html, '<input type="submit"' ) ) {
666 $pattern = '/(<input type="submit")([^>]*)(\/>)/';
667 $html = preg_replace( $pattern, '$1$2[button_action] $3', $html, 1 );
668 } else {
669 $pattern = '/(<button)(.*)(class=")(.*)(frm_button_submit)(.*)(")(.*)([^>]+)(>)/';
670 $html = preg_replace( $pattern, '$1$2$3$4$5$6$7 [button_action]$8$9$10', $html, 1 );
671 }
672 }
673 if ( $included_back_hook ) {
674 $html = str_replace( 'class="frm_prev_page"', 'class="frm_prev_page" [back_hook]', $html );
675 }
676 if ( $included_draft_hook ) {
677 $html = str_replace( 'class="frm_save_draft"', 'class="frm_save_draft" [draft_hook]', $html );
678 }
679 return $html;
680 }
681
682 /**
683 * @since 5.0.13
684 *
685 * @param array $allowed_attr
686 * @return array
687 */
688 public static function allow_visibility_style( $allowed_attr ) {
689 $allowed_attr[] = 'visibility';
690 return $allowed_attr;
691 }
692
693 /**
694 * @since 5.0.13
695 *
696 * @param array $allowed_html
697 * @return array
698 */
699 public static function add_allowed_submit_button_tags( $allowed_html ) {
700 $allowed_html['input'] = array(
701 'type' => true,
702 'value' => true,
703 'formnovalidate' => true,
704 'name' => true,
705 'class' => true,
706 );
707 $allowed_html['button']['formnovalidate'] = true;
708 $allowed_html['button']['name'] = true;
709 $allowed_html['img']['style'] = true;
710 return $allowed_html;
711 }
712
713 /**
714 * @since 2.05.03
715 */
716 private static function allowed_html( $allowed ) {
717 $html = self::safe_html();
718 $allowed_html = array();
719 if ( $allowed === 'all' ) {
720 $allowed_html = $html;
721 } elseif ( ! empty( $allowed ) ) {
722 foreach ( (array) $allowed as $a ) {
723 $allowed_html[ $a ] = isset( $html[ $a ] ) ? $html[ $a ] : array();
724 }
725 }
726
727 return apply_filters( 'frm_striphtml_allowed_tags', $allowed_html );
728 }
729
730 /**
731 * @since 2.05.03
732 */
733 private static function safe_html() {
734 $allow_class = array(
735 'class' => true,
736 'id' => true,
737 );
738
739 return array(
740 'a' => array(
741 'class' => true,
742 'href' => true,
743 'id' => true,
744 'rel' => true,
745 'target' => true,
746 'title' => true,
747 'tabindex' => true,
748 ),
749 'abbr' => array(
750 'title' => true,
751 ),
752 'aside' => $allow_class,
753 'b' => array(),
754 'blockquote' => array(
755 'cite' => true,
756 ),
757 'br' => array(),
758 'cite' => array(
759 'title' => true,
760 ),
761 'code' => array(),
762 'defs' => array(),
763 'del' => array(
764 'datetime' => true,
765 'title' => true,
766 ),
767 'dd' => array(),
768 'div' => array(
769 'class' => true,
770 'id' => true,
771 'title' => true,
772 'style' => true,
773 'role' => true,
774 ),
775 'dl' => array(),
776 'dt' => array(),
777 'em' => array(),
778 'h1' => $allow_class,
779 'h2' => $allow_class,
780 'h3' => $allow_class,
781 'h4' => $allow_class,
782 'h5' => $allow_class,
783 'h6' => $allow_class,
784 'i' => array(
785 'class' => true,
786 'id' => true,
787 'icon' => true,
788 'style' => true,
789 ),
790 'img' => array(
791 'alt' => true,
792 'class' => true,
793 'height' => true,
794 'id' => true,
795 'src' => true,
796 'width' => true,
797 ),
798 'li' => $allow_class,
799 'ol' => $allow_class,
800 'p' => $allow_class,
801 'path' => array(
802 'd' => true,
803 'fill' => true,
804 ),
805 'pre' => array(),
806 'q' => array(
807 'cite' => true,
808 'title' => true,
809 ),
810 'rect' => array(
811 'class' => true,
812 'fill' => true,
813 'height' => true,
814 'width' => true,
815 'x' => true,
816 'y' => true,
817 'rx' => true,
818 'stroke' => true,
819 'stroke-opacity' => true,
820 'stroke-width' => true,
821 ),
822 'section' => $allow_class,
823 'span' => array(
824 'class' => true,
825 'id' => true,
826 'title' => true,
827 'style' => true,
828 'aria-hidden' => true,
829 ),
830 'strike' => array(),
831 'strong' => array(),
832 'symbol' => array(
833 'class' => true,
834 'id' => true,
835 'viewbox' => true,
836 ),
837 'svg' => array(
838 'class' => true,
839 'id' => true,
840 'xmlns' => true,
841 'viewbox' => true,
842 'width' => true,
843 'height' => true,
844 'style' => true,
845 'fill' => true,
846 'aria-label' => true,
847 'aria-hidden' => true,
848 ),
849 'use' => array(
850 'href' => true,
851 'xlink:href' => true,
852 ),
853 'ul' => $allow_class,
854 'label' => array(
855 'for' => true,
856 'class' => true,
857 'id' => true,
858 ),
859 'button' => array(
860 'class' => true,
861 'type' => true,
862 ),
863 'legend' => array(
864 'class' => true,
865 ),
866 );
867 }
868
869 /**
870 * Used when switching the action for a bulk action
871 *
872 * @since 2.0
873 */
874 public static function remove_get_action() {
875 if ( ! isset( $_GET ) ) {
876 return;
877 }
878
879 $action_name = isset( $_GET['action'] ) ? 'action' : ( isset( $_GET['action2'] ) ? 'action2' : '' );
880 if ( empty( $action_name ) ) {
881 return;
882 }
883
884 $new_action = self::get_param( $action_name, '', 'get', 'sanitize_text_field' );
885 if ( ! empty( $new_action ) ) {
886 $_SERVER['REQUEST_URI'] = str_replace( '&action=' . $new_action, '', self::get_server_value( 'REQUEST_URI' ) );
887 }
888 }
889
890 /**
891 * Check the WP query for a parameter
892 *
893 * @since 2.0
894 * @return string|array
895 */
896 public static function get_query_var( $value, $param ) {
897 if ( $value != '' ) {
898 return $value;
899 }
900
901 global $wp_query;
902 if ( isset( $wp_query->query_vars[ $param ] ) ) {
903 $value = $wp_query->query_vars[ $param ];
904 }
905
906 return $value;
907 }
908
909 /**
910 * Try to show the SVG if possible. Otherwise, use the font icon.
911 *
912 * @since 4.0.02
913 * @param string $class
914 * @param array $atts
915 */
916 public static function icon_by_class( $class, $atts = array() ) {
917 $echo = ! isset( $atts['echo'] ) || $atts['echo'];
918 if ( isset( $atts['echo'] ) ) {
919 unset( $atts['echo'] );
920 }
921
922 $html_atts = self::array_to_html_params( $atts );
923
924 $icon = trim( str_replace( array( 'frm_icon_font', 'frmfont ' ), '', $class ) );
925 if ( $icon === $class ) {
926 $icon = '<i class="' . esc_attr( $class ) . '"' . $html_atts . '></i>';
927 } else {
928 $class = strpos( $icon, ' ' ) === false ? '' : ' ' . $icon;
929 if ( strpos( $icon, ' ' ) ) {
930 $icon = explode( ' ', $icon );
931 $icon = reset( $icon );
932 }
933 $icon = '<svg class="frmsvg' . esc_attr( $class ) . '"' . $html_atts . '>
934 <use xlink:href="#' . esc_attr( $icon ) . '" />
935 </svg>';
936 }
937
938 if ( $echo ) {
939 echo self::kses_icon( $icon ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
940 } else {
941 return $icon;
942 }
943 }
944
945 /**
946 * Run kses for icons. It needs to add a few filters first in order to preserve some custom style values.
947 *
948 * @since 5.0.13
949 *
950 * @param string $icon
951 * @return string
952 */
953 public static function kses_icon( $icon ) {
954 add_filter( 'safe_style_css', 'FrmAppHelper::allow_vars_in_styles' );
955 add_filter( 'safecss_filter_attr_allow_css', 'FrmAppHelper::allow_style', 10, 2 );
956 add_filter( 'frm_striphtml_allowed_tags', 'FrmAppHelper::add_allowed_icon_tags' );
957 $icon = self::kses( $icon, 'all' );
958 remove_filter( 'safe_style_css', 'FrmAppHelper::allow_vars_in_styles' );
959 remove_filter( 'safecss_filter_attr_allow_css', 'FrmAppHelper::allow_style' );
960 remove_filter( 'frm_striphtml_allowed_tags', 'FrmAppHelper::add_allowed_icon_tags' );
961 return $icon;
962 }
963
964 /**
965 * @since 5.0.13.1
966 *
967 * @param array $allowed_html
968 * @return array
969 */
970 public static function add_allowed_icon_tags( $allowed_html ) {
971 $allowed_html['svg']['data-open'] = true;
972 $allowed_html['svg']['title'] = true;
973 return $allowed_html;
974 }
975
976 /**
977 * @since 5.0.13
978 *
979 * @param array $allowed_attr
980 * @return array
981 */
982 public static function allow_vars_in_styles( $allowed_attr ) {
983 $allowed_attr[] = '--primary-hover';
984 return $allowed_attr;
985 }
986
987 /**
988 * @since 5.0.13
989 *
990 * @param bool $allow_css
991 * @param string $css_string
992 */
993 public static function allow_style( $allow_css, $css_string ) {
994 if ( ! $allow_css && 0 === strpos( $css_string, '--primary-hover:' ) ) {
995 $split = explode( ':', $css_string, 2 );
996 $allow_css = 2 === count( $split ) && self::is_a_valid_color( $split[1] );
997 }
998 return $allow_css;
999 }
1000
1001 /**
1002 * @since 5.0.13
1003 *
1004 * @param string $value
1005 * @return bool
1006 */
1007 private static function is_a_valid_color( $value ) {
1008 $match = 0;
1009 if ( 0 === strpos( $value, 'rgba(' ) ) {
1010 $match = preg_match( '/^rgba\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),\s*(\d*(?:\.\d+)?)\)$/', $value );
1011 } elseif ( 0 === strpos( $value, 'rgb(' ) ) {
1012 $match = preg_match( '/^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/', $value );
1013 } elseif ( 0 === strpos( $value, '#' ) ) {
1014 $match = preg_match( '/^#([a-f0-9]{6}|[a-f0-9]{3})\b$/', $value );
1015 }
1016 return (bool) $match;
1017 }
1018
1019 /**
1020 * Include svg images.
1021 *
1022 * @since 4.0.02
1023 */
1024 public static function include_svg() {
1025 include_once self::plugin_path() . '/images/icons.svg';
1026 }
1027
1028 /**
1029 * Convert an associative array to HTML values.
1030 *
1031 * @since 4.0.02
1032 * @since 5.0.13 added $echo parameter.
1033 *
1034 * @param array $atts
1035 * @param bool $echo
1036 * @return string|void
1037 */
1038 public static function array_to_html_params( $atts, $echo = false ) {
1039 $callback = function() use ( $atts ) {
1040 if ( $atts ) {
1041 foreach ( $atts as $key => $value ) {
1042 echo ' ' . esc_attr( $key ) . '="' . esc_attr( $value ) . '"';
1043 }
1044 }
1045 };
1046 return self::clip( $callback, $echo );
1047 }
1048
1049 /**
1050 * Call an echo function and either echo it or return the result as a string.
1051 *
1052 * @since 5.0.13
1053 *
1054 * @param Closure $echo_function
1055 * @param bool $echo
1056 * @return string|void
1057 */
1058 public static function clip( $echo_function, $echo = false ) {
1059 if ( ! $echo ) {
1060 ob_start();
1061 }
1062
1063 $echo_function();
1064
1065 if ( ! $echo ) {
1066 $return = ob_get_contents();
1067 ob_end_clean();
1068 return $return;
1069 }
1070 }
1071
1072 /**
1073 * @since 3.0
1074 */
1075 public static function get_admin_header( $atts ) {
1076 $has_nav = ( isset( $atts['form'] ) && ! empty( $atts['form'] ) && ( ! isset( $atts['is_template'] ) || ! $atts['is_template'] ) );
1077 if ( ! isset( $atts['close'] ) || empty( $atts['close'] ) ) {
1078 $atts['close'] = admin_url( 'admin.php?page=formidable' );
1079 }
1080 if ( ! isset( $atts['import_link'] ) ) {
1081 $atts['import_link'] = false;
1082 }
1083
1084 include( self::plugin_path() . '/classes/views/shared/admin-header.php' );
1085 }
1086
1087 /**
1088 * Render a button for a new item (Form, Application, etc).
1089 *
1090 * @since 3.0
1091 * @param array $atts {
1092 * @type array $link_hook Custom link hook, calls do_action and exits early.
1093 * @type string $new_link Href value, default #.
1094 * @type string $class Custom class names, space separated.
1095 * @type string $button_text Button text. Default "Add New".
1096 * }
1097 * @return void
1098 */
1099 public static function add_new_item_link( $atts ) {
1100 if ( isset( $atts['link_hook'] ) ) {
1101 do_action( $atts['link_hook']['hook'], $atts['link_hook']['param'] );
1102 return;
1103 }
1104
1105 $href = ! empty( $atts['new_link'] ) ? esc_url( $atts['new_link'] ) : '#';
1106 $class = 'button button-primary frm-button-primary frm-with-plus';
1107
1108 if ( ! empty( $atts['trigger_new_form_modal'] ) ) {
1109 $class .= ' frm-trigger-new-form-modal';
1110 }
1111
1112 if ( ! empty( $atts['class'] ) ) {
1113 $class .= ' ' . $atts['class'];
1114 }
1115
1116 $button_text = ! empty( $atts['button_text'] ) ? $atts['button_text'] : __( 'Add New', 'formidable' );
1117
1118 require self::plugin_path() . '/classes/views/shared/add-button.php';
1119 }
1120
1121 /**
1122 * @since 3.06
1123 */
1124 public static function show_search_box( $atts ) {
1125 $defaults = array(
1126 'placeholder' => '',
1127 'tosearch' => '',
1128 'text' => __( 'Search', 'formidable' ),
1129 'input_id' => '',
1130 );
1131 $atts = array_merge( $defaults, $atts );
1132
1133 if ( $atts['input_id'] === 'template' && empty( $atts['tosearch'] ) ) {
1134 $atts['tosearch'] = 'frm-card';
1135 }
1136
1137 $class = 'frm-search-input';
1138 if ( ! empty( $atts['tosearch'] ) ) {
1139 $class .= ' frm-auto-search';
1140 }
1141
1142 $input_id = $atts['input_id'] . '-search-input';
1143
1144 ?>
1145 <p class="frm-search">
1146 <label class="screen-reader-text" for="<?php echo esc_attr( $input_id ); ?>">
1147 <?php echo esc_html( $atts['text'] ); ?>:
1148 </label>
1149 <span class="frmfont frm_search_icon"></span>
1150 <input type="search" id="<?php echo esc_attr( $input_id ); ?>" name="s"
1151 value="<?php _admin_search_query(); ?>" placeholder="<?php echo esc_attr( $atts['placeholder'] ); ?>"
1152 class="<?php echo esc_attr( $class ); ?>" data-tosearch="<?php echo esc_attr( $atts['tosearch'] ); ?>"
1153 <?php if ( ! empty( $atts['tosearch'] ) ) { ?>
1154 autocomplete="off"
1155 <?php } ?>
1156 />
1157 <?php
1158 if ( empty( $atts['tosearch'] ) ) {
1159 submit_button( $atts['text'], 'button-secondary', '', false, array( 'id' => 'search-submit' ) );
1160 }
1161 ?>
1162 </p>
1163 <?php
1164 }
1165
1166 /**
1167 * @param string $type
1168 */
1169 public static function trigger_hook_load( $type, $object = null ) {
1170 // Only load the form hooks once.
1171 $hooks_loaded = apply_filters( 'frm_' . $type . '_hooks_loaded', false, $object );
1172 if ( ! $hooks_loaded ) {
1173 do_action( 'frm_load_' . $type . '_hooks' );
1174 }
1175 }
1176
1177 /**
1178 * Save all front-end js scripts into a single file
1179 *
1180 * @since 3.0
1181 */
1182 public static function save_combined_js() {
1183 $file_atts = apply_filters(
1184 'frm_js_location',
1185 array(
1186 'file_name' => 'frm.min.js',
1187 'new_file_path' => self::plugin_path() . '/js',
1188 )
1189 );
1190 $new_file = new FrmCreateFile( $file_atts );
1191
1192 $files = array(
1193 self::plugin_path() . '/js/formidable.min.js',
1194 );
1195 $files = apply_filters( 'frm_combined_js_files', $files );
1196 $new_file->combine_files( $files );
1197 }
1198
1199 /**
1200 * Check a value from a shortcode to see if true or false.
1201 * True when value is 1, true, 'true', 'yes'
1202 *
1203 * @since 1.07.10
1204 *
1205 * @param string $value The value to compare
1206 *
1207 * @return boolean True or False
1208 */
1209 public static function is_true( $value ) {
1210 return ( true === $value || 1 == $value || 'true' == $value || 'yes' == $value );
1211 }
1212
1213 /**
1214 * Gets all post from a specific post type.
1215 * This gets the entire WP_Post object so it can require a lot of memory. When only id and title are needed, consider using FrmAppHelper::get_post_ids_and_titles instead.
1216 *
1217 * @since 4.10.01 Add `$post_type` argument.
1218 *
1219 * @param string $post_type Post type to query. Default is `page`.
1220 * @return WP_Post[]
1221 */
1222 public static function get_pages( $post_type = 'page' ) {
1223 $query = array(
1224 'post_type' => $post_type,
1225 'post_status' => array( 'publish', 'private' ),
1226 'numberposts' => - 1,
1227 'orderby' => 'title',
1228 'order' => 'ASC',
1229 );
1230
1231 return get_posts( $query );
1232 }
1233
1234 /**
1235 * Gets post ids and titles for a specific post type.
1236 *
1237 * @since 5.0.09
1238 *
1239 * @param string $post_type Post type to query. Default is `page`.
1240 * @return array
1241 */
1242 public static function get_post_ids_and_titles( $post_type = 'page' ) {
1243 return FrmDb::get_results(
1244 'posts',
1245 array(
1246 'post_type' => $post_type,
1247 'post_status' => array( 'publish', 'private' ),
1248 ),
1249 'ID, post_title',
1250 array(
1251 'order_by' => 'post_title ASC',
1252 )
1253 );
1254 }
1255
1256 /**
1257 * Renders an autocomplete page selection or a regular dropdown depending on
1258 * the total page count
1259 *
1260 * @since 4.03.06
1261 * @since 4.10.01 Added `post_type` and `autocomplete_placeholder` to the arguments array.
1262 *
1263 * @param array $args Selection arguments.
1264 */
1265 public static function maybe_autocomplete_pages_options( $args ) {
1266 $args = self::preformat_selection_args( $args );
1267
1268 $pages_count = wp_count_posts( $args['post_type'] );
1269
1270 if ( $pages_count->publish <= 50 ) {
1271 self::wp_pages_dropdown( $args );
1272 return;
1273 }
1274
1275 wp_enqueue_script( 'jquery-ui-autocomplete' );
1276
1277 $selected = self::get_post_param( $args['field_name'], $args['page_id'], 'absint' );
1278 $title = '';
1279
1280 if ( $selected ) {
1281 $title = get_the_title( $selected );
1282 }
1283
1284 ?>
1285 <input type="text" class="frm-page-search"
1286 data-post-type="<?php echo esc_attr( $args['post_type'] ); ?>"
1287 placeholder="<?php echo esc_attr( $args['autocomplete_placeholder'] ); ?>"
1288 value="<?php echo esc_attr( $title ); ?>" />
1289 <input type="hidden" name="<?php echo esc_attr( $args['field_name'] ); ?>"
1290 class="frm_autocomplete_value_input"
1291 value="<?php echo esc_attr( $selected ); ?>" />
1292 <?php
1293 }
1294
1295 /**
1296 * @param array $args
1297 * @param string $page_id Deprecated.
1298 * @param boolean $truncate Deprecated.
1299 */
1300 public static function wp_pages_dropdown( $args = array(), $page_id = '', $truncate = false ) {
1301 self::prep_page_dropdown_params( $page_id, $truncate, $args );
1302
1303 $pages = self::get_post_ids_and_titles( $args['post_type'] );
1304 $selected = self::get_post_param( $args['field_name'], $args['page_id'], 'absint' );
1305
1306 ?>
1307 <select name="<?php echo esc_attr( $args['field_name'] ); ?>" id="<?php echo esc_attr( $args['field_name'] ); ?>" class="frm-pages-dropdown">
1308 <option value=""><?php echo esc_html( $args['placeholder'] ); ?></option>
1309 <?php foreach ( $pages as $page ) { ?>
1310 <option value="<?php echo esc_attr( $page->ID ); ?>" <?php selected( $selected, $page->ID ); ?>>
1311 <?php echo esc_html( $args['truncate'] ? self::truncate( $page->post_title, $args['truncate'] ) : $page->post_title ); ?>
1312 </option>
1313 <?php } ?>
1314 </select>
1315 <?php
1316 }
1317
1318 /**
1319 * Fill in missing parameters passed to wp_pages_dropdown().
1320 * This is for reverse compatibility with switching 3 params to 1.
1321 *
1322 * @since 4.03.06
1323 */
1324 private static function prep_page_dropdown_params( $page_id, $truncate, &$args ) {
1325 if ( ! is_array( $args ) ) {
1326 $args = array(
1327 'field_name' => $args,
1328 'page_id' => $page_id,
1329 'truncate' => $truncate,
1330 );
1331 }
1332
1333 $args = self::preformat_selection_args( $args );
1334 }
1335
1336 /**
1337 * Filter to format args for page dropdown or autocomplete
1338 *
1339 * @since 4.03.06
1340 * @since 4.10.01 Added `post_type` and `autocomplete_placeholder` to the arguments array.
1341 */
1342 private static function preformat_selection_args( $args ) {
1343 $defaults = array(
1344 'truncate' => false,
1345 'placeholder' => ' ',
1346 'field_name' => '',
1347 'page_id' => '',
1348 'post_type' => 'page',
1349 'autocomplete_placeholder' => __( 'Select a Page', 'formidable' ),
1350 );
1351
1352 return array_merge( $defaults, $args );
1353 }
1354
1355 public static function post_edit_link( $post_id ) {
1356 $post = get_post( $post_id );
1357 if ( $post ) {
1358 $post_url = admin_url( 'post.php?post=' . $post_id . '&action=edit' );
1359 $post_url = self::maybe_full_screen_link( $post_url );
1360
1361 return '<a href="' . esc_url( $post_url ) . '">' . self::truncate( $post->post_title, 50 ) . '</a>';
1362 }
1363
1364 return '';
1365 }
1366
1367 /**
1368 * Hide the WordPress menus on some pages.
1369 *
1370 * @since 4.0
1371 *
1372 * @return bool
1373 */
1374 public static function is_full_screen() {
1375 return self::is_form_builder_page() ||
1376 self::is_admin_page( 'formidable-styles' ) ||
1377 self::is_admin_page( 'formidable-styles2' ) ||
1378 self::simple_get( 'frm-full', 'absint' ) ||
1379 self::is_view_builder_page();
1380 }
1381
1382 /**
1383 * @since 4.0
1384 */
1385 public static function maybe_full_screen_link( $link ) {
1386 $is_full = self::simple_get( 'frm-full', 'absint' );
1387 if ( $is_full && ! empty( $link ) && $link !== '#' ) {
1388 $link .= '&frm-full=1';
1389 }
1390 return $link;
1391 }
1392
1393 /**
1394 * @param string $field_name
1395 * @param string|array $capability
1396 * @param string $multiple 'single' and 'multiple'
1397 */
1398 public static function wp_roles_dropdown( $field_name, $capability, $multiple = 'single' ) {
1399 ?>
1400 <select name="<?php echo esc_attr( $field_name ); ?>" id="<?php echo esc_attr( $field_name ); ?>"
1401 <?php echo ( 'multiple' === $multiple ) ? 'multiple="multiple"' : ''; ?>
1402 class="frm_multiselect">
1403 <?php self::roles_options( $capability ); ?>
1404 </select>
1405 <?php
1406 }
1407
1408 /**
1409 * @since 4.07
1410 * @param array|string $selected
1411 * @param string $current
1412 */
1413 private static function selected( $selected, $current ) {
1414 if ( is_callable( 'FrmProAppHelper::selected' ) ) {
1415 FrmProAppHelper::selected( $selected, $current );
1416 } else {
1417 selected( in_array( $current, (array) $selected, true ) );
1418 }
1419 }
1420
1421 /**
1422 * @param string|array $capability
1423 */
1424 public static function roles_options( $capability ) {
1425 global $frm_vars;
1426 if ( isset( $frm_vars['editable_roles'] ) ) {
1427 $editable_roles = $frm_vars['editable_roles'];
1428 } else {
1429 $editable_roles = get_editable_roles();
1430 $frm_vars['editable_roles'] = $editable_roles;
1431 }
1432
1433 foreach ( $editable_roles as $role => $details ) {
1434 $name = translate_user_role( $details['name'] );
1435 ?>
1436 <option value="<?php echo esc_attr( $role ); ?>" <?php self::selected( $capability, $role ); ?>><?php echo esc_attr( $name ); ?> </option>
1437 <?php
1438 unset( $role, $details );
1439 }
1440 }
1441
1442 /**
1443 * Gets the list of capabilities.
1444 *
1445 * @since 5.0 Parameter `$type` supports `pro_only` value.
1446 *
1447 * @param string $type Supports `auto`, `pro`, or `pro_only`.
1448 * @return array
1449 */
1450 public static function frm_capabilities( $type = 'auto' ) {
1451 $cap = array(
1452 'frm_view_forms' => __( 'View Forms', 'formidable' ),
1453 'frm_edit_forms' => __( 'Add and Edit Forms', 'formidable' ),
1454 'frm_delete_forms' => __( 'Delete Forms', 'formidable' ),
1455 'frm_change_settings' => __( 'Access this Settings Page', 'formidable' ),
1456 'frm_view_entries' => __( 'View Entries from Admin Area', 'formidable' ),
1457 'frm_delete_entries' => __( 'Delete Entries from Admin Area', 'formidable' ),
1458 );
1459
1460 if ( ! self::pro_is_installed() && 'pro' !== $type && 'pro_only' !== $type ) {
1461 return $cap;
1462 }
1463
1464 $pro_cap = array(
1465 'frm_create_entries' => __( 'Add Entries from Admin Area', 'formidable' ),
1466 'frm_edit_entries' => __( 'Edit Entries from Admin Area', 'formidable' ),
1467 'frm_view_reports' => __( 'View Reports', 'formidable' ),
1468 'frm_edit_displays' => __( 'Add/Edit Views', 'formidable' ),
1469 );
1470
1471 if ( 'pro_only' === $type ) {
1472 return $pro_cap;
1473 }
1474
1475 return $cap + $pro_cap;
1476 }
1477
1478 /**
1479 * Call the WordPress current_user_can but also validate empty strings as true for any logged in user
1480 *
1481 * @since 4.06.03
1482 *
1483 * @param string $role
1484 *
1485 * @return bool
1486 */
1487 public static function current_user_can( $role ) {
1488 if ( $role === '-1' ) {
1489 return false;
1490 }
1491
1492 if ( $role === 'loggedout' ) {
1493 return ! is_user_logged_in();
1494 }
1495
1496 if ( $role === 'loggedin' || ! $role ) {
1497 return is_user_logged_in();
1498 }
1499
1500 if ( $role == 1 ) {
1501 $role = 'administrator';
1502 }
1503
1504 if ( ! is_user_logged_in() ) {
1505 return false;
1506 }
1507
1508 return current_user_can( $role );
1509 }
1510
1511 /**
1512 * @param string|array $needed_role
1513 * @return bool
1514 */
1515 public static function user_has_permission( $needed_role ) {
1516 if ( is_array( $needed_role ) ) {
1517 foreach ( $needed_role as $role ) {
1518 if ( self::current_user_can( $role ) ) {
1519 return true;
1520 }
1521 }
1522
1523 return false;
1524 }
1525
1526 $can = self::current_user_can( $needed_role );
1527
1528 if ( $can || in_array( $needed_role, array( '-1', 'loggedout' ) ) ) {
1529 return $can;
1530 }
1531
1532 $roles = array( 'administrator', 'editor', 'author', 'contributor', 'subscriber' );
1533 foreach ( $roles as $role ) {
1534 if ( current_user_can( $role ) ) {
1535 return true;
1536 }
1537 if ( $role == $needed_role ) {
1538 break;
1539 }
1540 }
1541
1542 return false;
1543 }
1544
1545 /**
1546 * Make sure administrators can see Formidable menu
1547 *
1548 * @since 2.0
1549 */
1550 public static function maybe_add_permissions() {
1551 self::force_capability( 'frm_view_entries' );
1552
1553 if ( ! current_user_can( 'administrator' ) || current_user_can( 'frm_view_forms' ) ) {
1554 return;
1555 }
1556
1557 $user_id = get_current_user_id();
1558 $user = new WP_User( $user_id );
1559 $frm_roles = self::frm_capabilities();
1560 foreach ( $frm_roles as $frm_role => $frm_role_description ) {
1561 $user->add_cap( $frm_role );
1562 unset( $frm_role, $frm_role_description );
1563 }
1564 }
1565
1566 /**
1567 * Make sure admins have permission to see the menu items
1568 *
1569 * @since 2.0.6
1570 */
1571 public static function force_capability( $cap = 'frm_change_settings' ) {
1572 if ( current_user_can( 'administrator' ) && ! current_user_can( $cap ) ) {
1573 $role = get_role( 'administrator' );
1574 $frm_roles = self::frm_capabilities();
1575 foreach ( $frm_roles as $frm_role => $frm_role_description ) {
1576 $role->add_cap( $frm_role );
1577 }
1578 }
1579 }
1580
1581 /**
1582 * Check if the user has permision for action.
1583 * Return permission message and stop the action if no permission
1584 *
1585 * @since 2.0
1586 *
1587 * @param string $permission
1588 */
1589 public static function permission_check( $permission, $show_message = 'show' ) {
1590 $permission_error = self::permission_nonce_error( $permission );
1591 if ( $permission_error !== false ) {
1592 if ( 'hide' == $show_message ) {
1593 $permission_error = '';
1594 }
1595 wp_die( esc_html( $permission_error ) );
1596 }
1597 }
1598
1599 /**
1600 * Check user permission and nonce
1601 *
1602 * @since 2.0
1603 *
1604 * @param string $permission
1605 *
1606 * @return false|string The permission message or false if allowed
1607 */
1608 public static function permission_nonce_error( $permission, $nonce_name = '', $nonce = '' ) {
1609 if ( ! empty( $permission ) && ! current_user_can( $permission ) && ! current_user_can( 'administrator' ) ) {
1610 $frm_settings = self::get_settings();
1611
1612 return $frm_settings->admin_permission;
1613 }
1614
1615 $error = false;
1616 if ( empty( $nonce_name ) ) {
1617 return $error;
1618 }
1619
1620 $nonce_value = ( $_REQUEST && isset( $_REQUEST[ $nonce_name ] ) ) ? sanitize_text_field( wp_unslash( $_REQUEST[ $nonce_name ] ) ) : '';
1621 if ( $_REQUEST && ( ! isset( $_REQUEST[ $nonce_name ] ) || ! wp_verify_nonce( $nonce_value, $nonce ) ) ) {
1622 $frm_settings = self::get_settings();
1623 $error = $frm_settings->admin_permission;
1624 }
1625
1626 return $error;
1627 }
1628
1629 public static function checked( $values, $current ) {
1630 if ( self::check_selected( $values, $current ) ) {
1631 echo ' checked="checked"';
1632 }
1633 }
1634
1635 public static function check_selected( $values, $current ) {
1636 $values = self::recursive_function_map( $values, 'trim' );
1637 $values = self::recursive_function_map( $values, 'htmlspecialchars_decode' );
1638 $current = htmlspecialchars_decode( trim( $current ) );
1639
1640 return ( is_array( $values ) && in_array( $current, $values ) ) || ( ! is_array( $values ) && $values == $current );
1641 }
1642
1643 public static function recursive_function_map( $value, $function ) {
1644 if ( is_array( $value ) ) {
1645 $original_function = $function;
1646 if ( count( $value ) ) {
1647 $function = explode( ', ', FrmDb::prepare_array_values( $value, $function ) );
1648 } else {
1649 $function = array( $function );
1650 }
1651 if ( ! self::is_assoc( $value ) ) {
1652 $value = array_map( array( 'FrmAppHelper', 'recursive_function_map' ), $value, $function );
1653 } else {
1654 foreach ( $value as $k => $v ) {
1655 if ( ! is_array( $v ) ) {
1656 $value[ $k ] = call_user_func( $original_function, $v );
1657 }
1658 }
1659 }
1660 } else {
1661 $value = call_user_func( $function, $value );
1662 }
1663
1664 return $value;
1665 }
1666
1667 public static function is_assoc( $array ) {
1668 return (bool) count( array_filter( array_keys( $array ), 'is_string' ) );
1669 }
1670
1671 /**
1672 * Flatten a multi-dimensional array
1673 */
1674 public static function array_flatten( $array, $keys = 'keep' ) {
1675 $return = array();
1676 foreach ( $array as $key => $value ) {
1677 if ( is_array( $value ) ) {
1678 $return = array_merge( $return, self::array_flatten( $value, $keys ) );
1679 } else {
1680 if ( $keys == 'keep' ) {
1681 $return[ $key ] = $value;
1682 } else {
1683 $return[] = $value;
1684 }
1685 }
1686 }
1687
1688 return $return;
1689 }
1690
1691 public static function esc_textarea( $text, $is_rich_text = false ) {
1692 $safe_text = str_replace( '&quot;', '"', $text );
1693 if ( ! $is_rich_text ) {
1694 $safe_text = htmlspecialchars( $safe_text, ENT_NOQUOTES );
1695 }
1696 $safe_text = str_replace( '&amp; ', '& ', $safe_text );
1697
1698 return apply_filters( 'esc_textarea', $safe_text, $text );
1699 }
1700
1701 /**
1702 * Add auto paragraphs to text areas
1703 *
1704 * @since 2.0
1705 */
1706 public static function use_wpautop( $content ) {
1707 if ( apply_filters( 'frm_use_wpautop', true ) && ! is_array( $content ) ) {
1708 $content = wpautop( str_replace( '<br>', '<br />', $content ) );
1709 }
1710
1711 return $content;
1712 }
1713
1714 public static function replace_quotes( $val ) {
1715 // Replace double quotes.
1716 $val = str_replace( array( '&#8220;', '&#8221;', '&#8243;' ), '"', $val );
1717
1718 // Replace single quotes.
1719 $val = str_replace( array( '&#8216;', '&#8217;', '&#8242;', '&prime;', '&rsquo;', '&lsquo;' ), "'", $val );
1720
1721 return $val;
1722 }
1723
1724 /**
1725 * @param string $handle
1726 */
1727 public static function script_version( $handle, $default = 0 ) {
1728 global $wp_scripts;
1729 if ( ! $wp_scripts ) {
1730 return $default;
1731 }
1732
1733 $ver = $default;
1734 if ( ! isset( $wp_scripts->registered[ $handle ] ) ) {
1735 return $ver;
1736 }
1737
1738 $query = $wp_scripts->registered[ $handle ];
1739 if ( is_object( $query ) && ! empty( $query->ver ) ) {
1740 $ver = $query->ver;
1741 }
1742
1743 return $ver;
1744 }
1745
1746 /**
1747 * @since 5.0.13 added $echo param.
1748 *
1749 * @param string $url
1750 * @param bool $echo
1751 * @return string|void
1752 */
1753 public static function js_redirect( $url, $echo = false ) {
1754 $callback = function() use ( $url ) {
1755 echo '<script type="text/javascript">window.location="' . esc_url_raw( $url ) . '"</script>';
1756 };
1757 return self::clip( $callback, $echo );
1758 }
1759
1760 public static function get_user_id_param( $user_id ) {
1761 if ( ! $user_id || is_numeric( $user_id ) ) {
1762 return $user_id;
1763 }
1764
1765 $user_id = sanitize_text_field( $user_id );
1766 if ( $user_id == 'current' ) {
1767 $user_id = get_current_user_id();
1768 } else {
1769 if ( is_email( $user_id ) ) {
1770 $user = get_user_by( 'email', $user_id );
1771 } else {
1772 $user = get_user_by( 'login', $user_id );
1773 }
1774
1775 if ( $user ) {
1776 $user_id = $user->ID;
1777 }
1778 unset( $user );
1779 }
1780
1781 return $user_id;
1782 }
1783
1784 public static function get_file_contents( $filename, $atts = array() ) {
1785 if ( ! is_file( $filename ) ) {
1786 return false;
1787 }
1788
1789 extract( $atts ); // phpcs:ignore WordPress.PHP.DontExtract
1790 ob_start();
1791 include( $filename );
1792 $contents = ob_get_contents();
1793 ob_end_clean();
1794
1795 return $contents;
1796 }
1797
1798 /**
1799 * @param string $name
1800 * @param string $table_name
1801 * @param string $column
1802 * @param int $id
1803 * @param int $num_chars
1804 */
1805 public static function get_unique_key( $name, $table_name, $column, $id = 0, $num_chars = 5 ) {
1806 $key = '';
1807 if ( $name ) {
1808 $key = sanitize_key( $name );
1809 $key = self::maybe_clear_long_key( $key, $column );
1810 }
1811
1812 if ( ! $key ) {
1813 $key = self::generate_new_key( $num_chars );
1814 }
1815
1816 $key = self::prevent_numeric_and_reserved_keys( $key );
1817
1818 $similar_keys = FrmDb::get_col(
1819 $table_name,
1820 array(
1821 $column . ' like%' => $key,
1822 'ID !' => $id,
1823 ),
1824 $column
1825 );
1826
1827 // Create a unique field id if it has already been used.
1828 if ( in_array( $key, $similar_keys, true ) ) {
1829 $key = self::maybe_truncate_key_before_appending( $column, $key );
1830
1831 /**
1832 * Allow for a custom separator between the attempted key and the generated suffix.
1833 *
1834 * @since 5.2.03
1835 *
1836 * @param string $separator. Default empty.
1837 * @param string $key the key without the added suffix.
1838 */
1839 $separator = apply_filters( 'frm_unique_' . $column . '_separator', '', $key );
1840
1841 $suffix = 2;
1842 do {
1843 $key_check = $key . $separator . $suffix;
1844 ++$suffix;
1845 } while ( in_array( $key_check, $similar_keys, true ) );
1846
1847 $key = $key_check;
1848 }
1849
1850 return $key;
1851 }
1852
1853 /**
1854 * Avoid trying to append to a really long key,
1855 * The database limit is 100 for form and field keys so we want to avoid getting too close.
1856 *
1857 * @param string $column
1858 * @param string $key
1859 * @return string
1860 */
1861 private static function maybe_truncate_key_before_appending( $column, $key ) {
1862 if ( in_array( $column, array( 'form_key', 'field_key' ), true ) ) {
1863 $max_key_length_before_truncating = 60;
1864 if ( strlen( $key ) > $max_key_length_before_truncating ) {
1865 $key = substr( $key, 0, $max_key_length_before_truncating );
1866 if ( is_numeric( $key ) ) {
1867 $key .= 'a';
1868 }
1869 }
1870 }
1871 return $key;
1872 }
1873
1874 /**
1875 * Possibly reset a key to avoid conflicts with column size limits.
1876 *
1877 * @param string $key
1878 * @param string $column
1879 * @return string either the original key value, or an empty string if the key was too long.
1880 */
1881 private static function maybe_clear_long_key( $key, $column ) {
1882 if ( 'field_key' === $column && strlen( $key ) >= 70 ) {
1883 $key = '';
1884 }
1885 return $key;
1886 }
1887
1888 /**
1889 * @param int $num_chars
1890 * @return string
1891 */
1892 private static function generate_new_key( $num_chars ) {
1893 $max_slug_value = pow( 36, $num_chars );
1894 $min_slug_value = 37; // we want to have at least 2 characters in the slug
1895 return base_convert( rand( $min_slug_value, $max_slug_value ), 10, 36 );
1896 }
1897
1898 /**
1899 * @param string $key
1900 * @return string
1901 */
1902 private static function prevent_numeric_and_reserved_keys( $key ) {
1903 if ( is_numeric( $key ) ) {
1904 $key .= 'a';
1905 } else {
1906 $not_allowed = array(
1907 'id',
1908 'key',
1909 'created-at',
1910 'detaillink',
1911 'editlink',
1912 'siteurl',
1913 'evenodd',
1914 );
1915 if ( in_array( $key, $not_allowed, true ) ) {
1916 $key .= 'a';
1917 }
1918 }
1919 return $key;
1920 }
1921
1922 /**
1923 * Editing a Form or Entry
1924 *
1925 * @param string $table
1926 *
1927 * @return bool|array
1928 */
1929 public static function setup_edit_vars( $record, $table, $fields = '', $default = false, $post_values = array(), $args = array() ) {
1930 if ( ! $record ) {
1931 return false;
1932 }
1933
1934 if ( empty( $post_values ) ) {
1935 $post_values = wp_unslash( $_POST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
1936 }
1937
1938 $values = array(
1939 'id' => $record->id,
1940 'fields' => array(),
1941 );
1942
1943 foreach ( array( 'name', 'description' ) as $var ) {
1944 $default_val = isset( $record->{$var} ) ? $record->{$var} : '';
1945 $values[ $var ] = self::get_param( $var, $default_val, 'get', 'wp_kses_post' );
1946 unset( $var, $default_val );
1947 }
1948
1949 $values['description'] = self::use_wpautop( $values['description'] );
1950
1951 self::fill_form_opts( $record, $table, $post_values, $values );
1952
1953 self::prepare_field_arrays( $fields, $record, $values, array_merge( $args, compact( 'default', 'post_values' ) ) );
1954
1955 if ( $table === 'entries' ) {
1956 $values = FrmEntriesHelper::setup_edit_vars( $values, $record );
1957 } elseif ( $table === 'forms' ) {
1958 $values = FrmFormsHelper::setup_edit_vars( $values, $record, $post_values );
1959 }
1960
1961 return $values;
1962 }
1963
1964 private static function prepare_field_arrays( $fields, $record, array &$values, $args ) {
1965 if ( ! empty( $fields ) ) {
1966 foreach ( (array) $fields as $field ) {
1967 if ( ! self::is_admin_page() ) {
1968 // Don't prep default values on the form settings page.
1969 $field->default_value = apply_filters( 'frm_get_default_value', $field->default_value, $field, true );
1970 }
1971 $args['parent_form_id'] = isset( $args['parent_form_id'] ) ? $args['parent_form_id'] : $field->form_id;
1972 self::fill_field_defaults( $field, $record, $values, $args );
1973 }
1974 }
1975 }
1976
1977 private static function fill_field_defaults( $field, $record, array &$values, $args ) {
1978 $post_values = $args['post_values'];
1979
1980 if ( $args['default'] ) {
1981 $meta_value = $field->default_value;
1982 } else {
1983 if ( $record->post_id && self::pro_is_installed() && isset( $field->field_options['post_field'] ) && $field->field_options['post_field'] ) {
1984 if ( ! isset( $field->field_options['custom_field'] ) ) {
1985 $field->field_options['custom_field'] = '';
1986 }
1987 $meta_value = FrmProEntryMetaHelper::get_post_value(
1988 $record->post_id,
1989 $field->field_options['post_field'],
1990 $field->field_options['custom_field'],
1991 array(
1992 'truncate' => false,
1993 'type' => $field->type,
1994 'form_id' => $field->form_id,
1995 'field' => $field,
1996 )
1997 );
1998 } else {
1999 $meta_value = FrmEntryMeta::get_meta_value( $record, $field->id );
2000 }
2001 }
2002
2003 $field_type = isset( $post_values['field_options'][ 'type_' . $field->id ] ) ? $post_values['field_options'][ 'type_' . $field->id ] : $field->type;
2004 if ( isset( $post_values['item_meta'][ $field->id ] ) ) {
2005 $new_value = $post_values['item_meta'][ $field->id ];
2006 self::unserialize_or_decode( $new_value );
2007 } else {
2008 $new_value = $meta_value;
2009 }
2010
2011 $field_array = self::start_field_array( $field );
2012 $field_array['value'] = $new_value;
2013 $field_array['type'] = apply_filters( 'frm_field_type', $field_type, $field, $new_value );
2014 $field_array['parent_form_id'] = $args['parent_form_id'];
2015
2016 $args['field_type'] = $field_type;
2017
2018 FrmFieldsHelper::prepare_edit_front_field( $field_array, $field, $values['id'], $args );
2019
2020 if ( ! isset( $field_array['unique'] ) || ! $field_array['unique'] ) {
2021 $field_array['unique_msg'] = '';
2022 }
2023
2024 $field_array = array_merge( (array) $field->field_options, $field_array );
2025
2026 $values['fields'][ $field->id ] = $field_array;
2027 }
2028
2029 /**
2030 * @since 3.0
2031 *
2032 * @param object $field
2033 *
2034 * @return array
2035 */
2036 public static function start_field_array( $field ) {
2037 return array(
2038 'id' => $field->id,
2039 'default_value' => $field->default_value,
2040 'name' => $field->name,
2041 'description' => $field->description,
2042 'options' => $field->options,
2043 'required' => $field->required,
2044 'field_key' => $field->field_key,
2045 'field_order' => $field->field_order,
2046 'form_id' => $field->form_id,
2047 );
2048 }
2049
2050 /**
2051 * @param string $table
2052 */
2053 private static function fill_form_opts( $record, $table, $post_values, array &$values ) {
2054 if ( $table == 'entries' ) {
2055 $form = $record->form_id;
2056 FrmForm::maybe_get_form( $form );
2057 } else {
2058 $form = $record;
2059 }
2060
2061 if ( ! $form ) {
2062 return;
2063 }
2064
2065 $values['form_name'] = isset( $record->form_id ) ? $form->name : '';
2066 $values['parent_form_id'] = isset( $record->form_id ) ? $form->parent_form_id : 0;
2067
2068 if ( ! is_array( $form->options ) ) {
2069 return;
2070 }
2071
2072 foreach ( $form->options as $opt => $value ) {
2073 if ( isset( $post_values[ $opt ] ) ) {
2074 $values[ $opt ] = $post_values[ $opt ];
2075 self::unserialize_or_decode( $values[ $opt ] );
2076 } else {
2077 $values[ $opt ] = $value;
2078 }
2079 }
2080
2081 self::fill_form_defaults( $post_values, $values );
2082 }
2083
2084 /**
2085 * Set to POST value or default
2086 */
2087 private static function fill_form_defaults( $post_values, array &$values ) {
2088 $form_defaults = FrmFormsHelper::get_default_opts();
2089
2090 foreach ( $form_defaults as $opt => $default ) {
2091 if ( ! isset( $values[ $opt ] ) || $values[ $opt ] == '' ) {
2092 $values[ $opt ] = ( $post_values && isset( $post_values['options'][ $opt ] ) ) ? $post_values['options'][ $opt ] : $default;
2093 }
2094
2095 unset( $opt, $default );
2096 }
2097
2098 if ( ! isset( $values['custom_style'] ) ) {
2099 $values['custom_style'] = self::custom_style_value( $post_values );
2100 }
2101
2102 foreach ( array( 'before', 'after', 'submit' ) as $h ) {
2103 if ( ! isset( $values[ $h . '_html' ] ) ) {
2104 $values[ $h . '_html' ] = ( isset( $post_values['options'][ $h . '_html' ] ) ? $post_values['options'][ $h . '_html' ] : FrmFormsHelper::get_default_html( $h ) );
2105 }
2106 unset( $h );
2107 }
2108 }
2109
2110 /**
2111 * @since 2.2.10
2112 *
2113 * @param array $post_values
2114 *
2115 * @return boolean|int
2116 */
2117 public static function custom_style_value( $post_values ) {
2118 if ( ! empty( $post_values ) && isset( $post_values['options']['custom_style'] ) ) {
2119 $custom_style = absint( $post_values['options']['custom_style'] );
2120 } else {
2121 $frm_settings = self::get_settings();
2122 $custom_style = ( $frm_settings->load_style != 'none' );
2123 }
2124
2125 return $custom_style;
2126 }
2127
2128 public static function truncate( $str, $length, $minword = 3, $continue = '...' ) {
2129 if ( is_array( $str ) ) {
2130 return '';
2131 }
2132
2133 $length = (int) $length;
2134 $str = wp_strip_all_tags( $str );
2135 $original_len = self::mb_function( array( 'mb_strlen', 'strlen' ), array( $str ) );
2136
2137 if ( $length == 0 ) {
2138 return '';
2139 } elseif ( $length <= 10 ) {
2140 $sub = self::mb_function( array( 'mb_substr', 'substr' ), array( $str, 0, $length ) );
2141
2142 return $sub . ( ( $length < $original_len ) ? $continue : '' );
2143 }
2144
2145 $sub = '';
2146 $len = 0;
2147
2148 $words = self::mb_function( array( 'mb_split', 'explode' ), array( ' ', $str ) );
2149
2150 foreach ( $words as $word ) {
2151 $part = ( ( $sub != '' ) ? ' ' : '' ) . $word;
2152 $total_len = self::mb_function( array( 'mb_strlen', 'strlen' ), array( $sub . $part ) );
2153 if ( $total_len > $length && substr_count( $sub, ' ' ) ) {
2154 break;
2155 }
2156
2157 $sub .= $part;
2158 $len += self::mb_function( array( 'mb_strlen', 'strlen' ), array( $part ) );
2159
2160 if ( substr_count( $sub, ' ' ) > $minword && $total_len >= $length ) {
2161 break;
2162 }
2163
2164 unset( $total_len, $word );
2165 }
2166
2167 return $sub . ( ( $len < $original_len ) ? $continue : '' );
2168 }
2169
2170 public static function mb_function( $function_names, $args ) {
2171 $mb_function_name = $function_names[0];
2172 $function_name = $function_names[1];
2173 if ( function_exists( $mb_function_name ) ) {
2174 $function_name = $mb_function_name;
2175 }
2176
2177 return call_user_func_array( $function_name, $args );
2178 }
2179
2180 public static function get_formatted_time( $date, $date_format = '', $time_format = '' ) {
2181 if ( empty( $date ) ) {
2182 return $date;
2183 }
2184
2185 if ( empty( $date_format ) ) {
2186 $date_format = get_option( 'date_format' );
2187 }
2188
2189 if ( preg_match( '/^\d{1-2}\/\d{1-2}\/\d{4}$/', $date ) && self::pro_is_installed() ) {
2190 $frmpro_settings = new FrmProSettings();
2191 $date = FrmProAppHelper::convert_date( $date, $frmpro_settings->date_format, 'Y-m-d' );
2192 }
2193
2194 $formatted = self::get_localized_date( $date_format, $date );
2195
2196 $do_time = ( gmdate( 'H:i:s', strtotime( $date ) ) != '00:00:00' );
2197 if ( $do_time ) {
2198 $formatted .= self::add_time_to_date( $time_format, $date );
2199 }
2200
2201 return $formatted;
2202 }
2203
2204 private static function add_time_to_date( $time_format, $date ) {
2205 if ( empty( $time_format ) ) {
2206 $time_format = get_option( 'time_format' );
2207 }
2208
2209 $trimmed_format = trim( $time_format );
2210 $time = '';
2211 if ( $time_format && ! empty( $trimmed_format ) ) {
2212 $time = ' ' . __( 'at', 'formidable' ) . ' ' . self::get_localized_date( $time_format, $date );
2213 }
2214
2215 return $time;
2216 }
2217
2218 /**
2219 * @since 2.0.8
2220 */
2221 public static function get_localized_date( $date_format, $date ) {
2222 $date = get_date_from_gmt( $date );
2223
2224 return date_i18n( $date_format, strtotime( $date ) );
2225 }
2226
2227 /**
2228 * Gets the time ago in words
2229 *
2230 * @param int $from in seconds
2231 * @param int|string $to in seconds
2232 *
2233 * @return string $time_ago
2234 */
2235 public static function human_time_diff( $from, $to = '', $levels = 1 ) {
2236 if ( empty( $to ) ) {
2237 $now = new DateTime();
2238 } else {
2239 $now = new DateTime( '@' . $to );
2240 }
2241 $ago = new DateTime( '@' . $from );
2242
2243 // Get the time difference
2244 $diff_object = $now->diff( $ago );
2245 $diff = get_object_vars( $diff_object );
2246
2247 // Add week amount and update day amount
2248 $diff['w'] = floor( $diff['d'] / 7 );
2249 $diff['d'] -= $diff['w'] * 7;
2250
2251 $time_strings = self::get_time_strings();
2252
2253 if ( ! is_numeric( $levels ) ) {
2254 // Show time in specified unit.
2255 $levels = self::get_unit( $levels );
2256 if ( isset( $time_strings[ $levels ] ) ) {
2257 $diff = array(
2258 $levels => self::time_format( $levels, $diff ),
2259 );
2260 $time_strings = array(
2261 $levels => $time_strings[ $levels ],
2262 );
2263 }
2264 $levels = 1;
2265 }
2266
2267 foreach ( $time_strings as $k => $v ) {
2268 if ( isset( $diff[ $k ] ) && $diff[ $k ] ) {
2269 $time_strings[ $k ] = $diff[ $k ] . ' ' . ( $diff[ $k ] > 1 ? $v[1] : $v[0] );
2270 } elseif ( isset( $diff[ $k ] ) && count( $time_strings ) === 1 ) {
2271 // Account for 0.
2272 $time_strings[ $k ] = $diff[ $k ] . ' ' . $v[1];
2273 } else {
2274 unset( $time_strings[ $k ] );
2275 }
2276 }
2277
2278 $levels_deep = apply_filters( 'frm_time_ago_levels', $levels, compact( 'time_strings', 'from', 'to' ) );
2279 $time_strings = array_slice( $time_strings, 0, absint( $levels_deep ) );
2280 $time_ago_string = implode( ' ', $time_strings );
2281
2282 return $time_ago_string;
2283 }
2284
2285 /**
2286 * @since 4.05.01
2287 */
2288 private static function time_format( $unit, $diff ) {
2289 $return = array(
2290 'y' => 'y',
2291 'd' => 'days',
2292 );
2293 if ( isset( $return[ $unit ] ) ) {
2294 return $diff[ $return[ $unit ] ];
2295 }
2296
2297 $total = $diff['days'] * self::convert_time( 'd', $unit );
2298
2299 $times = array( 'h', 'i', 's' );
2300
2301 foreach ( $times as $time ) {
2302 if ( ! isset( $diff[ $time ] ) ) {
2303 continue;
2304 }
2305
2306 $total += $diff[ $time ] * self::convert_time( $time, $unit );
2307 }
2308
2309 return floor( $total );
2310 }
2311
2312 /**
2313 * @since 4.05.01
2314 */
2315 private static function convert_time( $from, $to ) {
2316 $convert = array(
2317 's' => 1,
2318 'i' => MINUTE_IN_SECONDS,
2319 'h' => HOUR_IN_SECONDS,
2320 'd' => DAY_IN_SECONDS,
2321 'w' => WEEK_IN_SECONDS,
2322 'm' => DAY_IN_SECONDS * 30.42,
2323 'y' => DAY_IN_SECONDS * 365.25,
2324 );
2325
2326 return $convert[ $from ] / $convert[ $to ];
2327 }
2328
2329 /**
2330 * @since 4.05.01
2331 */
2332 private static function get_unit( $unit ) {
2333 $units = self::get_time_strings();
2334 if ( isset( $units[ $unit ] ) || is_numeric( $unit ) ) {
2335 return $unit;
2336 }
2337
2338 foreach ( $units as $u => $strings ) {
2339 if ( in_array( $unit, $strings ) ) {
2340 return $u;
2341 }
2342 }
2343 return 1;
2344 }
2345
2346 /**
2347 * Get the translatable time strings. The untranslated version is a failsafe
2348 * in case langauges are changing for the unit set in the shortcode.
2349 *
2350 * @since 2.0.20
2351 * @return array
2352 */
2353 private static function get_time_strings() {
2354 return array(
2355 'y' => array(
2356 __( 'year', 'formidable' ),
2357 __( 'years', 'formidable' ),
2358 'year',
2359 ),
2360 'm' => array(
2361 __( 'month', 'formidable' ),
2362 __( 'months', 'formidable' ),
2363 'month',
2364 ),
2365 'w' => array(
2366 __( 'week', 'formidable' ),
2367 __( 'weeks', 'formidable' ),
2368 'week',
2369 ),
2370 'd' => array(
2371 __( 'day', 'formidable' ),
2372 __( 'days', 'formidable' ),
2373 'day',
2374 ),
2375 'h' => array(
2376 __( 'hour', 'formidable' ),
2377 __( 'hours', 'formidable' ),
2378 'hour',
2379 ),
2380 'i' => array(
2381 __( 'minute', 'formidable' ),
2382 __( 'minutes', 'formidable' ),
2383 'minute',
2384 ),
2385 's' => array(
2386 __( 'second', 'formidable' ),
2387 __( 'seconds', 'formidable' ),
2388 'second',
2389 ),
2390 );
2391 }
2392
2393 // Pagination Methods.
2394
2395 /**
2396 * @param integer $current_p
2397 */
2398 public static function get_last_record_num( $r_count, $current_p, $p_size ) {
2399 return ( ( $r_count < ( $current_p * $p_size ) ) ? $r_count : ( $current_p * $p_size ) );
2400 }
2401
2402 /**
2403 * @param integer $current_p
2404 */
2405 public static function get_first_record_num( $r_count, $current_p, $p_size ) {
2406 if ( $current_p == 1 ) {
2407 return 1;
2408 } else {
2409 return ( self::get_last_record_num( $r_count, ( $current_p - 1 ), $p_size ) + 1 );
2410 }
2411 }
2412
2413 /**
2414 * @return array
2415 */
2416 public static function json_to_array( $json_vars ) {
2417 $vars = array();
2418 foreach ( $json_vars as $jv ) {
2419 $jv_name = explode( '[', $jv['name'] );
2420 $last = count( $jv_name ) - 1;
2421 foreach ( $jv_name as $p => $n ) {
2422 $name = trim( $n, ']' );
2423 if ( ! isset( $l1 ) ) {
2424 $l1 = $name;
2425 }
2426
2427 if ( ! isset( $l2 ) ) {
2428 $l2 = $name;
2429 }
2430
2431 if ( ! isset( $l3 ) ) {
2432 $l3 = $name;
2433 }
2434
2435 $this_val = ( $p == $last ) ? $jv['value'] : array();
2436
2437 switch ( $p ) {
2438 case 0:
2439 $l1 = $name;
2440 self::add_value_to_array( $name, $l1, $this_val, $vars );
2441 break;
2442
2443 case 1:
2444 $l2 = $name;
2445 self::add_value_to_array( $name, $l2, $this_val, $vars[ $l1 ] );
2446 break;
2447
2448 case 2:
2449 $l3 = $name;
2450 self::add_value_to_array( $name, $l3, $this_val, $vars[ $l1 ][ $l2 ] );
2451 break;
2452
2453 case 3:
2454 $l4 = $name;
2455 self::add_value_to_array( $name, $l4, $this_val, $vars[ $l1 ][ $l2 ][ $l3 ] );
2456 }
2457
2458 unset( $this_val, $n );
2459 }
2460
2461 unset( $last, $jv );
2462 }
2463
2464 return $vars;
2465 }
2466
2467 /**
2468 * @param string $name
2469 * @param string $l1
2470 */
2471 public static function add_value_to_array( $name, $l1, $val, &$vars ) {
2472 if ( $name == '' ) {
2473 $vars[] = $val;
2474 } elseif ( ! isset( $vars[ $l1 ] ) ) {
2475 $vars[ $l1 ] = $val;
2476 }
2477 }
2478
2479 public static function maybe_add_tooltip( $name, $class = 'closed', $form_name = '' ) {
2480 $tooltips = array(
2481 'action_title' => __( 'Give this action a label for easy reference.', 'formidable' ),
2482 'email_to' => __( 'Add one or more recipient addresses separated by a ",". FORMAT: Name <name@email.com> or name@email.com. [admin_email] is the address set in WP General Settings.', 'formidable' ),
2483 'cc' => __( 'Add CC addresses separated by a ",". FORMAT: Name <name@email.com> or name@email.com.', 'formidable' ),
2484 'bcc' => __( 'Add BCC addresses separated by a ",". FORMAT: Name <name@email.com> or name@email.com.', 'formidable' ),
2485 'reply_to' => __( 'If you would like a different reply to address than the "from" address, add a single address here. FORMAT: Name <name@email.com> or name@email.com.', 'formidable' ),
2486 'from' => __( 'Enter the name and/or email address of the sender. FORMAT: John Bates <john@example.com> or john@example.com.', 'formidable' ),
2487 /* translators: %1$s: Form name, %2$s: Date */
2488 'email_subject' => esc_attr( sprintf( __( 'If you leave the subject blank, the default will be used: %1$s Form submitted on %2$s', 'formidable' ), $form_name, self::site_name() ) ),
2489 );
2490
2491 if ( ! isset( $tooltips[ $name ] ) ) {
2492 return;
2493 }
2494
2495 if ( 'open' == $class ) {
2496 echo ' frm_help"';
2497 } else {
2498 echo ' class="frm_help"';
2499 }
2500
2501 echo ' title="' . esc_attr( $tooltips[ $name ] );
2502
2503 if ( 'open' != $class ) {
2504 echo '"';
2505 }
2506 }
2507
2508 /**
2509 * Add the current_page class to that page in the form nav
2510 */
2511 public static function select_current_page( $page, $current_page, $action = array() ) {
2512 if ( $current_page != $page ) {
2513 return;
2514 }
2515
2516 $frm_action = self::simple_get( 'frm_action', 'sanitize_title' );
2517 if ( empty( $action ) || ( ! empty( $frm_action ) && in_array( $frm_action, $action ) ) ) {
2518 echo ' class="current_page"';
2519 }
2520 }
2521
2522 /**
2523 * Prepare and json_encode post content
2524 *
2525 * @since 2.0
2526 *
2527 * @param array $post_content
2528 *
2529 * @return string $post_content ( json encoded array )
2530 */
2531 public static function prepare_and_encode( $post_content ) {
2532 // Loop through array to strip slashes and add only the needed ones.
2533 foreach ( $post_content as $key => $val ) {
2534 // Replace problematic characters (like &quot;)
2535 if ( is_string( $val ) ) {
2536 $val = str_replace( '&quot;', '"', $val );
2537 }
2538
2539 self::prepare_action_slashes( $val, $key, $post_content );
2540 unset( $key, $val );
2541 }
2542
2543 // json_encode the array.
2544 $post_content = json_encode( $post_content );
2545
2546 // Add extra slashes for \r\n since WP strips them.
2547 $post_content = str_replace( array( '\\r', '\\n', '\\u', '\\t' ), array( '\\\\r', '\\\\n', '\\\\u', '\\\\t' ), $post_content );
2548
2549 // allow for &quot
2550 $post_content = str_replace( '&quot;', '\\"', $post_content );
2551
2552 return $post_content;
2553 }
2554
2555 private static function prepare_action_slashes( $val, $key, &$post_content ) {
2556 if ( ! isset( $post_content[ $key ] ) || is_numeric( $val ) ) {
2557 return;
2558 }
2559
2560 if ( is_array( $val ) ) {
2561 foreach ( $val as $k1 => $v1 ) {
2562 self::prepare_action_slashes( $v1, $k1, $post_content[ $key ] );
2563 unset( $k1, $v1 );
2564 }
2565 } else {
2566 // Strip all slashes so everything is the same, no matter where the value is coming from
2567 $val = stripslashes( $val );
2568
2569 // Add backslashes before double quotes and forward slashes only
2570 $post_content[ $key ] = addcslashes( $val, '"\\/' );
2571 }
2572 }
2573
2574 /**
2575 * Check for either json or serilized data. This is temporary while transitioning
2576 * all data to json.
2577 *
2578 * @since 4.02.03
2579 */
2580 public static function unserialize_or_decode( &$value ) {
2581 if ( is_array( $value ) ) {
2582 return;
2583 }
2584
2585 if ( is_serialized( $value ) ) {
2586 $value = maybe_unserialize( $value );
2587 } else {
2588 $value = self::maybe_json_decode( $value, false );
2589 }
2590 }
2591
2592 /**
2593 * Decode a JSON string.
2594 * Do not switch shortcodes like [24] to array unless intentional ie XML values.
2595 *
2596 * @param mixed $string
2597 * @param bool $single_to_array
2598 * @return mixed
2599 */
2600 public static function maybe_json_decode( $string, $single_to_array = true ) {
2601 if ( is_array( $string ) || is_null( $string ) ) {
2602 return $string;
2603 }
2604
2605 $new_string = json_decode( $string, true );
2606 if ( function_exists( 'json_last_error' ) ) {
2607 // php 5.3+
2608 $single_value = false;
2609 if ( ! $single_to_array ) {
2610 $single_value = is_array( $new_string ) && count( $new_string ) === 1 && isset( $new_string[0] );
2611 }
2612 if ( json_last_error() == JSON_ERROR_NONE && is_array( $new_string ) && ! $single_value ) {
2613 $string = $new_string;
2614 }
2615 }
2616
2617 return $string;
2618 }
2619
2620 /**
2621 * Reformat the json serialized array in name => value array.
2622 *
2623 * @since 4.02.03
2624 */
2625 public static function format_form_data( &$form ) {
2626 $formatted = array();
2627
2628 foreach ( $form as $input ) {
2629 if ( ! isset( $input['name'] ) ) {
2630 continue;
2631 }
2632 $key = $input['name'];
2633 if ( isset( $formatted[ $key ] ) ) {
2634 if ( is_array( $formatted[ $key ] ) ) {
2635 $formatted[ $key ][] = $input['value'];
2636 } else {
2637 $formatted[ $key ] = array( $formatted[ $key ], $input['value'] );
2638 }
2639 } else {
2640 $formatted[ $key ] = $input['value'];
2641 }
2642 }
2643
2644 parse_str( http_build_query( $formatted ), $form );
2645 }
2646
2647 /**
2648 * @since 4.02.03
2649 */
2650 public static function maybe_json_encode( $value ) {
2651 if ( is_array( $value ) ) {
2652 $value = wp_json_encode( $value );
2653 }
2654 return $value;
2655 }
2656
2657 /**
2658 * @since 1.07.10
2659 *
2660 * @param string $post_type The name of the post type that may need to be highlighted
2661 * echo The javascript to open and highlight the Formidable menu
2662 */
2663 public static function maybe_highlight_menu( $post_type ) {
2664 global $post;
2665
2666 if ( isset( $_REQUEST['post_type'] ) && $_REQUEST['post_type'] != $post_type ) {
2667 return;
2668 }
2669
2670 if ( is_object( $post ) && $post->post_type != $post_type ) {
2671 return;
2672 }
2673
2674 self::load_admin_wide_js();
2675 echo '<script type="text/javascript">jQuery(document).ready(function(){frmSelectSubnav();});</script>';
2676 }
2677
2678 /**
2679 * Load the JS file on non-Formidable pages in the admin area
2680 *
2681 * @since 2.0
2682 */
2683 public static function load_admin_wide_js( $load = true ) {
2684 $version = self::plugin_version();
2685 wp_register_script( 'formidable_admin_global', self::plugin_url() . '/js/formidable_admin_global.js', array( 'jquery' ), $version );
2686
2687 $global_strings = array(
2688 'updating_msg' => __( 'Please wait while your site updates.', 'formidable' ),
2689 'deauthorize' => __( 'Are you sure you want to deauthorize Formidable Forms on this site?', 'formidable' ),
2690 'url' => self::plugin_url(),
2691 'app_url' => 'https://formidableforms.com/',
2692 'applicationsUrl' => admin_url( 'admin.php?page=formidable-applications' ),
2693 'loading' => __( 'Loading&hellip;', 'formidable' ),
2694 'nonce' => wp_create_nonce( 'frm_ajax' ),
2695 );
2696 wp_localize_script( 'formidable_admin_global', 'frmGlobal', $global_strings );
2697
2698 if ( $load ) {
2699 wp_enqueue_script( 'formidable_admin_global' );
2700 }
2701 }
2702
2703 /**
2704 * @since 2.0.9
2705 */
2706 public static function load_font_style() {
2707 wp_enqueue_style( 'frm_fonts', self::plugin_url() . '/css/frm_fonts.css', array(), self::plugin_version() );
2708 }
2709
2710 /**
2711 * @param string $location
2712 */
2713 public static function localize_script( $location ) {
2714 global $wp_scripts;
2715
2716 $ajax_url = admin_url( 'admin-ajax.php', is_ssl() ? 'admin' : 'http' );
2717 $ajax_url = apply_filters( 'frm_ajax_url', $ajax_url );
2718
2719 $script_strings = array(
2720 'ajax_url' => $ajax_url,
2721 'images_url' => self::plugin_url() . '/images',
2722 'loading' => __( 'Loading&hellip;', 'formidable' ),
2723 'remove' => __( 'Remove', 'formidable' ),
2724 'offset' => apply_filters( 'frm_scroll_offset', 4 ),
2725 'nonce' => wp_create_nonce( 'frm_ajax' ),
2726 'id' => __( 'ID', 'formidable' ),
2727 'no_results' => __( 'No results match', 'formidable' ),
2728 'file_spam' => __( 'That file looks like Spam.', 'formidable' ),
2729 'calc_error' => __( 'There is an error in the calculation in the field with key', 'formidable' ),
2730 'empty_fields' => __( 'Please complete the preceding required fields before uploading a file.', 'formidable' ),
2731 'focus_first_error' => self::should_focus_first_error(),
2732 'include_alert_role' => self::should_include_alert_role_on_field_errors(),
2733 );
2734
2735 $data = $wp_scripts->get_data( 'formidable', 'data' );
2736 if ( empty( $data ) ) {
2737 wp_localize_script( 'formidable', 'frm_js', $script_strings );
2738 }
2739
2740 if ( $location == 'admin' ) {
2741 $frm_settings = self::get_settings();
2742 $admin_script_strings = array(
2743 'desc' => __( '(Click to add description)', 'formidable' ),
2744 'blank' => __( '(Blank)', 'formidable' ),
2745 'no_label' => __( '(no label)', 'formidable' ),
2746 'saving' => esc_attr( __( 'Saving', 'formidable' ) ),
2747 'saved' => esc_attr( __( 'Saved', 'formidable' ) ),
2748 'ok' => __( 'OK', 'formidable' ),
2749 'cancel' => __( 'Cancel', 'formidable' ),
2750 'default_label' => __( 'Default', 'formidable' ),
2751 'clear_default' => __( 'Clear default value when typing', 'formidable' ),
2752 'no_clear_default' => __( 'Do not clear default value when typing', 'formidable' ),
2753 'valid_default' => __( 'Default value will pass form validation', 'formidable' ),
2754 'no_valid_default' => __( 'Default value will NOT pass form validation', 'formidable' ),
2755 'caution' => __( 'Heads up', 'formidable' ),
2756 'confirm' => __( 'Are you sure?', 'formidable' ),
2757 'conf_delete' => __( 'Are you sure you want to delete this field and all data associated with it?', 'formidable' ),
2758 'conf_delete_sec' => __( 'All fields inside this Section will be deleted along with their data. Are you sure you want to delete this group of fields?', 'formidable' ),
2759 'conf_no_repeat' => __( 'Warning: If you have entries with multiple rows, all but the first row will be lost.', 'formidable' ),
2760 'default_unique' => $frm_settings->unique_msg,
2761 'default_conf' => __( 'The entered values do not match', 'formidable' ),
2762 'enter_email' => __( 'Enter Email', 'formidable' ),
2763 'confirm_email' => __( 'Confirm Email', 'formidable' ),
2764 'conditional_text' => __( 'Conditional content here', 'formidable' ),
2765 'new_option' => __( 'New Option', 'formidable' ),
2766 'css_invalid_size' => __( 'In certain browsers (e.g. Firefox) text will not display correctly if the field height is too small relative to the field padding and text size. Please increase your field height or decrease your field padding.', 'formidable' ),
2767 'enter_password' => __( 'Enter Password', 'formidable' ),
2768 'confirm_password' => __( 'Confirm Password', 'formidable' ),
2769 'import_complete' => __( 'Import Complete', 'formidable' ),
2770 'updating' => __( 'Please wait while your site updates.', 'formidable' ),
2771 'no_save_warning' => __( 'Warning: There is no way to retrieve unsaved entries.', 'formidable' ),
2772 'private_label' => __( 'Private', 'formidable' ),
2773 'jquery_ui_url' => '',
2774 'pro_url' => is_callable( 'FrmProAppHelper::plugin_url' ) ? FrmProAppHelper::plugin_url() : '',
2775 'no_licenses' => __( 'No new licenses were found', 'formidable' ),
2776 'unmatched_parens' => __( 'This calculation has at least one unmatched ( ) { } [ ].', 'formidable' ),
2777 'view_shortcodes' => __( 'This calculation may have shortcodes that work in Views but not forms.', 'formidable' ),
2778 'text_shortcodes' => __( 'This calculation may have shortcodes that work in text calculations but not numeric calculations.', 'formidable' ),
2779 'only_one_action' => __( 'This form action is limited to one per form. Please edit the existing form action.', 'formidable' ),
2780 'unsafe_params' => FrmFormsHelper::reserved_words(),
2781 /* Translators: %s is the name of a Detail Page Slug that is a reserved word.*/
2782 'slug_is_reserved' => sprintf( __( 'The Detail Page Slug "%s" is reserved by WordPress. This may cause problems. Is this intentional?', 'formidable' ), '****' ),
2783 /* Translators: %s is the name of a parameter that is a reserved word. More than one word could be listed here, though that would not be common. */
2784 'param_is_reserved' => sprintf( __( 'The parameter "%s" is reserved by WordPress. This may cause problems when included in the URL. Is this intentional? ', 'formidable' ), '****' ),
2785 'reserved_words' => __( 'See the list of reserved words in WordPress.', 'formidable' ),
2786 'repeat_limit_min' => __( 'Please enter a Repeat Limit that is greater than 1.', 'formidable' ),
2787 'checkbox_limit' => __( 'Please select a limit between 0 and 200.', 'formidable' ),
2788 'install' => __( 'Install', 'formidable' ),
2789 'active' => __( 'Active', 'formidable' ),
2790 'select_a_field' => __( 'Select a Field', 'formidable' ),
2791 'no_items_found' => __( 'No items found.', 'formidable' ),
2792 );
2793 $admin_script_strings = apply_filters( 'frm_admin_script_strings', $admin_script_strings );
2794
2795 $data = $wp_scripts->get_data( 'formidable_admin', 'data' );
2796 if ( empty( $data ) ) {
2797 wp_localize_script( 'formidable_admin', 'frm_admin_js', $admin_script_strings );
2798 }
2799 }
2800 }
2801
2802 /**
2803 * Returns whether or not the first errored input should be auto-focused (default true).
2804 *
2805 * @since 5.2.05
2806 *
2807 * @return bool
2808 */
2809 private static function should_focus_first_error() {
2810 return (bool) apply_filters( 'frm_focus_first_error', true );
2811 }
2812
2813 /**
2814 * Returns whether or not field errors should include role="alert" (default true).
2815 *
2816 * @since 5.2.05
2817 *
2818 * @return bool
2819 */
2820 public static function should_include_alert_role_on_field_errors() {
2821 return (bool) apply_filters( 'frm_include_alert_role_on_field_errors', true );
2822 }
2823
2824 /**
2825 * Echo the message on the plugins listing page
2826 *
2827 * @since 1.07.10
2828 *
2829 * @param float $min_version The version the add-on requires
2830 */
2831 public static function min_version_notice( $min_version ) {
2832 $frm_version = self::plugin_version();
2833
2834 // Check if Formidable meets minimum requirements.
2835 if ( version_compare( $frm_version, $min_version, '>=' ) ) {
2836 return;
2837 }
2838
2839 $wp_list_table = _get_list_table( 'WP_Plugins_List_Table' );
2840 echo '<tr class="plugin-update-tr active"><th colspan="' . absint( $wp_list_table->get_column_count() ) . '" class="check-column plugin-update colspanchange"><div class="update-message">' .
2841 esc_html__( 'You are running an outdated version of Formidable. This plugin may not work correctly if you do not update Formidable.', 'formidable' ) .
2842 '</div></td></tr>';
2843 }
2844
2845 /**
2846 * If Pro is far outdated, show a message.
2847 *
2848 * @since 4.0.01
2849 */
2850 public static function min_pro_version_notice( $min_version ) {
2851 if ( ! self::is_formidable_admin() ) {
2852 // Don't show admin-wide.
2853 return;
2854 }
2855
2856 self::php_version_notice();
2857
2858 $is_pro = self::pro_is_installed() && class_exists( 'FrmProDb' );
2859 if ( ! $is_pro || self::meets_min_pro_version( $min_version ) ) {
2860 return;
2861 }
2862
2863 $pro_version = FrmProDb::$plug_version;
2864 $expired = FrmAddonsController::is_license_expired();
2865 ?>
2866 <div class="error frm_previous_install">
2867 <?php
2868 esc_html_e( 'You are running a version of Formidable Forms that may not be compatible with your version of Formidable Forms Pro.', 'formidable' );
2869 if ( empty( $expired ) ) {
2870 echo ' Please <a href="' . esc_url( admin_url( 'plugins.php?s=formidable%20forms%20pro' ) ) . '">update now</a>.';
2871 } else {
2872 echo '<br/>Please <a href="https://formidableforms.com/account/downloads/?utm_source=WordPress&utm_medium=outdated">renew now</a> to get the latest version.';
2873 }
2874 ?>
2875 </div>
2876 <?php
2877 }
2878
2879 /**
2880 * If Pro is installed, check the version number.
2881 *
2882 * @since 4.0.01
2883 */
2884 public static function meets_min_pro_version( $min_version ) {
2885 return ! class_exists( 'FrmProDb' ) || version_compare( FrmProDb::$plug_version, $min_version, '>=' );
2886 }
2887
2888 /**
2889 * Show a message if the browser or PHP version is below the recommendations.
2890 *
2891 * @since 4.0.02
2892 */
2893 private static function php_version_notice() {
2894 $message = array();
2895 if ( version_compare( phpversion(), '5.6', '<' ) ) {
2896 $message[] = __( 'The version of PHP on your server is too low. If this is not corrected, you may see issues with Formidable Forms. Please contact your web host and ask to be updated to PHP 7.0+.', 'formidable' );
2897 }
2898
2899 $browser = self::get_server_value( 'HTTP_USER_AGENT' );
2900 $is_ie = strpos( $browser, 'MSIE' ) !== false;
2901 if ( $is_ie ) {
2902 $message[] = __( 'You are using an outdated browser that is not compatible with Formidable Forms. Please update to a more current browser (we recommend Chrome).', 'formidable' );
2903 }
2904
2905 foreach ( $message as $m ) {
2906 ?>
2907 <div class="error frm_previous_install">
2908 <?php echo esc_html( $m ); ?>
2909 </div>
2910 <?php
2911 }
2912 }
2913
2914 public static function locales( $type = 'date' ) {
2915 $locales = array(
2916 'en' => __( 'English', 'formidable' ),
2917 'af' => __( 'Afrikaans', 'formidable' ),
2918 'sq' => __( 'Albanian', 'formidable' ),
2919 'ar' => __( 'Arabic', 'formidable' ),
2920 'hy' => __( 'Armenian', 'formidable' ),
2921 'az' => __( 'Azerbaijani', 'formidable' ),
2922 'eu' => __( 'Basque', 'formidable' ),
2923 'bs' => __( 'Bosnian', 'formidable' ),
2924 'bg' => __( 'Bulgarian', 'formidable' ),
2925 'ca' => __( 'Catalan', 'formidable' ),
2926 'zh-HK' => __( 'Chinese Hong Kong', 'formidable' ),
2927 'zh-CN' => __( 'Chinese Simplified', 'formidable' ),
2928 'zh-TW' => __( 'Chinese Traditional', 'formidable' ),
2929 'hr' => __( 'Croatian', 'formidable' ),
2930 'cs' => __( 'Czech', 'formidable' ),
2931 'da' => __( 'Danish', 'formidable' ),
2932 'nl' => __( 'Dutch', 'formidable' ),
2933 'en-GB' => __( 'English/UK', 'formidable' ),
2934 'eo' => __( 'Esperanto', 'formidable' ),
2935 'et' => __( 'Estonian', 'formidable' ),
2936 'fo' => __( 'Faroese', 'formidable' ),
2937 'fa' => __( 'Farsi/Persian', 'formidable' ),
2938 'fil' => __( 'Filipino', 'formidable' ),
2939 'fi' => __( 'Finnish', 'formidable' ),
2940 'fr' => __( 'French', 'formidable' ),
2941 'fr-CA' => __( 'French/Canadian', 'formidable' ),
2942 'fr-CH' => __( 'French/Swiss', 'formidable' ),
2943 'de' => __( 'German', 'formidable' ),
2944 'de-AT' => __( 'German/Austria', 'formidable' ),
2945 'de-CH' => __( 'German/Switzerland', 'formidable' ),
2946 'el' => __( 'Greek', 'formidable' ),
2947 'he' => __( 'Hebrew', 'formidable' ),
2948 'iw' => __( 'Hebrew', 'formidable' ),
2949 'hi' => __( 'Hindi', 'formidable' ),
2950 'hu' => __( 'Hungarian', 'formidable' ),
2951 'is' => __( 'Icelandic', 'formidable' ),
2952 'id' => __( 'Indonesian', 'formidable' ),
2953 'it' => __( 'Italian', 'formidable' ),
2954 'ja' => __( 'Japanese', 'formidable' ),
2955 'ko' => __( 'Korean', 'formidable' ),
2956 'lv' => __( 'Latvian', 'formidable' ),
2957 'lt' => __( 'Lithuanian', 'formidable' ),
2958 'ms' => __( 'Malaysian', 'formidable' ),
2959 'no' => __( 'Norwegian', 'formidable' ),
2960 'pl' => __( 'Polish', 'formidable' ),
2961 'pt' => __( 'Portuguese', 'formidable' ),
2962 'pt-BR' => __( 'Portuguese/Brazilian', 'formidable' ),
2963 'pt-PT' => __( 'Portuguese/Portugal', 'formidable' ),
2964 'ro' => __( 'Romanian', 'formidable' ),
2965 'ru' => __( 'Russian', 'formidable' ),
2966 'sr' => __( 'Serbian', 'formidable' ),
2967 'sr-SR' => __( 'Serbian', 'formidable' ),
2968 'sk' => __( 'Slovak', 'formidable' ),
2969 'sl' => __( 'Slovenian', 'formidable' ),
2970 'es' => __( 'Spanish', 'formidable' ),
2971 'es-419' => __( 'Spanish/Latin America', 'formidable' ),
2972 'sv' => __( 'Swedish', 'formidable' ),
2973 'ta' => __( 'Tamil', 'formidable' ),
2974 'th' => __( 'Thai', 'formidable' ),
2975 'tr' => __( 'Turkish', 'formidable' ),
2976 'uk' => __( 'Ukrainian', 'formidable' ),
2977 'vi' => __( 'Vietnamese', 'formidable' ),
2978 );
2979
2980 if ( $type === 'captcha' ) {
2981 // remove the languages unavailable for the captcha
2982 $unset = array( 'af', 'sq', 'hy', 'az', 'eu', 'bs', 'zh-HK', 'eo', 'et', 'fo', 'fr-CH', 'he', 'is', 'ms', 'sr-SR', 'ta' );
2983 } else {
2984 // remove the languages unavailable for the datepicker
2985 $unset = array( 'fil', 'fr-CA', 'de-AT', 'de-CH', 'iw', 'hi', 'pt', 'pt-PT', 'es-419' );
2986 }
2987
2988 $locales = array_diff_key( $locales, array_flip( $unset ) );
2989 $locales = apply_filters( 'frm_locales', $locales );
2990
2991 return $locales;
2992 }
2993
2994 /**
2995 * Output HTML containing reference text for accessibility
2996 *
2997 * @deprecated 5.1
2998 */
2999 public static function multiselect_accessibility() {
3000 _deprecated_function( __METHOD__, '5.1' );
3001 }
3002
3003 public static function get_menu_icon_class() {
3004 if ( is_callable( 'FrmProAppHelper::get_settings' ) ) {
3005 $settings = FrmProAppHelper::get_settings();
3006 if ( is_object( $settings ) && ! empty( $settings->menu_icon ) ) {
3007 return $settings->menu_icon;
3008 }
3009 }
3010 return 'frmfont frm_logo_icon';
3011 }
3012
3013 /**
3014 * Shows the images dropdown.
3015 *
3016 * @since 5.0.04
3017 *
3018 * @param array $args {
3019 * Arguments.
3020 *
3021 * @type string $selected Selected value.
3022 * @type array[] $options Array of options with keys are option values and values are array.
3023 * The option array contains `text`, `svg` and `custom_atts`.
3024 * @type string $classes Custom CSS classes for the wrapper element.
3025 * @type array $input_attrs Attributes of value input.
3026 * }
3027 */
3028 public static function images_dropdown( $args ) {
3029 $args = self::fill_default_images_dropdown_args( $args );
3030
3031 $input_attrs_str = self::get_images_dropdown_input_attrs( $args );
3032 ob_start();
3033 include self::plugin_path() . '/classes/views/shared/images-dropdown.php';
3034 $output = ob_get_clean();
3035
3036 /**
3037 * Allows modifying the output of FrmAppHelper::images_dropdown() method.
3038 *
3039 * @since 5.0.04
3040 *
3041 * @param string $output The output.
3042 * @param array $args Passed arguments.
3043 */
3044 echo apply_filters( 'frm_images_dropdown_output', $output, $args ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
3045 }
3046
3047 /**
3048 * Fills the default images_dropdown() arguments.
3049 *
3050 * @since 5.0.04
3051 *
3052 * @param array $args The arguments.
3053 * @return array
3054 */
3055 private static function fill_default_images_dropdown_args( $args ) {
3056 $defaults = array(
3057 'selected' => '',
3058 'options' => array(),
3059 'classes' => '',
3060 'input_attrs' => array(),
3061 );
3062 $new_args = wp_parse_args( $args, $defaults );
3063
3064 $new_args['options'] = (array) $new_args['options'];
3065 $new_args['input_attrs'] = (array) $new_args['input_attrs'];
3066
3067 // Set the number of columns.
3068 $new_args['col_class'] = ceil( 12 / count( $new_args['options'] ) );
3069 if ( $new_args['col_class'] > 6 ) {
3070 $new_args['col_class'] = ceil( $new_args['col_class'] / 2 );
3071 }
3072
3073 /**
3074 * Allows modifying the arguments of images_dropdown() method.
3075 *
3076 * @since 5.0.04
3077 *
3078 * @param array $new_args Arguments after filling the defaults.
3079 * @param array $args Arguments passed to the method, before filling the defaults.
3080 */
3081 return apply_filters( 'frm_images_dropdown_args', $new_args, $args );
3082 }
3083
3084 /**
3085 * Gets HTML attributes of the input in images_dropdown() method.
3086 *
3087 * @since 5.0.04
3088 *
3089 * @param array $args The arguments.
3090 * @return string
3091 */
3092 private static function get_images_dropdown_input_attrs( $args ) {
3093 $input_attrs = $args['input_attrs'];
3094 $input_attrs['type'] = 'radio';
3095 $input_attrs['name'] = $args['name'];
3096
3097 $input_attrs_str = '';
3098 foreach ( $input_attrs as $key => $input_attr ) {
3099 $input_attrs_str .= ' ' . sprintf( '%s="%s"', esc_attr( $key ), esc_attr( $input_attr ) );
3100 }
3101
3102 /**
3103 * Allows modifying the HTML attributes of the input in images_dropdown() method.
3104 *
3105 * @since 5.0.04
3106 *
3107 * @param string $input_attrs_str HTML attributes string.
3108 * @param array $args The arguments of images_dropdown() method.
3109 */
3110 return apply_filters( 'frm_images_dropdown_input_attrs', $input_attrs_str, $args );
3111 }
3112
3113 /**
3114 * Gets the image of each option in images_dropdown() method.
3115 *
3116 * @since 5.0.04
3117 *
3118 * @param array $option Option data.
3119 * @param array $args The arguments of images_dropdown() method.
3120 * @return string
3121 */
3122 private static function get_images_dropdown_option_image( $option, $args ) {
3123 $image = self::icon_by_class(
3124 'frmfont ' . $option['svg'],
3125 array(
3126 'echo' => false,
3127 )
3128 );
3129
3130 $args['option'] = $option;
3131
3132 /**
3133 * Allows modifying the image of each option in images_dropdown() method.
3134 *
3135 * @since 5.0.04
3136 *
3137 * @param string $image The image HTML.
3138 * @param array $args The arguments of images_dropdown() method, with `option` array is added.
3139 */
3140 return apply_filters( 'frm_images_dropdown_option_image', $image, $args );
3141 }
3142
3143 /**
3144 * Gets the HTML classes of each option in images_dropdown() method.
3145 *
3146 * @since 5.0.04
3147 *
3148 * @param array $option Option data.
3149 * @param array $args The arguments of images_dropdown() method.
3150 * @return string
3151 */
3152 private static function get_images_dropdown_option_classes( $option, $args ) {
3153 $classes = '';
3154
3155 if ( ! empty( $option['custom_attrs']['class'] ) ) {
3156 $classes .= ' ' . $option['custom_attrs']['class'];
3157 }
3158
3159 $args['option'] = $option;
3160
3161 /**
3162 * Allows modifying the CSS classes of each option in images_dropdown() method.
3163 *
3164 * @since 5.0.04
3165 *
3166 * @param string $classes CSS classes.
3167 * @param array $args The arguments of images_dropdown() method, with `option` array is added.
3168 */
3169 return apply_filters( 'frm_images_dropdown_option_classes', $classes, $args );
3170 }
3171
3172 /**
3173 * Gets the custom HTML attributes of each option in images_dropdown() method.
3174 *
3175 * @since 5.0.04
3176 *
3177 * @param array $option Option data.
3178 * @param array $args The arguments of images_dropdown() method.
3179 * @return string
3180 */
3181 private static function get_images_dropdown_option_html_attrs( $option, $args ) {
3182 $html_attrs = '';
3183 if ( ! empty( $option['custom_attrs'] ) && is_array( $option['custom_attrs'] ) ) {
3184 $html_attrs_arr = array();
3185
3186 foreach ( $option['custom_attrs'] as $key => $value ) {
3187 if ( in_array( $key, array( 'type', 'class', 'data-value' ) ) ) {
3188 continue;
3189 }
3190
3191 $html_attrs_arr[] = sprintf( '%s="%s"', esc_attr( $key ), esc_attr( $value ) );
3192 }
3193
3194 $html_attrs = implode( ' ', $html_attrs_arr );
3195 }
3196
3197 $args['option'] = $option;
3198
3199 /**
3200 * Allows modifying the custom HTML attributes of each option in images_dropdown() method.
3201 *
3202 * @since 5.0.04
3203 *
3204 * @param string $html_attrs The HTML attributes string.
3205 * @param array $args The arguments of images_dropdown() method, with `option` array is added.
3206 */
3207 return apply_filters( 'frm_images_dropdown_option_html_attrs', $html_attrs, $args );
3208 }
3209
3210 /**
3211 * @since 5.0.07
3212 *
3213 * @return bool true if the current user is allowed to save unfiltered HTML.
3214 */
3215 public static function allow_unfiltered_html() {
3216 if ( self::should_never_allow_unfiltered_html() ) {
3217 return false;
3218 }
3219 return current_user_can( 'unfiltered_html' );
3220 }
3221
3222 /**
3223 * @since 5.0.13
3224 *
3225 * @return bool
3226 */
3227 public static function should_never_allow_unfiltered_html() {
3228 if ( defined( 'DISALLOW_UNFILTERED_HTML' ) && DISALLOW_UNFILTERED_HTML ) {
3229 return true;
3230 }
3231
3232 /**
3233 * Formidable will check DISALLOW_UNFILTERED_HTML to determine if some form HTML should be filtered or not.
3234 * In many cases, scripts are added intentionally to forms and will not be stripped if DISALLOW_UNFILTERED_HTML is not set.
3235 * It is also possible to filter Formidable without defining DISALLOW_UNFILTERED_HTML, with add_filter( 'frm_disallow_unfiltered_html', '__return_true' );
3236 *
3237 * @since 5.0.13
3238 */
3239 return apply_filters( 'frm_disallow_unfiltered_html', false );
3240 }
3241
3242 /**
3243 * @since 5.0.07
3244 *
3245 * @param array $values
3246 * @param array $keys
3247 * @return array
3248 */
3249 public static function maybe_filter_array( $values, $keys ) {
3250 $allow_unfiltered_html = self::allow_unfiltered_html();
3251
3252 if ( $allow_unfiltered_html ) {
3253 return $values;
3254 }
3255
3256 foreach ( $keys as $key ) {
3257 if ( isset( $values[ $key ] ) ) {
3258 $values[ $key ] = self::kses( $values[ $key ], 'all' );
3259 }
3260 }
3261
3262 return $values;
3263 }
3264
3265 /**
3266 * Some back end fields allow privleged users to add scripts.
3267 * A site that uses the DISALLOW_UNFILTERED_HTML always remove scripts on echo.
3268 *
3269 * @since 5.0.13
3270 *
3271 * @param string $value
3272 * @param array|string $allowed 'all' for everything included as defaults
3273 * @return string
3274 */
3275 public static function maybe_kses( $value, $allowed = 'all' ) {
3276 if ( self::should_never_allow_unfiltered_html() ) {
3277 $value = self::kses( $value, $allowed );
3278 }
3279 return $value;
3280 }
3281
3282 /**
3283 * @since 5.0.16
3284 *
3285 * @return bool
3286 */
3287 public static function show_landing_pages() {
3288 return self::show_new_feature( 'landing' );
3289 }
3290
3291 /**
3292 * @since 5.0.16
3293 *
3294 * @return array
3295 */
3296 public static function get_landing_page_upgrade_data_params( $medium = 'landing' ) {
3297 $params = array(
3298 'medium' => $medium,
3299 'upgrade' => __( 'Form Landing Pages', 'formidable' ),
3300 'message' => __( 'Easily manage a landing page for your form. Upgrade to get form landing pages.', 'formidable' ),
3301 );
3302 return self::get_upgrade_data_params( 'landing', $params );
3303 }
3304
3305 /**
3306 * @since 5.0.17
3307 *
3308 * @param string $plugin
3309 * @return string|false
3310 */
3311 private static function get_plan_required( $plugin ) {
3312 $link = FrmAddonsController::install_link( $plugin );
3313 return FrmFormsHelper::get_plan_required( $link );
3314 }
3315
3316 /**
3317 * @since 5.0.17
3318 *
3319 * @param string
3320 * @return bool
3321 */
3322 public static function show_new_feature( $feature ) {
3323 $link = FrmAddonsController::install_link( $feature );
3324 return array_key_exists( 'status', $link ) || array_key_exists( 'class', $link );
3325 }
3326
3327 /**
3328 * @since 5.0.17
3329 *
3330 * @param string $plugin
3331 * @param array $params
3332 * @return array
3333 */
3334 public static function get_upgrade_data_params( $plugin, $params ) {
3335 $link = FrmAddonsController::install_link( $plugin );
3336 if ( ! $link ) {
3337 return $params;
3338 }
3339
3340 if ( ! empty( $link['url'] ) && self::pro_is_installed() ) {
3341 $params['oneclick'] = json_encode( $link );
3342 unset( $params['message'] );
3343 if ( ! isset( $params['medium'] ) ) {
3344 $params['medium'] = $plugin;
3345 }
3346 } else {
3347 $params['requires'] = FrmFormsHelper::get_plan_required( $link );
3348 }
3349
3350 return $params;
3351 }
3352
3353 /**
3354 * Returns true if every character in text is a hexadecimal 'digit', that is a decimal digit or a character from [A-Fa-f], false otherwise.
3355 * Not every server installs the ctype extension, so use a fallback if the function does not exist.
3356 *
3357 * @since 5.0.17
3358 *
3359 * @param string $text
3360 * @return bool
3361 */
3362 public static function ctype_xdigit( $text ) {
3363 if ( function_exists( 'ctype_xdigit' ) ) {
3364 return ctype_xdigit( $text );
3365 }
3366 return is_string( $text ) && '' !== $text && ! preg_match( '/[^A-Fa-f0-9]/', $text );
3367 }
3368
3369 /**
3370 * Set the current screen to avoid undefined notices.
3371 *
3372 * @since 5.2.01
3373 */
3374 public static function set_current_screen_and_hook_suffix() {
3375 global $hook_suffix;
3376 if ( is_null( $hook_suffix ) ) {
3377 // $hook_suffix gets used in substr so make sure it's not null. PHP 8.1 deprecates null in substr.
3378 $hook_suffix = ''; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
3379 }
3380 set_current_screen();
3381 }
3382
3383 /**
3384 * @since 4.07
3385 * @deprecated 4.09.01
3386 */
3387 public static function renewal_message() {
3388 if ( is_callable( 'FrmProAddonsController::renewal_message' ) ) {
3389 FrmProAddonsController::renewal_message();
3390 return;
3391 }
3392
3393 if ( ! FrmAddonsController::is_license_expired() ) {
3394 return;
3395 }
3396
3397 ?>
3398 <div class="frm_error_style" style="text-align:left">
3399 <?php self::icon_by_class( 'frmfont frm_alert_icon' ); ?>
3400 &nbsp;
3401 <?php esc_html_e( 'Your account has expired', 'formidable' ); ?>
3402 <div style="float:right">
3403 <a href="<?php echo esc_url( self::admin_upgrade_link( 'form-expired', 'account/downloads/' ) ); ?>">
3404 <?php esc_html_e( 'Renew Now', 'formidable' ); ?>
3405 </a>
3406 </div>
3407 </div>
3408 <?php
3409 }
3410
3411 /**
3412 * Shows pill text.
3413 *
3414 * @since 5.2.02
3415 *
3416 * @param string $text Text in the pill. Default is NEW.
3417 */
3418 public static function show_pill_text( $text = null ) {
3419 if ( null === $text ) {
3420 $text = __( 'NEW', 'formidable' );
3421 }
3422 echo '<span class="frm-new-pill">' . esc_html( $text ) . '</span>';
3423 }
3424
3425 /**
3426 * Count the number of decimals digits.
3427 *
3428 * @since 5.2.07
3429 *
3430 * @param mixed $num Number.
3431 * @return int|false Returns `false` if the passed parameter is not number.
3432 */
3433 public static function count_decimals( $num ) {
3434 if ( ! is_numeric( $num ) ) {
3435 return false;
3436 }
3437
3438 $num = (string) $num;
3439 $parts = explode( '.', $num );
3440 if ( 1 === count( $parts ) ) {
3441 return 0;
3442 }
3443
3444 return strlen( $parts[ count( $parts ) - 1 ] );
3445 }
3446
3447 /**
3448 * @since 4.08
3449 * @deprecated 4.09.01
3450 */
3451 public static function expiring_message() {
3452 _deprecated_function( __METHOD__, '4.09.01', 'FrmProAddonsController::expiring_message' );
3453 if ( is_callable( 'FrmProAddonsController::expiring_message' ) ) {
3454 FrmProAddonsController::expiring_message();
3455 }
3456 }
3457
3458 /**
3459 * Use the WP 4.7 wp_doing_ajax function
3460 *
3461 * @since 2.05.07
3462 * @deprecated 4.04.04
3463 */
3464 public static function wp_doing_ajax() {
3465 _deprecated_function( __METHOD__, '4.04.04', 'wp_doing_ajax' );
3466 return wp_doing_ajax();
3467 }
3468
3469 /**
3470 * @deprecated 4.0
3471 */
3472 public static function insert_opt_html( $args ) {
3473 _deprecated_function( __METHOD__, '4.0', 'FrmFormsHelper::insert_opt_html' );
3474 FrmFormsHelper::insert_opt_html( $args );
3475 }
3476
3477 /**
3478 * Used to filter shortcode in text widgets
3479 *
3480 * @deprecated 2.5.4
3481 * @codeCoverageIgnore
3482 */
3483 public static function widget_text_filter_callback( $matches ) {
3484 return FrmDeprecated::widget_text_filter_callback( $matches );
3485 }
3486
3487 /**
3488 * @deprecated 3.01
3489 * @codeCoverageIgnore
3490 */
3491 public static function sanitize_array( &$values ) {
3492 FrmDeprecated::sanitize_array( $values );
3493 }
3494
3495 /**
3496 * @param array $settings
3497 * @param string $group
3498 *
3499 * @since 2.0.6
3500 * @deprecated 2.05.06
3501 * @codeCoverageIgnore
3502 */
3503 public static function save_settings( $settings, $group ) {
3504 return FrmDeprecated::save_settings( $settings, $group );
3505 }
3506
3507 /**
3508 * @since 2.0.4
3509 * @deprecated 2.05.06
3510 * @codeCoverageIgnore
3511 */
3512 public static function save_json_post( $settings ) {
3513 return FrmDeprecated::save_json_post( $settings );
3514 }
3515
3516 /**
3517 * @since 2.0
3518 * @deprecated 2.05.06
3519 * @codeCoverageIgnore
3520 *
3521 * @param string $cache_key The unique name for this cache
3522 * @param string $group The name of the cache group
3523 * @param string $query If blank, don't run a db call
3524 * @param string $type The wpdb function to use with this query
3525 *
3526 * @return mixed $results The cache or query results
3527 */
3528 public static function check_cache( $cache_key, $group = '', $query = '', $type = 'get_var', $time = 300 ) {
3529 return FrmDeprecated::check_cache( $cache_key, $group, $query, $type, $time );
3530 }
3531
3532 /**
3533 * @deprecated 2.05.06
3534 * @codeCoverageIgnore
3535 */
3536 public static function set_cache( $cache_key, $results, $group = '', $time = 300 ) {
3537 return FrmDeprecated::set_cache( $cache_key, $results, $group, $time );
3538 }
3539
3540 /**
3541 * @deprecated 2.05.06
3542 * @codeCoverageIgnore
3543 */
3544 public static function add_key_to_group_cache( $key, $group ) {
3545 FrmDeprecated::add_key_to_group_cache( $key, $group );
3546 }
3547
3548 /**
3549 * @deprecated 2.05.06
3550 * @codeCoverageIgnore
3551 */
3552 public static function get_group_cached_keys( $group ) {
3553 return FrmDeprecated::get_group_cached_keys( $group );
3554 }
3555
3556 /**
3557 * @since 2.0
3558 * @deprecated 2.05.06
3559 * @codeCoverageIgnore
3560 * @return mixed The cached value or false
3561 */
3562 public static function check_cache_and_transient( $cache_key ) {
3563 return FrmDeprecated::check_cache( $cache_key );
3564 }
3565
3566 /**
3567 * @since 2.0
3568 * @deprecated 2.05.06
3569 * @codeCoverageIgnore
3570 *
3571 * @param string $cache_key
3572 */
3573 public static function delete_cache_and_transient( $cache_key, $group = 'default' ) {
3574 FrmDeprecated::delete_cache_and_transient( $cache_key, $group );
3575 }
3576
3577 /**
3578 * @since 2.0
3579 * @deprecated 2.05.06
3580 * @codeCoverageIgnore
3581 *
3582 * @param string $group The name of the cache group
3583 */
3584 public static function cache_delete_group( $group ) {
3585 FrmDeprecated::cache_delete_group( $group );
3586 }
3587
3588 /**
3589 * @since 1.07.10
3590 * @deprecated 2.05.06
3591 * @codeCoverageIgnore
3592 *
3593 * @param string $term The value to escape
3594 *
3595 * @return string The escaped value
3596 */
3597 public static function esc_like( $term ) {
3598 return FrmDeprecated::esc_like( $term );
3599 }
3600
3601 /**
3602 * @param string $order_query
3603 *
3604 * @deprecated 2.05.06
3605 * @codeCoverageIgnore
3606 */
3607 public static function esc_order( $order_query ) {
3608 return FrmDeprecated::esc_order( $order_query );
3609 }
3610
3611 /**
3612 * @deprecated 2.05.06
3613 * @codeCoverageIgnore
3614 */
3615 public static function esc_order_by( &$order_by ) {
3616 FrmDeprecated::esc_order_by( $order_by );
3617 }
3618
3619 /**
3620 * @param string $limit
3621 *
3622 * @deprecated 2.05.06
3623 * @codeCoverageIgnore
3624 */
3625 public static function esc_limit( $limit ) {
3626 return FrmDeprecated::esc_limit( $limit );
3627 }
3628
3629 /**
3630 * @since 2.0
3631 * @deprecated 2.05.06
3632 * @codeCoverageIgnore
3633 */
3634 public static function prepare_array_values( $array, $type = '%s' ) {
3635 return FrmDeprecated::prepare_array_values( $array, $type );
3636 }
3637
3638 /**
3639 * @deprecated 2.05.06
3640 * @codeCoverageIgnore
3641 */
3642 public static function prepend_and_or_where( $starts_with = ' WHERE ', $where = '' ) {
3643 return FrmDeprecated::prepend_and_or_where( $starts_with, $where );
3644 }
3645
3646 /**
3647 * @since 2.0
3648 * @deprecated 5.0.13
3649 *
3650 * @return string The base Google APIS url for the current version of jQuery UI
3651 */
3652 public static function jquery_ui_base_url() {
3653 _deprecated_function( __FUNCTION__, '5.0.13', 'FrmProAppHelper::jquery_ui_base_url' );
3654 return is_callable( 'FrmProAppHelper::jquery_ui_base_url' ) ? FrmProAppHelper::jquery_ui_base_url() : '';
3655 }
3656 }
3657