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

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