PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 5.0.09
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v5.0.09
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
formidable / classes / helpers / FrmAppHelper.php

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

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