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

3,646 lines 101.0 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.2.07';
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 * @since 3.0
1089 * @param array $atts
1090 */
1091 public static function add_new_item_link( $atts ) {
1092 if ( ! empty( $atts['new_link'] ) ) {
1093 ?>
1094 <a href="<?php echo esc_url( $atts['new_link'] ); ?>" class="button button-primary frm-button-primary frm-with-plus">
1095 <?php self::icon_by_class( 'frmfont frm_plus_icon frm_svg15' ); ?>
1096 <?php esc_html_e( 'Add New', 'formidable' ); ?>
1097 </a>
1098 <?php
1099 } elseif ( ! empty( $atts['trigger_new_form_modal'] ) ) {
1100 ?>
1101 <a href="#" class="button button-primary frm-button-primary frm-with-plus frm-trigger-new-form-modal">
1102 <?php
1103 self::icon_by_class( 'frmfont frm_plus_icon frm_svg15' );
1104 esc_html_e( 'Add New', 'formidable' );
1105 ?>
1106 </a>
1107 <?php
1108 } elseif ( isset( $atts['link_hook'] ) ) {
1109 do_action( $atts['link_hook']['hook'], $atts['link_hook']['param'] );
1110 }
1111 }
1112
1113 /**
1114 * @since 3.06
1115 */
1116 public static function show_search_box( $atts ) {
1117 $defaults = array(
1118 'placeholder' => '',
1119 'tosearch' => '',
1120 'text' => __( 'Search', 'formidable' ),
1121 'input_id' => '',
1122 );
1123 $atts = array_merge( $defaults, $atts );
1124
1125 if ( $atts['input_id'] === 'template' && empty( $atts['tosearch'] ) ) {
1126 $atts['tosearch'] = 'frm-card';
1127 }
1128
1129 $class = 'frm-search-input';
1130 if ( ! empty( $atts['tosearch'] ) ) {
1131 $class .= ' frm-auto-search';
1132 }
1133
1134 $input_id = $atts['input_id'] . '-search-input';
1135
1136 ?>
1137 <p class="frm-search">
1138 <label class="screen-reader-text" for="<?php echo esc_attr( $input_id ); ?>">
1139 <?php echo esc_html( $atts['text'] ); ?>:
1140 </label>
1141 <span class="frmfont frm_search_icon"></span>
1142 <input type="search" id="<?php echo esc_attr( $input_id ); ?>" name="s"
1143 value="<?php _admin_search_query(); ?>" placeholder="<?php echo esc_attr( $atts['placeholder'] ); ?>"
1144 class="<?php echo esc_attr( $class ); ?>" data-tosearch="<?php echo esc_attr( $atts['tosearch'] ); ?>"
1145 <?php if ( ! empty( $atts['tosearch'] ) ) { ?>
1146 autocomplete="off"
1147 <?php } ?>
1148 />
1149 <?php
1150 if ( empty( $atts['tosearch'] ) ) {
1151 submit_button( $atts['text'], 'button-secondary', '', false, array( 'id' => 'search-submit' ) );
1152 }
1153 ?>
1154 </p>
1155 <?php
1156 }
1157
1158 /**
1159 * @param string $type
1160 */
1161 public static function trigger_hook_load( $type, $object = null ) {
1162 // Only load the form hooks once.
1163 $hooks_loaded = apply_filters( 'frm_' . $type . '_hooks_loaded', false, $object );
1164 if ( ! $hooks_loaded ) {
1165 do_action( 'frm_load_' . $type . '_hooks' );
1166 }
1167 }
1168
1169 /**
1170 * Save all front-end js scripts into a single file
1171 *
1172 * @since 3.0
1173 */
1174 public static function save_combined_js() {
1175 $file_atts = apply_filters(
1176 'frm_js_location',
1177 array(
1178 'file_name' => 'frm.min.js',
1179 'new_file_path' => self::plugin_path() . '/js',
1180 )
1181 );
1182 $new_file = new FrmCreateFile( $file_atts );
1183
1184 $files = array(
1185 self::plugin_path() . '/js/formidable.min.js',
1186 );
1187 $files = apply_filters( 'frm_combined_js_files', $files );
1188 $new_file->combine_files( $files );
1189 }
1190
1191 /**
1192 * Check a value from a shortcode to see if true or false.
1193 * True when value is 1, true, 'true', 'yes'
1194 *
1195 * @since 1.07.10
1196 *
1197 * @param string $value The value to compare
1198 *
1199 * @return boolean True or False
1200 */
1201 public static function is_true( $value ) {
1202 return ( true === $value || 1 == $value || 'true' == $value || 'yes' == $value );
1203 }
1204
1205 /**
1206 * Gets all post from a specific post type.
1207 * 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.
1208 *
1209 * @since 4.10.01 Add `$post_type` argument.
1210 *
1211 * @param string $post_type Post type to query. Default is `page`.
1212 * @return WP_Post[]
1213 */
1214 public static function get_pages( $post_type = 'page' ) {
1215 $query = array(
1216 'post_type' => $post_type,
1217 'post_status' => array( 'publish', 'private' ),
1218 'numberposts' => - 1,
1219 'orderby' => 'title',
1220 'order' => 'ASC',
1221 );
1222
1223 return get_posts( $query );
1224 }
1225
1226 /**
1227 * Gets post ids and titles for a specific post type.
1228 *
1229 * @since 5.0.09
1230 *
1231 * @param string $post_type Post type to query. Default is `page`.
1232 * @return array
1233 */
1234 public static function get_post_ids_and_titles( $post_type = 'page' ) {
1235 return FrmDb::get_results(
1236 'posts',
1237 array(
1238 'post_type' => $post_type,
1239 'post_status' => array( 'publish', 'private' ),
1240 ),
1241 'ID, post_title',
1242 array(
1243 'order_by' => 'post_title ASC',
1244 )
1245 );
1246 }
1247
1248 /**
1249 * Renders an autocomplete page selection or a regular dropdown depending on
1250 * the total page count
1251 *
1252 * @since 4.03.06
1253 * @since 4.10.01 Added `post_type` and `autocomplete_placeholder` to the arguments array.
1254 *
1255 * @param array $args Selection arguments.
1256 */
1257 public static function maybe_autocomplete_pages_options( $args ) {
1258 $args = self::preformat_selection_args( $args );
1259
1260 $pages_count = wp_count_posts( $args['post_type'] );
1261
1262 if ( $pages_count->publish <= 50 ) {
1263 self::wp_pages_dropdown( $args );
1264 return;
1265 }
1266
1267 wp_enqueue_script( 'jquery-ui-autocomplete' );
1268
1269 $selected = self::get_post_param( $args['field_name'], $args['page_id'], 'absint' );
1270 $title = '';
1271
1272 if ( $selected ) {
1273 $title = get_the_title( $selected );
1274 }
1275
1276 ?>
1277 <input type="text" class="frm-page-search"
1278 data-post-type="<?php echo esc_attr( $args['post_type'] ); ?>"
1279 placeholder="<?php echo esc_attr( $args['autocomplete_placeholder'] ); ?>"
1280 value="<?php echo esc_attr( $title ); ?>" />
1281 <input type="hidden" name="<?php echo esc_attr( $args['field_name'] ); ?>"
1282 class="frm_autocomplete_value_input"
1283 value="<?php echo esc_attr( $selected ); ?>" />
1284 <?php
1285 }
1286
1287 /**
1288 * @param array $args
1289 * @param string $page_id Deprecated.
1290 * @param boolean $truncate Deprecated.
1291 */
1292 public static function wp_pages_dropdown( $args = array(), $page_id = '', $truncate = false ) {
1293 self::prep_page_dropdown_params( $page_id, $truncate, $args );
1294
1295 $pages = self::get_post_ids_and_titles( $args['post_type'] );
1296 $selected = self::get_post_param( $args['field_name'], $args['page_id'], 'absint' );
1297
1298 ?>
1299 <select name="<?php echo esc_attr( $args['field_name'] ); ?>" id="<?php echo esc_attr( $args['field_name'] ); ?>" class="frm-pages-dropdown">
1300 <option value=""><?php echo esc_html( $args['placeholder'] ); ?></option>
1301 <?php foreach ( $pages as $page ) { ?>
1302 <option value="<?php echo esc_attr( $page->ID ); ?>" <?php selected( $selected, $page->ID ); ?>>
1303 <?php echo esc_html( $args['truncate'] ? self::truncate( $page->post_title, $args['truncate'] ) : $page->post_title ); ?>
1304 </option>
1305 <?php } ?>
1306 </select>
1307 <?php
1308 }
1309
1310 /**
1311 * Fill in missing parameters passed to wp_pages_dropdown().
1312 * This is for reverse compatibility with switching 3 params to 1.
1313 *
1314 * @since 4.03.06
1315 */
1316 private static function prep_page_dropdown_params( $page_id, $truncate, &$args ) {
1317 if ( ! is_array( $args ) ) {
1318 $args = array(
1319 'field_name' => $args,
1320 'page_id' => $page_id,
1321 'truncate' => $truncate,
1322 );
1323 }
1324
1325 $args = self::preformat_selection_args( $args );
1326 }
1327
1328 /**
1329 * Filter to format args for page dropdown or autocomplete
1330 *
1331 * @since 4.03.06
1332 * @since 4.10.01 Added `post_type` and `autocomplete_placeholder` to the arguments array.
1333 */
1334 private static function preformat_selection_args( $args ) {
1335 $defaults = array(
1336 'truncate' => false,
1337 'placeholder' => ' ',
1338 'field_name' => '',
1339 'page_id' => '',
1340 'post_type' => 'page',
1341 'autocomplete_placeholder' => __( 'Select a Page', 'formidable' ),
1342 );
1343
1344 return array_merge( $defaults, $args );
1345 }
1346
1347 public static function post_edit_link( $post_id ) {
1348 $post = get_post( $post_id );
1349 if ( $post ) {
1350 $post_url = admin_url( 'post.php?post=' . $post_id . '&action=edit' );
1351 $post_url = self::maybe_full_screen_link( $post_url );
1352
1353 return '<a href="' . esc_url( $post_url ) . '">' . self::truncate( $post->post_title, 50 ) . '</a>';
1354 }
1355
1356 return '';
1357 }
1358
1359 /**
1360 * Hide the WordPress menus on some pages.
1361 *
1362 * @since 4.0
1363 */
1364 public static function is_full_screen() {
1365 $full_builder = self::is_form_builder_page();
1366 $styler = self::is_admin_page( 'formidable-styles' ) || self::is_admin_page( 'formidable-styles2' );
1367 $full_entries = self::simple_get( 'frm-full', 'absint' );
1368
1369 return $full_builder || $full_entries || $styler || self::is_view_builder_page();
1370 }
1371
1372 /**
1373 * @since 4.0
1374 */
1375 public static function maybe_full_screen_link( $link ) {
1376 $is_full = self::simple_get( 'frm-full', 'absint' );
1377 if ( $is_full && ! empty( $link ) && $link !== '#' ) {
1378 $link .= '&frm-full=1';
1379 }
1380 return $link;
1381 }
1382
1383 /**
1384 * @param string $field_name
1385 * @param string|array $capability
1386 * @param string $multiple 'single' and 'multiple'
1387 */
1388 public static function wp_roles_dropdown( $field_name, $capability, $multiple = 'single' ) {
1389 ?>
1390 <select name="<?php echo esc_attr( $field_name ); ?>" id="<?php echo esc_attr( $field_name ); ?>"
1391 <?php echo ( 'multiple' === $multiple ) ? 'multiple="multiple"' : ''; ?>
1392 class="frm_multiselect">
1393 <?php self::roles_options( $capability ); ?>
1394 </select>
1395 <?php
1396 }
1397
1398 /**
1399 * @since 4.07
1400 * @param array|string $selected
1401 * @param string $current
1402 */
1403 private static function selected( $selected, $current ) {
1404 if ( is_callable( 'FrmProAppHelper::selected' ) ) {
1405 FrmProAppHelper::selected( $selected, $current );
1406 } else {
1407 selected( in_array( $current, (array) $selected, true ) );
1408 }
1409 }
1410
1411 /**
1412 * @param string|array $capability
1413 */
1414 public static function roles_options( $capability ) {
1415 global $frm_vars;
1416 if ( isset( $frm_vars['editable_roles'] ) ) {
1417 $editable_roles = $frm_vars['editable_roles'];
1418 } else {
1419 $editable_roles = get_editable_roles();
1420 $frm_vars['editable_roles'] = $editable_roles;
1421 }
1422
1423 foreach ( $editable_roles as $role => $details ) {
1424 $name = translate_user_role( $details['name'] );
1425 ?>
1426 <option value="<?php echo esc_attr( $role ); ?>" <?php self::selected( $capability, $role ); ?>><?php echo esc_attr( $name ); ?> </option>
1427 <?php
1428 unset( $role, $details );
1429 }
1430 }
1431
1432 /**
1433 * Gets the list of capabilities.
1434 *
1435 * @since 5.0 Parameter `$type` supports `pro_only` value.
1436 *
1437 * @param string $type Supports `auto`, `pro`, or `pro_only`.
1438 * @return array
1439 */
1440 public static function frm_capabilities( $type = 'auto' ) {
1441 $cap = array(
1442 'frm_view_forms' => __( 'View Forms', 'formidable' ),
1443 'frm_edit_forms' => __( 'Add and Edit Forms', 'formidable' ),
1444 'frm_delete_forms' => __( 'Delete Forms', 'formidable' ),
1445 'frm_change_settings' => __( 'Access this Settings Page', 'formidable' ),
1446 'frm_view_entries' => __( 'View Entries from Admin Area', 'formidable' ),
1447 'frm_delete_entries' => __( 'Delete Entries from Admin Area', 'formidable' ),
1448 );
1449
1450 if ( ! self::pro_is_installed() && 'pro' !== $type && 'pro_only' !== $type ) {
1451 return $cap;
1452 }
1453
1454 $pro_cap = array(
1455 'frm_create_entries' => __( 'Add Entries from Admin Area', 'formidable' ),
1456 'frm_edit_entries' => __( 'Edit Entries from Admin Area', 'formidable' ),
1457 'frm_view_reports' => __( 'View Reports', 'formidable' ),
1458 'frm_edit_displays' => __( 'Add/Edit Views', 'formidable' ),
1459 );
1460
1461 if ( 'pro_only' === $type ) {
1462 return $pro_cap;
1463 }
1464
1465 return $cap + $pro_cap;
1466 }
1467
1468 /**
1469 * Call the WordPress current_user_can but also validate empty strings as true for any logged in user
1470 *
1471 * @since 4.06.03
1472 *
1473 * @param string $role
1474 *
1475 * @return bool
1476 */
1477 public static function current_user_can( $role ) {
1478 if ( $role === '-1' ) {
1479 return false;
1480 }
1481
1482 if ( $role === 'loggedout' ) {
1483 return ! is_user_logged_in();
1484 }
1485
1486 if ( $role === 'loggedin' || ! $role ) {
1487 return is_user_logged_in();
1488 }
1489
1490 if ( $role == 1 ) {
1491 $role = 'administrator';
1492 }
1493
1494 if ( ! is_user_logged_in() ) {
1495 return false;
1496 }
1497
1498 return current_user_can( $role );
1499 }
1500
1501 /**
1502 * @param string|array $needed_role
1503 * @return bool
1504 */
1505 public static function user_has_permission( $needed_role ) {
1506 if ( is_array( $needed_role ) ) {
1507 foreach ( $needed_role as $role ) {
1508 if ( self::current_user_can( $role ) ) {
1509 return true;
1510 }
1511 }
1512
1513 return false;
1514 }
1515
1516 $can = self::current_user_can( $needed_role );
1517
1518 if ( $can || in_array( $needed_role, array( '-1', 'loggedout' ) ) ) {
1519 return $can;
1520 }
1521
1522 $roles = array( 'administrator', 'editor', 'author', 'contributor', 'subscriber' );
1523 foreach ( $roles as $role ) {
1524 if ( current_user_can( $role ) ) {
1525 return true;
1526 }
1527 if ( $role == $needed_role ) {
1528 break;
1529 }
1530 }
1531
1532 return false;
1533 }
1534
1535 /**
1536 * Make sure administrators can see Formidable menu
1537 *
1538 * @since 2.0
1539 */
1540 public static function maybe_add_permissions() {
1541 self::force_capability( 'frm_view_entries' );
1542
1543 if ( ! current_user_can( 'administrator' ) || current_user_can( 'frm_view_forms' ) ) {
1544 return;
1545 }
1546
1547 $user_id = get_current_user_id();
1548 $user = new WP_User( $user_id );
1549 $frm_roles = self::frm_capabilities();
1550 foreach ( $frm_roles as $frm_role => $frm_role_description ) {
1551 $user->add_cap( $frm_role );
1552 unset( $frm_role, $frm_role_description );
1553 }
1554 }
1555
1556 /**
1557 * Make sure admins have permission to see the menu items
1558 *
1559 * @since 2.0.6
1560 */
1561 public static function force_capability( $cap = 'frm_change_settings' ) {
1562 if ( current_user_can( 'administrator' ) && ! current_user_can( $cap ) ) {
1563 $role = get_role( 'administrator' );
1564 $frm_roles = self::frm_capabilities();
1565 foreach ( $frm_roles as $frm_role => $frm_role_description ) {
1566 $role->add_cap( $frm_role );
1567 }
1568 }
1569 }
1570
1571 /**
1572 * Check if the user has permision for action.
1573 * Return permission message and stop the action if no permission
1574 *
1575 * @since 2.0
1576 *
1577 * @param string $permission
1578 */
1579 public static function permission_check( $permission, $show_message = 'show' ) {
1580 $permission_error = self::permission_nonce_error( $permission );
1581 if ( $permission_error !== false ) {
1582 if ( 'hide' == $show_message ) {
1583 $permission_error = '';
1584 }
1585 wp_die( esc_html( $permission_error ) );
1586 }
1587 }
1588
1589 /**
1590 * Check user permission and nonce
1591 *
1592 * @since 2.0
1593 *
1594 * @param string $permission
1595 *
1596 * @return false|string The permission message or false if allowed
1597 */
1598 public static function permission_nonce_error( $permission, $nonce_name = '', $nonce = '' ) {
1599 if ( ! empty( $permission ) && ! current_user_can( $permission ) && ! current_user_can( 'administrator' ) ) {
1600 $frm_settings = self::get_settings();
1601
1602 return $frm_settings->admin_permission;
1603 }
1604
1605 $error = false;
1606 if ( empty( $nonce_name ) ) {
1607 return $error;
1608 }
1609
1610 $nonce_value = ( $_REQUEST && isset( $_REQUEST[ $nonce_name ] ) ) ? sanitize_text_field( wp_unslash( $_REQUEST[ $nonce_name ] ) ) : '';
1611 if ( $_REQUEST && ( ! isset( $_REQUEST[ $nonce_name ] ) || ! wp_verify_nonce( $nonce_value, $nonce ) ) ) {
1612 $frm_settings = self::get_settings();
1613 $error = $frm_settings->admin_permission;
1614 }
1615
1616 return $error;
1617 }
1618
1619 public static function checked( $values, $current ) {
1620 if ( self::check_selected( $values, $current ) ) {
1621 echo ' checked="checked"';
1622 }
1623 }
1624
1625 public static function check_selected( $values, $current ) {
1626 $values = self::recursive_function_map( $values, 'trim' );
1627 $values = self::recursive_function_map( $values, 'htmlspecialchars_decode' );
1628 $current = htmlspecialchars_decode( trim( $current ) );
1629
1630 return ( is_array( $values ) && in_array( $current, $values ) ) || ( ! is_array( $values ) && $values == $current );
1631 }
1632
1633 public static function recursive_function_map( $value, $function ) {
1634 if ( is_array( $value ) ) {
1635 $original_function = $function;
1636 if ( count( $value ) ) {
1637 $function = explode( ', ', FrmDb::prepare_array_values( $value, $function ) );
1638 } else {
1639 $function = array( $function );
1640 }
1641 if ( ! self::is_assoc( $value ) ) {
1642 $value = array_map( array( 'FrmAppHelper', 'recursive_function_map' ), $value, $function );
1643 } else {
1644 foreach ( $value as $k => $v ) {
1645 if ( ! is_array( $v ) ) {
1646 $value[ $k ] = call_user_func( $original_function, $v );
1647 }
1648 }
1649 }
1650 } else {
1651 $value = call_user_func( $function, $value );
1652 }
1653
1654 return $value;
1655 }
1656
1657 public static function is_assoc( $array ) {
1658 return (bool) count( array_filter( array_keys( $array ), 'is_string' ) );
1659 }
1660
1661 /**
1662 * Flatten a multi-dimensional array
1663 */
1664 public static function array_flatten( $array, $keys = 'keep' ) {
1665 $return = array();
1666 foreach ( $array as $key => $value ) {
1667 if ( is_array( $value ) ) {
1668 $return = array_merge( $return, self::array_flatten( $value, $keys ) );
1669 } else {
1670 if ( $keys == 'keep' ) {
1671 $return[ $key ] = $value;
1672 } else {
1673 $return[] = $value;
1674 }
1675 }
1676 }
1677
1678 return $return;
1679 }
1680
1681 public static function esc_textarea( $text, $is_rich_text = false ) {
1682 $safe_text = str_replace( '&quot;', '"', $text );
1683 if ( ! $is_rich_text ) {
1684 $safe_text = htmlspecialchars( $safe_text, ENT_NOQUOTES );
1685 }
1686 $safe_text = str_replace( '&amp; ', '& ', $safe_text );
1687
1688 return apply_filters( 'esc_textarea', $safe_text, $text );
1689 }
1690
1691 /**
1692 * Add auto paragraphs to text areas
1693 *
1694 * @since 2.0
1695 */
1696 public static function use_wpautop( $content ) {
1697 if ( apply_filters( 'frm_use_wpautop', true ) && ! is_array( $content ) ) {
1698 $content = wpautop( str_replace( '<br>', '<br />', $content ) );
1699 }
1700
1701 return $content;
1702 }
1703
1704 public static function replace_quotes( $val ) {
1705 // Replace double quotes.
1706 $val = str_replace( array( '&#8220;', '&#8221;', '&#8243;' ), '"', $val );
1707
1708 // Replace single quotes.
1709 $val = str_replace( array( '&#8216;', '&#8217;', '&#8242;', '&prime;', '&rsquo;', '&lsquo;' ), "'", $val );
1710
1711 return $val;
1712 }
1713
1714 /**
1715 * @param string $handle
1716 */
1717 public static function script_version( $handle, $default = 0 ) {
1718 global $wp_scripts;
1719 if ( ! $wp_scripts ) {
1720 return $default;
1721 }
1722
1723 $ver = $default;
1724 if ( ! isset( $wp_scripts->registered[ $handle ] ) ) {
1725 return $ver;
1726 }
1727
1728 $query = $wp_scripts->registered[ $handle ];
1729 if ( is_object( $query ) && ! empty( $query->ver ) ) {
1730 $ver = $query->ver;
1731 }
1732
1733 return $ver;
1734 }
1735
1736 /**
1737 * @since 5.0.13 added $echo param.
1738 *
1739 * @param string $url
1740 * @param bool $echo
1741 * @return string|void
1742 */
1743 public static function js_redirect( $url, $echo = false ) {
1744 $callback = function() use ( $url ) {
1745 echo '<script type="text/javascript">window.location="' . esc_url_raw( $url ) . '"</script>';
1746 };
1747 return self::clip( $callback, $echo );
1748 }
1749
1750 public static function get_user_id_param( $user_id ) {
1751 if ( ! $user_id || is_numeric( $user_id ) ) {
1752 return $user_id;
1753 }
1754
1755 $user_id = sanitize_text_field( $user_id );
1756 if ( $user_id == 'current' ) {
1757 $user_id = get_current_user_id();
1758 } else {
1759 if ( is_email( $user_id ) ) {
1760 $user = get_user_by( 'email', $user_id );
1761 } else {
1762 $user = get_user_by( 'login', $user_id );
1763 }
1764
1765 if ( $user ) {
1766 $user_id = $user->ID;
1767 }
1768 unset( $user );
1769 }
1770
1771 return $user_id;
1772 }
1773
1774 public static function get_file_contents( $filename, $atts = array() ) {
1775 if ( ! is_file( $filename ) ) {
1776 return false;
1777 }
1778
1779 extract( $atts ); // phpcs:ignore WordPress.PHP.DontExtract
1780 ob_start();
1781 include( $filename );
1782 $contents = ob_get_contents();
1783 ob_end_clean();
1784
1785 return $contents;
1786 }
1787
1788 /**
1789 * @param string $name
1790 * @param string $table_name
1791 * @param string $column
1792 * @param int $id
1793 * @param int $num_chars
1794 */
1795 public static function get_unique_key( $name, $table_name, $column, $id = 0, $num_chars = 5 ) {
1796 $key = '';
1797 if ( $name ) {
1798 $key = sanitize_key( $name );
1799 $key = self::maybe_clear_long_key( $key, $column );
1800 }
1801
1802 if ( ! $key ) {
1803 $key = self::generate_new_key( $num_chars );
1804 }
1805
1806 $key = self::prevent_numeric_and_reserved_keys( $key );
1807
1808 $similar_keys = FrmDb::get_col(
1809 $table_name,
1810 array(
1811 $column . ' like%' => $key,
1812 'ID !' => $id,
1813 ),
1814 $column
1815 );
1816
1817 // Create a unique field id if it has already been used.
1818 if ( in_array( $key, $similar_keys, true ) ) {
1819 $key = self::maybe_truncate_key_before_appending( $column, $key );
1820
1821 /**
1822 * Allow for a custom separator between the attempted key and the generated suffix.
1823 *
1824 * @since 5.2.03
1825 *
1826 * @param string $separator. Default empty.
1827 * @param string $key the key without the added suffix.
1828 */
1829 $separator = apply_filters( 'frm_unique_' . $column . '_separator', '', $key );
1830
1831 $suffix = 2;
1832 do {
1833 $key_check = $key . $separator . $suffix;
1834 ++$suffix;
1835 } while ( in_array( $key_check, $similar_keys, true ) );
1836
1837 $key = $key_check;
1838 }
1839
1840 return $key;
1841 }
1842
1843 /**
1844 * Avoid trying to append to a really long key,
1845 * The database limit is 100 for form and field keys so we want to avoid getting too close.
1846 *
1847 * @param string $column
1848 * @param string $key
1849 * @return string
1850 */
1851 private static function maybe_truncate_key_before_appending( $column, $key ) {
1852 if ( in_array( $column, array( 'form_key', 'field_key' ), true ) ) {
1853 $max_key_length_before_truncating = 60;
1854 if ( strlen( $key ) > $max_key_length_before_truncating ) {
1855 $key = substr( $key, 0, $max_key_length_before_truncating );
1856 if ( is_numeric( $key ) ) {
1857 $key .= 'a';
1858 }
1859 }
1860 }
1861 return $key;
1862 }
1863
1864 /**
1865 * Possibly reset a key to avoid conflicts with column size limits.
1866 *
1867 * @param string $key
1868 * @param string $column
1869 * @return string either the original key value, or an empty string if the key was too long.
1870 */
1871 private static function maybe_clear_long_key( $key, $column ) {
1872 if ( 'field_key' === $column && strlen( $key ) >= 70 ) {
1873 $key = '';
1874 }
1875 return $key;
1876 }
1877
1878 /**
1879 * @param int $num_chars
1880 * @return string
1881 */
1882 private static function generate_new_key( $num_chars ) {
1883 $max_slug_value = pow( 36, $num_chars );
1884 $min_slug_value = 37; // we want to have at least 2 characters in the slug
1885 return base_convert( rand( $min_slug_value, $max_slug_value ), 10, 36 );
1886 }
1887
1888 /**
1889 * @param string $key
1890 * @return string
1891 */
1892 private static function prevent_numeric_and_reserved_keys( $key ) {
1893 if ( is_numeric( $key ) ) {
1894 $key .= 'a';
1895 } else {
1896 $not_allowed = array(
1897 'id',
1898 'key',
1899 'created-at',
1900 'detaillink',
1901 'editlink',
1902 'siteurl',
1903 'evenodd',
1904 );
1905 if ( in_array( $key, $not_allowed, true ) ) {
1906 $key .= 'a';
1907 }
1908 }
1909 return $key;
1910 }
1911
1912 /**
1913 * Editing a Form or Entry
1914 *
1915 * @param string $table
1916 *
1917 * @return bool|array
1918 */
1919 public static function setup_edit_vars( $record, $table, $fields = '', $default = false, $post_values = array(), $args = array() ) {
1920 if ( ! $record ) {
1921 return false;
1922 }
1923
1924 if ( empty( $post_values ) ) {
1925 $post_values = wp_unslash( $_POST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
1926 }
1927
1928 $values = array(
1929 'id' => $record->id,
1930 'fields' => array(),
1931 );
1932
1933 foreach ( array( 'name', 'description' ) as $var ) {
1934 $default_val = isset( $record->{$var} ) ? $record->{$var} : '';
1935 $values[ $var ] = self::get_param( $var, $default_val, 'get', 'wp_kses_post' );
1936 unset( $var, $default_val );
1937 }
1938
1939 $values['description'] = self::use_wpautop( $values['description'] );
1940
1941 self::fill_form_opts( $record, $table, $post_values, $values );
1942
1943 self::prepare_field_arrays( $fields, $record, $values, array_merge( $args, compact( 'default', 'post_values' ) ) );
1944
1945 if ( $table === 'entries' ) {
1946 $values = FrmEntriesHelper::setup_edit_vars( $values, $record );
1947 } elseif ( $table === 'forms' ) {
1948 $values = FrmFormsHelper::setup_edit_vars( $values, $record, $post_values );
1949 }
1950
1951 return $values;
1952 }
1953
1954 private static function prepare_field_arrays( $fields, $record, array &$values, $args ) {
1955 if ( ! empty( $fields ) ) {
1956 foreach ( (array) $fields as $field ) {
1957 if ( ! self::is_admin_page() ) {
1958 // Don't prep default values on the form settings page.
1959 $field->default_value = apply_filters( 'frm_get_default_value', $field->default_value, $field, true );
1960 }
1961 $args['parent_form_id'] = isset( $args['parent_form_id'] ) ? $args['parent_form_id'] : $field->form_id;
1962 self::fill_field_defaults( $field, $record, $values, $args );
1963 }
1964 }
1965 }
1966
1967 private static function fill_field_defaults( $field, $record, array &$values, $args ) {
1968 $post_values = $args['post_values'];
1969
1970 if ( $args['default'] ) {
1971 $meta_value = $field->default_value;
1972 } else {
1973 if ( $record->post_id && self::pro_is_installed() && isset( $field->field_options['post_field'] ) && $field->field_options['post_field'] ) {
1974 if ( ! isset( $field->field_options['custom_field'] ) ) {
1975 $field->field_options['custom_field'] = '';
1976 }
1977 $meta_value = FrmProEntryMetaHelper::get_post_value(
1978 $record->post_id,
1979 $field->field_options['post_field'],
1980 $field->field_options['custom_field'],
1981 array(
1982 'truncate' => false,
1983 'type' => $field->type,
1984 'form_id' => $field->form_id,
1985 'field' => $field,
1986 )
1987 );
1988 } else {
1989 $meta_value = FrmEntryMeta::get_meta_value( $record, $field->id );
1990 }
1991 }
1992
1993 $field_type = isset( $post_values['field_options'][ 'type_' . $field->id ] ) ? $post_values['field_options'][ 'type_' . $field->id ] : $field->type;
1994 if ( isset( $post_values['item_meta'][ $field->id ] ) ) {
1995 $new_value = $post_values['item_meta'][ $field->id ];
1996 self::unserialize_or_decode( $new_value );
1997 } else {
1998 $new_value = $meta_value;
1999 }
2000
2001 $field_array = self::start_field_array( $field );
2002 $field_array['value'] = $new_value;
2003 $field_array['type'] = apply_filters( 'frm_field_type', $field_type, $field, $new_value );
2004 $field_array['parent_form_id'] = $args['parent_form_id'];
2005
2006 $args['field_type'] = $field_type;
2007
2008 FrmFieldsHelper::prepare_edit_front_field( $field_array, $field, $values['id'], $args );
2009
2010 if ( ! isset( $field_array['unique'] ) || ! $field_array['unique'] ) {
2011 $field_array['unique_msg'] = '';
2012 }
2013
2014 $field_array = array_merge( (array) $field->field_options, $field_array );
2015
2016 $values['fields'][ $field->id ] = $field_array;
2017 }
2018
2019 /**
2020 * @since 3.0
2021 *
2022 * @param object $field
2023 *
2024 * @return array
2025 */
2026 public static function start_field_array( $field ) {
2027 return array(
2028 'id' => $field->id,
2029 'default_value' => $field->default_value,
2030 'name' => $field->name,
2031 'description' => $field->description,
2032 'options' => $field->options,
2033 'required' => $field->required,
2034 'field_key' => $field->field_key,
2035 'field_order' => $field->field_order,
2036 'form_id' => $field->form_id,
2037 );
2038 }
2039
2040 /**
2041 * @param string $table
2042 */
2043 private static function fill_form_opts( $record, $table, $post_values, array &$values ) {
2044 if ( $table == 'entries' ) {
2045 $form = $record->form_id;
2046 FrmForm::maybe_get_form( $form );
2047 } else {
2048 $form = $record;
2049 }
2050
2051 if ( ! $form ) {
2052 return;
2053 }
2054
2055 $values['form_name'] = isset( $record->form_id ) ? $form->name : '';
2056 $values['parent_form_id'] = isset( $record->form_id ) ? $form->parent_form_id : 0;
2057
2058 if ( ! is_array( $form->options ) ) {
2059 return;
2060 }
2061
2062 foreach ( $form->options as $opt => $value ) {
2063 if ( isset( $post_values[ $opt ] ) ) {
2064 $values[ $opt ] = $post_values[ $opt ];
2065 self::unserialize_or_decode( $values[ $opt ] );
2066 } else {
2067 $values[ $opt ] = $value;
2068 }
2069 }
2070
2071 self::fill_form_defaults( $post_values, $values );
2072 }
2073
2074 /**
2075 * Set to POST value or default
2076 */
2077 private static function fill_form_defaults( $post_values, array &$values ) {
2078 $form_defaults = FrmFormsHelper::get_default_opts();
2079
2080 foreach ( $form_defaults as $opt => $default ) {
2081 if ( ! isset( $values[ $opt ] ) || $values[ $opt ] == '' ) {
2082 $values[ $opt ] = ( $post_values && isset( $post_values['options'][ $opt ] ) ) ? $post_values['options'][ $opt ] : $default;
2083 }
2084
2085 unset( $opt, $default );
2086 }
2087
2088 if ( ! isset( $values['custom_style'] ) ) {
2089 $values['custom_style'] = self::custom_style_value( $post_values );
2090 }
2091
2092 foreach ( array( 'before', 'after', 'submit' ) as $h ) {
2093 if ( ! isset( $values[ $h . '_html' ] ) ) {
2094 $values[ $h . '_html' ] = ( isset( $post_values['options'][ $h . '_html' ] ) ? $post_values['options'][ $h . '_html' ] : FrmFormsHelper::get_default_html( $h ) );
2095 }
2096 unset( $h );
2097 }
2098 }
2099
2100 /**
2101 * @since 2.2.10
2102 *
2103 * @param array $post_values
2104 *
2105 * @return boolean|int
2106 */
2107 public static function custom_style_value( $post_values ) {
2108 if ( ! empty( $post_values ) && isset( $post_values['options']['custom_style'] ) ) {
2109 $custom_style = absint( $post_values['options']['custom_style'] );
2110 } else {
2111 $frm_settings = self::get_settings();
2112 $custom_style = ( $frm_settings->load_style != 'none' );
2113 }
2114
2115 return $custom_style;
2116 }
2117
2118 public static function truncate( $str, $length, $minword = 3, $continue = '...' ) {
2119 if ( is_array( $str ) ) {
2120 return '';
2121 }
2122
2123 $length = (int) $length;
2124 $str = wp_strip_all_tags( $str );
2125 $original_len = self::mb_function( array( 'mb_strlen', 'strlen' ), array( $str ) );
2126
2127 if ( $length == 0 ) {
2128 return '';
2129 } elseif ( $length <= 10 ) {
2130 $sub = self::mb_function( array( 'mb_substr', 'substr' ), array( $str, 0, $length ) );
2131
2132 return $sub . ( ( $length < $original_len ) ? $continue : '' );
2133 }
2134
2135 $sub = '';
2136 $len = 0;
2137
2138 $words = self::mb_function( array( 'mb_split', 'explode' ), array( ' ', $str ) );
2139
2140 foreach ( $words as $word ) {
2141 $part = ( ( $sub != '' ) ? ' ' : '' ) . $word;
2142 $total_len = self::mb_function( array( 'mb_strlen', 'strlen' ), array( $sub . $part ) );
2143 if ( $total_len > $length && substr_count( $sub, ' ' ) ) {
2144 break;
2145 }
2146
2147 $sub .= $part;
2148 $len += self::mb_function( array( 'mb_strlen', 'strlen' ), array( $part ) );
2149
2150 if ( substr_count( $sub, ' ' ) > $minword && $total_len >= $length ) {
2151 break;
2152 }
2153
2154 unset( $total_len, $word );
2155 }
2156
2157 return $sub . ( ( $len < $original_len ) ? $continue : '' );
2158 }
2159
2160 public static function mb_function( $function_names, $args ) {
2161 $mb_function_name = $function_names[0];
2162 $function_name = $function_names[1];
2163 if ( function_exists( $mb_function_name ) ) {
2164 $function_name = $mb_function_name;
2165 }
2166
2167 return call_user_func_array( $function_name, $args );
2168 }
2169
2170 public static function get_formatted_time( $date, $date_format = '', $time_format = '' ) {
2171 if ( empty( $date ) ) {
2172 return $date;
2173 }
2174
2175 if ( empty( $date_format ) ) {
2176 $date_format = get_option( 'date_format' );
2177 }
2178
2179 if ( preg_match( '/^\d{1-2}\/\d{1-2}\/\d{4}$/', $date ) && self::pro_is_installed() ) {
2180 $frmpro_settings = new FrmProSettings();
2181 $date = FrmProAppHelper::convert_date( $date, $frmpro_settings->date_format, 'Y-m-d' );
2182 }
2183
2184 $formatted = self::get_localized_date( $date_format, $date );
2185
2186 $do_time = ( gmdate( 'H:i:s', strtotime( $date ) ) != '00:00:00' );
2187 if ( $do_time ) {
2188 $formatted .= self::add_time_to_date( $time_format, $date );
2189 }
2190
2191 return $formatted;
2192 }
2193
2194 private static function add_time_to_date( $time_format, $date ) {
2195 if ( empty( $time_format ) ) {
2196 $time_format = get_option( 'time_format' );
2197 }
2198
2199 $trimmed_format = trim( $time_format );
2200 $time = '';
2201 if ( $time_format && ! empty( $trimmed_format ) ) {
2202 $time = ' ' . __( 'at', 'formidable' ) . ' ' . self::get_localized_date( $time_format, $date );
2203 }
2204
2205 return $time;
2206 }
2207
2208 /**
2209 * @since 2.0.8
2210 */
2211 public static function get_localized_date( $date_format, $date ) {
2212 $date = get_date_from_gmt( $date );
2213
2214 return date_i18n( $date_format, strtotime( $date ) );
2215 }
2216
2217 /**
2218 * Gets the time ago in words
2219 *
2220 * @param int $from in seconds
2221 * @param int|string $to in seconds
2222 *
2223 * @return string $time_ago
2224 */
2225 public static function human_time_diff( $from, $to = '', $levels = 1 ) {
2226 if ( empty( $to ) ) {
2227 $now = new DateTime();
2228 } else {
2229 $now = new DateTime( '@' . $to );
2230 }
2231 $ago = new DateTime( '@' . $from );
2232
2233 // Get the time difference
2234 $diff_object = $now->diff( $ago );
2235 $diff = get_object_vars( $diff_object );
2236
2237 // Add week amount and update day amount
2238 $diff['w'] = floor( $diff['d'] / 7 );
2239 $diff['d'] -= $diff['w'] * 7;
2240
2241 $time_strings = self::get_time_strings();
2242
2243 if ( ! is_numeric( $levels ) ) {
2244 // Show time in specified unit.
2245 $levels = self::get_unit( $levels );
2246 if ( isset( $time_strings[ $levels ] ) ) {
2247 $diff = array(
2248 $levels => self::time_format( $levels, $diff ),
2249 );
2250 $time_strings = array(
2251 $levels => $time_strings[ $levels ],
2252 );
2253 }
2254 $levels = 1;
2255 }
2256
2257 foreach ( $time_strings as $k => $v ) {
2258 if ( isset( $diff[ $k ] ) && $diff[ $k ] ) {
2259 $time_strings[ $k ] = $diff[ $k ] . ' ' . ( $diff[ $k ] > 1 ? $v[1] : $v[0] );
2260 } elseif ( isset( $diff[ $k ] ) && count( $time_strings ) === 1 ) {
2261 // Account for 0.
2262 $time_strings[ $k ] = $diff[ $k ] . ' ' . $v[1];
2263 } else {
2264 unset( $time_strings[ $k ] );
2265 }
2266 }
2267
2268 $levels_deep = apply_filters( 'frm_time_ago_levels', $levels, compact( 'time_strings', 'from', 'to' ) );
2269 $time_strings = array_slice( $time_strings, 0, absint( $levels_deep ) );
2270 $time_ago_string = implode( ' ', $time_strings );
2271
2272 return $time_ago_string;
2273 }
2274
2275 /**
2276 * @since 4.05.01
2277 */
2278 private static function time_format( $unit, $diff ) {
2279 $return = array(
2280 'y' => 'y',
2281 'd' => 'days',
2282 );
2283 if ( isset( $return[ $unit ] ) ) {
2284 return $diff[ $return[ $unit ] ];
2285 }
2286
2287 $total = $diff['days'] * self::convert_time( 'd', $unit );
2288
2289 $times = array( 'h', 'i', 's' );
2290
2291 foreach ( $times as $time ) {
2292 if ( ! isset( $diff[ $time ] ) ) {
2293 continue;
2294 }
2295
2296 $total += $diff[ $time ] * self::convert_time( $time, $unit );
2297 }
2298
2299 return floor( $total );
2300 }
2301
2302 /**
2303 * @since 4.05.01
2304 */
2305 private static function convert_time( $from, $to ) {
2306 $convert = array(
2307 's' => 1,
2308 'i' => MINUTE_IN_SECONDS,
2309 'h' => HOUR_IN_SECONDS,
2310 'd' => DAY_IN_SECONDS,
2311 'w' => WEEK_IN_SECONDS,
2312 'm' => DAY_IN_SECONDS * 30.42,
2313 'y' => DAY_IN_SECONDS * 365.25,
2314 );
2315
2316 return $convert[ $from ] / $convert[ $to ];
2317 }
2318
2319 /**
2320 * @since 4.05.01
2321 */
2322 private static function get_unit( $unit ) {
2323 $units = self::get_time_strings();
2324 if ( isset( $units[ $unit ] ) || is_numeric( $unit ) ) {
2325 return $unit;
2326 }
2327
2328 foreach ( $units as $u => $strings ) {
2329 if ( in_array( $unit, $strings ) ) {
2330 return $u;
2331 }
2332 }
2333 return 1;
2334 }
2335
2336 /**
2337 * Get the translatable time strings. The untranslated version is a failsafe
2338 * in case langauges are changing for the unit set in the shortcode.
2339 *
2340 * @since 2.0.20
2341 * @return array
2342 */
2343 private static function get_time_strings() {
2344 return array(
2345 'y' => array(
2346 __( 'year', 'formidable' ),
2347 __( 'years', 'formidable' ),
2348 'year',
2349 ),
2350 'm' => array(
2351 __( 'month', 'formidable' ),
2352 __( 'months', 'formidable' ),
2353 'month',
2354 ),
2355 'w' => array(
2356 __( 'week', 'formidable' ),
2357 __( 'weeks', 'formidable' ),
2358 'week',
2359 ),
2360 'd' => array(
2361 __( 'day', 'formidable' ),
2362 __( 'days', 'formidable' ),
2363 'day',
2364 ),
2365 'h' => array(
2366 __( 'hour', 'formidable' ),
2367 __( 'hours', 'formidable' ),
2368 'hour',
2369 ),
2370 'i' => array(
2371 __( 'minute', 'formidable' ),
2372 __( 'minutes', 'formidable' ),
2373 'minute',
2374 ),
2375 's' => array(
2376 __( 'second', 'formidable' ),
2377 __( 'seconds', 'formidable' ),
2378 'second',
2379 ),
2380 );
2381 }
2382
2383 // Pagination Methods.
2384
2385 /**
2386 * @param integer $current_p
2387 */
2388 public static function get_last_record_num( $r_count, $current_p, $p_size ) {
2389 return ( ( $r_count < ( $current_p * $p_size ) ) ? $r_count : ( $current_p * $p_size ) );
2390 }
2391
2392 /**
2393 * @param integer $current_p
2394 */
2395 public static function get_first_record_num( $r_count, $current_p, $p_size ) {
2396 if ( $current_p == 1 ) {
2397 return 1;
2398 } else {
2399 return ( self::get_last_record_num( $r_count, ( $current_p - 1 ), $p_size ) + 1 );
2400 }
2401 }
2402
2403 /**
2404 * @return array
2405 */
2406 public static function json_to_array( $json_vars ) {
2407 $vars = array();
2408 foreach ( $json_vars as $jv ) {
2409 $jv_name = explode( '[', $jv['name'] );
2410 $last = count( $jv_name ) - 1;
2411 foreach ( $jv_name as $p => $n ) {
2412 $name = trim( $n, ']' );
2413 if ( ! isset( $l1 ) ) {
2414 $l1 = $name;
2415 }
2416
2417 if ( ! isset( $l2 ) ) {
2418 $l2 = $name;
2419 }
2420
2421 if ( ! isset( $l3 ) ) {
2422 $l3 = $name;
2423 }
2424
2425 $this_val = ( $p == $last ) ? $jv['value'] : array();
2426
2427 switch ( $p ) {
2428 case 0:
2429 $l1 = $name;
2430 self::add_value_to_array( $name, $l1, $this_val, $vars );
2431 break;
2432
2433 case 1:
2434 $l2 = $name;
2435 self::add_value_to_array( $name, $l2, $this_val, $vars[ $l1 ] );
2436 break;
2437
2438 case 2:
2439 $l3 = $name;
2440 self::add_value_to_array( $name, $l3, $this_val, $vars[ $l1 ][ $l2 ] );
2441 break;
2442
2443 case 3:
2444 $l4 = $name;
2445 self::add_value_to_array( $name, $l4, $this_val, $vars[ $l1 ][ $l2 ][ $l3 ] );
2446 }
2447
2448 unset( $this_val, $n );
2449 }
2450
2451 unset( $last, $jv );
2452 }
2453
2454 return $vars;
2455 }
2456
2457 /**
2458 * @param string $name
2459 * @param string $l1
2460 */
2461 public static function add_value_to_array( $name, $l1, $val, &$vars ) {
2462 if ( $name == '' ) {
2463 $vars[] = $val;
2464 } elseif ( ! isset( $vars[ $l1 ] ) ) {
2465 $vars[ $l1 ] = $val;
2466 }
2467 }
2468
2469 public static function maybe_add_tooltip( $name, $class = 'closed', $form_name = '' ) {
2470 $tooltips = array(
2471 'action_title' => __( 'Give this action a label for easy reference.', 'formidable' ),
2472 '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' ),
2473 'cc' => __( 'Add CC addresses separated by a ",". FORMAT: Name <name@email.com> or name@email.com.', 'formidable' ),
2474 'bcc' => __( 'Add BCC addresses separated by a ",". FORMAT: Name <name@email.com> or name@email.com.', 'formidable' ),
2475 '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' ),
2476 'from' => __( 'Enter the name and/or email address of the sender. FORMAT: John Bates <john@example.com> or john@example.com.', 'formidable' ),
2477 /* translators: %1$s: Form name, %2$s: Date */
2478 '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() ) ),
2479 );
2480
2481 if ( ! isset( $tooltips[ $name ] ) ) {
2482 return;
2483 }
2484
2485 if ( 'open' == $class ) {
2486 echo ' frm_help"';
2487 } else {
2488 echo ' class="frm_help"';
2489 }
2490
2491 echo ' title="' . esc_attr( $tooltips[ $name ] );
2492
2493 if ( 'open' != $class ) {
2494 echo '"';
2495 }
2496 }
2497
2498 /**
2499 * Add the current_page class to that page in the form nav
2500 */
2501 public static function select_current_page( $page, $current_page, $action = array() ) {
2502 if ( $current_page != $page ) {
2503 return;
2504 }
2505
2506 $frm_action = self::simple_get( 'frm_action', 'sanitize_title' );
2507 if ( empty( $action ) || ( ! empty( $frm_action ) && in_array( $frm_action, $action ) ) ) {
2508 echo ' class="current_page"';
2509 }
2510 }
2511
2512 /**
2513 * Prepare and json_encode post content
2514 *
2515 * @since 2.0
2516 *
2517 * @param array $post_content
2518 *
2519 * @return string $post_content ( json encoded array )
2520 */
2521 public static function prepare_and_encode( $post_content ) {
2522 // Loop through array to strip slashes and add only the needed ones.
2523 foreach ( $post_content as $key => $val ) {
2524 // Replace problematic characters (like &quot;)
2525 if ( is_string( $val ) ) {
2526 $val = str_replace( '&quot;', '"', $val );
2527 }
2528
2529 self::prepare_action_slashes( $val, $key, $post_content );
2530 unset( $key, $val );
2531 }
2532
2533 // json_encode the array.
2534 $post_content = json_encode( $post_content );
2535
2536 // Add extra slashes for \r\n since WP strips them.
2537 $post_content = str_replace( array( '\\r', '\\n', '\\u', '\\t' ), array( '\\\\r', '\\\\n', '\\\\u', '\\\\t' ), $post_content );
2538
2539 // allow for &quot
2540 $post_content = str_replace( '&quot;', '\\"', $post_content );
2541
2542 return $post_content;
2543 }
2544
2545 private static function prepare_action_slashes( $val, $key, &$post_content ) {
2546 if ( ! isset( $post_content[ $key ] ) || is_numeric( $val ) ) {
2547 return;
2548 }
2549
2550 if ( is_array( $val ) ) {
2551 foreach ( $val as $k1 => $v1 ) {
2552 self::prepare_action_slashes( $v1, $k1, $post_content[ $key ] );
2553 unset( $k1, $v1 );
2554 }
2555 } else {
2556 // Strip all slashes so everything is the same, no matter where the value is coming from
2557 $val = stripslashes( $val );
2558
2559 // Add backslashes before double quotes and forward slashes only
2560 $post_content[ $key ] = addcslashes( $val, '"\\/' );
2561 }
2562 }
2563
2564 /**
2565 * Check for either json or serilized data. This is temporary while transitioning
2566 * all data to json.
2567 *
2568 * @since 4.02.03
2569 */
2570 public static function unserialize_or_decode( &$value ) {
2571 if ( is_array( $value ) ) {
2572 return;
2573 }
2574
2575 if ( is_serialized( $value ) ) {
2576 $value = maybe_unserialize( $value );
2577 } else {
2578 $value = self::maybe_json_decode( $value, false );
2579 }
2580 }
2581
2582 /**
2583 * Decode a JSON string.
2584 * Do not switch shortcodes like [24] to array unless intentional ie XML values.
2585 *
2586 * @param mixed $string
2587 * @param bool $single_to_array
2588 * @return mixed
2589 */
2590 public static function maybe_json_decode( $string, $single_to_array = true ) {
2591 if ( is_array( $string ) || is_null( $string ) ) {
2592 return $string;
2593 }
2594
2595 $new_string = json_decode( $string, true );
2596 if ( function_exists( 'json_last_error' ) ) {
2597 // php 5.3+
2598 $single_value = false;
2599 if ( ! $single_to_array ) {
2600 $single_value = is_array( $new_string ) && count( $new_string ) === 1 && isset( $new_string[0] );
2601 }
2602 if ( json_last_error() == JSON_ERROR_NONE && is_array( $new_string ) && ! $single_value ) {
2603 $string = $new_string;
2604 }
2605 }
2606
2607 return $string;
2608 }
2609
2610 /**
2611 * Reformat the json serialized array in name => value array.
2612 *
2613 * @since 4.02.03
2614 */
2615 public static function format_form_data( &$form ) {
2616 $formatted = array();
2617
2618 foreach ( $form as $input ) {
2619 if ( ! isset( $input['name'] ) ) {
2620 continue;
2621 }
2622 $key = $input['name'];
2623 if ( isset( $formatted[ $key ] ) ) {
2624 if ( is_array( $formatted[ $key ] ) ) {
2625 $formatted[ $key ][] = $input['value'];
2626 } else {
2627 $formatted[ $key ] = array( $formatted[ $key ], $input['value'] );
2628 }
2629 } else {
2630 $formatted[ $key ] = $input['value'];
2631 }
2632 }
2633
2634 parse_str( http_build_query( $formatted ), $form );
2635 }
2636
2637 /**
2638 * @since 4.02.03
2639 */
2640 public static function maybe_json_encode( $value ) {
2641 if ( is_array( $value ) ) {
2642 $value = wp_json_encode( $value );
2643 }
2644 return $value;
2645 }
2646
2647 /**
2648 * @since 1.07.10
2649 *
2650 * @param string $post_type The name of the post type that may need to be highlighted
2651 * echo The javascript to open and highlight the Formidable menu
2652 */
2653 public static function maybe_highlight_menu( $post_type ) {
2654 global $post;
2655
2656 if ( isset( $_REQUEST['post_type'] ) && $_REQUEST['post_type'] != $post_type ) {
2657 return;
2658 }
2659
2660 if ( is_object( $post ) && $post->post_type != $post_type ) {
2661 return;
2662 }
2663
2664 self::load_admin_wide_js();
2665 echo '<script type="text/javascript">jQuery(document).ready(function(){frmSelectSubnav();});</script>';
2666 }
2667
2668 /**
2669 * Load the JS file on non-Formidable pages in the admin area
2670 *
2671 * @since 2.0
2672 */
2673 public static function load_admin_wide_js( $load = true ) {
2674 $version = self::plugin_version();
2675 wp_register_script( 'formidable_admin_global', self::plugin_url() . '/js/formidable_admin_global.js', array( 'jquery' ), $version );
2676
2677 $global_strings = array(
2678 'updating_msg' => __( 'Please wait while your site updates.', 'formidable' ),
2679 'deauthorize' => __( 'Are you sure you want to deauthorize Formidable Forms on this site?', 'formidable' ),
2680 'url' => self::plugin_url(),
2681 'app_url' => 'https://formidableforms.com/',
2682 'loading' => __( 'Loading&hellip;', 'formidable' ),
2683 'nonce' => wp_create_nonce( 'frm_ajax' ),
2684 );
2685 wp_localize_script( 'formidable_admin_global', 'frmGlobal', $global_strings );
2686
2687 if ( $load ) {
2688 wp_enqueue_script( 'formidable_admin_global' );
2689 }
2690 }
2691
2692 /**
2693 * @since 2.0.9
2694 */
2695 public static function load_font_style() {
2696 wp_enqueue_style( 'frm_fonts', self::plugin_url() . '/css/frm_fonts.css', array(), self::plugin_version() );
2697 }
2698
2699 /**
2700 * @param string $location
2701 */
2702 public static function localize_script( $location ) {
2703 global $wp_scripts;
2704
2705 $ajax_url = admin_url( 'admin-ajax.php', is_ssl() ? 'admin' : 'http' );
2706 $ajax_url = apply_filters( 'frm_ajax_url', $ajax_url );
2707
2708 $script_strings = array(
2709 'ajax_url' => $ajax_url,
2710 'images_url' => self::plugin_url() . '/images',
2711 'loading' => __( 'Loading&hellip;', 'formidable' ),
2712 'remove' => __( 'Remove', 'formidable' ),
2713 'offset' => apply_filters( 'frm_scroll_offset', 4 ),
2714 'nonce' => wp_create_nonce( 'frm_ajax' ),
2715 'id' => __( 'ID', 'formidable' ),
2716 'no_results' => __( 'No results match', 'formidable' ),
2717 'file_spam' => __( 'That file looks like Spam.', 'formidable' ),
2718 'calc_error' => __( 'There is an error in the calculation in the field with key', 'formidable' ),
2719 'empty_fields' => __( 'Please complete the preceding required fields before uploading a file.', 'formidable' ),
2720 'focus_first_error' => self::should_focus_first_error(),
2721 'include_alert_role' => self::should_include_alert_role_on_field_errors(),
2722 );
2723
2724 $data = $wp_scripts->get_data( 'formidable', 'data' );
2725 if ( empty( $data ) ) {
2726 wp_localize_script( 'formidable', 'frm_js', $script_strings );
2727 }
2728
2729 if ( $location == 'admin' ) {
2730 $frm_settings = self::get_settings();
2731 $admin_script_strings = array(
2732 'desc' => __( '(Click to add description)', 'formidable' ),
2733 'blank' => __( '(Blank)', 'formidable' ),
2734 'no_label' => __( '(no label)', 'formidable' ),
2735 'saving' => esc_attr( __( 'Saving', 'formidable' ) ),
2736 'saved' => esc_attr( __( 'Saved', 'formidable' ) ),
2737 'ok' => __( 'OK', 'formidable' ),
2738 'cancel' => __( 'Cancel', 'formidable' ),
2739 'default_label' => __( 'Default', 'formidable' ),
2740 'clear_default' => __( 'Clear default value when typing', 'formidable' ),
2741 'no_clear_default' => __( 'Do not clear default value when typing', 'formidable' ),
2742 'valid_default' => __( 'Default value will pass form validation', 'formidable' ),
2743 'no_valid_default' => __( 'Default value will NOT pass form validation', 'formidable' ),
2744 'caution' => __( 'Heads up', 'formidable' ),
2745 'confirm' => __( 'Are you sure?', 'formidable' ),
2746 'conf_delete' => __( 'Are you sure you want to delete this field and all data associated with it?', 'formidable' ),
2747 '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' ),
2748 'conf_no_repeat' => __( 'Warning: If you have entries with multiple rows, all but the first row will be lost.', 'formidable' ),
2749 'default_unique' => $frm_settings->unique_msg,
2750 'default_conf' => __( 'The entered values do not match', 'formidable' ),
2751 'enter_email' => __( 'Enter Email', 'formidable' ),
2752 'confirm_email' => __( 'Confirm Email', 'formidable' ),
2753 'conditional_text' => __( 'Conditional content here', 'formidable' ),
2754 'new_option' => __( 'New Option', 'formidable' ),
2755 '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' ),
2756 'enter_password' => __( 'Enter Password', 'formidable' ),
2757 'confirm_password' => __( 'Confirm Password', 'formidable' ),
2758 'import_complete' => __( 'Import Complete', 'formidable' ),
2759 'updating' => __( 'Please wait while your site updates.', 'formidable' ),
2760 'no_save_warning' => __( 'Warning: There is no way to retrieve unsaved entries.', 'formidable' ),
2761 'private_label' => __( 'Private', 'formidable' ),
2762 'jquery_ui_url' => '',
2763 'pro_url' => is_callable( 'FrmProAppHelper::plugin_url' ) ? FrmProAppHelper::plugin_url() : '',
2764 'no_licenses' => __( 'No new licenses were found', 'formidable' ),
2765 'unmatched_parens' => __( 'This calculation has at least one unmatched ( ) { } [ ].', 'formidable' ),
2766 'view_shortcodes' => __( 'This calculation may have shortcodes that work in Views but not forms.', 'formidable' ),
2767 'text_shortcodes' => __( 'This calculation may have shortcodes that work in text calculations but not numeric calculations.', 'formidable' ),
2768 'only_one_action' => __( 'This form action is limited to one per form. Please edit the existing form action.', 'formidable' ),
2769 'unsafe_params' => FrmFormsHelper::reserved_words(),
2770 /* Translators: %s is the name of a Detail Page Slug that is a reserved word.*/
2771 'slug_is_reserved' => sprintf( __( 'The Detail Page Slug "%s" is reserved by WordPress. This may cause problems. Is this intentional?', 'formidable' ), '****' ),
2772 /* 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. */
2773 'param_is_reserved' => sprintf( __( 'The parameter "%s" is reserved by WordPress. This may cause problems when included in the URL. Is this intentional? ', 'formidable' ), '****' ),
2774 'reserved_words' => __( 'See the list of reserved words in WordPress.', 'formidable' ),
2775 'repeat_limit_min' => __( 'Please enter a Repeat Limit that is greater than 1.', 'formidable' ),
2776 'checkbox_limit' => __( 'Please select a limit between 0 and 200.', 'formidable' ),
2777 'install' => __( 'Install', 'formidable' ),
2778 'active' => __( 'Active', 'formidable' ),
2779 'select_a_field' => __( 'Select a Field', 'formidable' ),
2780 'no_items_found' => __( 'No items found.', 'formidable' ),
2781 );
2782 $admin_script_strings = apply_filters( 'frm_admin_script_strings', $admin_script_strings );
2783
2784 $data = $wp_scripts->get_data( 'formidable_admin', 'data' );
2785 if ( empty( $data ) ) {
2786 wp_localize_script( 'formidable_admin', 'frm_admin_js', $admin_script_strings );
2787 }
2788 }
2789 }
2790
2791 /**
2792 * Returns whether or not the first errored input should be auto-focused (default true).
2793 *
2794 * @since 5.2.05
2795 *
2796 * @return bool
2797 */
2798 private static function should_focus_first_error() {
2799 return (bool) apply_filters( 'frm_focus_first_error', true );
2800 }
2801
2802 /**
2803 * Returns whether or not field errors should include role="alert" (default true).
2804 *
2805 * @since 5.2.05
2806 *
2807 * @return bool
2808 */
2809 public static function should_include_alert_role_on_field_errors() {
2810 return (bool) apply_filters( 'frm_include_alert_role_on_field_errors', true );
2811 }
2812
2813 /**
2814 * Echo the message on the plugins listing page
2815 *
2816 * @since 1.07.10
2817 *
2818 * @param float $min_version The version the add-on requires
2819 */
2820 public static function min_version_notice( $min_version ) {
2821 $frm_version = self::plugin_version();
2822
2823 // Check if Formidable meets minimum requirements.
2824 if ( version_compare( $frm_version, $min_version, '>=' ) ) {
2825 return;
2826 }
2827
2828 $wp_list_table = _get_list_table( 'WP_Plugins_List_Table' );
2829 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">' .
2830 esc_html__( 'You are running an outdated version of Formidable. This plugin may not work correctly if you do not update Formidable.', 'formidable' ) .
2831 '</div></td></tr>';
2832 }
2833
2834 /**
2835 * If Pro is far outdated, show a message.
2836 *
2837 * @since 4.0.01
2838 */
2839 public static function min_pro_version_notice( $min_version ) {
2840 if ( ! self::is_formidable_admin() ) {
2841 // Don't show admin-wide.
2842 return;
2843 }
2844
2845 self::php_version_notice();
2846
2847 $is_pro = self::pro_is_installed() && class_exists( 'FrmProDb' );
2848 if ( ! $is_pro || self::meets_min_pro_version( $min_version ) ) {
2849 return;
2850 }
2851
2852 $pro_version = FrmProDb::$plug_version;
2853 $expired = FrmAddonsController::is_license_expired();
2854 ?>
2855 <div class="error frm_previous_install">
2856 <?php
2857 esc_html_e( 'You are running a version of Formidable Forms that may not be compatible with your version of Formidable Forms Pro.', 'formidable' );
2858 if ( empty( $expired ) ) {
2859 echo ' Please <a href="' . esc_url( admin_url( 'plugins.php?s=formidable%20forms%20pro' ) ) . '">update now</a>.';
2860 } else {
2861 echo '<br/>Please <a href="https://formidableforms.com/account/downloads/?utm_source=WordPress&utm_medium=outdated">renew now</a> to get the latest version.';
2862 }
2863 ?>
2864 </div>
2865 <?php
2866 }
2867
2868 /**
2869 * If Pro is installed, check the version number.
2870 *
2871 * @since 4.0.01
2872 */
2873 public static function meets_min_pro_version( $min_version ) {
2874 return ! class_exists( 'FrmProDb' ) || version_compare( FrmProDb::$plug_version, $min_version, '>=' );
2875 }
2876
2877 /**
2878 * Show a message if the browser or PHP version is below the recommendations.
2879 *
2880 * @since 4.0.02
2881 */
2882 private static function php_version_notice() {
2883 $message = array();
2884 if ( version_compare( phpversion(), '5.6', '<' ) ) {
2885 $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' );
2886 }
2887
2888 $browser = self::get_server_value( 'HTTP_USER_AGENT' );
2889 $is_ie = strpos( $browser, 'MSIE' ) !== false;
2890 if ( $is_ie ) {
2891 $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' );
2892 }
2893
2894 foreach ( $message as $m ) {
2895 ?>
2896 <div class="error frm_previous_install">
2897 <?php echo esc_html( $m ); ?>
2898 </div>
2899 <?php
2900 }
2901 }
2902
2903 public static function locales( $type = 'date' ) {
2904 $locales = array(
2905 'en' => __( 'English', 'formidable' ),
2906 'af' => __( 'Afrikaans', 'formidable' ),
2907 'sq' => __( 'Albanian', 'formidable' ),
2908 'ar' => __( 'Arabic', 'formidable' ),
2909 'hy' => __( 'Armenian', 'formidable' ),
2910 'az' => __( 'Azerbaijani', 'formidable' ),
2911 'eu' => __( 'Basque', 'formidable' ),
2912 'bs' => __( 'Bosnian', 'formidable' ),
2913 'bg' => __( 'Bulgarian', 'formidable' ),
2914 'ca' => __( 'Catalan', 'formidable' ),
2915 'zh-HK' => __( 'Chinese Hong Kong', 'formidable' ),
2916 'zh-CN' => __( 'Chinese Simplified', 'formidable' ),
2917 'zh-TW' => __( 'Chinese Traditional', 'formidable' ),
2918 'hr' => __( 'Croatian', 'formidable' ),
2919 'cs' => __( 'Czech', 'formidable' ),
2920 'da' => __( 'Danish', 'formidable' ),
2921 'nl' => __( 'Dutch', 'formidable' ),
2922 'en-GB' => __( 'English/UK', 'formidable' ),
2923 'eo' => __( 'Esperanto', 'formidable' ),
2924 'et' => __( 'Estonian', 'formidable' ),
2925 'fo' => __( 'Faroese', 'formidable' ),
2926 'fa' => __( 'Farsi/Persian', 'formidable' ),
2927 'fil' => __( 'Filipino', 'formidable' ),
2928 'fi' => __( 'Finnish', 'formidable' ),
2929 'fr' => __( 'French', 'formidable' ),
2930 'fr-CA' => __( 'French/Canadian', 'formidable' ),
2931 'fr-CH' => __( 'French/Swiss', 'formidable' ),
2932 'de' => __( 'German', 'formidable' ),
2933 'de-AT' => __( 'German/Austria', 'formidable' ),
2934 'de-CH' => __( 'German/Switzerland', 'formidable' ),
2935 'el' => __( 'Greek', 'formidable' ),
2936 'he' => __( 'Hebrew', 'formidable' ),
2937 'iw' => __( 'Hebrew', 'formidable' ),
2938 'hi' => __( 'Hindi', 'formidable' ),
2939 'hu' => __( 'Hungarian', 'formidable' ),
2940 'is' => __( 'Icelandic', 'formidable' ),
2941 'id' => __( 'Indonesian', 'formidable' ),
2942 'it' => __( 'Italian', 'formidable' ),
2943 'ja' => __( 'Japanese', 'formidable' ),
2944 'ko' => __( 'Korean', 'formidable' ),
2945 'lv' => __( 'Latvian', 'formidable' ),
2946 'lt' => __( 'Lithuanian', 'formidable' ),
2947 'ms' => __( 'Malaysian', 'formidable' ),
2948 'no' => __( 'Norwegian', 'formidable' ),
2949 'pl' => __( 'Polish', 'formidable' ),
2950 'pt' => __( 'Portuguese', 'formidable' ),
2951 'pt-BR' => __( 'Portuguese/Brazilian', 'formidable' ),
2952 'pt-PT' => __( 'Portuguese/Portugal', 'formidable' ),
2953 'ro' => __( 'Romanian', 'formidable' ),
2954 'ru' => __( 'Russian', 'formidable' ),
2955 'sr' => __( 'Serbian', 'formidable' ),
2956 'sr-SR' => __( 'Serbian', 'formidable' ),
2957 'sk' => __( 'Slovak', 'formidable' ),
2958 'sl' => __( 'Slovenian', 'formidable' ),
2959 'es' => __( 'Spanish', 'formidable' ),
2960 'es-419' => __( 'Spanish/Latin America', 'formidable' ),
2961 'sv' => __( 'Swedish', 'formidable' ),
2962 'ta' => __( 'Tamil', 'formidable' ),
2963 'th' => __( 'Thai', 'formidable' ),
2964 'tr' => __( 'Turkish', 'formidable' ),
2965 'uk' => __( 'Ukrainian', 'formidable' ),
2966 'vi' => __( 'Vietnamese', 'formidable' ),
2967 );
2968
2969 if ( $type === 'captcha' ) {
2970 // remove the languages unavailable for the captcha
2971 $unset = array( 'af', 'sq', 'hy', 'az', 'eu', 'bs', 'zh-HK', 'eo', 'et', 'fo', 'fr-CH', 'he', 'is', 'ms', 'sr-SR', 'ta' );
2972 } else {
2973 // remove the languages unavailable for the datepicker
2974 $unset = array( 'fil', 'fr-CA', 'de-AT', 'de-CH', 'iw', 'hi', 'pt', 'pt-PT', 'es-419' );
2975 }
2976
2977 $locales = array_diff_key( $locales, array_flip( $unset ) );
2978 $locales = apply_filters( 'frm_locales', $locales );
2979
2980 return $locales;
2981 }
2982
2983 /**
2984 * Output HTML containing reference text for accessibility
2985 *
2986 * @deprecated 5.1
2987 */
2988 public static function multiselect_accessibility() {
2989 _deprecated_function( __METHOD__, '5.1' );
2990 }
2991
2992 public static function get_menu_icon_class() {
2993 if ( is_callable( 'FrmProAppHelper::get_settings' ) ) {
2994 $settings = FrmProAppHelper::get_settings();
2995 if ( is_object( $settings ) && ! empty( $settings->menu_icon ) ) {
2996 return $settings->menu_icon;
2997 }
2998 }
2999 return 'frmfont frm_logo_icon';
3000 }
3001
3002 /**
3003 * Shows the images dropdown.
3004 *
3005 * @since 5.0.04
3006 *
3007 * @param array $args {
3008 * Arguments.
3009 *
3010 * @type string $selected Selected value.
3011 * @type array[] $options Array of options with keys are option values and values are array.
3012 * The option array contains `text`, `svg` and `custom_atts`.
3013 * @type string $classes Custom CSS classes for the wrapper element.
3014 * @type array $input_attrs Attributes of value input.
3015 * }
3016 */
3017 public static function images_dropdown( $args ) {
3018 $args = self::fill_default_images_dropdown_args( $args );
3019
3020 $input_attrs_str = self::get_images_dropdown_input_attrs( $args );
3021 ob_start();
3022 include self::plugin_path() . '/classes/views/shared/images-dropdown.php';
3023 $output = ob_get_clean();
3024
3025 /**
3026 * Allows modifying the output of FrmAppHelper::images_dropdown() method.
3027 *
3028 * @since 5.0.04
3029 *
3030 * @param string $output The output.
3031 * @param array $args Passed arguments.
3032 */
3033 echo apply_filters( 'frm_images_dropdown_output', $output, $args ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
3034 }
3035
3036 /**
3037 * Fills the default images_dropdown() arguments.
3038 *
3039 * @since 5.0.04
3040 *
3041 * @param array $args The arguments.
3042 * @return array
3043 */
3044 private static function fill_default_images_dropdown_args( $args ) {
3045 $defaults = array(
3046 'selected' => '',
3047 'options' => array(),
3048 'classes' => '',
3049 'input_attrs' => array(),
3050 );
3051 $new_args = wp_parse_args( $args, $defaults );
3052
3053 $new_args['options'] = (array) $new_args['options'];
3054 $new_args['input_attrs'] = (array) $new_args['input_attrs'];
3055
3056 // Set the number of columns.
3057 $new_args['col_class'] = ceil( 12 / count( $new_args['options'] ) );
3058 if ( $new_args['col_class'] > 6 ) {
3059 $new_args['col_class'] = ceil( $new_args['col_class'] / 2 );
3060 }
3061
3062 /**
3063 * Allows modifying the arguments of images_dropdown() method.
3064 *
3065 * @since 5.0.04
3066 *
3067 * @param array $new_args Arguments after filling the defaults.
3068 * @param array $args Arguments passed to the method, before filling the defaults.
3069 */
3070 return apply_filters( 'frm_images_dropdown_args', $new_args, $args );
3071 }
3072
3073 /**
3074 * Gets HTML attributes of the input in images_dropdown() method.
3075 *
3076 * @since 5.0.04
3077 *
3078 * @param array $args The arguments.
3079 * @return string
3080 */
3081 private static function get_images_dropdown_input_attrs( $args ) {
3082 $input_attrs = $args['input_attrs'];
3083 $input_attrs['type'] = 'radio';
3084 $input_attrs['name'] = $args['name'];
3085
3086 $input_attrs_str = '';
3087 foreach ( $input_attrs as $key => $input_attr ) {
3088 $input_attrs_str .= ' ' . sprintf( '%s="%s"', esc_attr( $key ), esc_attr( $input_attr ) );
3089 }
3090
3091 /**
3092 * Allows modifying the HTML attributes of the input in images_dropdown() method.
3093 *
3094 * @since 5.0.04
3095 *
3096 * @param string $input_attrs_str HTML attributes string.
3097 * @param array $args The arguments of images_dropdown() method.
3098 */
3099 return apply_filters( 'frm_images_dropdown_input_attrs', $input_attrs_str, $args );
3100 }
3101
3102 /**
3103 * Gets the image of each option in images_dropdown() method.
3104 *
3105 * @since 5.0.04
3106 *
3107 * @param array $option Option data.
3108 * @param array $args The arguments of images_dropdown() method.
3109 * @return string
3110 */
3111 private static function get_images_dropdown_option_image( $option, $args ) {
3112 $image = self::icon_by_class(
3113 'frmfont ' . $option['svg'],
3114 array(
3115 'echo' => false,
3116 )
3117 );
3118
3119 $args['option'] = $option;
3120
3121 /**
3122 * Allows modifying the image of each option in images_dropdown() method.
3123 *
3124 * @since 5.0.04
3125 *
3126 * @param string $image The image HTML.
3127 * @param array $args The arguments of images_dropdown() method, with `option` array is added.
3128 */
3129 return apply_filters( 'frm_images_dropdown_option_image', $image, $args );
3130 }
3131
3132 /**
3133 * Gets the HTML classes of each option in images_dropdown() method.
3134 *
3135 * @since 5.0.04
3136 *
3137 * @param array $option Option data.
3138 * @param array $args The arguments of images_dropdown() method.
3139 * @return string
3140 */
3141 private static function get_images_dropdown_option_classes( $option, $args ) {
3142 $classes = '';
3143
3144 if ( ! empty( $option['custom_attrs']['class'] ) ) {
3145 $classes .= ' ' . $option['custom_attrs']['class'];
3146 }
3147
3148 $args['option'] = $option;
3149
3150 /**
3151 * Allows modifying the CSS classes of each option in images_dropdown() method.
3152 *
3153 * @since 5.0.04
3154 *
3155 * @param string $classes CSS classes.
3156 * @param array $args The arguments of images_dropdown() method, with `option` array is added.
3157 */
3158 return apply_filters( 'frm_images_dropdown_option_classes', $classes, $args );
3159 }
3160
3161 /**
3162 * Gets the custom HTML attributes of each option in images_dropdown() method.
3163 *
3164 * @since 5.0.04
3165 *
3166 * @param array $option Option data.
3167 * @param array $args The arguments of images_dropdown() method.
3168 * @return string
3169 */
3170 private static function get_images_dropdown_option_html_attrs( $option, $args ) {
3171 $html_attrs = '';
3172 if ( ! empty( $option['custom_attrs'] ) && is_array( $option['custom_attrs'] ) ) {
3173 $html_attrs_arr = array();
3174
3175 foreach ( $option['custom_attrs'] as $key => $value ) {
3176 if ( in_array( $key, array( 'type', 'class', 'data-value' ) ) ) {
3177 continue;
3178 }
3179
3180 $html_attrs_arr[] = sprintf( '%s="%s"', esc_attr( $key ), esc_attr( $value ) );
3181 }
3182
3183 $html_attrs = implode( ' ', $html_attrs_arr );
3184 }
3185
3186 $args['option'] = $option;
3187
3188 /**
3189 * Allows modifying the custom HTML attributes of each option in images_dropdown() method.
3190 *
3191 * @since 5.0.04
3192 *
3193 * @param string $html_attrs The HTML attributes string.
3194 * @param array $args The arguments of images_dropdown() method, with `option` array is added.
3195 */
3196 return apply_filters( 'frm_images_dropdown_option_html_attrs', $html_attrs, $args );
3197 }
3198
3199 /**
3200 * @since 5.0.07
3201 *
3202 * @return bool true if the current user is allowed to save unfiltered HTML.
3203 */
3204 public static function allow_unfiltered_html() {
3205 if ( self::should_never_allow_unfiltered_html() ) {
3206 return false;
3207 }
3208 return current_user_can( 'unfiltered_html' );
3209 }
3210
3211 /**
3212 * @since 5.0.13
3213 *
3214 * @return bool
3215 */
3216 public static function should_never_allow_unfiltered_html() {
3217 if ( defined( 'DISALLOW_UNFILTERED_HTML' ) && DISALLOW_UNFILTERED_HTML ) {
3218 return true;
3219 }
3220
3221 /**
3222 * Formidable will check DISALLOW_UNFILTERED_HTML to determine if some form HTML should be filtered or not.
3223 * In many cases, scripts are added intentionally to forms and will not be stripped if DISALLOW_UNFILTERED_HTML is not set.
3224 * It is also possible to filter Formidable without defining DISALLOW_UNFILTERED_HTML, with add_filter( 'frm_disallow_unfiltered_html', '__return_true' );
3225 *
3226 * @since 5.0.13
3227 */
3228 return apply_filters( 'frm_disallow_unfiltered_html', false );
3229 }
3230
3231 /**
3232 * @since 5.0.07
3233 *
3234 * @param array $values
3235 * @param array $keys
3236 * @return array
3237 */
3238 public static function maybe_filter_array( $values, $keys ) {
3239 $allow_unfiltered_html = self::allow_unfiltered_html();
3240
3241 if ( $allow_unfiltered_html ) {
3242 return $values;
3243 }
3244
3245 foreach ( $keys as $key ) {
3246 if ( isset( $values[ $key ] ) ) {
3247 $values[ $key ] = self::kses( $values[ $key ], 'all' );
3248 }
3249 }
3250
3251 return $values;
3252 }
3253
3254 /**
3255 * Some back end fields allow privleged users to add scripts.
3256 * A site that uses the DISALLOW_UNFILTERED_HTML always remove scripts on echo.
3257 *
3258 * @since 5.0.13
3259 *
3260 * @param string $value
3261 * @param array|string $allowed 'all' for everything included as defaults
3262 * @return string
3263 */
3264 public static function maybe_kses( $value, $allowed = 'all' ) {
3265 if ( self::should_never_allow_unfiltered_html() ) {
3266 $value = self::kses( $value, $allowed );
3267 }
3268 return $value;
3269 }
3270
3271 /**
3272 * @since 5.0.16
3273 *
3274 * @return bool
3275 */
3276 public static function show_landing_pages() {
3277 return self::show_new_feature( 'landing' );
3278 }
3279
3280 /**
3281 * @since 5.0.16
3282 *
3283 * @return array
3284 */
3285 public static function get_landing_page_upgrade_data_params( $medium = 'landing' ) {
3286 $params = array(
3287 'medium' => $medium,
3288 'upgrade' => __( 'Form Landing Pages', 'formidable' ),
3289 'message' => __( 'Easily manage a landing page for your form. Upgrade to get form landing pages.', 'formidable' ),
3290 );
3291 return self::get_upgrade_data_params( 'landing', $params );
3292 }
3293
3294 /**
3295 * @since 5.0.17
3296 *
3297 * @param string $plugin
3298 * @return string|false
3299 */
3300 private static function get_plan_required( $plugin ) {
3301 $link = FrmAddonsController::install_link( $plugin );
3302 return FrmFormsHelper::get_plan_required( $link );
3303 }
3304
3305 /**
3306 * @since 5.0.17
3307 *
3308 * @param string
3309 * @return bool
3310 */
3311 public static function show_new_feature( $feature ) {
3312 $link = FrmAddonsController::install_link( $feature );
3313 return array_key_exists( 'status', $link ) || array_key_exists( 'class', $link );
3314 }
3315
3316 /**
3317 * @since 5.0.17
3318 *
3319 * @param string $plugin
3320 * @param array $params
3321 * @return array
3322 */
3323 public static function get_upgrade_data_params( $plugin, $params ) {
3324 $link = FrmAddonsController::install_link( $plugin );
3325 if ( ! $link ) {
3326 return $params;
3327 }
3328
3329 if ( ! empty( $link['url'] ) && self::pro_is_installed() ) {
3330 $params['oneclick'] = json_encode( $link );
3331 unset( $params['message'] );
3332 if ( ! isset( $params['medium'] ) ) {
3333 $params['medium'] = $plugin;
3334 }
3335 } else {
3336 $params['requires'] = FrmFormsHelper::get_plan_required( $link );
3337 }
3338
3339 return $params;
3340 }
3341
3342 /**
3343 * 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.
3344 * Not every server installs the ctype extension, so use a fallback if the function does not exist.
3345 *
3346 * @since 5.0.17
3347 *
3348 * @param string $text
3349 * @return bool
3350 */
3351 public static function ctype_xdigit( $text ) {
3352 if ( function_exists( 'ctype_xdigit' ) ) {
3353 return ctype_xdigit( $text );
3354 }
3355 return is_string( $text ) && '' !== $text && ! preg_match( '/[^A-Fa-f0-9]/', $text );
3356 }
3357
3358 /**
3359 * Set the current screen to avoid undefined notices.
3360 *
3361 * @since 5.2.01
3362 */
3363 public static function set_current_screen_and_hook_suffix() {
3364 global $hook_suffix;
3365 if ( is_null( $hook_suffix ) ) {
3366 // $hook_suffix gets used in substr so make sure it's not null. PHP 8.1 deprecates null in substr.
3367 $hook_suffix = ''; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
3368 }
3369 set_current_screen();
3370 }
3371
3372 /**
3373 * @since 4.07
3374 * @deprecated 4.09.01
3375 */
3376 public static function renewal_message() {
3377 if ( is_callable( 'FrmProAddonsController::renewal_message' ) ) {
3378 FrmProAddonsController::renewal_message();
3379 return;
3380 }
3381
3382 if ( ! FrmAddonsController::is_license_expired() ) {
3383 return;
3384 }
3385
3386 ?>
3387 <div class="frm_error_style" style="text-align:left">
3388 <?php self::icon_by_class( 'frmfont frm_alert_icon' ); ?>
3389 &nbsp;
3390 <?php esc_html_e( 'Your account has expired', 'formidable' ); ?>
3391 <div style="float:right">
3392 <a href="<?php echo esc_url( self::admin_upgrade_link( 'form-expired', 'account/downloads/' ) ); ?>">
3393 <?php esc_html_e( 'Renew Now', 'formidable' ); ?>
3394 </a>
3395 </div>
3396 </div>
3397 <?php
3398 }
3399
3400 /**
3401 * Shows pill text.
3402 *
3403 * @since 5.2.02
3404 *
3405 * @param string $text Text in the pill. Default is NEW.
3406 */
3407 public static function show_pill_text( $text = null ) {
3408 if ( null === $text ) {
3409 $text = __( 'NEW', 'formidable' );
3410 }
3411 echo '<span class="frm-new-pill">' . esc_html( $text ) . '</span>';
3412 }
3413
3414 /**
3415 * Count the number of decimals digits.
3416 *
3417 * @since 5.2.07
3418 *
3419 * @param mixed $num Number.
3420 * @return int|false Returns `false` if the passed parameter is not number.
3421 */
3422 public static function count_decimals( $num ) {
3423 if ( ! is_numeric( $num ) ) {
3424 return false;
3425 }
3426
3427 $num = (string) $num;
3428 $parts = explode( '.', $num );
3429 if ( 1 === count( $parts ) ) {
3430 return 0;
3431 }
3432
3433 return strlen( $parts[ count( $parts ) - 1 ] );
3434 }
3435
3436 /**
3437 * @since 4.08
3438 * @deprecated 4.09.01
3439 */
3440 public static function expiring_message() {
3441 _deprecated_function( __METHOD__, '4.09.01', 'FrmProAddonsController::expiring_message' );
3442 if ( is_callable( 'FrmProAddonsController::expiring_message' ) ) {
3443 FrmProAddonsController::expiring_message();
3444 }
3445 }
3446
3447 /**
3448 * Use the WP 4.7 wp_doing_ajax function
3449 *
3450 * @since 2.05.07
3451 * @deprecated 4.04.04
3452 */
3453 public static function wp_doing_ajax() {
3454 _deprecated_function( __METHOD__, '4.04.04', 'wp_doing_ajax' );
3455 return wp_doing_ajax();
3456 }
3457
3458 /**
3459 * @deprecated 4.0
3460 */
3461 public static function insert_opt_html( $args ) {
3462 _deprecated_function( __METHOD__, '4.0', 'FrmFormsHelper::insert_opt_html' );
3463 FrmFormsHelper::insert_opt_html( $args );
3464 }
3465
3466 /**
3467 * Used to filter shortcode in text widgets
3468 *
3469 * @deprecated 2.5.4
3470 * @codeCoverageIgnore
3471 */
3472 public static function widget_text_filter_callback( $matches ) {
3473 return FrmDeprecated::widget_text_filter_callback( $matches );
3474 }
3475
3476 /**
3477 * @deprecated 3.01
3478 * @codeCoverageIgnore
3479 */
3480 public static function sanitize_array( &$values ) {
3481 FrmDeprecated::sanitize_array( $values );
3482 }
3483
3484 /**
3485 * @param array $settings
3486 * @param string $group
3487 *
3488 * @since 2.0.6
3489 * @deprecated 2.05.06
3490 * @codeCoverageIgnore
3491 */
3492 public static function save_settings( $settings, $group ) {
3493 return FrmDeprecated::save_settings( $settings, $group );
3494 }
3495
3496 /**
3497 * @since 2.0.4
3498 * @deprecated 2.05.06
3499 * @codeCoverageIgnore
3500 */
3501 public static function save_json_post( $settings ) {
3502 return FrmDeprecated::save_json_post( $settings );
3503 }
3504
3505 /**
3506 * @since 2.0
3507 * @deprecated 2.05.06
3508 * @codeCoverageIgnore
3509 *
3510 * @param string $cache_key The unique name for this cache
3511 * @param string $group The name of the cache group
3512 * @param string $query If blank, don't run a db call
3513 * @param string $type The wpdb function to use with this query
3514 *
3515 * @return mixed $results The cache or query results
3516 */
3517 public static function check_cache( $cache_key, $group = '', $query = '', $type = 'get_var', $time = 300 ) {
3518 return FrmDeprecated::check_cache( $cache_key, $group, $query, $type, $time );
3519 }
3520
3521 /**
3522 * @deprecated 2.05.06
3523 * @codeCoverageIgnore
3524 */
3525 public static function set_cache( $cache_key, $results, $group = '', $time = 300 ) {
3526 return FrmDeprecated::set_cache( $cache_key, $results, $group, $time );
3527 }
3528
3529 /**
3530 * @deprecated 2.05.06
3531 * @codeCoverageIgnore
3532 */
3533 public static function add_key_to_group_cache( $key, $group ) {
3534 FrmDeprecated::add_key_to_group_cache( $key, $group );
3535 }
3536
3537 /**
3538 * @deprecated 2.05.06
3539 * @codeCoverageIgnore
3540 */
3541 public static function get_group_cached_keys( $group ) {
3542 return FrmDeprecated::get_group_cached_keys( $group );
3543 }
3544
3545 /**
3546 * @since 2.0
3547 * @deprecated 2.05.06
3548 * @codeCoverageIgnore
3549 * @return mixed The cached value or false
3550 */
3551 public static function check_cache_and_transient( $cache_key ) {
3552 return FrmDeprecated::check_cache( $cache_key );
3553 }
3554
3555 /**
3556 * @since 2.0
3557 * @deprecated 2.05.06
3558 * @codeCoverageIgnore
3559 *
3560 * @param string $cache_key
3561 */
3562 public static function delete_cache_and_transient( $cache_key, $group = 'default' ) {
3563 FrmDeprecated::delete_cache_and_transient( $cache_key, $group );
3564 }
3565
3566 /**
3567 * @since 2.0
3568 * @deprecated 2.05.06
3569 * @codeCoverageIgnore
3570 *
3571 * @param string $group The name of the cache group
3572 */
3573 public static function cache_delete_group( $group ) {
3574 FrmDeprecated::cache_delete_group( $group );
3575 }
3576
3577 /**
3578 * @since 1.07.10
3579 * @deprecated 2.05.06
3580 * @codeCoverageIgnore
3581 *
3582 * @param string $term The value to escape
3583 *
3584 * @return string The escaped value
3585 */
3586 public static function esc_like( $term ) {
3587 return FrmDeprecated::esc_like( $term );
3588 }
3589
3590 /**
3591 * @param string $order_query
3592 *
3593 * @deprecated 2.05.06
3594 * @codeCoverageIgnore
3595 */
3596 public static function esc_order( $order_query ) {
3597 return FrmDeprecated::esc_order( $order_query );
3598 }
3599
3600 /**
3601 * @deprecated 2.05.06
3602 * @codeCoverageIgnore
3603 */
3604 public static function esc_order_by( &$order_by ) {
3605 FrmDeprecated::esc_order_by( $order_by );
3606 }
3607
3608 /**
3609 * @param string $limit
3610 *
3611 * @deprecated 2.05.06
3612 * @codeCoverageIgnore
3613 */
3614 public static function esc_limit( $limit ) {
3615 return FrmDeprecated::esc_limit( $limit );
3616 }
3617
3618 /**
3619 * @since 2.0
3620 * @deprecated 2.05.06
3621 * @codeCoverageIgnore
3622 */
3623 public static function prepare_array_values( $array, $type = '%s' ) {
3624 return FrmDeprecated::prepare_array_values( $array, $type );
3625 }
3626
3627 /**
3628 * @deprecated 2.05.06
3629 * @codeCoverageIgnore
3630 */
3631 public static function prepend_and_or_where( $starts_with = ' WHERE ', $where = '' ) {
3632 return FrmDeprecated::prepend_and_or_where( $starts_with, $where );
3633 }
3634
3635 /**
3636 * @since 2.0
3637 * @deprecated 5.0.13
3638 *
3639 * @return string The base Google APIS url for the current version of jQuery UI
3640 */
3641 public static function jquery_ui_base_url() {
3642 _deprecated_function( __FUNCTION__, '5.0.13', 'FrmProAppHelper::jquery_ui_base_url' );
3643 return is_callable( 'FrmProAppHelper::jquery_ui_base_url' ) ? FrmProAppHelper::jquery_ui_base_url() : '';
3644 }
3645 }
3646