PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.22.1
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.22.1
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
← All changes | classes/helpers/FrmAppHelper.php +1050 -359 6.36.22.1 View file →
@@ -3,27 +3,43 @@
3 3 die( 'You are not allowed to call this page directly.' );
4 4 }
5 5
6 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 7
11 8 /**
12 - * @var bool $added_gmt_offset_filter
9 + * Version of the database we are moving to.
10 + *
11 + * @var int
13 12 */
13 + public static $db_version = 103;
14 +
15 + /**
16 + * Used by the API add-on.
17 + *
18 + * @var float
19 + */
20 + public static $font_version = 7;
21 +
22 + /**
23 + * @var bool
24 + */
14 25 private static $added_gmt_offset_filter = false;
15 26
16 27 /**
17 28 * @since 2.0
29 + *
30 + * @var string
18 31 */
19 - public static $plug_version = '6.3';
32 + public static $plug_version = '6.22.1';
20 33
21 34 /**
35 + * @var bool
36 + */
37 + private static $included_svg = false;
38 +
39 + /**
22 40 * @since 1.07.02
23 41 *
24 - * @param none
25 - *
26 42 * @return string The version of this plugin
27 43 */
28 44 public static function plugin_version() {
29 45 return self::$plug_version;
@@ -28,21 +44,33 @@
28 44 public static function plugin_version() {
29 45 return self::$plug_version;
30 46 }
31 47
48 + /**
49 + * @return string
50 + */
32 51 public static function plugin_folder() {
33 52 return basename( self::plugin_path() );
34 53 }
35 54
55 + /**
56 + * @return string
57 + */
36 58 public static function plugin_path() {
37 - return dirname( dirname( dirname( __FILE__ ) ) );
59 + return dirname( dirname( __DIR__ ) );
38 60 }
39 61
62 + /**
63 + * @return string
64 + */
40 65 public static function plugin_url() {
41 - // Prevously FRM_URL constant.
66 + // Previously FRM_URL constant.
42 67 return plugins_url( '', self::plugin_path() . '/formidable.php' );
43 68 }
44 69
70 + /**
71 + * @return string
72 + */
45 73 public static function relative_plugin_url() {
46 74 return str_replace( array( 'https:', 'http:' ), '', self::plugin_url() );
47 75 }
48 76
@@ -63,8 +91,12 @@
63 91 public static function site_name() {
64 92 return get_option( 'blogname' );
65 93 }
66 94
95 + /**
96 + * @param string $url
97 + * @return string
98 + */
67 99 public static function make_affiliate_url( $url ) {
68 100 $affiliate_id = self::get_affiliate();
69 101 if ( ! empty( $affiliate_id ) ) {
70 102 $url = str_replace( array( 'http://', 'https://' ), '', $url );
@@ -73,8 +105,11 @@
73 105
74 106 return $url;
75 107 }
76 108
109 + /**
110 + * @return int
111 + */
77 112 public static function get_affiliate() {
78 113 return absint( apply_filters( 'frm_affiliate_id', 0 ) );
79 114 }
80 115
@@ -92,9 +127,9 @@
92 127 }
93 128
94 129 $anchor = '';
95 130 if ( is_array( $args ) ) {
96 - $medium = $args['medium'];
131 + $medium = isset( $args['medium'] ) ? $args['medium'] : '';
97 132 if ( isset( $args['content'] ) ) {
98 133 $content = $args['content'];
99 134 }
100 135 if ( isset( $args['anchor'] ) ) {
@@ -126,14 +161,42 @@
126 161 return self::make_affiliate_url( $link );
127 162 }
128 163
129 164 /**
165 + * @since 6.21
166 + *
167 + * @param string $cta_link
168 + * @param array $utm
169 + */
170 + public static function maybe_add_missing_utm( $cta_link, $utm ) {
171 + $query_args = array();
172 +
173 + if ( false === strpos( $cta_link, 'utm_source' ) ) {
174 + $query_args['utm_source'] = 'WordPress';
175 + }
176 +
177 + if ( false === strpos( $cta_link, 'utm_campaign' ) ) {
178 + $query_args['utm_campaign'] = 'liteplugin';
179 + }
180 +
181 + if ( false === strpos( $cta_link, 'utm_medium' ) && isset( $utm['medium'] ) ) {
182 + $query_args['utm_medium'] = $utm['medium'];
183 + }
184 +
185 + if ( false === strpos( $cta_link, 'utm_content' ) && isset( $utm['content'] ) ) {
186 + $query_args['utm_content'] = $utm['content'];
187 + }
188 +
189 + return $query_args ? add_query_arg( $query_args, $cta_link ) : $cta_link;
190 + }
191 +
192 + /**
130 193 * Get the Formidable settings
131 194 *
132 195 * @since 2.0
133 196 *
134 197 * @param array $args - May include the form id when values need translation.
135 - * @return FrmSettings $frm_setings
198 + * @return FrmSettings $frm_settings
136 199 */
137 200 public static function get_settings( $args = array() ) {
138 201 global $frm_settings;
139 202 if ( empty( $frm_settings ) ) {
@@ -145,16 +208,39 @@
145 208
146 209 return $frm_settings;
147 210 }
148 211
212 + /**
213 + * @return string
214 + */
149 215 public static function get_menu_name() {
150 216 $frm_settings = self::get_settings();
151 217
152 - return $frm_settings->menu;
218 + return FrmAddonsController::is_license_expired() ? 'Formidable' : $frm_settings->menu;
153 219 }
154 220
155 221 /**
222 + * Determine if the current branding is set to 'formidable'.
223 + * Checks the menu icon, and verifies if it matches the formidable branding.
224 + *
225 + * @since 6.4.2
226 + *
227 + * @return bool True if the menu icon is the logo, false otherwise.
228 + */
229 + public static function is_formidable_branding() {
230 + if ( ! self::pro_is_installed() ) {
231 + return true;
232 + }
233 +
234 + $menu_icon = self::get_menu_icon_class();
235 + return strpos( $menu_icon, 'frm_logo_icon' ) !== false;
236 + }
237 +
238 + /**
156 239 * @since 3.05
240 + *
241 + * @param array $atts
242 + * @return string
157 243 */
158 244 public static function svg_logo( $atts = array() ) {
159 245 $defaults = array(
160 246 'height' => 18,
@@ -171,8 +257,11 @@
171 257 }
172 258
173 259 /**
174 260 * @since 4.0
261 + *
262 + * @param array $atts
263 + * @return void
175 264 */
176 265 public static function show_logo( $atts = array() ) {
177 266 echo self::kses( self::svg_logo( $atts ), 'all' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
178 267 }
@@ -178,8 +267,10 @@
178 267 }
179 268
180 269 /**
181 270 * @since 4.03.02
271 + *
272 + * @return void
182 273 */
183 274 public static function show_header_logo() {
184 275 $icon = self::svg_logo(
185 276 array(
@@ -201,21 +292,38 @@
201 292 }
202 293
203 294 /**
204 295 * @since 2.02.04
296 + *
297 + * @return bool
205 298 */
206 299 public static function ips_saved() {
207 300 $frm_settings = self::get_settings();
208 -
209 301 return ! $frm_settings->no_ips;
210 302 }
211 303
304 + /**
305 + * @return bool
306 + */
212 307 public static function pro_is_installed() {
213 - return apply_filters( 'frm_pro_installed', false );
308 + return (bool) apply_filters( 'frm_pro_installed', false );
214 309 }
215 310
216 311 /**
312 + * Check if the Pro plugin is installed, whether authorized or not.
313 + *
314 + * @since 6.8.3
315 + *
316 + * @return bool
317 + */
318 + public static function pro_is_included() {
319 + return function_exists( 'load_formidable_pro' );
320 + }
321 +
322 + /**
217 323 * @since 4.06.02
324 + *
325 + * @return bool
218 326 */
219 327 public static function pro_is_connected() {
220 328 global $frm_vars;
221 329 return self::pro_is_installed() && $frm_vars['pro_is_authorized'];
@@ -222,14 +330,26 @@
222 330 }
223 331
224 332 /**
225 333 * @since 4.06
334 + * @since 6.16.2 Added $check_for_settings parameter
335 + *
336 + * @param bool $check_for_settings
337 + *
338 + * @return bool
226 339 */
227 - public static function is_form_builder_page() {
228 - $action = self::simple_get( 'frm_action', 'sanitize_title' );
229 - return self::is_admin_page( 'formidable' ) && ( $action === 'edit' || $action === 'settings' || $action === 'duplicate' );
340 + public static function is_form_builder_page( $check_for_settings = true ) {
341 + $action = self::simple_get( 'frm_action', 'sanitize_title' );
342 + $check_actions = array( 'edit', 'duplicate' );
343 + if ( $check_for_settings ) {
344 + $check_actions[] = 'settings';
345 + }
346 + return self::is_admin_page( 'formidable' ) && in_array( $action, $check_actions, true );
230 347 }
231 348
349 + /**
350 + * @return bool
351 + */
232 352 public static function is_formidable_admin() {
233 353 $page = self::simple_get( 'page', 'sanitize_title' );
234 354 $is_formidable = strpos( $page, 'formidable' ) !== false;
235 355 if ( empty( $page ) ) {
@@ -239,15 +359,52 @@
239 359 return $is_formidable;
240 360 }
241 361
242 362 /**
363 + * Checks if is a list page.
364 + *
365 + * @since 6.19
366 + *
367 + * @param string $page The name of the page to check.
368 + * @return bool
369 + */
370 + public static function is_admin_list_page( $page = 'formidable' ) {
371 + if ( 'formidable' === $page ) {
372 + return self::on_form_listing_page();
373 + }
374 +
375 + if ( ! self::is_admin_page( $page ) ) {
376 + return false;
377 + }
378 +
379 + if ( 'formidable-entries' === $page ) {
380 + $action = self::simple_get( 'frm_action' );
381 + if ( ! $action || in_array( $action, self::get_entries_listing_page_form_actions(), true ) ) {
382 + return true;
383 + }
384 + }
385 +
386 + // Check edit or settings page.
387 + return ! self::simple_get( 'frm_action' );
388 + }
389 +
390 + /**
391 + * @since 6.20
392 + *
393 + * @return array<string>
394 + */
395 + private static function get_entries_listing_page_form_actions() {
396 + return array( 'list', 'destroy' );
397 + }
398 +
399 + /**
243 400 * Check for certain page in Formidable settings
244 401 *
245 402 * @since 2.0
246 403 *
247 - * @param string $page The name of the page to check
404 + * @param string $page The name of the page to check.
248 405 *
249 - * @return boolean
406 + * @return bool
250 407 */
251 408 public static function is_admin_page( $page = 'formidable' ) {
252 409 global $pagenow;
253 410 $get_page = self::simple_get( 'page', 'sanitize_title' );
@@ -266,8 +423,10 @@
266 423 * If the current page is for editing or creating a view.
267 424 * Returns false for the views listing page.
268 425 *
269 426 * @since 4.0
427 + *
428 + * @return bool
270 429 */
271 430 public static function is_view_builder_page() {
272 431 global $pagenow;
273 432
@@ -277,10 +436,10 @@
277 436
278 437 $post_type = self::simple_get( 'post_type', 'sanitize_title' );
279 438
280 439 if ( empty( $post_type ) ) {
281 - $post_id = self::simple_get( 'post', 'absint' );
282 - $post = get_post( $post_id );
440 + $post_id = self::simple_get( 'post', 'absint' );
441 + $post = get_post( $post_id );
283 442 $post_type = $post ? $post->post_type : '';
284 443 }
285 444
286 445 return $post_type === 'frm_display';
@@ -290,17 +449,15 @@
290 449 * Check for the form preview page
291 450 *
292 451 * @since 2.0
293 452 *
294 - * @param None
295 - *
296 - * @return boolean
453 + * @return bool
297 454 */
298 455 public static function is_preview_page() {
299 456 global $pagenow;
300 457 $action = self::simple_get( 'action', 'sanitize_title' );
301 458
302 - return $pagenow && $pagenow == 'admin-ajax.php' && $action == 'frm_forms_preview';
459 + return $pagenow && $pagenow === 'admin-ajax.php' && $action === 'frm_forms_preview';
303 460 }
304 461
305 462 /**
306 463 * Check for ajax except the form preview page
@@ -306,16 +463,17 @@
306 463 * Check for ajax except the form preview page
307 464 *
308 465 * @since 2.0
309 466 *
310 - * @param None
311 - *
312 - * @return boolean
467 + * @return bool
313 468 */
314 469 public static function doing_ajax() {
315 470 return wp_doing_ajax() && ! self::is_preview_page();
316 471 }
317 472
473 + /**
474 + * @return string
475 + */
318 476 public static function js_suffix() {
319 477 return defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
320 478 }
321 479
@@ -320,13 +478,13 @@
320 478 }
321 479
322 480 /**
323 481 * @since 2.0.8
482 + * @return bool
324 483 */
325 484 public static function prevent_caching() {
326 485 global $frm_vars;
327 -
328 - return isset( $frm_vars['prevent_caching'] ) && $frm_vars['prevent_caching'];
486 + return ! empty( $frm_vars['prevent_caching'] );
329 487 }
330 488
331 489 /**
332 490 * Check if on an admin page
@@ -332,9 +490,9 @@
332 490 * Check if on an admin page
333 491 *
334 492 * @since 2.0
335 493 *
336 - * @return boolean
494 + * @return bool
337 495 */
338 496 public static function is_admin() {
339 497 $is_admin = is_admin() && ! wp_doing_ajax();
340 498
@@ -349,17 +507,22 @@
349 507 * Check if value contains blank value or empty array
350 508 *
351 509 * @since 2.0
352 510 *
353 - * @param mixed $value - value to check
354 - * @param string
511 + * @param mixed $value Value to check.
512 + * @param string $empty
355 513 *
356 - * @return boolean
514 + * @return bool
357 515 */
358 516 public static function is_empty_value( $value, $empty = '' ) {
359 517 return ( is_array( $value ) && empty( $value ) ) || $value === $empty;
360 518 }
361 519
520 + /**
521 + * @param mixed $value
522 + * @param string $empty
523 + * @return bool
524 + */
362 525 public static function is_not_empty_value( $value, $empty = '' ) {
363 526 return ! self::is_empty_value( $value, $empty );
364 527 }
365 528
@@ -376,8 +539,28 @@
376 539 return isset( $_SERVER[ $value ] ) ? wp_strip_all_tags( wp_unslash( $_SERVER[ $value ] ) ) : '';
377 540 }
378 541
379 542 /**
543 + * Get the server OS
544 + *
545 + * @since 6.4.2
546 + *
547 + * @return string
548 + */
549 + public static function get_server_os() {
550 + if ( function_exists( 'php_uname' ) ) {
551 + return php_uname( 's' );
552 + }
553 +
554 + if ( ! defined( 'PHP_OS' ) ) {
555 + return '';
556 + }
557 +
558 + // match the same response for Windows server as php_uname('s')
559 + return in_array( PHP_OS, array( 'WIN32', 'WINNT', 'Windows_NT' ), true ) ? 'Windows NT' : PHP_OS;
560 + }
561 +
562 + /**
380 563 * Check for the IP address in several places (when custom headers are enabled).
381 564 * Used by [ip] shortcode.
382 565 *
383 566 * @return string The IP address of the current user
@@ -392,9 +575,10 @@
392 575 }
393 576
394 577 $key = self::get_server_value( $key );
395 578 foreach ( explode( ',', $key ) as $ip ) {
396 - $ip = trim( $ip ); // Just to be safe.
579 + // Just to be safe.
580 + $ip = trim( $ip );
397 581
398 582 if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) !== false ) {
399 583 return sanitize_text_field( $ip );
400 584 }
@@ -485,15 +669,24 @@
485 669
486 670 return $value;
487 671 }
488 672
673 + /**
674 + * Get a value from $_POST data.
675 + *
676 + * @param string $param The key we are trying to access data from in $_POST.
677 + * @param mixed $default The default if nothing is being sent.
678 + * @param callable|string $sanitize Make sure to pass a sanitize method here. This function will NOT sanitize by default.
679 + * @param bool $serialized
680 + * @return mixed
681 + */
489 682 public static function get_post_param( $param, $default = '', $sanitize = '', $serialized = false ) {
490 683 return self::get_simple_request(
491 684 array(
492 - 'type' => 'post',
493 - 'param' => $param,
494 - 'default' => $default,
495 - 'sanitize' => $sanitize,
685 + 'type' => 'post',
686 + 'param' => $param,
687 + 'default' => $default,
688 + 'sanitize' => $sanitize,
496 689 'serialized' => $serialized,
497 690 )
498 691 );
499 692 }
@@ -504,9 +697,9 @@
504 697 * @param string $param
505 698 * @param string $sanitize
506 699 * @param string $default
507 700 *
508 - * @return string|array
701 + * @return array|string
509 702 */
510 703 public static function simple_get( $param, $sanitize = 'sanitize_text_field', $default = '' ) {
511 704 return self::get_simple_request(
512 705 array(
@@ -524,27 +717,27 @@
524 717 * @since 2.0.6
525 718 *
526 719 * @param array $args
527 720 *
528 - * @return string|array
721 + * @return array|string
529 722 */
530 723 public static function get_simple_request( $args ) {
531 724 $defaults = array(
532 - 'param' => '',
533 - 'default' => '',
534 - 'type' => 'get',
535 - 'sanitize' => 'sanitize_text_field',
725 + 'param' => '',
726 + 'default' => '',
727 + 'type' => 'get',
728 + 'sanitize' => 'sanitize_text_field',
536 729 'serialized' => false,
537 730 );
538 731 $args = wp_parse_args( $args, $defaults );
539 732
540 733 $value = $args['default'];
541 - if ( $args['type'] == 'get' ) {
734 + if ( $args['type'] === 'get' ) {
542 735 if ( $_GET && isset( $_GET[ $args['param'] ] ) ) {
543 736 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing
544 737 $value = wp_unslash( $_GET[ $args['param'] ] );
545 738 }
546 - } elseif ( $args['type'] == 'post' ) {
739 + } elseif ( $args['type'] === 'post' ) {
547 740 if ( isset( $_POST[ $args['param'] ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
548 741 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing
549 742 $value = wp_unslash( $_POST[ $args['param'] ] );
550 743 if ( $args['serialized'] === true && is_serialized_string( $value ) && is_serialized( $value ) ) {
@@ -550,13 +743,12 @@
550 743 if ( $args['serialized'] === true && is_serialized_string( $value ) && is_serialized( $value ) ) {
551 744 self::unserialize_or_decode( $value );
552 745 }
553 746 }
554 - } else {
555 - if ( isset( $_REQUEST[ $args['param'] ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
747 + } elseif ( isset( $_REQUEST[ $args['param'] ] ) ) {
748 + // phpcs:ignore WordPress.Security.NonceVerification.Missing
556 749 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
557 750 $value = wp_unslash( $_REQUEST[ $args['param'] ] );
558 - }
559 751 }
560 752
561 753 self::sanitize_value( $args['sanitize'], $value );
562 754
@@ -580,19 +772,35 @@
580 772
581 773 return $value;
582 774 }
583 775
776 + /**
777 + * Sanitize a value in-place.
778 + * If $value is an array, the sanitize function will get called for each item.
779 + *
780 + * @param callable $sanitize
781 + * @param mixed $value
782 + * @return void
783 + */
584 784 public static function sanitize_value( $sanitize, &$value ) {
585 - if ( ! empty( $sanitize ) ) {
586 - if ( is_array( $value ) ) {
587 - $temp_values = $value;
588 - foreach ( $temp_values as $k => $v ) {
589 - self::sanitize_value( $sanitize, $value[ $k ] );
590 - }
591 - } else {
592 - $value = call_user_func( $sanitize, $value );
785 + if ( ! $sanitize ) {
786 + return;
787 + }
788 +
789 + if ( is_object( $value ) ) {
790 + $value = '';
791 + return;
792 + }
793 +
794 + if ( is_array( $value ) ) {
795 + $temp_values = $value;
796 + foreach ( $temp_values as $k => $v ) {
797 + self::sanitize_value( $sanitize, $value[ $k ] );
593 798 }
799 + return;
594 800 }
801 +
802 + $value = call_user_func( $sanitize, $value );
595 803 }
596 804
597 805 public static function sanitize_request( $sanitize_method, &$values ) {
598 806 $temp_values = $values;
@@ -604,15 +812,52 @@
604 812 }
605 813
606 814 /**
607 815 * @since 4.0.04
816 + *
817 + * @param mixed $value
818 + * @return void
608 819 */
609 820 public static function sanitize_with_html( &$value ) {
610 - self::sanitize_value( 'wp_kses_post', $value );
821 + if ( current_user_can( 'frm_edit_entries' ) || current_user_can( 'administrator' ) ) {
822 + // Only strip unsafe HTML like scripts for a privileged user submitting a form.
823 + self::sanitize_value( 'wp_kses_post', $value );
824 + } else {
825 + self::sanitize_value( self::class . '::strip_most_html', $value );
826 + }
611 827 self::decode_specialchars( $value );
612 828 }
613 829
614 830 /**
831 + * Allow only a small set of very basic HTML for unprivileged users.
832 + *
833 + * @since 6.7.1
834 + *
835 + * @param string $value
836 + */
837 + public static function strip_most_html( $value ) {
838 + $allowed_html = array(
839 + 'b' => array(),
840 + 'br' => array(),
841 + 'strong' => array(),
842 + 'p' => array(),
843 + 'i' => array(),
844 + 'ul' => array(),
845 + 'ol' => array(),
846 + 'li' => array(),
847 + );
848 +
849 + /**
850 + * @since 6.7.1
851 + *
852 + * @param array $allowed_html
853 + */
854 + $allowed_html = apply_filters( 'frm_allowed_form_input_html', $allowed_html );
855 +
856 + return wp_kses( $value, $allowed_html );
857 + }
858 +
859 + /**
615 860 * Do wp_specialchars_decode to get back '&' that wp_kses_post might have turned to '&amp;'
616 861 * this MUST be done, else we'll be back to the '& entity' problem.
617 862 *
618 863 * @since 4.0.04
@@ -646,10 +891,12 @@
646 891 $translation = array(
647 892 '&quot;' => '"',
648 893 '&#034;' => '"',
649 894 '&#x22;' => '"',
650 - '&lt; ' => '< ', // The space preserves the HTML.
651 - '&#060; ' => '< ', // The space preserves the HTML.
895 + // The space preserves the HTML.
896 + '&lt; ' => '< ',
897 + // The space preserves the HTML.
898 + '&#060; ' => '< ',
652 899 '&gt;' => '>',
653 900 '&#062;' => '>',
654 901 '&amp;' => '&',
655 902 '&#038;' => '&',
@@ -676,10 +923,10 @@
676 923 * Sanitize the value, and allow some HTML
677 924 *
678 925 * @since 2.0
679 926 *
680 - * @param string $value
681 - * @param array|string $allowed 'all' for everything included as defaults
927 + * @param string $value
928 + * @param array|string $allowed 'all' for everything included as defaults.
682 929 *
683 930 * @return string
684 931 */
685 932 public static function kses( $value, $allowed = array() ) {
@@ -688,8 +935,21 @@
688 935 return wp_kses( $value, $allowed_html );
689 936 }
690 937
691 938 /**
939 + * Sanitizes and echoes a given value.
940 + *
941 + * @since 6.18
942 + *
943 + * @param string $value The value to sanitize and output.
944 + * @param array|string $allowed Allowed HTML tags and attributes.
945 + * @return void
946 + */
947 + public static function kses_echo( $value, $allowed = array() ) {
948 + echo self::kses( $value, $allowed ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
949 + }
950 +
951 + /**
692 952 * The regular kses function strips [button_action] from submit button HTML.
693 953 *
694 954 * @since 5.0.13
695 955 *
@@ -780,9 +1040,9 @@
780 1040 'id' => true,
781 1041 );
782 1042
783 1043 return array(
784 - 'a' => array(
1044 + 'a' => array(
785 1045 'class' => true,
786 1046 'href' => true,
787 1047 'id' => true,
788 1048 'rel' => true,
@@ -851,16 +1111,16 @@
851 1111 'cite' => true,
852 1112 'title' => true,
853 1113 ),
854 1114 'rect' => array(
855 - 'class' => true,
856 - 'fill' => true,
857 - 'height' => true,
858 - 'width' => true,
859 - 'x' => true,
860 - 'y' => true,
861 - 'rx' => true,
862 - 'stroke' => true,
1115 + 'class' => true,
1116 + 'fill' => true,
1117 + 'height' => true,
1118 + 'width' => true,
1119 + 'x' => true,
1120 + 'y' => true,
1121 + 'rx' => true,
1122 + 'stroke' => true,
863 1123 'stroke-opacity' => true,
864 1124 'stroke-width' => true,
865 1125 ),
866 1126 'section' => $allow_class,
@@ -895,9 +1155,9 @@
895 1155 'xlink:href' => true,
896 1156 ),
897 1157 'ul' => $allow_class,
898 1158 'label' => array(
899 - 'for' => true,
1159 + 'for' => true,
900 1160 'class' => true,
901 1161 'id' => true,
902 1162 ),
903 1163 'button' => array(
@@ -906,8 +1166,13 @@
906 1166 ),
907 1167 'legend' => array(
908 1168 'class' => true,
909 1169 ),
1170 + 'option' => array(
1171 + 'class' => true,
1172 + 'value' => true,
1173 + 'selected' => true,
1174 + ),
910 1175 );
911 1176 }
912 1177
913 1178 /**
@@ -915,9 +1180,9 @@
915 1180 *
916 1181 * @since 2.0
917 1182 */
918 1183 public static function remove_get_action() {
919 - if ( ! isset( $_GET ) ) {
1184 + if ( empty( $_GET ) ) {
920 1185 return;
921 1186 }
922 1187
923 1188 $action_name = isset( $_GET['action'] ) ? 'action' : ( isset( $_GET['action2'] ) ? 'action2' : '' );
@@ -934,9 +1199,9 @@
934 1199 /**
935 1200 * Check the WP query for a parameter
936 1201 *
937 1202 * @since 2.0
938 - * @return string|array
1203 + * @return array|string
939 1204 */
940 1205 public static function get_query_var( $value, $param ) {
941 1206 if ( $value != '' ) {
942 1207 return $value;
@@ -953,10 +1218,12 @@
953 1218 /**
954 1219 * Try to show the SVG if possible. Otherwise, use the font icon.
955 1220 *
956 1221 * @since 4.0.02
1222 + *
957 1223 * @param string $class
958 1224 * @param array $atts
1225 + * @return string|null
959 1226 */
960 1227 public static function icon_by_class( $class, $atts = array() ) {
961 1228 $echo = ! isset( $atts['echo'] ) || $atts['echo'];
962 1229 if ( isset( $atts['echo'] ) ) {
@@ -966,14 +1233,16 @@
966 1233 $html_atts = self::array_to_html_params( $atts );
967 1234
968 1235 $icon = trim( str_replace( array( 'frm_icon_font', 'frmfont ' ), '', $class ) );
969 1236
970 - // Replace icons that have been removed.
1237 + // Replace icons that have been removed or renamed.
971 1238 $deprecated = array(
972 - 'frm_clone_solid_icon' => 'frm_clone_icon',
1239 + 'frm_clone_solid_icon' => 'frm_clone_icon',
1240 + 'frm_keyalt_icon' => 'frm_key_icon',
1241 + 'frm_keyalt_solid_icon' => 'frm_key_solid_icon',
973 1242 );
974 1243 if ( isset( $deprecated[ $icon ] ) ) {
975 - $icon = $deprecated[ $icon ];
1244 + $icon = $deprecated[ $icon ];
976 1245 $class = str_replace( $icon, $deprecated[ $icon ], $class );
977 1246 }
978 1247
979 1248 if ( $icon === $class ) {
@@ -983,11 +1252,9 @@
983 1252 if ( strpos( $icon, ' ' ) ) {
984 1253 $icon = explode( ' ', $icon );
985 1254 $icon = reset( $icon );
986 1255 }
987 - $icon = '<svg class="frmsvg' . esc_attr( $class ) . '"' . $html_atts . '>
988 - <use xlink:href="#' . esc_attr( $icon ) . '" />
989 - </svg>';
1256 + $icon = '<svg class="frmsvg' . esc_attr( $class ) . '"' . $html_atts . '><use xlink:href="#' . esc_attr( $icon ) . '" /></svg>';
990 1257 }
991 1258
992 1259 if ( $echo ) {
993 1260 echo self::kses_icon( $icon ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
@@ -1073,11 +1340,18 @@
1073 1340 /**
1074 1341 * Include svg images.
1075 1342 *
1076 1343 * @since 4.0.02
1344 + * @return void
1077 1345 */
1078 1346 public static function include_svg() {
1079 - include_once self::plugin_path() . '/images/icons.svg';
1347 + if ( self::$included_svg ) {
1348 + return;
1349 + }
1350 +
1351 + // Use readfile instead of include_once because of a default security rule in Snuffleupagus.
1352 + readfile( self::plugin_path() . '/images/icons.svg' );
1353 + self::$included_svg = true;
1080 1354 }
1081 1355
1082 1356 /**
1083 1357 * Convert an associative array to HTML values.
@@ -1089,9 +1363,9 @@
1089 1363 * @param bool $echo
1090 1364 * @return string|void
1091 1365 */
1092 1366 public static function array_to_html_params( $atts, $echo = false ) {
1093 - $callback = function() use ( $atts ) {
1367 + $callback = function () use ( $atts ) {
1094 1368 if ( $atts ) {
1095 1369 foreach ( $atts as $key => $value ) {
1096 1370 echo ' ' . esc_attr( $key ) . '="' . esc_attr( $value ) . '"';
1097 1371 }
@@ -1106,9 +1380,9 @@
1106 1380 * @since 5.0.13
1107 1381 *
1108 1382 * @param Closure $echo_function
1109 1383 * @param bool $echo
1110 - * @return string|void
1384 + * @return string|null
1111 1385 */
1112 1386 public static function clip( $echo_function, $echo = false ) {
1113 1387 if ( ! $echo ) {
1114 1388 ob_start();
@@ -1126,12 +1400,15 @@
1126 1400 }
1127 1401
1128 1402 /**
1129 1403 * @since 3.0
1404 + *
1405 + * @param array $atts
1406 + * @return void
1130 1407 */
1131 1408 public static function get_admin_header( $atts ) {
1132 - $has_nav = ( isset( $atts['form'] ) && ! empty( $atts['form'] ) && ( ! isset( $atts['is_template'] ) || ! $atts['is_template'] ) );
1133 - if ( ! isset( $atts['close'] ) || empty( $atts['close'] ) ) {
1409 + $has_nav = ! empty( $atts['form'] ) && empty( $atts['is_template'] );
1410 + if ( empty( $atts['close'] ) ) {
1134 1411 $atts['close'] = admin_url( 'admin.php?page=formidable' );
1135 1412 }
1136 1413 if ( ! isset( $atts['import_link'] ) ) {
1137 1414 $atts['import_link'] = false;
@@ -1136,9 +1413,9 @@
1136 1413 if ( ! isset( $atts['import_link'] ) ) {
1137 1414 $atts['import_link'] = false;
1138 1415 }
1139 1416
1140 - include( self::plugin_path() . '/classes/views/shared/admin-header.php' );
1417 + include self::plugin_path() . '/classes/views/shared/admin-header.php';
1141 1418 }
1142 1419
1143 1420 /**
1144 1421 * @since 6.0
@@ -1167,9 +1444,9 @@
1167 1444 FrmInbox::maybe_show_banner();
1168 1445 return;
1169 1446 }
1170 1447
1171 - if ( self::maybe_show_license_warning() || FrmInbox::maybe_show_banner() || ! $should_show_lite_upgrade || self::pro_is_installed() ) {
1448 + if ( FrmSalesApi::maybe_show_banner() || self::maybe_show_license_warning() || FrmInbox::maybe_show_banner() || ! $should_show_lite_upgrade || self::pro_is_installed() ) {
1172 1449 // Print license warning or inbox banner and exit if either prints.
1173 1450 // And exit before printing the upgrade bar if it shouldn't be shown.
1174 1451 return;
1175 1452 }
@@ -1174,10 +1451,36 @@
1174 1451 return;
1175 1452 }
1176 1453 ?>
1177 1454 <div class="frm-upgrade-bar">
1178 - <span>You're using Formidable Forms Lite. To unlock more features consider</span>
1179 - <a href="<?php echo esc_url( self::admin_upgrade_link( 'top-bar' ) ); ?>" target="_blank" rel="noopener">upgrading to Pro</a>.
1455 + <div class="frm-upgrade-bar-inner">
1456 + <?php
1457 + $cta_text = FrmSalesApi::get_best_sale_value( 'lite_banner_cta_text' );
1458 + if ( ! $cta_text ) {
1459 + $cta_text = __( 'upgrading to PRO', 'formidable' );
1460 + }
1461 +
1462 + $upgrade_link = FrmSalesApi::get_best_sale_value( 'lite_banner_cta_link' );
1463 + $utm = array(
1464 + 'medium' => 'settings-license',
1465 + 'content' => 'lite-banner',
1466 + );
1467 +
1468 + if ( $upgrade_link ) {
1469 + $upgrade_link = self::maybe_add_missing_utm( $upgrade_link, $utm );
1470 + } else {
1471 + $upgrade_link = self::admin_upgrade_link( $utm );
1472 + }
1473 +
1474 + printf(
1475 + /* translators: %1$s: Start link HTML, %2$s: CTA text ("upgrading to PRO" by default), %3$s: End link HTML */
1476 + esc_html__( 'You\'re using Formidable Forms Lite. To unlock more features consider %1$s%2$s%3$s.', 'formidable' ),
1477 + '<a href="' . esc_url( $upgrade_link ) . '">',
1478 + esc_html( $cta_text ),
1479 + '</a>'
1480 + );
1481 + ?>
1482 + </div>
1180 1483 </div>
1181 1484 <?php
1182 1485 }
1183 1486
@@ -1194,8 +1497,10 @@
1194 1497 * Render a button for a new item (Form, Application, etc).
1195 1498 *
1196 1499 * @since 3.0
1197 1500 * @param array $atts {
1501 + * Details about the button.
1502 + *
1198 1503 * @type array $link_hook Custom link hook, calls do_action and exits early.
1199 1504 * @type string $new_link Href value, default #.
1200 1505 * @type string $class Custom class names, space separated.
1201 1506 * @type string $button_text Button text. Default "Add New".
@@ -1207,9 +1512,9 @@
1207 1512 do_action( $atts['link_hook']['hook'], $atts['link_hook']['param'] );
1208 1513 return;
1209 1514 }
1210 1515
1211 - if ( empty( $atts['new_link'] ) && empty( $atts['trigger_new_form_modal'] ) && empty( $atts['class'] ) ) {
1516 + if ( empty( $atts['new_link'] ) && empty( $atts['create_form'] ) && empty( $atts['class'] ) ) {
1212 1517 // Do not render a button if none of these attributes are set.
1213 1518 return;
1214 1519 }
1215 1520
@@ -1215,12 +1520,8 @@
1215 1520
1216 1521 $href = ! empty( $atts['new_link'] ) ? esc_url( $atts['new_link'] ) : '#';
1217 1522 $class = 'button button-primary frm-button-primary';
1218 1523
1219 - if ( ! empty( $atts['trigger_new_form_modal'] ) ) {
1220 - $class .= ' frm-trigger-new-form-modal';
1221 - }
1222 -
1223 1524 if ( ! empty( $atts['class'] ) ) {
1224 1525 $class .= ' ' . $atts['class'];
1225 1526 }
1226 1527
@@ -1237,10 +1538,11 @@
1237 1538 'placeholder' => '',
1238 1539 'tosearch' => '',
1239 1540 'text' => __( 'Search', 'formidable' ),
1240 1541 'input_id' => '',
1542 + 'value' => false,
1241 1543 );
1242 - $atts = array_merge( $defaults, $atts );
1544 + $atts = array_merge( $defaults, $atts );
1243 1545
1244 1546 if ( $atts['input_id'] === 'template' && empty( $atts['tosearch'] ) ) {
1245 1547 $atts['tosearch'] = 'frm-card';
1246 1548 }
@@ -1251,8 +1553,27 @@
1251 1553 }
1252 1554
1253 1555 $input_id = $atts['input_id'] . '-search-input';
1254 1556
1557 + $input_atts = array(
1558 + 'type' => 'search',
1559 + 'id' => $input_id,
1560 + 'name' => 's',
1561 + 'placeholder' => $atts['placeholder'],
1562 + 'class' => $class,
1563 + 'data-tosearch' => $atts['tosearch'],
1564 + );
1565 +
1566 + if ( is_string( $atts['value'] ) ) {
1567 + $input_atts['value'] = $atts['value'];
1568 + } elseif ( isset( $_REQUEST['s'] ) ) {
1569 + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1570 + $input_atts['value'] = wp_unslash( $_REQUEST['s'] );
1571 + }
1572 +
1573 + if ( ! empty( $atts['tosearch'] ) ) {
1574 + $input_atts['autocomplete'] = 'off';
1575 + }
1255 1576 ?>
1256 1577 <p class="frm-search">
1257 1578 <label class="screen-reader-text" for="<?php echo esc_attr( $input_id ); ?>">
1258 1579 <?php echo esc_html( $atts['text'] ); ?>:
@@ -1257,15 +1578,9 @@
1257 1578 <label class="screen-reader-text" for="<?php echo esc_attr( $input_id ); ?>">
1258 1579 <?php echo esc_html( $atts['text'] ); ?>:
1259 1580 </label>
1260 1581 <span class="frmfont frm_search_icon"></span>
1261 - <input type="search" id="<?php echo esc_attr( $input_id ); ?>" name="s"
1262 - value="<?php _admin_search_query(); ?>" placeholder="<?php echo esc_attr( $atts['placeholder'] ); ?>"
1263 - class="<?php echo esc_attr( $class ); ?>" data-tosearch="<?php echo esc_attr( $atts['tosearch'] ); ?>"
1264 - <?php if ( ! empty( $atts['tosearch'] ) ) { ?>
1265 - autocomplete="off"
1266 - <?php } ?>
1267 - />
1582 + <input <?php self::array_to_html_params( $input_atts, true ); ?> />
1268 1583 <?php
1269 1584 if ( empty( $atts['tosearch'] ) ) {
1270 1585 submit_button( $atts['text'], 'button-secondary', '', false, array( 'id' => 'search-submit' ) );
1271 1586 }
@@ -1275,8 +1590,9 @@
1275 1590 }
1276 1591
1277 1592 /**
1278 1593 * @param string $type
1594 + * @return void
1279 1595 */
1280 1596 public static function trigger_hook_load( $type, $object = null ) {
1281 1597 // Only load the form hooks once.
1282 1598 $hooks_loaded = apply_filters( 'frm_' . $type . '_hooks_loaded', false, $object );
@@ -1285,11 +1601,14 @@
1285 1601 }
1286 1602 }
1287 1603
1288 1604 /**
1289 - * Save all front-end js scripts into a single file
1605 + * Save all front-end js scripts into a single file.
1606 + * And save an additional single file of all front-end Stripe JS scripts.
1290 1607 *
1291 1608 * @since 3.0
1609 + *
1610 + * @return void
1292 1611 */
1293 1612 public static function save_combined_js() {
1294 1613 $file_atts = apply_filters(
1295 1614 'frm_js_location',
@@ -1302,10 +1621,34 @@
1302 1621
1303 1622 $files = array(
1304 1623 self::plugin_path() . '/js/formidable.min.js',
1305 1624 );
1625 + /**
1626 + * @param array $files
1627 + */
1306 1628 $files = apply_filters( 'frm_combined_js_files', $files );
1307 1629 $new_file->combine_files( $files );
1630 +
1631 + // Create the minified Stripe Script.
1632 + $file_atts = apply_filters(
1633 + 'frm_stripe_js_location',
1634 + array(
1635 + 'file_name' => 'frmstrp.min.js',
1636 + 'new_file_path' => self::plugin_path() . '/js',
1637 + )
1638 + );
1639 + $new_file = new FrmCreateFile( $file_atts );
1640 + $files = array(
1641 + FrmStrpLiteAppHelper::plugin_path() . 'js/frmstrp.min.js',
1642 + );
1643 +
1644 + /**
1645 + * @since 6.5
1646 + *
1647 + * @param array $files
1648 + */
1649 + $files = apply_filters( 'frm_stripe_combined_js_files', $files );
1650 + $new_file->combine_files( $files );
1308 1651 }
1309 1652
1310 1653 /**
1311 1654 * Check a value from a shortcode to see if true or false.
@@ -1312,14 +1655,14 @@
1312 1655 * True when value is 1, true, 'true', 'yes'
1313 1656 *
1314 1657 * @since 1.07.10
1315 1658 *
1316 - * @param string $value The value to compare
1659 + * @param string $value The value to compare.
1317 1660 *
1318 - * @return boolean True or False
1661 + * @return bool
1319 1662 */
1320 1663 public static function is_true( $value ) {
1321 - return ( true === $value || 1 == $value || 'true' == $value || 'yes' == $value );
1664 + return true === $value || 1 == $value || 'true' === $value || 'yes' === $value;
1322 1665 }
1323 1666
1324 1667 /**
1325 1668 * Gets all post from a specific post type.
@@ -1377,9 +1720,9 @@
1377 1720 $args = self::preformat_selection_args( $args );
1378 1721
1379 1722 $pages_count = wp_count_posts( $args['post_type'] );
1380 1723
1381 - if ( $pages_count->publish <= 50 ) {
1724 + if ( ! isset( $pages_count->publish ) || $pages_count->publish <= 50 ) {
1382 1725 self::wp_pages_dropdown( $args );
1383 1726 return;
1384 1727 }
1385 1728
@@ -1385,9 +1728,9 @@
1385 1728
1386 1729 wp_enqueue_script( 'jquery-ui-autocomplete' );
1387 1730
1388 1731 $selected = self::get_post_param( $args['field_name'], $args['page_id'], 'absint' );
1389 - $title = '';
1732 + $title = '';
1390 1733
1391 1734 if ( $selected ) {
1392 1735 $title = get_the_title( $selected );
1393 1736 }
@@ -1403,18 +1746,112 @@
1403 1746 <?php
1404 1747 }
1405 1748
1406 1749 /**
1407 - * @param array $args
1408 - * @param string $page_id Deprecated.
1409 - * @param boolean $truncate Deprecated.
1750 + * Maybe show an HTML select or autocomplete input based on the number of options.
1751 + *
1752 + * @since 6.21
1753 + *
1754 + * @param array $args Args. See the method for details.
1410 1755 */
1756 + public static function maybe_autocomplete_options( $args ) {
1757 + $defaults = array(
1758 + 'truncate' => false,
1759 + 'placeholder' => ' ',
1760 + 'name' => '',
1761 + 'id' => '',
1762 + 'selected' => '',
1763 + 'source' => array(),
1764 + 'dropdown_limit' => 50,
1765 + 'autocomplete_placeholder' => __( 'Select an option', 'formidable' ),
1766 + 'value_key' => 'value',
1767 + 'label_key' => 'label',
1768 + );
1769 +
1770 + $args = wp_parse_args( $args, $defaults );
1771 +
1772 + $html_attrs = array();
1773 + if ( ! empty( $args['name'] ) ) {
1774 + $html_attrs['name'] = $args['name'];
1775 + }
1776 +
1777 + if ( ! empty( $args['id'] ) ) {
1778 + $html_attrs['id'] = $args['id'];
1779 + }
1780 +
1781 + if ( count( $args['source'] ) <= $args['dropdown_limit'] ) {
1782 + ?>
1783 + <select <?php self::array_to_html_params( $html_attrs, true ); ?>>
1784 + <option value=""><?php echo esc_html( $args['placeholder'] ); ?></option>
1785 + <?php
1786 + foreach ( $args['source'] as $key => $source ) :
1787 + $value_label = self::get_dropdown_value_and_label_from_option( $source, $key, $args );
1788 + if ( ! empty( $args['truncate'] ) ) {
1789 + $value_label['label'] = self::truncate( $value_label['label'], $args['truncate'] );
1790 + }
1791 + ?>
1792 + <option value="<?php echo esc_attr( $value_label['value'] ); ?>" <?php selected( $value_label['value'], $args['selected'] ); ?>><?php echo esc_html( $value_label['label'] ); ?></option>
1793 + <?php endforeach; ?>
1794 + </select>
1795 + <?php
1796 + } else {
1797 + $options = array();
1798 + $autocomplete_value = '';
1799 + foreach ( $args['source'] as $key => $source ) {
1800 + $value_label = self::get_dropdown_value_and_label_from_option( $source, $key, $args );
1801 +
1802 + if ( $value_label['value'] === $args['selected'] ) {
1803 + $autocomplete_value = $value_label['label'];
1804 + }
1805 +
1806 + $options[] = $value_label;
1807 + }
1808 +
1809 + $html_attrs['type'] = 'hidden';
1810 + $html_attrs['class'] = 'frm_autocomplete_value_input';
1811 + $html_attrs['value'] = $args['selected'];
1812 + ?>
1813 + <input type="text" class="frm-custom-search"
1814 + data-source="<?php echo esc_attr( wp_json_encode( $options ) ); ?>"
1815 + placeholder="<?php echo esc_attr( $args['autocomplete_placeholder'] ); ?>"
1816 + value="<?php echo esc_attr( $autocomplete_value ); ?>" />
1817 + <input <?php self::array_to_html_params( $html_attrs, true ); ?> />
1818 + <?php
1819 + }//end if
1820 + }
1821 +
1822 + /**
1823 + * Gets dropdown value and label from autodropdown option.
1824 + *
1825 + * @since 6.21
1826 + *
1827 + * @param array|string $option Autocomplete option.
1828 + * @param string $key Array key of the option.
1829 + * @param array $args See {@see FrmAppHelper::maybe_autocomplete_options()}.
1830 + * @return array
1831 + */
1832 + private static function get_dropdown_value_and_label_from_option( $option, $key, $args ) {
1833 + if ( is_array( $option ) ) {
1834 + $value = isset( $option[ $args['value_key'] ] ) ? $option[ $args['value_key'] ] : '';
1835 + $label = isset( $option[ $args['label_key'] ] ) ? $option[ $args['label_key'] ] : '';
1836 + } else {
1837 + $value = $key;
1838 + $label = $option;
1839 + }
1840 +
1841 + return compact( 'value', 'label' );
1842 + }
1843 +
1844 + /**
1845 + * @param array $args
1846 + * @param string $page_id Deprecated.
1847 + * @param bool $truncate Deprecated.
1848 + */
1411 1849 public static function wp_pages_dropdown( $args = array(), $page_id = '', $truncate = false ) {
1412 1850 self::prep_page_dropdown_params( $page_id, $truncate, $args );
1413 1851
1414 1852 $pages = self::get_post_ids_and_titles( $args['post_type'] );
1415 1853 $selected = self::get_post_param( $args['field_name'], $args['page_id'], 'absint' );
1416 -
1417 1854 ?>
1418 1855 <select name="<?php echo esc_attr( $args['field_name'] ); ?>" id="<?php echo esc_attr( $args['field_name'] ); ?>" class="frm-pages-dropdown">
1419 1856 <option value=""><?php echo esc_html( $args['placeholder'] ); ?></option>
1420 1857 <?php foreach ( $pages as $page ) { ?>
@@ -1451,13 +1888,13 @@
1451 1888 * @since 4.10.01 Added `post_type` and `autocomplete_placeholder` to the arguments array.
1452 1889 */
1453 1890 private static function preformat_selection_args( $args ) {
1454 1891 $defaults = array(
1455 - 'truncate' => false,
1456 - 'placeholder' => ' ',
1457 - 'field_name' => '',
1458 - 'page_id' => '',
1459 - 'post_type' => 'page',
1892 + 'truncate' => false,
1893 + 'placeholder' => ' ',
1894 + 'field_name' => '',
1895 + 'page_id' => '',
1896 + 'post_type' => 'page',
1460 1897 'autocomplete_placeholder' => __( 'Select a Page', 'formidable' ),
1461 1898 );
1462 1899
1463 1900 return array_merge( $defaults, $args );
@@ -1528,25 +1965,16 @@
1528 1965 return self::is_admin_page( 'formidable-views-editor' );
1529 1966 }
1530 1967
1531 1968 /**
1532 - * @since 4.0
1533 - * @deprecated 5.5.3
1969 + * @param string $field_name
1970 + * @param array|string $capability
1971 + * @param string $multiple 'single' and 'multiple'.
1534 1972 */
1535 - public static function maybe_full_screen_link( $link ) {
1536 - _deprecated_function( __METHOD__, '5.5.3' );
1537 - return $link;
1538 - }
1539 -
1540 - /**
1541 - * @param string $field_name
1542 - * @param string|array $capability
1543 - * @param string $multiple 'single' and 'multiple'
1544 - */
1545 1973 public static function wp_roles_dropdown( $field_name, $capability, $multiple = 'single' ) {
1546 1974 ?>
1547 1975 <select name="<?php echo esc_attr( $field_name ); ?>" id="<?php echo esc_attr( $field_name ); ?>"
1548 - <?php echo ( 'multiple' === $multiple ) ? 'multiple="multiple"' : ''; ?>
1976 + <?php echo 'multiple' === $multiple ? 'multiple="multiple"' : ''; ?>
1549 1977 class="frm_multiselect">
1550 1978 <?php self::roles_options( $capability ); ?>
1551 1979 </select>
1552 1980 <?php
@@ -1554,9 +1982,9 @@
1554 1982
1555 1983 /**
1556 1984 * @since 4.07
1557 1985 * @param array|string $selected
1558 - * @param string $current
1986 + * @param string $current
1559 1987 */
1560 1988 private static function selected( $selected, $current ) {
1561 1989 if ( is_callable( 'FrmProAppHelper::selected' ) ) {
1562 1990 FrmProAppHelper::selected( $selected, $current );
@@ -1565,9 +1993,9 @@
1565 1993 }
1566 1994 }
1567 1995
1568 1996 /**
1569 - * @param string|array $capability
1997 + * @param array|string $capability
1570 1998 */
1571 1999 public static function roles_options( $capability ) {
1572 2000 global $frm_vars;
1573 2001 if ( isset( $frm_vars['editable_roles'] ) ) {
@@ -1579,9 +2007,9 @@
1579 2007
1580 2008 foreach ( $editable_roles as $role => $details ) {
1581 2009 $name = translate_user_role( $details['name'] );
1582 2010 ?>
1583 - <option value="<?php echo esc_attr( $role ); ?>" <?php self::selected( $capability, $role ); ?>><?php echo esc_attr( $name ); ?> </option>
2011 + <option value="<?php echo esc_attr( $role ); ?>" <?php self::selected( $capability, $role ); ?>><?php echo esc_html( $name ); ?> </option>
1584 2012 <?php
1585 2013 unset( $role, $details );
1586 2014 }
1587 2015 }
@@ -1602,9 +2030,8 @@
1602 2030 $pro_cap = array(
1603 2031 'frm_create_entries' => __( 'Add Entries from Admin Area', 'formidable' ),
1604 2032 'frm_edit_entries' => __( 'Edit Entries from Admin Area', 'formidable' ),
1605 2033 'frm_view_reports' => __( 'View Reports', 'formidable' ),
1606 - 'frm_edit_displays' => __( 'Add/Edit Views', 'formidable' ),
1607 2034 );
1608 2035 /**
1609 2036 * @since 5.3.1
1610 2037 *
@@ -1611,8 +2038,14 @@
1611 2038 * @param array<string,string> $pro_cap
1612 2039 */
1613 2040 $pro_cap = apply_filters( 'frm_pro_capabilities', $pro_cap );
1614 2041
2042 + if ( ! array_key_exists( 'frm_edit_displays', $pro_cap ) && is_callable( 'FrmProAppHelper::views_is_installed' ) && FrmProAppHelper::views_is_installed() ) {
2043 + // For backward compatibility, add the Add/Edit Views permission if Pro is not up to date.
2044 + // This was added in 6.5.4. Remove this in the future.
2045 + $pro_cap['frm_edit_displays'] = __( 'Add/Edit Views', 'formidable' );
2046 + }
2047 +
1615 2048 if ( 'pro_only' === $type ) {
1616 2049 return $pro_cap;
1617 2050 }
1618 2051
@@ -1627,9 +2060,9 @@
1627 2060 * @return array<string,string>
1628 2061 */
1629 2062 private static function get_lite_capabilities() {
1630 2063 return array(
1631 - 'frm_view_forms' => __( 'View Forms', 'formidable' ),
2064 + 'frm_view_forms' => __( 'View Forms List', 'formidable' ),
1632 2065 'frm_edit_forms' => __( 'Add and Edit Forms', 'formidable' ),
1633 2066 'frm_delete_forms' => __( 'Delete Forms', 'formidable' ),
1634 2067 'frm_change_settings' => __( 'Access this Settings Page', 'formidable' ),
1635 2068 'frm_view_entries' => __( 'View Entries from Admin Area', 'formidable' ),
@@ -1670,9 +2103,9 @@
1670 2103 return current_user_can( $role );
1671 2104 }
1672 2105
1673 2106 /**
1674 - * @param string|array $needed_role
2107 + * @param array|string $needed_role
1675 2108 * @return bool
1676 2109 */
1677 2110 public static function user_has_permission( $needed_role ) {
1678 2111 if ( is_array( $needed_role ) ) {
@@ -1728,8 +2161,11 @@
1728 2161 /**
1729 2162 * Make sure admins have permission to see the menu items
1730 2163 *
1731 2164 * @since 2.0.6
2165 + *
2166 + * @param string $cap
2167 + * @return void
1732 2168 */
1733 2169 public static function force_capability( $cap = 'frm_change_settings' ) {
1734 2170 if ( current_user_can( 'administrator' ) && ! current_user_can( $cap ) ) {
1735 2171 $role = get_role( 'administrator' );
@@ -1740,9 +2176,9 @@
1740 2176 }
1741 2177 }
1742 2178
1743 2179 /**
1744 - * Check if the user has permision for action.
2180 + * Check if the user has permission for action.
1745 2181 * Return permission message and stop the action if no permission
1746 2182 *
1747 2183 * @since 2.0
1748 2184 *
@@ -1778,9 +2214,9 @@
1778 2214 if ( empty( $nonce_name ) ) {
1779 2215 return $error;
1780 2216 }
1781 2217
1782 - $nonce_value = ( $_REQUEST && isset( $_REQUEST[ $nonce_name ] ) ) ? sanitize_text_field( wp_unslash( $_REQUEST[ $nonce_name ] ) ) : '';
2218 + $nonce_value = $_REQUEST && isset( $_REQUEST[ $nonce_name ] ) ? sanitize_text_field( wp_unslash( $_REQUEST[ $nonce_name ] ) ) : '';
1783 2219 if ( $_REQUEST && ( ! isset( $_REQUEST[ $nonce_name ] ) || ! wp_verify_nonce( $nonce_value, $nonce ) ) ) {
1784 2220 $frm_settings = self::get_settings();
1785 2221 $error = $frm_settings->admin_permission;
1786 2222 }
@@ -1794,12 +2230,13 @@
1794 2230 }
1795 2231 }
1796 2232
1797 2233 public static function check_selected( $values, $current ) {
1798 - $values = self::recursive_function_map( $values, 'trim' );
1799 - $values = self::recursive_function_map( $values, 'htmlspecialchars_decode' );
1800 - $current = htmlspecialchars_decode( trim( $current ) );
2234 + $values = self::recursive_function_map( $values, 'trim' );
2235 + $values = self::recursive_function_map( $values, 'htmlspecialchars_decode' );
1801 2236
2237 + $current = is_null( $current ) ? '' : htmlspecialchars_decode( trim( $current ) );
2238 +
1802 2239 return ( is_array( $values ) && in_array( $current, $values ) ) || ( ! is_array( $values ) && $values == $current );
1803 2240 }
1804 2241
1805 2242 public static function recursive_function_map( $value, $function ) {
@@ -1819,8 +2256,9 @@
1819 2256 }
1820 2257 }
1821 2258 }
1822 2259 } else {
2260 + $value = self::maybe_update_value_if_null( $value, $function );
1823 2261 $value = call_user_func( $function, $value );
1824 2262 }
1825 2263
1826 2264 return $value;
@@ -1825,8 +2263,24 @@
1825 2263
1826 2264 return $value;
1827 2265 }
1828 2266
2267 + /**
2268 + * Updates value to empty string if it is null and being passed to a string function.
2269 + *
2270 + * @since 6.8.4
2271 + * @param mixed $value
2272 + * @param string $function
2273 + * @return mixed
2274 + */
2275 + private static function maybe_update_value_if_null( $value, $function ) {
2276 + if ( null === $value && in_array( $function, array( 'trim', 'strlen' ), true ) ) {
2277 + $value = '';
2278 + }
2279 +
2280 + return $value;
2281 + }
2282 +
1829 2283 public static function is_assoc( $array ) {
1830 2284 return (bool) count( array_filter( array_keys( $array ), 'is_string' ) );
1831 2285 }
1832 2286
@@ -1837,14 +2291,12 @@
1837 2291 $return = array();
1838 2292 foreach ( $array as $key => $value ) {
1839 2293 if ( is_array( $value ) ) {
1840 2294 $return = array_merge( $return, self::array_flatten( $value, $keys ) );
2295 + } elseif ( $keys === 'keep' ) {
2296 + $return[ $key ] = $value;
1841 2297 } else {
1842 - if ( $keys == 'keep' ) {
1843 - $return[ $key ] = $value;
1844 - } else {
1845 - $return[] = $value;
1846 - }
2298 + $return[] = $value;
1847 2299 }
1848 2300 }
1849 2301
1850 2302 return $return;
@@ -1849,8 +2301,27 @@
1849 2301
1850 2302 return $return;
1851 2303 }
1852 2304
2305 + /**
2306 + * Flatten an array before imploding it to avoid Array to string conversion warnings.
2307 + *
2308 + * @since 6.16.1
2309 + *
2310 + * @param string $sep
2311 + * @param array $array
2312 + * @return string
2313 + */
2314 + public static function safe_implode( $sep, $array ) {
2315 + $array = self::array_flatten( $array );
2316 + return implode( $sep, $array );
2317 + }
2318 +
2319 + /**
2320 + * @param string $text
2321 + * @param bool $is_rich_text
2322 + * @return string
2323 + */
1853 2324 public static function esc_textarea( $text, $is_rich_text = false ) {
1854 2325 $safe_text = str_replace( '&quot;', '"', $text );
1855 2326 if ( ! $is_rich_text ) {
1856 2327 $safe_text = htmlspecialchars( $safe_text, ENT_NOQUOTES );
@@ -1856,9 +2327,13 @@
1856 2327 $safe_text = htmlspecialchars( $safe_text, ENT_NOQUOTES );
1857 2328 }
1858 2329 $safe_text = str_replace( '&amp; ', '& ', $safe_text );
1859 2330
1860 - return apply_filters( 'esc_textarea', $safe_text, $text );
2331 + /**
2332 + * @param string $safe_text
2333 + * @param string $text
2334 + */
2335 + return (string) apply_filters( 'esc_textarea', $safe_text, $text );
1861 2336 }
1862 2337
1863 2338 /**
1864 2339 * Add auto paragraphs to text areas
@@ -1865,9 +2340,9 @@
1865 2340 *
1866 2341 * @since 2.0
1867 2342 */
1868 2343 public static function use_wpautop( $content ) {
1869 - if ( apply_filters( 'frm_use_wpautop', true ) && ! is_array( $content ) ) {
2344 + if ( apply_filters( 'frm_use_wpautop', true ) && is_string( $content ) ) {
1870 2345 $content = wpautop( str_replace( '<br>', '<br />', $content ) );
1871 2346 }
1872 2347
1873 2348 return $content;
@@ -1909,12 +2384,12 @@
1909 2384 * @since 5.0.13 added $echo param.
1910 2385 *
1911 2386 * @param string $url
1912 2387 * @param bool $echo
1913 - * @return string|void
2388 + * @return string|null
1914 2389 */
1915 2390 public static function js_redirect( $url, $echo = false ) {
1916 - $callback = function() use ( $url ) {
2391 + $callback = function () use ( $url ) {
1917 2392 echo '<script type="text/javascript">window.location="' . esc_url_raw( $url ) . '"</script>';
1918 2393 };
1919 2394 return self::clip( $callback, $echo );
1920 2395 }
@@ -1924,9 +2399,9 @@
1924 2399 return $user_id;
1925 2400 }
1926 2401
1927 2402 $user_id = sanitize_text_field( $user_id );
1928 - if ( $user_id == 'current' ) {
2403 + if ( $user_id === 'current' ) {
1929 2404 $user_id = get_current_user_id();
1930 2405 } else {
1931 2406 if ( is_email( $user_id ) ) {
1932 2407 $user = get_user_by( 'email', $user_id );
@@ -1942,8 +2417,13 @@
1942 2417
1943 2418 return $user_id;
1944 2419 }
1945 2420
2421 + /**
2422 + * @param string $filename
2423 + * @param array $atts
2424 + * @return false|string
2425 + */
1946 2426 public static function get_file_contents( $filename, $atts = array() ) {
1947 2427 if ( ! is_file( $filename ) ) {
1948 2428 return false;
1949 2429 }
@@ -1949,9 +2429,9 @@
1949 2429 }
1950 2430
1951 2431 extract( $atts ); // phpcs:ignore WordPress.PHP.DontExtract
1952 2432 ob_start();
1953 - include( $filename );
2433 + include $filename;
1954 2434 $contents = ob_get_contents();
1955 2435 ob_end_clean();
1956 2436
1957 2437 return $contents;
@@ -2006,9 +2486,9 @@
2006 2486 ++$suffix;
2007 2487 } while ( in_array( $key_check, $similar_keys, true ) );
2008 2488
2009 2489 $key = $key_check;
2010 - }
2490 + }//end if
2011 2491
2012 2492 return $key;
2013 2493 }
2014 2494
@@ -2047,14 +2527,18 @@
2047 2527 return $key;
2048 2528 }
2049 2529
2050 2530 /**
2531 + * @since 6.21 This is changed from `private` to `public`.
2532 + *
2051 2533 * @param int $num_chars
2052 2534 * @return string
2053 2535 */
2054 - private static function generate_new_key( $num_chars ) {
2536 + public static function generate_new_key( $num_chars ) {
2055 2537 $max_slug_value = pow( 36, $num_chars );
2056 - $min_slug_value = 37; // we want to have at least 2 characters in the slug
2538 +
2539 + // We want to have at least 2 characters in the slug.
2540 + $min_slug_value = 37;
2057 2541 return base_convert( rand( $min_slug_value, $max_slug_value ), 10, 36 );
2058 2542 }
2059 2543
2060 2544 /**
@@ -2083,11 +2567,16 @@
2083 2567
2084 2568 /**
2085 2569 * Editing a Form or Entry
2086 2570 *
2087 - * @param string $table
2571 + * @param object $record
2572 + * @param string $table
2573 + * @param array|string $fields
2574 + * @param bool $default
2575 + * @param array $post_values
2576 + * @param array $args
2088 2577 *
2089 - * @return bool|array
2578 + * @return array|bool
2090 2579 */
2091 2580 public static function setup_edit_vars( $record, $table, $fields = '', $default = false, $post_values = array(), $args = array() ) {
2092 2581 if ( ! $record ) {
2093 2582 return false;
@@ -2140,13 +2629,12 @@
2140 2629 $post_values = $args['post_values'];
2141 2630
2142 2631 if ( $args['default'] ) {
2143 2632 $meta_value = $field->default_value;
2144 - } else {
2145 - if ( $record->post_id && self::pro_is_installed() && isset( $field->field_options['post_field'] ) && $field->field_options['post_field'] ) {
2146 - if ( ! isset( $field->field_options['custom_field'] ) ) {
2147 - $field->field_options['custom_field'] = '';
2148 - }
2633 + } elseif ( $record->post_id && self::pro_is_installed() && isset( $field->field_options['post_field'] ) && $field->field_options['post_field'] ) {
2634 + if ( ! isset( $field->field_options['custom_field'] ) ) {
2635 + $field->field_options['custom_field'] = '';
2636 + }
2149 2637 $meta_value = FrmProEntryMetaHelper::get_post_value(
2150 2638 $record->post_id,
2151 2639 $field->field_options['post_field'],
2152 2640 $field->field_options['custom_field'],
@@ -2156,12 +2644,11 @@
2156 2644 'form_id' => $field->form_id,
2157 2645 'field' => $field,
2158 2646 )
2159 2647 );
2160 - } else {
2161 - $meta_value = FrmEntryMeta::get_meta_value( $record, $field->id );
2162 - }
2163 - }
2648 + } else {
2649 + $meta_value = FrmEntryMeta::get_meta_value( $record, $field->id );
2650 + }//end if
2164 2651
2165 2652 $field_type = isset( $post_values['field_options'][ 'type_' . $field->id ] ) ? $post_values['field_options'][ 'type_' . $field->id ] : $field->type;
2166 2653 if ( isset( $post_values['item_meta'][ $field->id ] ) ) {
2167 2654 $new_value = $post_values['item_meta'][ $field->id ];
@@ -2209,12 +2696,15 @@
2209 2696 );
2210 2697 }
2211 2698
2212 2699 /**
2700 + * @param object $record
2213 2701 * @param string $table
2702 + * @param array $post_values
2703 + * @param array $values
2214 2704 */
2215 2705 private static function fill_form_opts( $record, $table, $post_values, array &$values ) {
2216 - if ( $table == 'entries' ) {
2706 + if ( $table === 'entries' ) {
2217 2707 $form = $record->form_id;
2218 2708 FrmForm::maybe_get_form( $form );
2219 2709 } else {
2220 2710 $form = $record;
@@ -2250,9 +2740,9 @@
2250 2740 $form_defaults = FrmFormsHelper::get_default_opts();
2251 2741
2252 2742 foreach ( $form_defaults as $opt => $default ) {
2253 2743 if ( ! isset( $values[ $opt ] ) || $values[ $opt ] == '' ) {
2254 - $values[ $opt ] = ( $post_values && isset( $post_values['options'][ $opt ] ) ) ? $post_values['options'][ $opt ] : $default;
2744 + $values[ $opt ] = $post_values && isset( $post_values['options'][ $opt ] ) ? $post_values['options'][ $opt ] : $default;
2255 2745 }
2256 2746
2257 2747 unset( $opt, $default );
2258 2748 }
@@ -2273,9 +2763,9 @@
2273 2763 * @since 2.2.10
2274 2764 *
2275 2765 * @param array $post_values
2276 2766 *
2277 - * @return boolean|int
2767 + * @return bool|int
2278 2768 */
2279 2769 public static function custom_style_value( $post_values ) {
2280 2770 if ( ! empty( $post_values ) && isset( $post_values['options']['custom_style'] ) ) {
2281 2771 $custom_style = absint( $post_values['options']['custom_style'] );
@@ -2280,29 +2770,37 @@
2280 2770 if ( ! empty( $post_values ) && isset( $post_values['options']['custom_style'] ) ) {
2281 2771 $custom_style = absint( $post_values['options']['custom_style'] );
2282 2772 } else {
2283 2773 $frm_settings = self::get_settings();
2284 - $custom_style = ( $frm_settings->load_style != 'none' );
2774 + $custom_style = ( $frm_settings->load_style !== 'none' );
2285 2775 }
2286 2776
2287 2777 return $custom_style;
2288 2778 }
2289 2779
2290 - public static function truncate( $str, $length, $minword = 3, $continue = '...' ) {
2291 - if ( is_array( $str ) ) {
2780 + /**
2781 + * @param mixed $original_string
2782 + * @param int|string $length
2783 + * @param int $minword
2784 + * @param string $continue
2785 + * @return string
2786 + */
2787 + public static function truncate( $original_string, $length, $minword = 3, $continue = '...' ) {
2788 + if ( ! is_string( $original_string ) && ! is_int( $original_string ) ) {
2292 2789 return '';
2293 2790 }
2294 2791
2295 2792 $length = (int) $length;
2296 - $str = wp_strip_all_tags( $str );
2793 + $str = wp_strip_all_tags( (string) $original_string );
2297 2794 $original_len = self::mb_function( array( 'mb_strlen', 'strlen' ), array( $str ) );
2298 2795
2299 2796 if ( $length == 0 ) {
2300 2797 return '';
2301 - } elseif ( $length <= 10 ) {
2798 + }
2799 +
2800 + if ( $length <= 10 ) {
2302 2801 $sub = self::mb_function( array( 'mb_substr', 'substr' ), array( $str, 0, $length ) );
2303 -
2304 - return $sub . ( ( $length < $original_len ) ? $continue : '' );
2802 + return $sub . ( $length < $original_len ? $continue : '' );
2305 2803 }
2306 2804
2307 2805 $sub = '';
2308 2806 $len = 0;
@@ -2308,10 +2806,14 @@
2308 2806 $len = 0;
2309 2807
2310 2808 $words = self::mb_function( array( 'mb_split', 'explode' ), array( ' ', $str ) );
2311 2809
2810 + if ( ! is_array( $words ) ) {
2811 + return $original_string;
2812 + }
2813 +
2312 2814 foreach ( $words as $word ) {
2313 - $part = ( ( $sub != '' ) ? ' ' : '' ) . $word;
2815 + $part = ( $sub != '' ? ' ' : '' ) . $word;
2314 2816 $total_len = self::mb_function( array( 'mb_strlen', 'strlen' ), array( $sub . $part ) );
2315 2817 if ( $total_len > $length && substr_count( $sub, ' ' ) ) {
2316 2818 break;
2317 2819 }
@@ -2325,11 +2827,37 @@
2325 2827
2326 2828 unset( $total_len, $word );
2327 2829 }
2328 2830
2329 - return $sub . ( ( $len < $original_len ) ? $continue : '' );
2831 + $sub = self::maybe_force_truncate_on_string_with_no_spaces( $sub, $length );
2832 +
2833 + return $sub . ( $len < $original_len ? $continue : '' );
2330 2834 }
2331 2835
2836 + /**
2837 + * If the string is still too long because there may not have been any spaces, force truncate.
2838 + *
2839 + * @since 6.5.4
2840 + *
2841 + * @param string $sub Current substring.
2842 + * @param int $length The length limit.
2843 + * @return string
2844 + */
2845 + private static function maybe_force_truncate_on_string_with_no_spaces( $sub, $length ) {
2846 + if ( strlen( $sub ) < $length + 50 ) {
2847 + // If the string isn't way over the limit, leave it.
2848 + return $sub;
2849 + }
2850 +
2851 + $first_space = strpos( $sub, ' ', $length );
2852 + if ( false !== $first_space ) {
2853 + // Ignore anything with spaces.
2854 + return $sub;
2855 + }
2856 +
2857 + return substr( $sub, 0, $length + 10 );
2858 + }
2859 +
2332 2860 public static function mb_function( $function_names, $args ) {
2333 2861 $mb_function_name = $function_names[0];
2334 2862 $function_name = $function_names[1];
2335 2863 if ( function_exists( $mb_function_name ) ) {
@@ -2362,8 +2890,13 @@
2362 2890
2363 2891 return $formatted;
2364 2892 }
2365 2893
2894 + /**
2895 + * @param string $time_format
2896 + * @param string $date
2897 + * @return string
2898 + */
2366 2899 private static function add_time_to_date( $time_format, $date ) {
2367 2900 if ( empty( $time_format ) ) {
2368 2901 $time_format = get_option( 'time_format' );
2369 2902 }
@@ -2386,12 +2919,12 @@
2386 2919 return date_i18n( $date_format, strtotime( $date ) );
2387 2920 }
2388 2921
2389 2922 /**
2390 - * Gets the time ago in words
2923 + * Gets the time ago in words.
2391 2924 *
2392 - * @param int $from in seconds
2393 - * @param int|string $to in seconds
2925 + * @param int $from In seconds.
2926 + * @param int|string $to In seconds.
2394 2927 *
2395 2928 * @return string $time_ago
2396 2929 */
2397 2930 public static function human_time_diff( $from, $to = '', $levels = 1 ) {
@@ -2406,9 +2939,9 @@
2406 2939 $diff_object = $now->diff( $ago );
2407 2940 $diff = get_object_vars( $diff_object );
2408 2941
2409 2942 // Add week amount and update day amount
2410 - $diff['w'] = floor( $diff['d'] / 7 );
2943 + $diff['w'] = floor( $diff['d'] / 7 );
2411 2944 $diff['d'] -= $diff['w'] * 7;
2412 2945
2413 2946 $time_strings = self::get_time_strings();
2414 2947
@@ -2415,9 +2948,9 @@
2415 2948 if ( ! is_numeric( $levels ) ) {
2416 2949 // Show time in specified unit.
2417 2950 $levels = self::get_unit( $levels );
2418 2951 if ( isset( $time_strings[ $levels ] ) ) {
2419 - $diff = array(
2952 + $diff = array(
2420 2953 $levels => self::time_format( $levels, $diff ),
2421 2954 );
2422 2955 $time_strings = array(
2423 2956 $levels => $time_strings[ $levels ],
@@ -2506,9 +3039,9 @@
2506 3039 }
2507 3040
2508 3041 /**
2509 3042 * Get the translatable time strings. The untranslated version is a failsafe
2510 - * in case langauges are changing for the unit set in the shortcode.
3043 + * in case languages are changing for the unit set in the shortcode.
2511 3044 *
2512 3045 * @since 2.0.20
2513 3046 * @return array
2514 3047 */
@@ -2554,23 +3087,28 @@
2554 3087
2555 3088 // Pagination Methods.
2556 3089
2557 3090 /**
2558 - * @param integer $current_p
3091 + * @param int $r_count
3092 + * @param int $current_p
3093 + * @param int $p_size
3094 + * @return int
2559 3095 */
2560 3096 public static function get_last_record_num( $r_count, $current_p, $p_size ) {
2561 - return ( ( $r_count < ( $current_p * $p_size ) ) ? $r_count : ( $current_p * $p_size ) );
3097 + return ( $r_count < $current_p * $p_size ? $r_count : $current_p * $p_size );
2562 3098 }
2563 3099
2564 3100 /**
2565 - * @param integer $current_p
3101 + * @param int $r_count
3102 + * @param int $current_p
3103 + * @param int $p_size
3104 + * @return int
2566 3105 */
2567 3106 public static function get_first_record_num( $r_count, $current_p, $p_size ) {
2568 3107 if ( $current_p == 1 ) {
2569 3108 return 1;
2570 - } else {
2571 - return ( self::get_last_record_num( $r_count, ( $current_p - 1 ), $p_size ) + 1 );
2572 3109 }
3110 + return self::get_last_record_num( $r_count, $current_p - 1, $p_size ) + 1;
2573 3111 }
2574 3112
2575 3113 /**
2576 3114 * @return array
@@ -2593,9 +3131,9 @@
2593 3131 if ( ! isset( $l3 ) ) {
2594 3132 $l3 = $name;
2595 3133 }
2596 3134
2597 - $this_val = ( $p == $last ) ? $jv['value'] : array();
3135 + $this_val = $p == $last ? $jv['value'] : array();
2598 3136
2599 3137 switch ( $p ) {
2600 3138 case 0:
2601 3139 $l1 = $name;
@@ -2617,12 +3155,12 @@
2617 3155 self::add_value_to_array( $name, $l4, $this_val, $vars[ $l1 ][ $l2 ][ $l3 ] );
2618 3156 }
2619 3157
2620 3158 unset( $this_val, $n );
2621 - }
3159 + }//end foreach
2622 3160
2623 3161 unset( $last, $jv );
2624 - }
3162 + }//end foreach
2625 3163
2626 3164 return $vars;
2627 3165 }
2628 3166
@@ -2647,8 +3185,9 @@
2647 3185 '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' ),
2648 3186 'from' => __( 'Enter the name and/or email address of the sender. FORMAT: John Bates <john@example.com> or john@example.com.', 'formidable' ),
2649 3187 /* translators: %1$s: Form name, %2$s: Date */
2650 3188 '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() ) ),
3189 + 'new_tab' => __( 'This option will open the link in a new browser tab. Please note that some popup blockers may prevent this from happening, in which case the link will be displayed.', 'formidable' ),
2651 3190 );
2652 3191
2653 3192 if ( ! isset( $tooltips[ $name ] ) ) {
2654 3193 return;
@@ -2869,8 +3408,11 @@
2869 3408 }
2870 3409
2871 3410 /**
2872 3411 * @since 4.02.03
3412 + *
3413 + * @param array|string $value
3414 + * @return string
2873 3415 */
2874 3416 public static function maybe_json_encode( $value ) {
2875 3417 if ( is_array( $value ) ) {
2876 3418 $value = wp_json_encode( $value );
@@ -2878,12 +3420,14 @@
2878 3420 return $value;
2879 3421 }
2880 3422
2881 3423 /**
3424 + * Echo The javascript to open and highlight the Formidable menu
3425 + *
2882 3426 * @since 1.07.10
2883 3427 *
2884 - * @param string $post_type The name of the post type that may need to be highlighted
2885 - * echo The javascript to open and highlight the Formidable menu
3428 + * @param string $post_type The name of the post type that may need to be highlighted.
3429 + * @return void
2886 3430 */
2887 3431 public static function maybe_highlight_menu( $post_type ) {
2888 3432 global $post;
2889 3433
@@ -2902,8 +3446,11 @@
2902 3446 /**
2903 3447 * Load the JS file on non-Formidable pages in the admin area
2904 3448 *
2905 3449 * @since 2.0
3450 + *
3451 + * @param bool $load
3452 + * @return void
2906 3453 */
2907 3454 public static function load_admin_wide_js( $load = true ) {
2908 3455 $version = self::plugin_version();
2909 3456 wp_register_script( 'formidable_admin_global', self::plugin_url() . '/js/formidable_admin_global.js', array( 'jquery' ), $version );
@@ -2917,8 +3464,9 @@
2917 3464 'canAccessApplicationDashboard' => current_user_can( is_callable( 'FrmProApplicationsHelper::get_required_templates_capability' ) ? FrmProApplicationsHelper::get_required_templates_capability() : 'frm_edit_forms' ),
2918 3465 'loading' => __( 'Loading&hellip;', 'formidable' ),
2919 3466 'nonce' => wp_create_nonce( 'frm_ajax' ),
2920 3467 'proIncludesSliderJs' => is_callable( 'FrmProFormsHelper::prepare_custom_currency' ),
3468 + 'inboxSlideIn' => FrmInbox::get_inbox_slide_in_value_for_js(),
2921 3469 );
2922 3470 wp_localize_script( 'formidable_admin_global', 'frmGlobal', $global_strings );
2923 3471
2924 3472 if ( $load ) {
@@ -2927,8 +3475,9 @@
2927 3475 }
2928 3476
2929 3477 /**
2930 3478 * @since 2.0.9
3479 + * @return void
2931 3480 */
2932 3481 public static function load_font_style() {
2933 3482 wp_enqueue_style( 'frm_fonts', self::plugin_url() . '/css/frm_fonts.css', array(), self::plugin_version() );
2934 3483 }
@@ -2934,99 +3483,133 @@
2934 3483 }
2935 3484
2936 3485 /**
2937 3486 * @param string $location
3487 + * @return void
2938 3488 */
2939 3489 public static function localize_script( $location ) {
2940 - global $wp_scripts;
3490 + global $wp_scripts, $wp_version;
2941 3491
2942 - $ajax_url = admin_url( 'admin-ajax.php', is_ssl() ? 'admin' : 'http' );
2943 - $ajax_url = apply_filters( 'frm_ajax_url', $ajax_url );
2944 -
2945 3492 $script_strings = array(
2946 - 'ajax_url' => $ajax_url,
2947 - 'images_url' => self::plugin_url() . '/images',
2948 - 'loading' => __( 'Loading&hellip;', 'formidable' ),
2949 - 'remove' => __( 'Remove', 'formidable' ),
2950 - 'offset' => apply_filters( 'frm_scroll_offset', 4 ),
2951 - 'nonce' => wp_create_nonce( 'frm_ajax' ),
2952 - 'id' => __( 'ID', 'formidable' ),
2953 - 'no_results' => __( 'No results match', 'formidable' ),
2954 - 'file_spam' => __( 'That file looks like Spam.', 'formidable' ),
2955 - 'calc_error' => __( 'There is an error in the calculation in the field with key', 'formidable' ),
2956 - 'empty_fields' => __( 'Please complete the preceding required fields before uploading a file.', 'formidable' ),
2957 - 'focus_first_error' => self::should_focus_first_error(),
2958 - 'include_alert_role' => self::should_include_alert_role_on_field_errors(),
3493 + 'ajax_url' => esc_url_raw( self::get_ajax_url() ),
3494 + 'images_url' => self::plugin_url() . '/images',
3495 + 'loading' => __( 'Loading&hellip;', 'formidable' ),
3496 + 'remove' => __( 'Remove', 'formidable' ),
3497 + 'offset' => apply_filters( 'frm_scroll_offset', 4 ),
3498 + 'nonce' => wp_create_nonce( 'frm_ajax' ),
3499 + 'id' => __( 'ID', 'formidable' ),
3500 + 'no_results' => __( 'No results match', 'formidable' ),
3501 + 'file_spam' => __( 'That file looks like Spam.', 'formidable' ),
3502 + 'calc_error' => __( 'There is an error in the calculation in the field with key', 'formidable' ),
3503 + 'empty_fields' => __( 'Please complete the preceding required fields before uploading a file.', 'formidable' ),
3504 + 'focus_first_error' => self::should_focus_first_error(),
3505 + 'include_alert_role' => self::should_include_alert_role_on_field_errors(),
3506 + // We need to keep this setting for a few versions because Pro checks for this.
3507 + 'include_resend_email' => false,
2959 3508 );
2960 3509
2961 3510 $data = $wp_scripts->get_data( 'formidable', 'data' );
2962 - if ( empty( $data ) ) {
3511 + if ( ! $data ) {
2963 3512 wp_localize_script( 'formidable', 'frm_js', $script_strings );
2964 3513 }
2965 3514
2966 - if ( $location == 'admin' ) {
2967 - $frm_settings = self::get_settings();
3515 + if ( $location === 'admin' ) {
2968 3516 $admin_script_strings = array(
2969 - 'desc' => __( '(Click to add description)', 'formidable' ),
2970 - 'blank' => __( '(Blank)', 'formidable' ),
2971 - 'no_label' => __( '(no label)', 'formidable' ),
2972 - 'saving' => '', // Deprecated in 6.0.
2973 - 'saved' => '', // Deprecated in 6.0.
2974 - 'ok' => __( 'OK', 'formidable' ),
2975 - 'cancel' => __( 'Cancel', 'formidable' ),
2976 - 'default_label' => __( 'Default', 'formidable' ),
2977 - 'clear_default' => __( 'Clear default value when typing', 'formidable' ),
2978 - 'no_clear_default' => __( 'Do not clear default value when typing', 'formidable' ),
2979 - 'valid_default' => __( 'Default value will pass form validation', 'formidable' ),
2980 - 'no_valid_default' => __( 'Default value will NOT pass form validation', 'formidable' ),
2981 - 'confirm' => __( 'Are you sure?', 'formidable' ),
2982 - 'conf_delete' => __( 'Are you sure you want to delete this field and all data associated with it?', 'formidable' ),
2983 - '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' ),
2984 - 'conf_no_repeat' => __( 'Warning: If you have entries with multiple rows, all but the first row will be lost.', 'formidable' ),
2985 - 'default_unique' => $frm_settings->unique_msg,
2986 - 'default_conf' => __( 'The entered values do not match', 'formidable' ),
2987 - 'enter_email' => __( 'Enter Email', 'formidable' ),
2988 - 'confirm_email' => __( 'Confirm Email', 'formidable' ),
2989 - 'conditional_text' => __( 'Conditional content here', 'formidable' ),
2990 - 'new_option' => __( 'New Option', 'formidable' ),
2991 - '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' ),
2992 - 'enter_password' => __( 'Enter Password', 'formidable' ),
2993 - 'confirm_password' => __( 'Confirm Password', 'formidable' ),
2994 - 'import_complete' => __( 'Import Complete', 'formidable' ),
2995 - 'updating' => __( 'Please wait while your site updates.', 'formidable' ),
2996 - 'no_save_warning' => __( 'Warning: There is no way to retrieve unsaved entries.', 'formidable' ),
2997 - 'private_label' => __( 'Private', 'formidable' ),
2998 - 'jquery_ui_url' => '',
2999 - 'pro_url' => is_callable( 'FrmProAppHelper::plugin_url' ) ? FrmProAppHelper::plugin_url() : '',
3000 - 'no_licenses' => __( 'No new licenses were found', 'formidable' ),
3001 - 'unmatched_parens' => __( 'This calculation has at least one unmatched ( ) { } [ ].', 'formidable' ),
3002 - 'view_shortcodes' => __( 'This calculation may have shortcodes that work in Views but not forms.', 'formidable' ),
3003 - 'text_shortcodes' => __( 'This calculation may have shortcodes that work in text calculations but not numeric calculations.', 'formidable' ),
3004 - 'only_one_action' => __( 'This form action is limited to one per form. Please edit the existing form action.', 'formidable' ),
3005 - 'unsafe_params' => FrmFormsHelper::reserved_words(),
3517 + 'desc' => __( '(Click to add description)', 'formidable' ),
3518 + 'blank' => __( '(Blank)', 'formidable' ),
3519 + 'no_label' => __( '(no label)', 'formidable' ),
3520 + 'ok' => __( 'OK', 'formidable' ),
3521 + 'cancel' => __( 'Cancel', 'formidable' ),
3522 + 'default_label' => __( 'Default', 'formidable' ),
3523 + 'clear_default' => __( 'Clear default value when typing', 'formidable' ),
3524 + 'no_clear_default' => __( 'Do not clear default value when typing', 'formidable' ),
3525 + 'valid_default' => __( 'Default value will pass form validation', 'formidable' ),
3526 + 'no_valid_default' => __( 'Default value will NOT pass form validation', 'formidable' ),
3527 + 'confirm' => __( 'Are you sure?', 'formidable' ),
3528 + 'conf_delete' => __( 'Are you sure you want to delete this field and all data associated with it?', 'formidable' ),
3529 + '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' ),
3530 + 'conf_no_repeat' => __( 'Warning: If you have entries with multiple rows, all but the first row will be lost.', 'formidable' ),
3531 + 'default_unique' => FrmFieldsHelper::default_unique_msg(),
3532 + 'default_conf' => __( 'The entered values do not match', 'formidable' ),
3533 + 'enter_email' => __( 'Enter Email', 'formidable' ),
3534 + 'confirm_email' => __( 'Confirm Email', 'formidable' ),
3535 + 'conditional_text' => __( 'Conditional content here', 'formidable' ),
3536 + 'new_option' => __( 'New Option', 'formidable' ),
3537 + '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' ),
3538 + 'enter_password' => __( 'Enter Password', 'formidable' ),
3539 + 'confirm_password' => __( 'Confirm Password', 'formidable' ),
3540 + 'import_complete' => __( 'Import Complete', 'formidable' ),
3541 + 'updating' => __( 'Please wait while your site updates.', 'formidable' ),
3542 + 'no_save_warning' => __( 'Warning: There is no way to retrieve unsaved entries.', 'formidable' ),
3543 + 'private_label' => __( 'Private', 'formidable' ),
3544 + 'jquery_ui_url' => '',
3545 + 'pro_url' => is_callable( 'FrmProAppHelper::plugin_url' ) ? FrmProAppHelper::plugin_url() : '',
3546 + 'no_licenses' => __( 'No new licenses were found', 'formidable' ),
3547 + 'unmatched_parens' => __( 'This calculation has at least one unmatched ( ) { } [ ].', 'formidable' ),
3548 + 'view_shortcodes' => __( 'This calculation may have shortcodes that work in Views but not forms.', 'formidable' ),
3549 + 'text_shortcodes' => __( 'This calculation may have shortcodes that work in text calculations but not numeric calculations.', 'formidable' ),
3550 + /* translators: %d is the number of allowed actions per form */
3551 + 'only_one_action' => sprintf( __( 'This form action is limited to %d per form.', 'formidable' ), 1 ),
3552 + 'edit_action_text' => __( 'Please edit the existing form action.', 'formidable' ),
3553 + 'unsafe_params' => FrmFormsHelper::reserved_words(),
3006 3554 /* Translators: %s is the name of a Detail Page Slug that is a reserved word.*/
3007 - 'slug_is_reserved' => sprintf( __( 'The Detail Page Slug "%s" is reserved by WordPress. This may cause problems. Is this intentional?', 'formidable' ), '****' ),
3555 + 'slug_is_reserved' => sprintf( __( 'The Detail Page Slug "%s" is reserved by WordPress. This may cause problems. Is this intentional?', 'formidable' ), '****' ),
3008 3556 /* 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. */
3009 - 'param_is_reserved' => sprintf( __( 'The parameter "%s" is reserved by WordPress. This may cause problems when included in the URL. Is this intentional? ', 'formidable' ), '****' ),
3010 - 'reserved_words' => __( 'See the list of reserved words in WordPress.', 'formidable' ),
3011 - 'repeat_limit_min' => __( 'Please enter a Repeat Limit that is greater than 1.', 'formidable' ),
3012 - 'checkbox_limit' => __( 'Please select a limit between 0 and 200.', 'formidable' ),
3013 - 'install' => __( 'Install', 'formidable' ),
3014 - 'active' => __( 'Active', 'formidable' ),
3015 - 'select_a_field' => __( 'Select a Field', 'formidable' ),
3016 - 'no_items_found' => __( 'No items found.', 'formidable' ),
3017 - 'field_already_used' => __( 'Oops. You have already used that field.', 'formidable' ),
3557 + 'param_is_reserved' => sprintf( __( 'The parameter "%s" is reserved by WordPress. This may cause problems when included in the URL. Is this intentional? ', 'formidable' ), '****' ),
3558 + 'reserved_words' => __( 'See the list of reserved words in WordPress.', 'formidable' ),
3559 + 'repeat_limit_min' => __( 'Please enter a Repeat Limit that is greater than 1.', 'formidable' ),
3560 + 'checkbox_limit' => __( 'Please select a limit between 0 and 200.', 'formidable' ),
3561 + 'install' => __( 'Install', 'formidable' ),
3562 + 'active' => __( 'Active', 'formidable' ),
3563 + 'installed' => __( 'Installed', 'formidable' ),
3564 + 'not_installed' => __( 'Not Installed', 'formidable' ),
3565 + 'select_a_field' => __( 'Select a Field', 'formidable' ),
3566 + 'no_items_found' => __( 'No items found.', 'formidable' ),
3567 + 'field_already_used' => __( 'Oops. You have already used that field.', 'formidable' ),
3568 +
3569 + // Deprecated in 6.0.
3570 + 'saving' => '',
3571 +
3572 + // Deprecated in 6.0.
3573 + 'saved' => '',
3574 +
3575 + // translators: %1$s: HTML open tag, %2$s: HTML end tag.
3576 + 'holdShiftMsg' => esc_html__( 'You can hold %1$sShift%2$s on your keyboard to select multiple fields', 'formidable' ),
3577 + 'noTitleText' => FrmFormsHelper::get_no_title_text(),
3578 +
3579 + // In older versions this event listener causes the section to immediately close again
3580 + // when the h3 element is clicked. It's only required in WP 6.7+.
3581 + 'requireAccordionTitleClickListener' => version_compare( $wp_version, '6.7', '>=' ),
3018 3582 );
3583 + /**
3584 + * @param array $admin_script_strings
3585 + */
3019 3586 $admin_script_strings = apply_filters( 'frm_admin_script_strings', $admin_script_strings );
3020 3587
3021 3588 $data = $wp_scripts->get_data( 'formidable_admin', 'data' );
3022 - if ( empty( $data ) ) {
3589 + if ( ! $data ) {
3023 3590 wp_localize_script( 'formidable_admin', 'frm_admin_js', $admin_script_strings );
3024 3591 }
3025 - }
3592 + }//end if
3026 3593 }
3027 3594
3028 3595 /**
3596 + * @since 6.5
3597 + *
3598 + * @return string
3599 + */
3600 + public static function get_ajax_url() {
3601 + $ajax_url = admin_url( 'admin-ajax.php', is_ssl() ? 'admin' : 'http' );
3602 +
3603 + /**
3604 + * @since 2.0.13
3605 + *
3606 + * @param string $ajax_url
3607 + */
3608 + return apply_filters( 'frm_ajax_url', $ajax_url );
3609 + }
3610 +
3611 + /**
3029 3612 * Returns whether or not the first errored input should be auto-focused (default true).
3030 3613 *
3031 3614 * @since 5.2.05
3032 3615 *
@@ -3051,9 +3634,10 @@
3051 3634 * Echo the message on the plugins listing page
3052 3635 *
3053 3636 * @since 1.07.10
3054 3637 *
3055 - * @param float $min_version The version the add-on requires
3638 + * @param float $min_version The version the add-on requires.
3639 + * @return void
3056 3640 */
3057 3641 public static function min_version_notice( $min_version ) {
3058 3642 $frm_version = self::plugin_version();
3059 3643
@@ -3087,9 +3671,8 @@
3087 3671 if ( ! $is_pro || self::meets_min_pro_version( $min_version ) ) {
3088 3672 return;
3089 3673 }
3090 3674
3091 - $pro_version = FrmProDb::$plug_version;
3092 3675 $expired = FrmAddonsController::is_license_expired();
3093 3676 ?>
3094 3677 <div class="frm-banner-alert frm_error_style frm_previous_install">
3095 3678 <?php
@@ -3107,8 +3690,11 @@
3107 3690 /**
3108 3691 * If Pro is installed, check the version number.
3109 3692 *
3110 3693 * @since 4.0.01
3694 + *
3695 + * @param string $min_version
3696 + * @return bool
3111 3697 */
3112 3698 public static function meets_min_pro_version( $min_version ) {
3113 3699 return ! class_exists( 'FrmProDb' ) || version_compare( FrmProDb::$plug_version, $min_version, '>=' );
3114 3700 }
@@ -3113,24 +3699,19 @@
3113 3699 return ! class_exists( 'FrmProDb' ) || version_compare( FrmProDb::$plug_version, $min_version, '>=' );
3114 3700 }
3115 3701
3116 3702 /**
3117 - * Show a message if the browser or PHP version is below the recommendations.
3703 + * Show a message if the PHP version is below the recommendations.
3118 3704 *
3119 3705 * @since 4.0.02
3706 + * @return void
3120 3707 */
3121 3708 private static function php_version_notice() {
3122 3709 $message = array();
3123 - if ( version_compare( phpversion(), '5.6', '<' ) ) {
3710 + if ( version_compare( phpversion(), '7.0', '<' ) ) {
3124 3711 $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' );
3125 3712 }
3126 3713
3127 - $browser = self::get_server_value( 'HTTP_USER_AGENT' );
3128 - $is_ie = strpos( $browser, 'MSIE' ) !== false;
3129 - if ( $is_ie ) {
3130 - $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' );
3131 - }
3132 -
3133 3714 foreach ( $message as $m ) {
3134 3715 ?>
3135 3716 <div class="frm-banner-alert frm_error_style frm_previous_install">
3136 3717 <?php echo esc_html( $m ); ?>
@@ -3260,16 +3841,10 @@
3260 3841 return $locales;
3261 3842 }
3262 3843
3263 3844 /**
3264 - * Output HTML containing reference text for accessibility
3265 - *
3266 - * @deprecated 5.1
3845 + * @return string
3267 3846 */
3268 - public static function multiselect_accessibility() {
3269 - _deprecated_function( __METHOD__, '5.1' );
3270 - }
3271 -
3272 3847 public static function get_menu_icon_class() {
3273 3848 if ( is_callable( 'FrmProAppHelper::get_settings' ) ) {
3274 3849 $settings = FrmProAppHelper::get_settings();
3275 3850 if ( is_object( $settings ) && ! empty( $settings->menu_icon ) ) {
@@ -3379,8 +3954,18 @@
3379 3954 return apply_filters( 'frm_images_dropdown_input_attrs', $input_attrs_str, $args );
3380 3955 }
3381 3956
3382 3957 /**
3958 + * @since 6.7.1
3959 + */
3960 + public static function get_images_dropdown_atts( $option, $args ) {
3961 + $image = self::get_images_dropdown_option_image( $option, $args );
3962 + $classes = self::get_images_dropdown_option_classes( $option, $args );
3963 + $custom_attrs = self::get_images_dropdown_option_html_attrs( $option, $args );
3964 + return compact( 'image', 'classes', 'custom_attrs' );
3965 + }
3966 +
3967 + /**
3383 3968 * Gets the image of each option in images_dropdown() method.
3384 3969 *
3385 3970 * @since 5.0.04
3386 3971 *
@@ -3391,9 +3976,9 @@
3391 3976 private static function get_images_dropdown_option_image( $option, $args ) {
3392 3977 $image = self::icon_by_class(
3393 3978 'frmfont ' . $option['svg'],
3394 3979 array(
3395 - 'echo' => false,
3980 + 'echo' => false,
3396 3981 )
3397 3982 );
3398 3983
3399 3984 $args['option'] = $option;
@@ -3531,15 +4116,15 @@
3531 4116 return $values;
3532 4117 }
3533 4118
3534 4119 /**
3535 - * Some back end fields allow privleged users to add scripts.
4120 + * Some back end fields allow privileged users to add scripts.
3536 4121 * A site that uses the DISALLOW_UNFILTERED_HTML always remove scripts on echo.
3537 4122 *
3538 4123 * @since 5.0.13
3539 4124 *
3540 4125 * @param string $value
3541 - * @param array|string $allowed 'all' for everything included as defaults
4126 + * @param array|string $allowed 'all' for everything included as defaults.
3542 4127 * @return string
3543 4128 */
3544 4129 public static function maybe_kses( $value, $allowed = 'all' ) {
3545 4130 if ( self::should_never_allow_unfiltered_html() ) {
@@ -3548,8 +4133,64 @@
3548 4133 return $value;
3549 4134 }
3550 4135
3551 4136 /**
4137 + * Check if an option attribute used in an [input] shortcode is safe.
4138 + *
4139 + * @since 6.11.2
4140 + *
4141 + * @param string $key
4142 + * @param string $context Either 'display' or 'update'. On update, we want to allow a few keys that are never displayed.
4143 + * @return bool
4144 + */
4145 + public static function input_key_is_safe( $key, $context = 'display' ) {
4146 + if ( 'update' === $context && in_array( $key, array( 'opt', 'label' ), true ) ) {
4147 + $safe = true;
4148 + } elseif ( 0 === strpos( $key, 'data-' ) ) {
4149 + // Allow all data attributes.
4150 + $safe = true;
4151 + } elseif ( 0 === strpos( $key, 'aria-' ) ) {
4152 + // Allow all aria attributes.
4153 + $safe = true;
4154 + } else {
4155 + $safe_keys = array(
4156 + 'class',
4157 + 'required',
4158 + 'title',
4159 + 'placeholder',
4160 + 'value',
4161 + 'readonly',
4162 + 'disabled',
4163 + 'size',
4164 + 'maxlength',
4165 + 'min',
4166 + 'max',
4167 + 'pattern',
4168 + 'step',
4169 + 'autofocus',
4170 + 'width',
4171 + 'height',
4172 + 'autocomplete',
4173 + 'tabindex',
4174 + 'role',
4175 + 'style',
4176 + );
4177 + $safe = in_array( $key, $safe_keys, true );
4178 + }//end if
4179 +
4180 + /**
4181 + * Filter the $safe value so additional keys can be allowed or disallowed.
4182 + *
4183 + * @since 6.11.2
4184 + *
4185 + * @param bool $safe True if the key is considered safe.
4186 + * @param string $key
4187 + * @param string $context Either 'display' or 'update'.
4188 + */
4189 + return (bool) apply_filters( 'frm_input_key_is_safe', $safe, $key, $context );
4190 + }
4191 +
4192 + /**
3552 4193 * @since 5.0.16
3553 4194 *
3554 4195 * @return bool
3555 4196 */
@@ -3574,20 +4215,9 @@
3574 4215
3575 4216 /**
3576 4217 * @since 5.0.17
3577 4218 *
3578 - * @param string $plugin
3579 - * @return string|false
3580 - */
3581 - private static function get_plan_required( $plugin ) {
3582 - $link = FrmAddonsController::install_link( $plugin );
3583 - return FrmFormsHelper::get_plan_required( $link );
3584 - }
3585 -
3586 - /**
3587 - * @since 5.0.17
3588 - *
3589 - * @param string
4219 + * @param string $feature
3590 4220 * @return bool
3591 4221 */
3592 4222 public static function show_new_feature( $feature ) {
3593 4223 $link = FrmAddonsController::install_link( $feature );
@@ -3660,9 +4290,9 @@
3660 4290 public static function show_pill_text( $text = null ) {
3661 4291 if ( null === $text ) {
3662 4292 $text = __( 'NEW', 'formidable' );
3663 4293 }
3664 - echo '<span class="frm-new-pill">' . esc_html( $text ) . '</span>';
4294 + echo '<span class="frm-meta-tag frm-new-pill">' . esc_html( $text ) . '</span>';
3665 4295 }
3666 4296
3667 4297 /**
3668 4298 * Count the number of decimals digits.
@@ -3669,9 +4299,9 @@
3669 4299 *
3670 4300 * @since 5.2.07
3671 4301 *
3672 4302 * @param mixed $num Number.
3673 - * @return int|false Returns `false` if the passed parameter is not number.
4303 + * @return false|int Returns `false` if the passed parameter is not number.
3674 4304 */
3675 4305 public static function count_decimals( $num ) {
3676 4306 if ( ! is_numeric( $num ) ) {
3677 4307 return false;
@@ -3702,9 +4332,9 @@
3702 4332 }
3703 4333
3704 4334 add_filter(
3705 4335 'option_gmt_offset',
3706 - function( $offset ) {
4336 + function ( $offset ) {
3707 4337 if ( ! is_string( $offset ) || is_numeric( $offset ) ) {
3708 4338 // Leave a valid value alone.
3709 4339 return $offset;
3710 4340 }
@@ -3786,65 +4416,8 @@
3786 4416 return true;
3787 4417 }
3788 4418
3789 4419 /**
3790 - * @since 4.08
3791 - * @deprecated 4.09.01
3792 - */
3793 - public static function expiring_message() {
3794 - _deprecated_function( __METHOD__, '4.09.01', 'FrmProAddonsController::expiring_message' );
3795 - if ( is_callable( 'FrmProAddonsController::expiring_message' ) ) {
3796 - FrmProAddonsController::expiring_message();
3797 - }
3798 - }
3799 -
3800 - /**
3801 - * Use the WP 4.7 wp_doing_ajax function
3802 - *
3803 - * @since 2.05.07
3804 - * @deprecated 4.04.04
3805 - */
3806 - public static function wp_doing_ajax() {
3807 - _deprecated_function( __METHOD__, '4.04.04', 'wp_doing_ajax' );
3808 - return wp_doing_ajax();
3809 - }
3810 -
3811 - /**
3812 - * @deprecated 4.0
3813 - */
3814 - public static function insert_opt_html( $args ) {
3815 - _deprecated_function( __METHOD__, '4.0', 'FrmFormsHelper::insert_opt_html' );
3816 - FrmFormsHelper::insert_opt_html( $args );
3817 - }
3818 -
3819 - /**
3820 - * @deprecated 3.01
3821 - * @codeCoverageIgnore
3822 - */
3823 - public static function sanitize_array( &$values ) {
3824 - FrmDeprecated::sanitize_array( $values );
3825 - }
3826 -
3827 - /**
3828 - * @since 2.0
3829 - * @deprecated 5.0.13
3830 - *
3831 - * @return string The base Google APIS url for the current version of jQuery UI
3832 - */
3833 - public static function jquery_ui_base_url() {
3834 - _deprecated_function( __FUNCTION__, '5.0.13', 'FrmProAppHelper::jquery_ui_base_url' );
3835 - return is_callable( 'FrmProAppHelper::jquery_ui_base_url' ) ? FrmProAppHelper::jquery_ui_base_url() : '';
3836 - }
3837 -
3838 - /**
3839 - * @since 4.07
3840 - * @deprecated 6.0
3841 - */
3842 - public static function renewal_message() {
3843 - _deprecated_function( __METHOD__, '6.0', 'FrmProAddonsController::renewal_message' );
3844 - }
3845 -
3846 - /**
3847 4420 * Display a dismissable warning message and save its dismissal state.
3848 4421 *
3849 4422 * @since 6.3
3850 4423 *
@@ -3856,9 +4429,9 @@
3856 4429 if ( ! $message || ! $option ) {
3857 4430 return;
3858 4431 }
3859 4432
3860 - $ajax_callback = function() use ( $option ) {
4433 + $ajax_callback = function () use ( $option ) {
3861 4434 self::dismiss_warning_message( $option );
3862 4435 };
3863 4436
3864 4437 // We're handling JS codes with `doJsonPost` and it adds 'frm_' to the beginning of the action.
@@ -3866,9 +4439,9 @@
3866 4439 add_action( 'wp_ajax_frm_' . $option, $ajax_callback );
3867 4440
3868 4441 add_filter(
3869 4442 'frm_message_list',
3870 - function( $show_messages ) use ( $message, $option ) {
4443 + function ( $show_messages ) use ( $message, $option ) {
3871 4444 if ( get_option( $option, false ) ) {
3872 4445 return $show_messages;
3873 4446 }
3874 4447
@@ -3875,9 +4448,9 @@
3875 4448 $dismiss_icon = self::icon_by_class(
3876 4449 'frmfont frm_close_icon',
3877 4450 array(
3878 4451 'aria-label' => _x( 'Dismiss', 'warning message: close icon label', 'formidable' ),
3879 - 'echo' => false,
4452 + 'echo' => false,
3880 4453 )
3881 4454 );
3882 4455
3883 4456 $show_messages[] = $message;
@@ -3904,6 +4477,124 @@
3904 4477 update_option( $option, true, 'no' );
3905 4478 }
3906 4479
3907 4480 wp_send_json_success();
4481 + }
4482 +
4483 + /**
4484 + * Lite license copy.
4485 + * Used in FrmDashboardController & FrmSettingsController
4486 + *
4487 + * @since 6.8
4488 + *
4489 + * @return string
4490 + */
4491 + public static function copy_for_lite_license() {
4492 + $message = __( 'You\'re using Formidable Forms Lite - no license needed. Enjoy!', 'formidable' ) . ' 🙂';
4493 +
4494 + if ( is_callable( 'FrmProAddonsController::get_readable_license_type' ) && ! class_exists( 'FrmProDashboardController' ) ) {
4495 + // Manage PRO versions without PRO dashboard functionality.
4496 + $license_type = FrmProAddonsController::get_readable_license_type();
4497 + if ( 'lite' !== strtolower( $license_type ) ) {
4498 + $message = 'Formidable Pro ' . $license_type;
4499 + }
4500 + }
4501 +
4502 + return apply_filters( 'frm_license_type_text', $message );
4503 + }
4504 +
4505 + /**
4506 + * Removes scripts that are unnecessarily loaded across the pages!
4507 + *
4508 + * @since 6.9
4509 + * @return void
4510 + */
4511 + public static function dequeue_extra_global_scripts() {
4512 + wp_dequeue_script( 'frm-surveys-admin' );
4513 + wp_dequeue_script( 'frm-quizzes-form-action' );
4514 + }
4515 +
4516 + /**
4517 + * Shows tooltip icon.
4518 + *
4519 + * @since 6.12
4520 + *
4521 + * @param string $tooltip_text Tooltip text.
4522 + * @param array $atts Tooltip wrapper HTML attributes.
4523 + *
4524 + * @return void
4525 + */
4526 + public static function tooltip_icon( $tooltip_text, $atts = array() ) {
4527 + $atts['title'] = $tooltip_text;
4528 + if ( isset( $atts['class'] ) ) {
4529 + $atts['class'] .= ' frm_help';
4530 + } else {
4531 + $atts['class'] = 'frm_help';
4532 + }
4533 + ?>
4534 + <span <?php self::array_to_html_params( $atts, true ); ?>>
4535 + <?php self::icon_by_class( 'frmfont frm_tooltip_icon' ); ?>
4536 + </span>
4537 + <?php
4538 + }
4539 +
4540 + /**
4541 + * Prints errors for settings in onboarding wizard or template settings.
4542 + *
4543 + * @since 6.15
4544 + *
4545 + * @param array $args Args.
4546 + *
4547 + * @return void
4548 + */
4549 + public static function print_setting_error( $args ) {
4550 + $args = wp_parse_args(
4551 + $args,
4552 + array(
4553 + 'id' => '',
4554 + 'errors' => array(),
4555 + 'class' => '',
4556 + )
4557 + );
4558 +
4559 + $args['class'] .= ' frm-validation-error frm-mt-xs frm_hidden';
4560 + ?>
4561 + <span id="<?php echo esc_attr( $args['id'] ); ?>" class="<?php echo esc_attr( $args['class'] ); ?>">
4562 + <?php
4563 + if ( is_array( $args['errors'] ) ) {
4564 + foreach ( $args['errors'] as $key => $msg ) {
4565 + ?>
4566 + <span frm-error="<?php echo esc_attr( $key ); ?>"><?php echo esc_html( $msg ); ?></span>
4567 + <?php
4568 + }
4569 + } else {
4570 + echo '<span>' . esc_html( $args['errors'] ) . '</span>';
4571 + }
4572 + ?>
4573 + </span>
4574 + <?php
4575 + }
4576 +
4577 + /**
4578 + * Check if GDPR is enabled.
4579 + *
4580 + * @since 6.19
4581 + *
4582 + * @return bool
4583 + */
4584 + public static function is_gdpr_enabled() {
4585 + $frm_settings = self::get_settings();
4586 + return $frm_settings->enable_gdpr || $frm_settings->no_ips || $frm_settings->custom_header_ip || $frm_settings->no_gdpr_cookies;
4587 + }
4588 +
4589 + /**
4590 + * Check if GDPR cookies are disabled.
4591 + *
4592 + * @since 6.19
4593 + *
4594 + * @return bool
4595 + */
4596 + public static function no_gdpr_cookies() {
4597 + $frm_settings = self::get_settings();
4598 + return $frm_settings->enable_gdpr && $frm_settings->no_gdpr_cookies;
3908 4599 }
3909 4600 }