PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / trunk
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More vtrunk
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 +931 -404 6.25.1 → trunk View file →
@@ -9,9 +9,9 @@
9 9 * Version of the database we are moving to.
10 10 *
11 11 * @var int
12 12 */
13 - public static $db_version = 104;
13 + public static $db_version = 106;
14 14
15 15 /**
16 16 * Used by the API add-on.
17 17 *
@@ -28,9 +28,9 @@
28 28 * @since 2.0
29 29 *
30 30 * @var string
31 31 */
32 - public static $plug_version = '6.25.1';
32 + public static $plug_version = '6.35';
33 33
34 34 /**
35 35 * @var bool
36 36 */
@@ -85,8 +85,9 @@
85 85 * Get the name of this site
86 86 * Used for [sitename] shortcode
87 87 *
88 88 * @since 2.0
89 + *
89 90 * @return string
90 91 */
91 92 public static function site_name() {
92 93 return get_option( 'blogname' );
@@ -93,13 +94,15 @@
93 94 }
94 95
95 96 /**
96 97 * @param string $url
98 + *
97 99 * @return string
98 100 */
99 101 public static function make_affiliate_url( $url ) {
100 102 $affiliate_id = self::get_affiliate();
101 - if ( ! empty( $affiliate_id ) ) {
103 +
104 + if ( $affiliate_id ) {
102 105 $url = str_replace( array( 'http://', 'https://' ), '', $url );
103 106 $url = 'http://www.shareasale.com/r.cfm?u=' . absint( $affiliate_id ) . '&b=841990&m=64739&afftrack=plugin&urllink=' . urlencode( $url );
104 107 }
105 108
@@ -126,13 +129,9 @@
126 129 } else {
127 130 $page = 'https://formidableforms.com/lite-upgrade/';
128 131 }
129 132
130 - if ( is_array( $args ) ) {
131 - $args = self::adjust_legacy_utm_args( $args );
132 - } else {
133 - $args = array( 'campaign' => $args );
134 - }
133 + $args = is_array( $args ) ? self::adjust_legacy_utm_args( $args ) : array( 'campaign' => $args );
135 134
136 135 $query_args = array(
137 136 'utm_source' => 'plugin',
138 137 'utm_medium' => self::get_utm_medium(),
@@ -168,13 +167,17 @@
168 167 * If medium is "pro", add an additional utm_license param with their active license type.
169 168 *
170 169 * @since 6.25.1
171 170 *
172 - * @param array $query_args
171 + * @param array $query_args
172 + * @param string $link
173 + *
173 174 * @return array
174 175 */
175 - private static function maybe_add_utm_license( $query_args ) {
176 - if ( 'pro' === $query_args['utm_medium'] && is_callable( 'FrmProAddonsController::get_readable_license_type' ) ) {
176 + private static function maybe_add_utm_license( $query_args, $link = '' ) {
177 + $medium = $query_args['utm_medium'] ?? self::pull_medium_from_link( $link );
178 +
179 + if ( 'pro' === $medium && is_callable( 'FrmProAddonsController::get_readable_license_type' ) ) {
177 180 $query_args['utm_license'] = strtolower( FrmProAddonsController::get_readable_license_type() );
178 181 }
179 182
180 183 return $query_args;
@@ -180,8 +183,31 @@
180 183 return $query_args;
181 184 }
182 185
183 186 /**
187 + * @since 6.26
188 + *
189 + * @param string $link
190 + *
191 + * @return string
192 + */
193 + private static function pull_medium_from_link( $link ) {
194 + if ( ! $link ) {
195 + return '';
196 + }
197 +
198 + $parsed = parse_url( $link );
199 +
200 + if ( ! is_array( $parsed ) || ! isset( $parsed['query'] ) ) {
201 + return '';
202 + }
203 +
204 + $query_args = wp_parse_args( $parsed['query'] );
205 +
206 + return ! empty( $query_args['utm_medium'] ) ? $query_args['utm_medium'] : '';
207 + }
208 +
209 + /**
184 210 * @since 6.25.1
185 211 *
186 212 * @return string
187 213 */
@@ -194,8 +220,9 @@
194 220 *
195 221 * @since 6.25.1
196 222 *
197 223 * @param array $args
224 + *
198 225 * @return array
199 226 */
200 227 private static function adjust_legacy_utm_args( $args ) {
201 228 if ( isset( $args['medium'] ) ) {
@@ -215,25 +242,25 @@
215 242 public static function maybe_add_missing_utm( $cta_link, $utm ) {
216 243 $utm = self::adjust_legacy_utm_args( $utm );
217 244 $query_args = array();
218 245
219 - if ( false === strpos( $cta_link, 'utm_source' ) ) {
246 + if ( ! str_contains( $cta_link, 'utm_source' ) ) {
220 247 $query_args['utm_source'] = 'plugin';
221 248 }
222 249
223 - if ( false === strpos( $cta_link, 'utm_medium' ) ) {
250 + if ( ! str_contains( $cta_link, 'utm_medium' ) ) {
224 251 $query_args['utm_medium'] = self::get_utm_medium();
225 252 }
226 253
227 - if ( false === strpos( $cta_link, 'utm_campaign' ) && isset( $utm['campaign'] ) ) {
254 + if ( ! str_contains( $cta_link, 'utm_campaign' ) && isset( $utm['campaign'] ) ) {
228 255 $query_args['utm_campaign'] = $utm['campaign'];
229 256 }
230 257
231 - if ( false === strpos( $cta_link, 'utm_content' ) && isset( $utm['content'] ) ) {
258 + if ( ! str_contains( $cta_link, 'utm_content' ) && isset( $utm['content'] ) ) {
232 259 $query_args['utm_content'] = $utm['content'];
233 260 }
234 261
235 - $query_args = self::maybe_add_utm_license( $query_args );
262 + $query_args = self::maybe_add_utm_license( $query_args, $cta_link );
236 263
237 264 return $query_args ? add_query_arg( $query_args, $cta_link ) : $cta_link;
238 265 }
239 266
@@ -242,13 +269,15 @@
242 269 *
243 270 * @since 2.0
244 271 *
245 272 * @param array $args - May include the form id when values need translation.
246 - * @return FrmSettings $frm_settings
273 + *
274 + * @return FrmSettings
247 275 */
248 276 public static function get_settings( $args = array() ) {
249 277 global $frm_settings;
250 - if ( empty( $frm_settings ) ) {
278 +
279 + if ( ! $frm_settings ) {
251 280 $frm_settings = new FrmSettings( $args );
252 281 } elseif ( isset( $args['current_form'] ) ) {
253 282 // If the global has already been set, allow strings to be filtered.
254 283 $frm_settings->maybe_filter_for_form( $args );
@@ -260,11 +289,13 @@
260 289 /**
261 290 * @return string
262 291 */
263 292 public static function get_menu_name() {
264 - $frm_settings = self::get_settings();
293 + if ( ! self::pro_is_installed() || FrmAddonsController::is_license_expired() ) {
294 + return 'Formidable';
295 + }
265 296
266 - return FrmAddonsController::is_license_expired() || ! self::pro_is_installed() ? 'Formidable' : $frm_settings->menu;
297 + return self::get_settings()->menu;
267 298 }
268 299
269 300 /**
270 301 * Determine if the current branding is set to 'formidable'.
@@ -278,10 +309,9 @@
278 309 if ( ! self::pro_is_installed() ) {
279 310 return true;
280 311 }
281 312
282 - $menu_icon = self::get_menu_icon_class();
283 - return strpos( $menu_icon, 'frm_logo_icon' ) !== false;
313 + return str_contains( self::get_menu_icon_class(), 'frm_logo_icon' );
284 314 }
285 315
286 316 /**
287 317 * @since 3.05
@@ -286,8 +316,9 @@
286 316 /**
287 317 * @since 3.05
288 318 *
289 319 * @param array $atts
320 + *
290 321 * @return string
291 322 */
292 323 public static function svg_logo( $atts = array() ) {
293 324 $defaults = array(
@@ -297,12 +328,14 @@
297 328 'orange' => '#f05a24',
298 329 );
299 330 $atts = array_merge( $defaults, $atts );
300 331
332 + // phpcs:disable SlevomatCodingStandard.Files.LineLength.LineTooLong
301 333 return '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 599.68 601.37" width="' . esc_attr( $atts['width'] ) . '" height="' . esc_attr( $atts['height'] ) . '">
302 334 <path fill="' . esc_attr( $atts['orange'] ) . '" d="M289.6 384h140v76h-140z"/>
303 335 <path fill="' . esc_attr( $atts['fill'] ) . '" d="M400.2 147h-200c-17 0-30.6 12.2-30.6 29.3V218h260v-71zM397.9 264H169.6v196h75V340H398a32.2 32.2 0 0 0 30.1-21.4 24.3 24.3 0 0 0 1.7-8.7V264zM299.8 601.4A300.3 300.3 0 0 1 0 300.7a299.8 299.8 0 1 1 511.9 212.6 297.4 297.4 0 0 1-212 88zm0-563A262 262 0 0 0 38.3 300.7a261.6 261.6 0 1 0 446.5-185.5 259.5 259.5 0 0 0-185-76.8z"/>
304 336 </svg>';
337 + // phpcs:enable SlevomatCodingStandard.Files.LineLength.LineTooLong
305 338 }
306 339
307 340 /**
308 341 * @since 4.0
@@ -307,12 +340,13 @@
307 340 /**
308 341 * @since 4.0
309 342 *
310 343 * @param array $atts
344 + *
311 345 * @return void
312 346 */
313 347 public static function show_logo( $atts = array() ) {
314 - echo self::kses( self::svg_logo( $atts ), 'all' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
348 + self::kses_echo( self::svg_logo( $atts ), 'all' );
315 349 }
316 350
317 351 /**
318 352 * @since 4.03.02
@@ -327,10 +361,11 @@
327 361 )
328 362 );
329 363
330 364 $new_icon = apply_filters( 'frm_icon', $icon, true );
365 +
331 366 if ( $new_icon !== $icon ) {
332 - if ( strpos( $new_icon, '<svg' ) === 0 ) {
367 + if ( str_starts_with( $new_icon, '<svg' ) ) {
333 368 $icon = str_replace( 'viewBox="0 0 20', 'width="30" height="35" style="color:#929699" viewBox="0 0 20', $new_icon );
334 369 } else {
335 370 // Show nothing if it isn't an SVG.
336 371 $icon = '<div style="height:39px"></div>';
@@ -335,9 +370,9 @@
335 370 // Show nothing if it isn't an SVG.
336 371 $icon = '<div style="height:39px"></div>';
337 372 }
338 373 }
339 - echo self::kses( $icon, 'all' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
374 + self::kses_echo( $icon, 'all' );
340 375 }
341 376
342 377 /**
343 378 * @since 2.02.04
@@ -387,11 +422,13 @@
387 422 */
388 423 public static function is_form_builder_page( $check_for_settings = true ) {
389 424 $action = self::simple_get( 'frm_action', 'sanitize_title' );
390 425 $check_actions = array( 'edit', 'duplicate' );
426 +
391 427 if ( $check_for_settings ) {
392 428 $check_actions[] = 'settings';
393 429 }
430 +
394 431 return self::is_admin_page( 'formidable' ) && in_array( $action, $check_actions, true );
395 432 }
396 433
397 434 /**
@@ -397,15 +434,15 @@
397 434 /**
398 435 * @return bool
399 436 */
400 437 public static function is_formidable_admin() {
401 - $page = self::simple_get( 'page', 'sanitize_title' );
402 - $is_formidable = strpos( $page, 'formidable' ) !== false;
403 - if ( empty( $page ) ) {
404 - $is_formidable = self::is_view_builder_page();
438 + $page = self::simple_get( 'page', 'sanitize_title' );
439 +
440 + if ( ! $page ) {
441 + return self::is_view_builder_page();
405 442 }
406 443
407 - return $is_formidable;
444 + return str_contains( $page, 'formidable' );
408 445 }
409 446
410 447 /**
411 448 * Checks if is a list page.
@@ -412,8 +449,9 @@
412 449 *
413 450 * @since 6.19
414 451 *
415 452 * @param string $page The name of the page to check.
453 + *
416 454 * @return bool
417 455 */
418 456 public static function is_admin_list_page( $page = 'formidable' ) {
419 457 if ( 'formidable' === $page ) {
@@ -425,8 +463,9 @@
425 463 }
426 464
427 465 if ( 'formidable-entries' === $page ) {
428 466 $action = self::simple_get( 'frm_action' );
467 +
429 468 if ( ! $action || in_array( $action, self::get_entries_listing_page_form_actions(), true ) ) {
430 469 return true;
431 470 }
432 471 }
@@ -455,11 +494,13 @@
455 494 */
456 495 public static function is_admin_page( $page = 'formidable' ) {
457 496 global $pagenow;
458 497 $get_page = self::simple_get( 'page', 'sanitize_title' );
498 +
459 499 if ( $pagenow ) {
460 - // allow this to be true during ajax load i.e. ajax form builder loading
500 + // Allow this to be true during ajax load i.e. ajax form builder loading
461 501 $is_page = ( $pagenow === 'admin.php' || $pagenow === 'admin-ajax.php' ) && $get_page === $page;
502 +
462 503 if ( $is_page ) {
463 504 return true;
464 505 }
465 506 }
@@ -477,15 +518,15 @@
477 518 */
478 519 public static function is_view_builder_page() {
479 520 global $pagenow;
480 521
481 - if ( $pagenow !== 'post.php' && $pagenow !== 'post-new.php' && $pagenow !== 'edit.php' ) {
522 + if ( ! in_array( $pagenow, array( 'post.php', 'post-new.php', 'edit.php' ), true ) ) {
482 523 return false;
483 524 }
484 525
485 526 $post_type = self::simple_get( 'post_type', 'sanitize_title' );
486 527
487 - if ( empty( $post_type ) ) {
528 + if ( ! $post_type ) {
488 529 $post_id = self::simple_get( 'post', 'absint' );
489 530 $post = get_post( $post_id );
490 531 $post_type = $post ? $post->post_type : '';
491 532 }
@@ -503,9 +544,9 @@
503 544 public static function is_preview_page() {
504 545 global $pagenow;
505 546 $action = self::simple_get( 'action', 'sanitize_title' );
506 547
507 - return $pagenow && $pagenow === 'admin-ajax.php' && $action === 'frm_forms_preview';
548 + return $pagenow === 'admin-ajax.php' && $action === 'frm_forms_preview';
508 549 }
509 550
510 551 /**
511 552 * Check for ajax except the form preview page
@@ -526,8 +567,9 @@
526 567 }
527 568
528 569 /**
529 570 * @since 2.0.8
571 + *
530 572 * @return bool
531 573 */
532 574 public static function prevent_caching() {
533 575 global $frm_vars;
@@ -545,8 +587,9 @@
545 587 $is_admin = is_admin() && ! wp_doing_ajax();
546 588
547 589 /**
548 590 * @since 6.0
591 + *
549 592 * @param bool $is_admin
550 593 */
551 594 return apply_filters( 'frm_is_admin', $is_admin );
552 595 }
@@ -561,14 +604,15 @@
561 604 *
562 605 * @return bool
563 606 */
564 607 public static function is_empty_value( $value, $empty = '' ) {
565 - return ( is_array( $value ) && empty( $value ) ) || $value === $empty;
608 + return $value === array() || $value === $empty;
566 609 }
567 610
568 611 /**
569 612 * @param mixed $value
570 613 * @param string $empty
614 + *
571 615 * @return bool
572 616 */
573 617 public static function is_not_empty_value( $value, $empty = '' ) {
574 618 return ! self::is_empty_value( $value, $empty );
@@ -622,8 +666,9 @@
622 666 continue;
623 667 }
624 668
625 669 $key = self::get_server_value( $key );
670 +
626 671 foreach ( explode( ',', $key ) as $ip ) {
627 672 // Just to be safe.
628 673 $ip = trim( $ip );
629 674
@@ -679,16 +724,26 @@
679 724 */
680 725 return apply_filters( 'frm_use_custom_header_ip', $should_use_custom_header_ip );
681 726 }
682 727
728 + /**
729 + * @param string $param
730 + * @param mixed $default
731 + * @param string $src
732 + * @param callable|string $sanitize
733 + *
734 + * @return mixed
735 + */
683 736 public static function get_param( $param, $default = '', $src = 'get', $sanitize = '' ) {
684 - if ( strpos( $param, '[' ) ) {
737 + if ( str_contains( $param, '[' ) ) {
685 738 $params = explode( '[', $param );
686 739 $param = $params[0];
687 740 }
688 741
689 742 if ( $src === 'get' ) {
690 - $value = isset( $_POST[ $param ] ) ? wp_unslash( $_POST[ $param ] ) : ( isset( $_GET[ $param ] ) ? wp_unslash( $_GET[ $param ] ) : $default ); // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
743 + // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
744 + $value = isset( $_POST[ $param ] ) ? wp_unslash( $_POST[ $param ] ) : ( isset( $_GET[ $param ] ) ? wp_unslash( $_GET[ $param ] ) : $default );
745 +
691 746 if ( ! isset( $_POST[ $param ] ) && isset( $_GET[ $param ] ) && ! is_array( $value ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
692 747 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
693 748 $value = htmlspecialchars_decode( wp_unslash( $_GET[ $param ] ) );
694 749 }
@@ -703,17 +758,19 @@
703 758 )
704 759 );
705 760 }
706 761
707 - if ( isset( $params ) && is_array( $value ) && ! empty( $value ) ) {
708 - foreach ( $params as $k => $p ) {
709 - if ( ! $k || ! is_array( $value ) ) {
710 - continue;
711 - }
762 + if ( ! isset( $params ) || ! is_array( $value ) || ! $value ) {
763 + return $value;
764 + }
712 765
713 - $p = trim( $p, ']' );
714 - $value = $value[ $p ] ?? $default;
766 + foreach ( $params as $k => $p ) {
767 + if ( ! $k || ! is_array( $value ) ) {
768 + continue;
715 769 }
770 +
771 + $p = trim( $p, ']' );
772 + $value = $value[ $p ] ?? $default;
716 773 }
717 774
718 775 return $value;
719 776 }
@@ -724,8 +781,9 @@
724 781 * @param string $param The key we are trying to access data from in $_POST.
725 782 * @param mixed $default The default if nothing is being sent.
726 783 * @param callable|string $sanitize Make sure to pass a sanitize method here. This function will NOT sanitize by default.
727 784 * @param bool $serialized
785 + *
728 786 * @return mixed
729 787 */
730 788 public static function get_post_param( $param, $default = '', $sanitize = '', $serialized = false ) {
731 789 return self::get_simple_request(
@@ -745,9 +803,9 @@
745 803 * @param string $param
746 804 * @param string $sanitize
747 805 * @param string $default
748 806 *
749 - * @return array|string
807 + * @return array|int|string
750 808 */
751 809 public static function simple_get( $param, $sanitize = 'sanitize_text_field', $default = '' ) {
752 810 return self::get_simple_request(
753 811 array(
@@ -776,10 +834,10 @@
776 834 'sanitize' => 'sanitize_text_field',
777 835 'serialized' => false,
778 836 );
779 837 $args = wp_parse_args( $args, $defaults );
838 + $value = $args['default'];
780 839
781 - $value = $args['default'];
782 840 if ( $args['type'] === 'get' ) {
783 841 if ( $_GET && isset( $_GET[ $args['param'] ] ) ) {
784 842 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing
785 843 $value = wp_unslash( $_GET[ $args['param'] ] );
@@ -787,16 +845,16 @@
787 845 } elseif ( $args['type'] === 'post' ) {
788 846 if ( isset( $_POST[ $args['param'] ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
789 847 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing
790 848 $value = wp_unslash( $_POST[ $args['param'] ] );
849 +
791 850 if ( $args['serialized'] === true && is_serialized_string( $value ) && is_serialized( $value ) ) {
792 851 self::unserialize_or_decode( $value );
793 852 }
794 853 }
795 854 } elseif ( isset( $_REQUEST[ $args['param'] ] ) ) {
796 - // phpcs:ignore WordPress.Security.NonceVerification.Missing
797 - // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
798 - $value = wp_unslash( $_REQUEST[ $args['param'] ] );
855 + // phpcs:ignore phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
856 + $value = wp_unslash( $_REQUEST[ $args['param'] ] );
799 857 }
800 858
801 859 self::sanitize_value( $args['sanitize'], $value );
802 860
@@ -809,17 +867,13 @@
809 867 * @since 2.0.8
810 868 *
811 869 * @param string $value
812 870 *
813 - * @return string $value
871 + * @return string Value.
814 872 */
815 873 public static function preserve_backslashes( $value ) {
816 874 // If backslashes have already been added, don't add them again
817 - if ( strpos( $value, '\\\\' ) === false ) {
818 - $value = addslashes( $value );
819 - }
820 -
821 - return $value;
875 + return str_contains( $value, '\\\\' ) ? $value : addslashes( $value );
822 876 }
823 877
824 878 /**
825 879 * Sanitize a value in-place.
@@ -826,8 +880,9 @@
826 880 * If $value is an array, the sanitize function will get called for each item.
827 881 *
828 882 * @param callable $sanitize
829 883 * @param mixed $value
884 + *
830 885 * @return void
831 886 */
832 887 public static function sanitize_value( $sanitize, &$value ) {
833 888 if ( ! $sanitize ) {
@@ -840,11 +895,13 @@
840 895 }
841 896
842 897 if ( is_array( $value ) ) {
843 898 $temp_values = $value;
899 +
844 900 foreach ( $temp_values as $k => $v ) {
845 901 self::sanitize_value( $sanitize, $value[ $k ] );
846 902 }
903 +
847 904 return;
848 905 }
849 906
850 907 $value = call_user_func( $sanitize, $value );
@@ -849,10 +906,17 @@
849 906
850 907 $value = call_user_func( $sanitize, $value );
851 908 }
852 909
910 + /**
911 + * @param array $sanitize_method
912 + * @param array $values
913 + *
914 + * @return void
915 + */
853 916 public static function sanitize_request( $sanitize_method, &$values ) {
854 917 $temp_values = $values;
918 +
855 919 foreach ( $temp_values as $k => $val ) {
856 920 if ( isset( $sanitize_method[ $k ] ) ) {
857 921 $values[ $k ] = call_user_func( $sanitize_method[ $k ], $val );
858 922 }
@@ -862,8 +926,9 @@
862 926 /**
863 927 * @since 4.0.04
864 928 *
865 929 * @param mixed $value
930 + *
866 931 * @return void
867 932 */
868 933 public static function sanitize_with_html( &$value ) {
869 934 if ( current_user_can( 'frm_edit_entries' ) || current_user_can( 'administrator' ) ) {
@@ -872,8 +937,9 @@
872 937 } else {
873 938 self::sanitize_value( self::class . '::strip_most_html', $value );
874 939 }
875 940 self::decode_specialchars( $value );
941 + self::sanitize_value( 'FrmHtmlSanitizer::sanitize_url_attributes', $value );
876 942 }
877 943
878 944 /**
879 945 * Allow only a small set of very basic HTML for unprivileged users.
@@ -882,8 +948,12 @@
882 948 *
883 949 * @param string $value
884 950 */
885 951 public static function strip_most_html( $value ) {
952 + if ( '' === $value ) {
953 + return $value;
954 + }
955 +
886 956 $allowed_html = array(
887 957 'b' => array(),
888 958 'br' => array(),
889 959 'strong' => array(),
@@ -908,12 +978,15 @@
908 978 * Do wp_specialchars_decode to get back '&' that wp_kses_post might have turned to '&amp;'
909 979 * this MUST be done, else we'll be back to the '& entity' problem.
910 980 *
911 981 * @since 4.0.04
982 + *
983 + * @param mixed $value Value to decode, passed by reference.
912 984 */
913 985 public static function decode_specialchars( &$value ) {
914 986 if ( is_array( $value ) ) {
915 987 $temp_values = $value;
988 +
916 989 foreach ( $temp_values as $k => $v ) {
917 990 self::decode_specialchars( $value[ $k ] );
918 991 }
919 992 } else {
@@ -927,13 +1000,13 @@
927 1000 * Adapted from wp_specialchars_decode().
928 1001 *
929 1002 * @since 4.03.01
930 1003 *
931 - * @param string $string The string to prep.
1004 + * @param string $string The string to prep, passed by reference.
932 1005 */
933 1006 private static function decode_amp( &$string ) {
934 1007 // Don't bother if there are no entities - saves a lot of processing
935 - if ( empty( $string ) || strpos( $string, '&' ) === false ) {
1008 + if ( ! $string || ! str_contains( $string, '&' ) ) {
936 1009 return;
937 1010 }
938 1011
939 1012 $translation = array(
@@ -971,17 +1044,15 @@
971 1044 * Sanitize the value, and allow some HTML
972 1045 *
973 1046 * @since 2.0
974 1047 *
975 - * @param string $value
976 - * @param array|string $allowed 'all' for everything included as defaults.
1048 + * @param string $value The value to sanitize.
1049 + * @param array|string $allowed Allowed HTML tags and attributes, or 'all' for defaults.
977 1050 *
978 1051 * @return string
979 1052 */
980 1053 public static function kses( $value, $allowed = array() ) {
981 - $allowed_html = self::allowed_html( $allowed );
982 -
983 - return wp_kses( $value, $allowed_html );
1054 + return wp_kses( $value, self::allowed_html( $allowed ) );
984 1055 }
985 1056
986 1057 /**
987 1058 * Sanitizes and echoes a given value.
@@ -989,8 +1060,9 @@
989 1060 * @since 6.18
990 1061 *
991 1062 * @param string $value The value to sanitize and output.
992 1063 * @param array|string $allowed Allowed HTML tags and attributes.
1064 + *
993 1065 * @return void
994 1066 */
995 1067 public static function kses_echo( $value, $allowed = array() ) {
996 1068 echo self::kses( $value, $allowed ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
@@ -1001,21 +1073,23 @@
1001 1073 *
1002 1074 * @since 5.0.13
1003 1075 *
1004 1076 * @param string $html
1077 + *
1005 1078 * @return string
1006 1079 */
1007 1080 public static function kses_submit_button( $html ) {
1008 - $included_button_action = false !== strpos( $html, '[button_action]' );
1009 - $included_back_hook = false !== strpos( $html, '[back_hook]' );
1010 - $included_draft_hook = false !== strpos( $html, '[draft_hook]' );
1081 + $included_button_action = str_contains( $html, '[button_action]' );
1082 + $included_back_hook = str_contains( $html, '[back_hook]' );
1083 + $included_draft_hook = str_contains( $html, '[draft_hook]' );
1011 1084 add_filter( 'safe_style_css', 'FrmAppHelper::allow_visibility_style' );
1012 1085 add_filter( 'frm_striphtml_allowed_tags', 'FrmAppHelper::add_allowed_submit_button_tags' );
1013 1086 $html = self::kses( $html, 'all' );
1014 1087 remove_filter( 'safe_style_css', 'FrmAppHelper::allow_visibility_style' );
1015 1088 remove_filter( 'frm_striphtml_allowed_tags', 'FrmAppHelper::add_allowed_submit_button_tags' );
1089 +
1016 1090 if ( $included_button_action ) {
1017 - if ( false !== strpos( $html, '<input type="submit"' ) ) {
1091 + if ( str_contains( $html, '<input type="submit"' ) ) {
1018 1092 $pattern = '/(<input type="submit")([^>]*)(\/>)/';
1019 1093 $html = preg_replace( $pattern, '$1$2[button_action] $3', $html, 1 );
1020 1094 } else {
1021 1095 $pattern = '/(<button)(.*)(class=")(.*)(frm_button_submit)(.*)(")(.*)([^>]+)(>)/';
@@ -1021,14 +1095,17 @@
1021 1095 $pattern = '/(<button)(.*)(class=")(.*)(frm_button_submit)(.*)(")(.*)([^>]+)(>)/';
1022 1096 $html = preg_replace( $pattern, '$1$2$3$4$5$6$7 [button_action]$8$9$10', $html, 1 );
1023 1097 }
1024 1098 }
1099 +
1025 1100 if ( $included_back_hook ) {
1026 1101 $html = str_replace( 'class="frm_prev_page"', 'class="frm_prev_page" [back_hook]', $html );
1027 1102 }
1103 +
1028 1104 if ( $included_draft_hook ) {
1029 - $html = str_replace( 'class="frm_save_draft"', 'class="frm_save_draft" [draft_hook]', $html );
1105 + return str_replace( 'class="frm_save_draft"', 'class="frm_save_draft" [draft_hook]', $html );
1030 1106 }
1107 +
1031 1108 return $html;
1032 1109 }
1033 1110
1034 1111 /**
@@ -1034,8 +1111,9 @@
1034 1111 /**
1035 1112 * @since 5.0.13
1036 1113 *
1037 1114 * @param array $allowed_attr
1115 + *
1038 1116 * @return array
1039 1117 */
1040 1118 public static function allow_visibility_style( $allowed_attr ) {
1041 1119 $allowed_attr[] = 'visibility';
@@ -1045,8 +1123,9 @@
1045 1123 /**
1046 1124 * @since 5.0.13
1047 1125 *
1048 1126 * @param array $allowed_html
1127 + *
1049 1128 * @return array
1050 1129 */
1051 1130 public static function add_allowed_submit_button_tags( $allowed_html ) {
1052 1131 $allowed_html['input'] = array(
@@ -1063,15 +1142,20 @@
1063 1142 }
1064 1143
1065 1144 /**
1066 1145 * @since 2.05.03
1146 + *
1147 + * @param array|string $allowed
1148 + *
1149 + * @return array
1067 1150 */
1068 1151 private static function allowed_html( $allowed ) {
1069 1152 $html = self::safe_html();
1070 1153 $allowed_html = array();
1154 +
1071 1155 if ( $allowed === 'all' ) {
1072 1156 $allowed_html = $html;
1073 - } elseif ( ! empty( $allowed ) ) {
1157 + } elseif ( $allowed ) {
1074 1158 foreach ( (array) $allowed as $a ) {
1075 1159 $allowed_html[ $a ] = $html[ $a ] ?? array();
1076 1160 }
1077 1161 }
@@ -1080,8 +1164,10 @@
1080 1164 }
1081 1165
1082 1166 /**
1083 1167 * @since 2.05.03
1168 + *
1169 + * @return array
1084 1170 */
1085 1171 private static function safe_html() {
1086 1172 $allow_class = array(
1087 1173 'class' => true,
@@ -1233,14 +1319,16 @@
1233 1319 return;
1234 1320 }
1235 1321
1236 1322 $action_name = isset( $_GET['action'] ) ? 'action' : ( isset( $_GET['action2'] ) ? 'action2' : '' );
1237 - if ( empty( $action_name ) ) {
1323 +
1324 + if ( ! $action_name ) {
1238 1325 return;
1239 1326 }
1240 1327
1241 1328 $new_action = self::get_param( $action_name, '', 'get', 'sanitize_text_field' );
1242 - if ( ! empty( $new_action ) ) {
1329 +
1330 + if ( $new_action ) {
1243 1331 $_SERVER['REQUEST_URI'] = str_replace( '&action=' . $new_action, '', self::get_server_value( 'REQUEST_URI' ) );
1244 1332 }
1245 1333 }
1246 1334
@@ -1247,21 +1335,23 @@
1247 1335 /**
1248 1336 * Check the WP query for a parameter
1249 1337 *
1250 1338 * @since 2.0
1339 + *
1340 + * @param array|string $value
1341 + * @param string $param
1342 + *
1251 1343 * @return array|string
1252 1344 */
1253 1345 public static function get_query_var( $value, $param ) {
1346 + // phpcs:ignore Universal.Operators.StrictComparisons
1254 1347 if ( $value != '' ) {
1255 1348 return $value;
1256 1349 }
1257 1350
1258 1351 global $wp_query;
1259 - if ( isset( $wp_query->query_vars[ $param ] ) ) {
1260 - $value = $wp_query->query_vars[ $param ];
1261 - }
1262 1352
1263 - return $value;
1353 + return $wp_query->query_vars[ $param ] ?? $value;
1264 1354 }
1265 1355
1266 1356 /**
1267 1357 * Try to show the SVG if possible. Otherwise, use the font icon.
@@ -1269,18 +1359,18 @@
1269 1359 * @since 4.0.02
1270 1360 *
1271 1361 * @param string $class
1272 1362 * @param array $atts
1363 + *
1273 1364 * @return string|null
1274 1365 */
1275 1366 public static function icon_by_class( $class, $atts = array() ) {
1276 1367 $echo = ! isset( $atts['echo'] ) || $atts['echo'];
1368 +
1277 1369 if ( isset( $atts['echo'] ) ) {
1278 1370 unset( $atts['echo'] );
1279 1371 }
1280 1372
1281 - $html_atts = self::array_to_html_params( $atts );
1282 -
1283 1373 $icon = trim( str_replace( array( 'frm_icon_font', 'frmfont ' ), '', $class ) );
1284 1374
1285 1375 // Replace icons that have been removed or renamed.
1286 1376 $deprecated = array(
@@ -1287,29 +1377,61 @@
1287 1377 'frm_clone_solid_icon' => 'frm_clone_icon',
1288 1378 'frm_keyalt_icon' => 'frm_key_icon',
1289 1379 'frm_keyalt_solid_icon' => 'frm_key_solid_icon',
1290 1380 );
1381 +
1291 1382 if ( isset( $deprecated[ $icon ] ) ) {
1292 1383 $icon = $deprecated[ $icon ];
1293 1384 $class = str_replace( $icon, $deprecated[ $icon ], $class );
1294 1385 }
1295 1386
1296 - if ( $icon === $class ) {
1297 - $icon = '<i class="' . esc_attr( $class ) . '"' . $html_atts . '></i>';
1298 - } else {
1299 - $class = strpos( $icon, ' ' ) === false ? '' : ' ' . $icon;
1300 - if ( strpos( $icon, ' ' ) ) {
1387 + $is_font_icon = $icon === $class;
1388 +
1389 + if ( ! $is_font_icon ) {
1390 + $class = str_contains( $icon, ' ' ) ? ' ' . $icon : '';
1391 +
1392 + if ( str_contains( $icon, ' ' ) ) {
1301 1393 $icon = explode( ' ', $icon );
1302 1394 $icon = reset( $icon );
1303 1395 }
1304 - $icon = '<svg class="frmsvg' . esc_attr( $class ) . '"' . $html_atts . '><use xlink:href="#' . esc_attr( $icon ) . '" /></svg>';
1305 1396 }
1306 1397
1307 - if ( $echo ) {
1308 - echo self::kses_icon( $icon ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1309 - } else {
1310 - return $icon;
1398 + if ( $atts ) {
1399 + // A caller passed attributes, so kses still has to decide which of them survive. Its
1400 + // allowlist comes from safe_html() through the frm_striphtml_allowed_tags filter, and
1401 + // add-ons widen it around their own icons, so there is no fixed list to check against.
1402 + $html_atts = self::array_to_html_params( $atts );
1403 + $markup = $is_font_icon
1404 + ? '<i class="' . esc_attr( $class ) . '"' . $html_atts . '></i>'
1405 + : '<svg class="frmsvg' . esc_attr( $class ) . '"' . $html_atts . '><use href="#' . esc_attr( $icon ) . '" /></svg>';
1406 +
1407 + if ( ! $echo ) {
1408 + return $markup;
1409 + }
1410 +
1411 + echo self::kses_icon( $markup ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1412 + return null;
1311 1413 }
1414 +
1415 + /**
1416 + * With no attributes from the caller, the tag is nothing but this method's own markup
1417 + * around an escaped class and icon id, so there is nothing left for kses to decide and it
1418 + * can be skipped. Echoing the pieces rather than a finished string keeps that safe to the
1419 + * escaping sniff without an annotation.
1420 + *
1421 + * This is the path the form builder takes for most of the tens of thousands of icons it
1422 + * renders on a large form, and the kses pass was most of what each one cost.
1423 + */
1424 + $callback = function () use ( $is_font_icon, $class, $icon ) {
1425 + if ( $is_font_icon ) {
1426 + echo '<i class="' . esc_attr( $class ) . '"></i>';
1427 + return;
1428 + }
1429 +
1430 + echo '<svg class="frmsvg' . esc_attr( $class ) . '"><use href="#' . esc_attr( $icon ) . '" /></svg>';
1431 + };
1432 +
1433 + return self::clip( $callback, $echo );
1312 1434 }
1313 1435
1314 1436 /**
1315 1437 * Run kses for icons. It needs to add a few filters first in order to preserve some custom style values.
@@ -1316,8 +1438,9 @@
1316 1438 *
1317 1439 * @since 5.0.13
1318 1440 *
1319 1441 * @param string $icon
1442 + *
1320 1443 * @return string
1321 1444 */
1322 1445 public static function kses_icon( $icon ) {
1323 1446 add_filter( 'safe_style_css', 'FrmAppHelper::allow_vars_in_styles' );
@@ -1333,8 +1456,9 @@
1333 1456 /**
1334 1457 * @since 5.0.13.1
1335 1458 *
1336 1459 * @param array $allowed_html
1460 + *
1337 1461 * @return array
1338 1462 */
1339 1463 public static function add_allowed_icon_tags( $allowed_html ) {
1340 1464 $allowed_html['svg']['data-open'] = true;
@@ -1346,8 +1470,9 @@
1346 1470 /**
1347 1471 * @since 5.0.13
1348 1472 *
1349 1473 * @param array $allowed_attr
1474 + *
1350 1475 * @return array
1351 1476 */
1352 1477 public static function allow_vars_in_styles( $allowed_attr ) {
1353 1478 $allowed_attr[] = '--primary-700';
@@ -1360,9 +1485,9 @@
1360 1485 * @param bool $allow_css
1361 1486 * @param string $css_string
1362 1487 */
1363 1488 public static function allow_style( $allow_css, $css_string ) {
1364 - if ( ! $allow_css && 0 === strpos( $css_string, '--primary-700:' ) ) {
1489 + if ( ! $allow_css && str_starts_with( $css_string, '--primary-700:' ) ) {
1365 1490 $split = explode( ':', $css_string, 2 );
1366 1491 $allow_css = 2 === count( $split ) && self::is_a_valid_color( $split[1] );
1367 1492 }
1368 1493 return $allow_css;
@@ -1371,19 +1496,22 @@
1371 1496 /**
1372 1497 * @since 5.0.13
1373 1498 *
1374 1499 * @param string $value
1500 + *
1375 1501 * @return bool
1376 1502 */
1377 1503 private static function is_a_valid_color( $value ) {
1378 1504 $match = 0;
1379 - if ( 0 === strpos( $value, 'rgba(' ) ) {
1505 +
1506 + if ( str_starts_with( $value, 'rgba(' ) ) {
1380 1507 $match = preg_match( '/^rgba\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),\s*(\d*(?:\.\d+)?)\)$/', $value );
1381 - } elseif ( 0 === strpos( $value, 'rgb(' ) ) {
1508 + } elseif ( str_starts_with( $value, 'rgb(' ) ) {
1382 1509 $match = preg_match( '/^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/', $value );
1383 - } elseif ( 0 === strpos( $value, '#' ) ) {
1510 + } elseif ( str_starts_with( $value, '#' ) ) {
1384 1511 $match = preg_match( '/^#([a-f0-9]{6}|[a-f0-9]{3})\b$/', $value );
1385 1512 }
1513 +
1386 1514 return (bool) $match;
1387 1515 }
1388 1516
1389 1517 /**
@@ -1389,8 +1517,9 @@
1389 1517 /**
1390 1518 * Include svg images.
1391 1519 *
1392 1520 * @since 4.0.02
1521 + *
1393 1522 * @return void
1394 1523 */
1395 1524 public static function include_svg() {
1396 1525 if ( self::$included_svg ) {
@@ -1409,17 +1538,20 @@
1409 1538 * @since 5.0.13 added $echo parameter.
1410 1539 *
1411 1540 * @param array $atts
1412 1541 * @param bool $echo
1542 + *
1413 1543 * @return string|void
1414 1544 */
1415 1545 public static function array_to_html_params( $atts, $echo = false ) {
1416 1546 $callback = function () use ( $atts ) {
1417 - if ( $atts ) {
1418 - foreach ( $atts as $key => $value ) {
1419 - echo ' ' . esc_attr( $key ) . '="' . esc_attr( $value ) . '"';
1420 - }
1547 + if ( ! $atts ) {
1548 + return;
1421 1549 }
1550 +
1551 + foreach ( $atts as $key => $value ) {
1552 + echo ' ' . esc_attr( $key ) . '="' . esc_attr( $value ) . '"';
1553 + }
1422 1554 };
1423 1555 return self::clip( $callback, $echo );
1424 1556 }
1425 1557
@@ -1429,9 +1561,12 @@
1429 1561 * @since 5.0.13
1430 1562 *
1431 1563 * @param Closure $echo_function
1432 1564 * @param bool $echo
1565 + *
1433 1566 * @return string|null
1567 + *
1568 + * @psalm-return ($echo is true ? null : string)
1434 1569 */
1435 1570 public static function clip( $echo_function, $echo = false ) {
1436 1571 if ( ! $echo ) {
1437 1572 ob_start();
@@ -1440,13 +1575,9 @@
1440 1575 if ( is_callable( $echo_function ) ) {
1441 1576 $echo_function();
1442 1577 }
1443 1578
1444 - if ( ! $echo ) {
1445 - $return = ob_get_contents();
1446 - ob_end_clean();
1447 - return $return;
1448 - }
1579 + return $echo ? null : ob_get_clean();
1449 1580 }
1450 1581
1451 1582 /**
1452 1583 * @since 3.0
@@ -1451,15 +1582,18 @@
1451 1582 /**
1452 1583 * @since 3.0
1453 1584 *
1454 1585 * @param array $atts
1586 + *
1455 1587 * @return void
1456 1588 */
1457 1589 public static function get_admin_header( $atts ) {
1458 1590 $has_nav = ! empty( $atts['form'] ) && empty( $atts['is_template'] );
1591 +
1459 1592 if ( empty( $atts['close'] ) ) {
1460 1593 $atts['close'] = admin_url( 'admin.php?page=formidable' );
1461 1594 }
1595 +
1462 1596 if ( ! isset( $atts['import_link'] ) ) {
1463 1597 $atts['import_link'] = false;
1464 1598 }
1465 1599
@@ -1469,16 +1603,19 @@
1469 1603 /**
1470 1604 * @since 6.0
1471 1605 *
1472 1606 * @param string $type
1607 + *
1473 1608 * @return void
1474 1609 */
1475 1610 public static function import_link( $type = 'secondary' ) {
1611 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
1476 1612 ?>
1477 1613 <a href="<?php echo esc_url( admin_url( 'admin.php?page=formidable-import' ) ); ?>" class="button frm-button-<?php echo esc_attr( $type ); ?> frm_animate_bg">
1478 1614 <?php esc_html_e( 'Import', 'formidable' ); ?>
1479 1615 </a>
1480 1616 <?php
1617 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
1481 1618 }
1482 1619
1483 1620 /**
1484 1621 * Print applicable admin banner.
@@ -1485,8 +1622,9 @@
1485 1622 *
1486 1623 * @since 5.4.2
1487 1624 *
1488 1625 * @param bool $should_show_lite_upgrade
1626 + *
1489 1627 * @return void
1490 1628 */
1491 1629 public static function print_admin_banner( $should_show_lite_upgrade ) {
1492 1630 if ( ! current_user_can( 'administrator' ) ) {
@@ -1498,13 +1636,15 @@
1498 1636 // Print license warning or inbox banner and exit if either prints.
1499 1637 // And exit before printing the upgrade bar if it shouldn't be shown.
1500 1638 return;
1501 1639 }
1640 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
1502 1641 ?>
1503 1642 <div class="frm-upgrade-bar">
1504 1643 <div class="frm-upgrade-bar-inner">
1505 1644 <?php
1506 1645 $cta_text = FrmSalesApi::get_best_sale_value( 'lite_banner_cta_text' );
1646 +
1507 1647 if ( ! $cta_text ) {
1508 1648 $cta_text = __( 'upgrading to PRO', 'formidable' );
1509 1649 }
1510 1650
@@ -1513,18 +1653,14 @@
1513 1653 'campaign' => 'settings-license',
1514 1654 'content' => 'lite-banner',
1515 1655 );
1516 1656
1517 - if ( $upgrade_link ) {
1518 - $upgrade_link = self::maybe_add_missing_utm( $upgrade_link, $utm );
1519 - } else {
1520 - $upgrade_link = self::admin_upgrade_link( $utm );
1521 - }
1657 + $upgrade_link = $upgrade_link ? self::maybe_add_missing_utm( $upgrade_link, $utm ) : self::admin_upgrade_link( $utm );
1522 1658
1523 1659 printf(
1524 1660 /* translators: %1$s: Start link HTML, %2$s: CTA text ("upgrading to PRO" by default), %3$s: End link HTML */
1525 1661 esc_html__( 'You\'re using Formidable Forms Lite. To unlock more features consider %1$s%2$s%3$s.', 'formidable' ),
1526 - '<a href="' . esc_url( $upgrade_link ) . '">',
1662 + '<a href="' . esc_url( $upgrade_link ) . '" target="_blank" rel="noopener">',
1527 1663 esc_html( $cta_text ),
1528 1664 '</a>'
1529 1665 );
1530 1666 ?>
@@ -1530,8 +1666,9 @@
1530 1666 ?>
1531 1667 </div>
1532 1668 </div>
1533 1669 <?php
1670 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
1534 1671 }
1535 1672
1536 1673 /**
1537 1674 * @since 5.4.2
@@ -1545,8 +1682,9 @@
1545 1682 /**
1546 1683 * Render a button for a new item (Form, Application, etc).
1547 1684 *
1548 1685 * @since 3.0
1686 + *
1549 1687 * @param array $atts {
1550 1688 * Details about the button.
1551 1689 *
1552 1690 * @type array $link_hook Custom link hook, calls do_action and exits early.
@@ -1553,8 +1691,9 @@
1553 1691 * @type string $new_link Href value, default #.
1554 1692 * @type string $class Custom class names, space separated.
1555 1693 * @type string $button_text Button text. Default "Add New".
1556 1694 * }
1695 + *
1557 1696 * @return void
1558 1697 */
1559 1698 public static function add_new_item_link( $atts ) {
1560 1699 if ( isset( $atts['link_hook'] ) ) {
@@ -1580,8 +1719,12 @@
1580 1719 }
1581 1720
1582 1721 /**
1583 1722 * @since 3.06
1723 + *
1724 + * @param array $atts
1725 + *
1726 + * @return void
1584 1727 */
1585 1728 public static function show_search_box( $atts ) {
1586 1729 $defaults = array(
1587 1730 'placeholder' => '',
@@ -1597,8 +1740,9 @@
1597 1740 $atts['tosearch'] = 'frm-card';
1598 1741 }
1599 1742
1600 1743 $class = 'frm-search-input';
1744 +
1601 1745 if ( ! empty( $atts['tosearch'] ) ) {
1602 1746 $class .= ' frm-auto-search';
1603 1747 }
1604 1748
@@ -1622,14 +1766,15 @@
1622 1766
1623 1767 if ( ! empty( $atts['tosearch'] ) ) {
1624 1768 $input_atts['autocomplete'] = 'off';
1625 1769 }
1770 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
1626 1771 ?>
1627 1772 <p class="frm-search <?php echo esc_attr( $atts['class'] ); ?>">
1628 1773 <label class="screen-reader-text" for="<?php echo esc_attr( $input_id ); ?>">
1629 1774 <?php echo esc_html( $atts['text'] ); ?>:
1630 1775 </label>
1631 - <?php self::icon_by_class( 'frm_icon_font frm_search_icon frm_svg20' ); ?>
1776 + <?php self::icon_by_class( 'frmfont frm_search_icon frm_svg20' ); ?>
1632 1777 <input <?php self::array_to_html_params( $input_atts, true ); ?> />
1633 1778 <?php
1634 1779 if ( empty( $atts['tosearch'] ) ) {
1635 1780 submit_button( $atts['text'], 'button-secondary', '', false, array( 'id' => 'search-submit' ) );
@@ -1636,17 +1781,21 @@
1636 1781 }
1637 1782 ?>
1638 1783 </p>
1639 1784 <?php
1785 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
1640 1786 }
1641 1787
1642 1788 /**
1643 - * @param string $type
1789 + * @param string $type Hook type slug.
1790 + * @param object|null $object Optional related object.
1791 + *
1644 1792 * @return void
1645 1793 */
1646 1794 public static function trigger_hook_load( $type, $object = null ) {
1647 1795 // Only load the form hooks once.
1648 1796 $hooks_loaded = apply_filters( 'frm_' . $type . '_hooks_loaded', false, $object );
1797 +
1649 1798 if ( ! $hooks_loaded ) {
1650 1799 do_action( 'frm_load_' . $type . '_hooks' );
1651 1800 }
1652 1801 }
@@ -1705,14 +1854,14 @@
1705 1854 * True when value is 1, true, 'true', 'yes'
1706 1855 *
1707 1856 * @since 1.07.10
1708 1857 *
1709 - * @param string $value The value to compare.
1858 + * @param bool|int|string $value The value to compare.
1710 1859 *
1711 1860 * @return bool
1712 1861 */
1713 1862 public static function is_true( $value ) {
1714 - return true === $value || 1 == $value || 'true' === $value || 'yes' === $value;
1863 + return true === $value || '1' === (string) $value || 'true' === $value || 'yes' === $value;
1715 1864 }
1716 1865
1717 1866 /**
1718 1867 * Gets all post from a specific post type.
@@ -1720,8 +1869,9 @@
1720 1869 *
1721 1870 * @since 4.10.01 Add `$post_type` argument.
1722 1871 *
1723 1872 * @param string $post_type Post type to query. Default is `page`.
1873 + *
1724 1874 * @return WP_Post[]
1725 1875 */
1726 1876 public static function get_pages( $post_type = 'page' ) {
1727 1877 $query = array(
@@ -1740,8 +1890,9 @@
1740 1890 *
1741 1891 * @since 5.0.09
1742 1892 *
1743 1893 * @param string $post_type Post type to query. Default is `page`.
1894 + *
1744 1895 * @return array
1745 1896 */
1746 1897 public static function get_post_ids_and_titles( $post_type = 'page' ) {
1747 1898 return FrmDb::get_results(
@@ -1766,10 +1917,9 @@
1766 1917 *
1767 1918 * @param array $args Selection arguments.
1768 1919 */
1769 1920 public static function maybe_autocomplete_pages_options( $args ) {
1770 - $args = self::preformat_selection_args( $args );
1771 -
1921 + $args = self::preformat_selection_args( $args );
1772 1922 $pages_count = wp_count_posts( $args['post_type'] );
1773 1923
1774 1924 if ( ! isset( $pages_count->publish ) || $pages_count->publish <= 50 ) {
1775 1925 self::wp_pages_dropdown( $args );
@@ -1816,11 +1966,11 @@
1816 1966 'value_key' => 'value',
1817 1967 'label_key' => 'label',
1818 1968 );
1819 1969
1820 - $args = wp_parse_args( $args, $defaults );
1970 + $args = wp_parse_args( $args, $defaults );
1971 + $html_attrs = array();
1821 1972
1822 - $html_attrs = array();
1823 1973 if ( ! empty( $args['name'] ) ) {
1824 1974 $html_attrs['name'] = $args['name'];
1825 1975 }
1826 1976
@@ -1827,8 +1977,9 @@
1827 1977 if ( ! empty( $args['id'] ) ) {
1828 1978 $html_attrs['id'] = $args['id'];
1829 1979 }
1830 1980
1981 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
1831 1982 if ( count( $args['source'] ) <= $args['dropdown_limit'] ) {
1832 1983 ?>
1833 1984 <select <?php self::array_to_html_params( $html_attrs, true ); ?>>
1834 1985 <option value=""><?php echo esc_html( $args['placeholder'] ); ?></option>
@@ -1834,40 +1985,44 @@
1834 1985 <option value=""><?php echo esc_html( $args['placeholder'] ); ?></option>
1835 1986 <?php
1836 1987 foreach ( $args['source'] as $key => $source ) :
1837 1988 $value_label = self::get_dropdown_value_and_label_from_option( $source, $key, $args );
1989 +
1838 1990 if ( ! empty( $args['truncate'] ) ) {
1839 1991 $value_label['label'] = self::truncate( $value_label['label'], $args['truncate'] );
1840 1992 }
1841 1993 ?>
1842 - <option value="<?php echo esc_attr( $value_label['value'] ); ?>" <?php selected( $value_label['value'], $args['selected'] ); ?>><?php echo esc_html( $value_label['label'] ); ?></option>
1994 + <option value="<?php echo esc_attr( $value_label['value'] ); ?>" <?php selected( $value_label['value'], $args['selected'] ); ?>><?php echo esc_html( $value_label['label'] ); ?></option><?php // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong ?>
1843 1995 <?php endforeach; ?>
1844 1996 </select>
1845 1997 <?php
1846 - } else {
1847 - $options = array();
1848 - $autocomplete_value = '';
1849 - foreach ( $args['source'] as $key => $source ) {
1850 - $value_label = self::get_dropdown_value_and_label_from_option( $source, $key, $args );
1998 + return;
1999 + }
2000 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
1851 2001
1852 - if ( $value_label['value'] === $args['selected'] ) {
1853 - $autocomplete_value = $value_label['label'];
1854 - }
2002 + $options = array();
2003 + $autocomplete_value = '';
1855 2004
1856 - $options[] = $value_label;
2005 + foreach ( $args['source'] as $key => $source ) {
2006 + $value_label = self::get_dropdown_value_and_label_from_option( $source, $key, $args );
2007 +
2008 + if ( $value_label['value'] === $args['selected'] ) {
2009 + $autocomplete_value = $value_label['label'];
1857 2010 }
1858 2011
1859 - $html_attrs['type'] = 'hidden';
1860 - $html_attrs['class'] = 'frm_autocomplete_value_input';
1861 - $html_attrs['value'] = $args['selected'];
1862 - ?>
1863 - <input type="text" class="frm-custom-search"
1864 - data-source="<?php echo esc_attr( wp_json_encode( $options ) ); ?>"
1865 - placeholder="<?php echo esc_attr( $args['autocomplete_placeholder'] ); ?>"
1866 - value="<?php echo esc_attr( $autocomplete_value ); ?>" />
1867 - <input <?php self::array_to_html_params( $html_attrs, true ); ?> />
1868 - <?php
1869 - }//end if
2012 + $options[] = $value_label;
2013 + }
2014 +
2015 + $html_attrs['type'] = 'hidden';
2016 + $html_attrs['class'] = 'frm_autocomplete_value_input';
2017 + $html_attrs['value'] = $args['selected'];
2018 + ?>
2019 + <input type="text" class="frm-custom-search"
2020 + data-source="<?php echo esc_attr( wp_json_encode( $options ) ); ?>"
2021 + placeholder="<?php echo esc_attr( $args['autocomplete_placeholder'] ); ?>"
2022 + value="<?php echo esc_attr( $autocomplete_value ); ?>" />
2023 + <input <?php self::array_to_html_params( $html_attrs, true ); ?> />
2024 + <?php
1870 2025 }
1871 2026
1872 2027 /**
1873 2028 * Gets dropdown value and label from autodropdown option.
@@ -1876,8 +2031,9 @@
1876 2031 *
1877 2032 * @param array|string $option Autocomplete option.
1878 2033 * @param string $key Array key of the option.
1879 2034 * @param array $args See {@see FrmAppHelper::maybe_autocomplete_options()}.
2035 + *
1880 2036 * @return array
1881 2037 */
1882 2038 private static function get_dropdown_value_and_label_from_option( $option, $key, $args ) {
1883 2039 if ( is_array( $option ) ) {
@@ -1900,8 +2056,9 @@
1900 2056 self::prep_page_dropdown_params( $page_id, $truncate, $args );
1901 2057
1902 2058 $pages = self::get_post_ids_and_titles( $args['post_type'] );
1903 2059 $selected = self::get_post_param( $args['field_name'], $args['page_id'], 'absint' );
2060 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
1904 2061 ?>
1905 2062 <select name="<?php echo esc_attr( $args['field_name'] ); ?>" id="<?php echo esc_attr( $args['field_name'] ); ?>" class="frm-pages-dropdown">
1906 2063 <option value=""><?php echo esc_html( $args['placeholder'] ); ?></option>
1907 2064 <?php foreach ( $pages as $page ) { ?>
@@ -1910,8 +2067,9 @@
1910 2067 </option>
1911 2068 <?php } ?>
1912 2069 </select>
1913 2070 <?php
2071 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
1914 2072 }
1915 2073
1916 2074 /**
1917 2075 * Fill in missing parameters passed to wp_pages_dropdown().
@@ -1917,8 +2075,14 @@
1917 2075 * Fill in missing parameters passed to wp_pages_dropdown().
1918 2076 * This is for reverse compatibility with switching 3 params to 1.
1919 2077 *
1920 2078 * @since 4.03.06
2079 + *
2080 + * @param string $page_id Deprecated.
2081 + * @param bool $truncate Deprecated.
2082 + * @param mixed $args
2083 + *
2084 + * @return void
1921 2085 */
1922 2086 private static function prep_page_dropdown_params( $page_id, $truncate, &$args ) {
1923 2087 if ( ! is_array( $args ) ) {
1924 2088 $args = array(
@@ -1935,8 +2099,12 @@
1935 2099 * Filter to format args for page dropdown or autocomplete
1936 2100 *
1937 2101 * @since 4.03.06
1938 2102 * @since 4.10.01 Added `post_type` and `autocomplete_placeholder` to the arguments array.
2103 + *
2104 + * @param array $args
2105 + *
2106 + * @return array
1939 2107 */
1940 2108 private static function preformat_selection_args( $args ) {
1941 2109 $defaults = array(
1942 2110 'truncate' => false,
@@ -1949,13 +2117,18 @@
1949 2117
1950 2118 return array_merge( $defaults, $args );
1951 2119 }
1952 2120
2121 + /**
2122 + * @param int $post_id
2123 + *
2124 + * @return string
2125 + */
1953 2126 public static function post_edit_link( $post_id ) {
1954 2127 $post = get_post( $post_id );
2128 +
1955 2129 if ( $post ) {
1956 2130 $post_url = admin_url( 'post.php?post=' . $post_id . '&action=edit' );
1957 -
1958 2131 return '<a href="' . esc_url( $post_url ) . '">' . self::truncate( $post->post_title, 50 ) . '</a>';
1959 2132 }
1960 2133
1961 2134 return '';
@@ -1968,11 +2141,9 @@
1968 2141 *
1969 2142 * @return bool
1970 2143 */
1971 2144 public static function is_full_screen() {
1972 - return self::is_form_builder_page() ||
1973 - self::is_style_editor_page() ||
1974 - self::is_full_screen_view_builder_page();
2145 + return self::is_form_builder_page() || self::is_style_editor_page() || self::is_full_screen_view_builder_page();
1975 2146 }
1976 2147
1977 2148 /**
1978 2149 * Check if user is on the style editor or its alternative URL.
@@ -1982,8 +2153,9 @@
1982 2153 * @since 5.5.3
1983 2154 * @since 6.0 Added the $view parameter. Previously there was only a 'edit' view.
1984 2155 *
1985 2156 * @param string $view Supports 'edit', 'list', and ''. If '', both 'edit' and 'list' will match.
2157 + *
1986 2158 * @return bool
1987 2159 */
1988 2160 public static function is_style_editor_page( $view = '' ) {
1989 2161 if ( ! self::is_admin_page( 'formidable-styles' ) && ! self::is_admin_page( 'formidable-styles2' ) ) {
@@ -2020,8 +2192,9 @@
2020 2192 * @param array|string $capability
2021 2193 * @param string $multiple 'single' and 'multiple'.
2022 2194 */
2023 2195 public static function wp_roles_dropdown( $field_name, $capability, $multiple = 'single' ) {
2196 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
2024 2197 ?>
2025 2198 <select name="<?php echo esc_attr( $field_name ); ?>" id="<?php echo esc_attr( $field_name ); ?>"
2026 2199 <?php echo 'multiple' === $multiple ? 'multiple="multiple"' : ''; ?>
2027 2200 class="frm_multiselect">
@@ -2027,12 +2200,14 @@
2027 2200 class="frm_multiselect">
2028 2201 <?php self::roles_options( $capability ); ?>
2029 2202 </select>
2030 2203 <?php
2204 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
2031 2205 }
2032 2206
2033 2207 /**
2034 2208 * @since 4.07
2209 + *
2035 2210 * @param array|string $selected
2036 2211 * @param string $current
2037 2212 */
2038 2213 private static function selected( $selected, $current ) {
@@ -2047,8 +2222,9 @@
2047 2222 * @param array|string $capability
2048 2223 */
2049 2224 public static function roles_options( $capability ) {
2050 2225 global $frm_vars;
2226 +
2051 2227 if ( isset( $frm_vars['editable_roles'] ) ) {
2052 2228 $editable_roles = $frm_vars['editable_roles'];
2053 2229 } else {
2054 2230 $editable_roles = get_editable_roles();
@@ -2069,8 +2245,9 @@
2069 2245 *
2070 2246 * @since 5.0 Parameter `$type` supports `pro_only` value.
2071 2247 *
2072 2248 * @param string $type Supports `auto`, `pro`, or `pro_only`.
2249 + *
2073 2250 * @return array
2074 2251 */
2075 2252 public static function frm_capabilities( $type = 'auto' ) {
2076 2253 if ( ! self::pro_is_installed() && ! in_array( $type, array( 'pro', 'pro_only' ), true ) ) {
@@ -2135,21 +2312,18 @@
2135 2312 if ( $role === 'loggedin' || ! $role ) {
2136 2313 return is_user_logged_in();
2137 2314 }
2138 2315
2139 - if ( $role == 1 ) {
2316 + if ( (int) $role === 1 ) {
2140 2317 $role = 'administrator';
2141 2318 }
2142 2319
2143 - if ( ! is_user_logged_in() ) {
2144 - return false;
2145 - }
2146 -
2147 - return current_user_can( $role );
2320 + return is_user_logged_in() ? current_user_can( $role ) : false;
2148 2321 }
2149 2322
2150 2323 /**
2151 2324 * @param array|string $needed_role
2325 + *
2152 2326 * @return bool
2153 2327 */
2154 2328 public static function user_has_permission( $needed_role ) {
2155 2329 if ( is_array( $needed_role ) ) {
@@ -2163,18 +2337,20 @@
2163 2337 }
2164 2338
2165 2339 $can = self::current_user_can( $needed_role );
2166 2340
2167 - if ( $can || in_array( $needed_role, array( '-1', 'loggedout' ) ) ) {
2341 + if ( $can || in_array( $needed_role, array( '-1', 'loggedout' ), true ) ) {
2168 2342 return $can;
2169 2343 }
2170 2344
2171 2345 $roles = array( 'administrator', 'editor', 'author', 'contributor', 'subscriber' );
2346 +
2172 2347 foreach ( $roles as $role ) {
2173 2348 if ( current_user_can( $role ) ) {
2174 2349 return true;
2175 2350 }
2176 - if ( $role == $needed_role ) {
2351 +
2352 + if ( $role === $needed_role ) {
2177 2353 break;
2178 2354 }
2179 2355 }
2180 2356
@@ -2192,11 +2368,11 @@
2192 2368 if ( ! current_user_can( 'administrator' ) || current_user_can( 'frm_view_forms' ) ) {
2193 2369 return;
2194 2370 }
2195 2371
2196 - $user_id = get_current_user_id();
2197 - $user = new WP_User( $user_id );
2372 + $user = new WP_User( get_current_user_id() );
2198 2373 $frm_roles = self::frm_capabilities();
2374 +
2199 2375 foreach ( $frm_roles as $frm_role => $frm_role_description ) {
2200 2376 $user->add_cap( $frm_role );
2201 2377 unset( $frm_role, $frm_role_description );
2202 2378 }
@@ -2207,18 +2383,22 @@
2207 2383 *
2208 2384 * @since 2.0.6
2209 2385 *
2210 2386 * @param string $cap
2387 + *
2211 2388 * @return void
2212 2389 */
2213 2390 public static function force_capability( $cap = 'frm_change_settings' ) {
2214 - if ( current_user_can( 'administrator' ) && ! current_user_can( $cap ) ) {
2215 - $role = get_role( 'administrator' );
2216 - $frm_roles = self::frm_capabilities();
2217 - foreach ( $frm_roles as $frm_role => $frm_role_description ) {
2218 - $role->add_cap( $frm_role );
2219 - }
2391 + if ( ! current_user_can( 'administrator' ) || current_user_can( $cap ) ) {
2392 + return;
2220 2393 }
2394 +
2395 + $role = get_role( 'administrator' );
2396 + $frm_roles = self::frm_capabilities();
2397 +
2398 + foreach ( $frm_roles as $frm_role => $frm_role_description ) {
2399 + $role->add_cap( $frm_role );
2400 + }
2221 2401 }
2222 2402
2223 2403 /**
2224 2404 * Check if the user has permission for action.
@@ -2226,17 +2406,22 @@
2226 2406 *
2227 2407 * @since 2.0
2228 2408 *
2229 2409 * @param string $permission
2410 + * @param string $show_message
2230 2411 */
2231 2412 public static function permission_check( $permission, $show_message = 'show' ) {
2232 2413 $permission_error = self::permission_nonce_error( $permission );
2233 - if ( $permission_error !== false ) {
2234 - if ( 'hide' == $show_message ) {
2235 - $permission_error = '';
2236 - }
2237 - wp_die( esc_html( $permission_error ) );
2414 +
2415 + if ( $permission_error === false ) {
2416 + return;
2238 2417 }
2418 +
2419 + if ( 'hide' === $show_message ) {
2420 + $permission_error = '';
2421 + }
2422 +
2423 + wp_die( esc_html( $permission_error ) );
2239 2424 }
2240 2425
2241 2426 /**
2242 2427 * Check user permission and nonce
@@ -2243,24 +2428,27 @@
2243 2428 *
2244 2429 * @since 2.0
2245 2430 *
2246 2431 * @param string $permission
2432 + * @param string $nonce_name
2433 + * @param string $nonce
2247 2434 *
2248 2435 * @return false|string The permission message or false if allowed
2249 2436 */
2250 2437 public static function permission_nonce_error( $permission, $nonce_name = '', $nonce = '' ) {
2251 - if ( ! empty( $permission ) && ! current_user_can( $permission ) && ! current_user_can( 'administrator' ) ) {
2438 + if ( $permission && ! current_user_can( $permission ) && ! current_user_can( 'administrator' ) ) {
2252 2439 $frm_settings = self::get_settings();
2253 -
2254 2440 return $frm_settings->admin_permission;
2255 2441 }
2256 2442
2257 2443 $error = false;
2258 - if ( empty( $nonce_name ) ) {
2444 +
2445 + if ( ! $nonce_name ) {
2259 2446 return $error;
2260 2447 }
2261 2448
2262 2449 $nonce_value = $_REQUEST && isset( $_REQUEST[ $nonce_name ] ) ? sanitize_text_field( wp_unslash( $_REQUEST[ $nonce_name ] ) ) : '';
2450 +
2263 2451 if ( $_REQUEST && ( ! isset( $_REQUEST[ $nonce_name ] ) || ! wp_verify_nonce( $nonce_value, $nonce ) ) ) {
2264 2452 $frm_settings = self::get_settings();
2265 2453 $error = $frm_settings->admin_permission;
2266 2454 }
@@ -2267,8 +2455,14 @@
2267 2455
2268 2456 return $error;
2269 2457 }
2270 2458
2459 + /**
2460 + * @param array|string $values
2461 + * @param string $current
2462 + *
2463 + * @return void
2464 + */
2271 2465 public static function checked( $values, $current ) {
2272 2466 if ( self::check_selected( $values, $current ) ) {
2273 2467 echo ' checked="checked"';
2274 2468 }
@@ -2273,38 +2467,47 @@
2273 2467 echo ' checked="checked"';
2274 2468 }
2275 2469 }
2276 2470
2471 + /**
2472 + * @param array|string $values
2473 + * @param string|null $current
2474 + *
2475 + * @return bool
2476 + */
2277 2477 public static function check_selected( $values, $current ) {
2278 - $values = self::recursive_function_map( $values, 'trim' );
2279 - $values = self::recursive_function_map( $values, 'htmlspecialchars_decode' );
2280 -
2478 + $values = self::recursive_function_map( $values, 'trim' );
2479 + $values = self::recursive_function_map( $values, 'htmlspecialchars_decode' );
2281 2480 $current = is_null( $current ) ? '' : htmlspecialchars_decode( trim( $current ) );
2282 2481
2283 - return ( is_array( $values ) && in_array( $current, $values ) ) || ( ! is_array( $values ) && $values == $current );
2482 + // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict, Universal.Operators.StrictComparisons
2483 + return is_array( $values ) ? in_array( $current, $values ) : $values == $current;
2284 2484 }
2285 2485
2486 + /**
2487 + * @param array|string $value
2488 + * @param callable|string $function
2489 + *
2490 + * @return array|string
2491 + */
2286 2492 public static function recursive_function_map( $value, $function ) {
2287 2493 if ( is_array( $value ) ) {
2288 2494 $original_function = $function;
2289 - if ( count( $value ) ) {
2290 - $function = explode( ', ', FrmDb::prepare_array_values( $value, $function ) );
2291 - } else {
2292 - $function = array( $function );
2293 - }
2294 - if ( ! self::is_assoc( $value ) ) {
2295 - $value = array_map( array( 'FrmAppHelper', 'recursive_function_map' ), $value, $function );
2296 - } else {
2495 + $function = count( $value ) ? explode( ', ', FrmDb::prepare_array_values( $value, $function ) ) : array( $function );
2496 +
2497 + if ( self::is_assoc( $value ) ) {
2297 2498 foreach ( $value as $k => $v ) {
2298 2499 if ( ! is_array( $v ) ) {
2299 2500 $value[ $k ] = call_user_func( $original_function, $v );
2300 2501 }
2301 2502 }
2503 + } else {
2504 + $value = array_map( array( 'FrmAppHelper', 'recursive_function_map' ), $value, $function );
2302 2505 }
2303 2506 } else {
2304 2507 $value = self::maybe_update_value_if_null( $value, $function );
2305 2508 $value = call_user_func( $function, $value );
2306 - }
2509 + }//end if
2307 2510
2308 2511 return $value;
2309 2512 }
2310 2513
@@ -2311,20 +2514,27 @@
2311 2514 /**
2312 2515 * Updates value to empty string if it is null and being passed to a string function.
2313 2516 *
2314 2517 * @since 6.8.4
2518 + *
2315 2519 * @param mixed $value
2316 2520 * @param string $function
2521 + *
2317 2522 * @return mixed
2318 2523 */
2319 2524 private static function maybe_update_value_if_null( $value, $function ) {
2320 2525 if ( null === $value && in_array( $function, array( 'trim', 'strlen' ), true ) ) {
2321 - $value = '';
2526 + return '';
2322 2527 }
2323 2528
2324 2529 return $value;
2325 2530 }
2326 2531
2532 + /**
2533 + * @param array $array
2534 + *
2535 + * @return bool
2536 + */
2327 2537 public static function is_assoc( $array ) {
2328 2538 return (bool) count( array_filter( array_keys( $array ), 'is_string' ) );
2329 2539 }
2330 2540
@@ -2329,16 +2539,22 @@
2329 2539 }
2330 2540
2331 2541 /**
2332 2542 * Flatten a multi-dimensional array
2543 + *
2544 + * @param array $array
2545 + * @param string $keys
2546 + *
2547 + * @return array
2333 2548 */
2334 2549 public static function array_flatten( $array, $keys = 'keep' ) {
2335 2550 $return = array();
2551 +
2336 2552 foreach ( $array as $key => $value ) {
2337 2553 if ( is_array( $value ) ) {
2338 2554 $return = array_merge( $return, self::array_flatten( $value, $keys ) );
2339 2555 } elseif ( $keys === 'keep' ) {
2340 - $return[ $key ] = $value;
2556 + $return[ $key ] = $value;
2341 2557 } else {
2342 2558 $return[] = $value;
2343 2559 }
2344 2560 }
@@ -2352,8 +2568,9 @@
2352 2568 * @since 6.16.1
2353 2569 *
2354 2570 * @param string $sep
2355 2571 * @param array $array
2572 + *
2356 2573 * @return string
2357 2574 */
2358 2575 public static function safe_implode( $sep, $array ) {
2359 2576 $array = self::array_flatten( $array );
@@ -2362,15 +2579,18 @@
2362 2579
2363 2580 /**
2364 2581 * @param string $text
2365 2582 * @param bool $is_rich_text
2583 + *
2366 2584 * @return string
2367 2585 */
2368 2586 public static function esc_textarea( $text, $is_rich_text = false ) {
2369 2587 $safe_text = str_replace( '&quot;', '"', $text );
2588 +
2370 2589 if ( ! $is_rich_text ) {
2371 2590 $safe_text = htmlspecialchars( $safe_text, ENT_NOQUOTES );
2372 2591 }
2592 +
2373 2593 $safe_text = str_replace( '&amp; ', '& ', $safe_text );
2374 2594
2375 2595 /**
2376 2596 * @param string $safe_text
@@ -2382,44 +2602,59 @@
2382 2602 /**
2383 2603 * Add auto paragraphs to text areas
2384 2604 *
2385 2605 * @since 2.0
2606 + *
2607 + * @param mixed $content
2608 + *
2609 + * @return mixed
2386 2610 */
2387 2611 public static function use_wpautop( $content ) {
2388 2612 if ( apply_filters( 'frm_use_wpautop', true ) && is_string( $content ) ) {
2389 - $content = wpautop( str_replace( '<br>', '<br />', $content ) );
2613 + return wpautop( str_replace( '<br>', '<br />', $content ) );
2390 2614 }
2391 2615
2392 2616 return $content;
2393 2617 }
2394 2618
2619 + /**
2620 + * Replace quotes with their HTML entities.
2621 + *
2622 + * @param string $val
2623 + *
2624 + * @return string
2625 + */
2395 2626 public static function replace_quotes( $val ) {
2396 2627 // Replace double quotes.
2397 2628 $val = str_replace( array( '&#8220;', '&#8221;', '&#8243;' ), '"', $val );
2398 2629
2399 2630 // Replace single quotes.
2400 - $val = str_replace( array( '&#8216;', '&#8217;', '&#8242;', '&prime;', '&rsquo;', '&lsquo;' ), "'", $val );
2401 -
2402 - return $val;
2631 + return str_replace( array( '&#8216;', '&#8217;', '&#8242;', '&prime;', '&rsquo;', '&lsquo;' ), "'", $val );
2403 2632 }
2404 2633
2405 2634 /**
2406 - * @param string $handle
2635 + * @param string $handle
2636 + * @param int|string $default
2637 + *
2638 + * @return int|string
2407 2639 */
2408 2640 public static function script_version( $handle, $default = 0 ) {
2409 2641 global $wp_scripts;
2642 +
2410 2643 if ( ! $wp_scripts ) {
2411 2644 return $default;
2412 2645 }
2413 2646
2414 2647 $ver = $default;
2648 +
2415 2649 if ( ! isset( $wp_scripts->registered[ $handle ] ) ) {
2416 2650 return $ver;
2417 2651 }
2418 2652
2419 2653 $query = $wp_scripts->registered[ $handle ];
2654 +
2420 2655 if ( is_object( $query ) && ! empty( $query->ver ) ) {
2421 - $ver = $query->ver;
2656 + return $query->ver;
2422 2657 }
2423 2658
2424 2659 return $ver;
2425 2660 }
@@ -2428,8 +2663,9 @@
2428 2663 * @since 5.0.13 added $echo param.
2429 2664 *
2430 2665 * @param string $url
2431 2666 * @param bool $echo
2667 + *
2432 2668 * @return string|null
2433 2669 */
2434 2670 public static function js_redirect( $url, $echo = false ) {
2435 2671 $callback = function () use ( $url ) {
@@ -2437,8 +2673,13 @@
2437 2673 };
2438 2674 return self::clip( $callback, $echo );
2439 2675 }
2440 2676
2677 + /**
2678 + * @param int|string $user_id
2679 + *
2680 + * @return int|string
2681 + */
2441 2682 public static function get_user_id_param( $user_id ) {
2442 2683 if ( ! $user_id || is_numeric( $user_id ) ) {
2443 2684 return $user_id;
2444 2685 }
@@ -2443,16 +2684,13 @@
2443 2684 return $user_id;
2444 2685 }
2445 2686
2446 2687 $user_id = sanitize_text_field( $user_id );
2688 +
2447 2689 if ( $user_id === 'current' ) {
2448 2690 $user_id = get_current_user_id();
2449 2691 } else {
2450 - if ( is_email( $user_id ) ) {
2451 - $user = get_user_by( 'email', $user_id );
2452 - } else {
2453 - $user = get_user_by( 'login', $user_id );
2454 - }
2692 + $user = is_email( $user_id ) ? get_user_by( 'email', $user_id ) : get_user_by( 'login', $user_id );
2455 2693
2456 2694 if ( $user ) {
2457 2695 $user_id = $user->ID;
2458 2696 }
@@ -2464,8 +2702,9 @@
2464 2702
2465 2703 /**
2466 2704 * @param string $filename
2467 2705 * @param array $atts
2706 + *
2468 2707 * @return false|string
2469 2708 */
2470 2709 public static function get_file_contents( $filename, $atts = array() ) {
2471 2710 if ( ! is_file( $filename ) ) {
@@ -2474,12 +2713,9 @@
2474 2713
2475 2714 extract( $atts ); // phpcs:ignore WordPress.PHP.DontExtract
2476 2715 ob_start();
2477 2716 include $filename;
2478 - $contents = ob_get_contents();
2479 - ob_end_clean();
2480 -
2481 - return $contents;
2717 + return ob_get_clean();
2482 2718 }
2483 2719
2484 2720 /**
2485 2721 * @param string $name
@@ -2489,8 +2725,9 @@
2489 2725 * @param int $num_chars
2490 2726 */
2491 2727 public static function get_unique_key( $name, $table_name, $column, $id = 0, $num_chars = 5 ) {
2492 2728 $key = '';
2729 +
2493 2730 if ( $name ) {
2494 2731 $key = sanitize_key( $name );
2495 2732 $key = self::maybe_clear_long_key( $key, $column );
2496 2733 }
@@ -2510,31 +2747,31 @@
2510 2747 $column
2511 2748 );
2512 2749
2513 2750 // Create a unique field id if it has already been used.
2514 - if ( in_array( $key, $similar_keys, true ) ) {
2515 - $key = self::maybe_truncate_key_before_appending( $column, $key );
2751 + if ( ! in_array( $key, $similar_keys, true ) ) {
2752 + return $key;
2753 + }
2516 2754
2517 - /**
2518 - * Allow for a custom separator between the attempted key and the generated suffix.
2519 - *
2520 - * @since 5.2.03
2521 - *
2522 - * @param string $separator. Default empty.
2523 - * @param string $key the key without the added suffix.
2524 - */
2525 - $separator = apply_filters( 'frm_unique_' . $column . '_separator', '', $key );
2755 + $key = self::maybe_truncate_key_before_appending( $column, $key );
2526 2756
2527 - $suffix = 2;
2528 - do {
2529 - $key_check = $key . $separator . $suffix;
2530 - ++$suffix;
2531 - } while ( in_array( $key_check, $similar_keys, true ) );
2757 + /**
2758 + * Allow for a custom separator between the attempted key and the generated suffix.
2759 + *
2760 + * @since 5.2.03
2761 + *
2762 + * @param string $separator. Default empty.
2763 + * @param string $key the key without the added suffix.
2764 + */
2765 + $separator = apply_filters( 'frm_unique_' . $column . '_separator', '', $key );
2532 2766
2533 - $key = $key_check;
2534 - }//end if
2767 + $suffix = 2;
2768 + do {
2769 + $key_check = $key . $separator . $suffix;
2770 + ++$suffix;
2771 + } while ( in_array( $key_check, $similar_keys, true ) );
2535 2772
2536 - return $key;
2773 + return $key_check;
2537 2774 }
2538 2775
2539 2776 /**
2540 2777 * Avoid trying to append to a really long key,
@@ -2541,20 +2778,26 @@
2541 2778 * The database limit is 100 for form and field keys so we want to avoid getting too close.
2542 2779 *
2543 2780 * @param string $column
2544 2781 * @param string $key
2782 + *
2545 2783 * @return string
2546 2784 */
2547 2785 private static function maybe_truncate_key_before_appending( $column, $key ) {
2548 - if ( in_array( $column, array( 'form_key', 'field_key' ), true ) ) {
2549 - $max_key_length_before_truncating = 60;
2550 - if ( strlen( $key ) > $max_key_length_before_truncating ) {
2551 - $key = substr( $key, 0, $max_key_length_before_truncating );
2552 - if ( is_numeric( $key ) ) {
2553 - $key .= 'a';
2554 - }
2786 + if ( ! in_array( $column, array( 'form_key', 'field_key' ), true ) ) {
2787 + return $key;
2788 + }
2789 +
2790 + $max_key_length_before_truncating = 60;
2791 +
2792 + if ( strlen( $key ) > $max_key_length_before_truncating ) {
2793 + $key = substr( $key, 0, $max_key_length_before_truncating );
2794 +
2795 + if ( is_numeric( $key ) ) {
2796 + $key .= 'a';
2555 2797 }
2556 2798 }
2799 +
2557 2800 return $key;
2558 2801 }
2559 2802
2560 2803 /**
@@ -2561,13 +2804,14 @@
2561 2804 * Possibly reset a key to avoid conflicts with column size limits.
2562 2805 *
2563 2806 * @param string $key
2564 2807 * @param string $column
2808 + *
2565 2809 * @return string either the original key value, or an empty string if the key was too long.
2566 2810 */
2567 2811 private static function maybe_clear_long_key( $key, $column ) {
2568 2812 if ( 'field_key' === $column && strlen( $key ) >= 70 ) {
2569 - $key = '';
2813 + return '';
2570 2814 }
2571 2815 return $key;
2572 2816 }
2573 2817
@@ -2574,8 +2818,9 @@
2574 2818 /**
2575 2819 * @since 6.21 This is changed from `private` to `public`.
2576 2820 *
2577 2821 * @param int $num_chars
2822 + *
2578 2823 * @return string
2579 2824 */
2580 2825 public static function generate_new_key( $num_chars ) {
2581 2826 $max_slug_value = 36 ** $num_chars;
@@ -2586,8 +2831,9 @@
2586 2831 }
2587 2832
2588 2833 /**
2589 2834 * @param string $key
2835 + *
2590 2836 * @return string
2591 2837 */
2592 2838 private static function prevent_numeric_and_reserved_keys( $key ) {
2593 2839 if ( is_numeric( $key ) ) {
@@ -2601,12 +2847,14 @@
2601 2847 'editlink',
2602 2848 'siteurl',
2603 2849 'evenodd',
2604 2850 );
2851 +
2605 2852 if ( in_array( $key, $not_allowed, true ) ) {
2606 2853 $key .= 'a';
2607 2854 }
2608 2855 }
2856 +
2609 2857 return $key;
2610 2858 }
2611 2859
2612 2860 /**
@@ -2625,9 +2873,9 @@
2625 2873 if ( ! $record ) {
2626 2874 return false;
2627 2875 }
2628 2876
2629 - if ( empty( $post_values ) ) {
2877 + if ( ! $post_values ) {
2630 2878 $post_values = wp_unslash( $_POST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
2631 2879 }
2632 2880
2633 2881 $values = array(
@@ -2655,46 +2903,67 @@
2655 2903
2656 2904 return $values;
2657 2905 }
2658 2906
2907 + /**
2908 + * @param array|string $fields
2909 + * @param object $record
2910 + * @param array $values
2911 + * @param array $args
2912 + *
2913 + * @return void
2914 + */
2659 2915 private static function prepare_field_arrays( $fields, $record, array &$values, $args ) {
2660 - if ( ! empty( $fields ) ) {
2661 - foreach ( (array) $fields as $field ) {
2662 - if ( ! self::is_admin_page() ) {
2663 - // Don't prep default values on the form settings page.
2664 - $field->default_value = apply_filters( 'frm_get_default_value', $field->default_value, $field, true );
2665 - }
2666 - $args['parent_form_id'] = $args['parent_form_id'] ?? $field->form_id;
2667 - self::fill_field_defaults( $field, $record, $values, $args );
2916 + if ( ! $fields ) {
2917 + return;
2918 + }
2919 +
2920 + foreach ( (array) $fields as $field ) {
2921 + if ( ! self::is_admin_page() ) {
2922 + // Don't prep default values on the form settings page.
2923 + $field->default_value = apply_filters( 'frm_get_default_value', $field->default_value, $field, true );
2668 2924 }
2925 +
2926 + $args['parent_form_id'] = $args['parent_form_id'] ?? $field->form_id;
2927 + self::fill_field_defaults( $field, $record, $values, $args );
2669 2928 }
2670 2929 }
2671 2930
2931 + /**
2932 + * @param object $field
2933 + * @param object $record
2934 + * @param array $values
2935 + * @param array $args
2936 + *
2937 + * @return void
2938 + */
2672 2939 private static function fill_field_defaults( $field, $record, array &$values, $args ) {
2673 2940 $post_values = $args['post_values'];
2674 2941
2675 2942 if ( $args['default'] ) {
2676 2943 $meta_value = $field->default_value;
2677 - } elseif ( $record->post_id && self::pro_is_installed() && isset( $field->field_options['post_field'] ) && $field->field_options['post_field'] ) {
2944 + } elseif ( $record->post_id && self::pro_is_installed() && ! empty( $field->field_options['post_field'] ) ) {
2678 2945 if ( ! isset( $field->field_options['custom_field'] ) ) {
2679 2946 $field->field_options['custom_field'] = '';
2680 2947 }
2681 - $meta_value = FrmProEntryMetaHelper::get_post_value(
2682 - $record->post_id,
2683 - $field->field_options['post_field'],
2684 - $field->field_options['custom_field'],
2685 - array(
2686 - 'truncate' => false,
2687 - 'type' => $field->type,
2688 - 'form_id' => $field->form_id,
2689 - 'field' => $field,
2690 - )
2691 - );
2948 +
2949 + $meta_value = FrmProEntryMetaHelper::get_post_value(
2950 + $record->post_id,
2951 + $field->field_options['post_field'],
2952 + $field->field_options['custom_field'],
2953 + array(
2954 + 'truncate' => false,
2955 + 'type' => $field->type,
2956 + 'form_id' => $field->form_id,
2957 + 'field' => $field,
2958 + )
2959 + );
2692 2960 } else {
2693 2961 $meta_value = FrmEntryMeta::get_meta_value( $record, $field->id );
2694 2962 }//end if
2695 2963
2696 2964 $field_type = $post_values['field_options'][ 'type_' . $field->id ] ?? $field->type;
2965 +
2697 2966 if ( isset( $post_values['item_meta'][ $field->id ] ) ) {
2698 2967 $new_value = $post_values['item_meta'][ $field->id ];
2699 2968 self::unserialize_or_decode( $new_value );
2700 2969 } else {
@@ -2709,9 +2978,9 @@
2709 2978 $args['field_type'] = $field_type;
2710 2979
2711 2980 FrmFieldsHelper::prepare_edit_front_field( $field_array, $field, $values['id'], $args );
2712 2981
2713 - if ( ! isset( $field_array['unique'] ) || ! $field_array['unique'] ) {
2982 + if ( empty( $field_array['unique'] ) ) {
2714 2983 $field_array['unique_msg'] = '';
2715 2984 }
2716 2985
2717 2986 $field_array = array_merge( (array) $field->field_options, $field_array );
@@ -2778,15 +3047,21 @@
2778 3047 }
2779 3048
2780 3049 /**
2781 3050 * Set to POST value or default
3051 + *
3052 + * @param array $post_values
3053 + * @param array $values
3054 + *
3055 + * @return void
2782 3056 */
2783 3057 private static function fill_form_defaults( $post_values, array &$values ) {
2784 3058 $form_defaults = FrmFormsHelper::get_default_opts();
2785 3059
2786 3060 foreach ( $form_defaults as $opt => $default ) {
3061 + // phpcs:ignore Universal.Operators.StrictComparisons
2787 3062 if ( ! isset( $values[ $opt ] ) || $values[ $opt ] == '' ) {
2788 - $values[ $opt ] = $post_values && isset( $post_values['options'][ $opt ] ) ? $post_values['options'][ $opt ] : $default;
3063 + $values[ $opt ] = $post_values['options'][ $opt ] ?? $default;
2789 3064 }
2790 3065
2791 3066 unset( $opt, $default );
2792 3067 }
@@ -2796,9 +3071,9 @@
2796 3071 }
2797 3072
2798 3073 foreach ( array( 'before', 'after', 'submit' ) as $h ) {
2799 3074 if ( ! isset( $values[ $h . '_html' ] ) ) {
2800 - $values[ $h . '_html' ] = ( $post_values['options'][ $h . '_html' ] ?? FrmFormsHelper::get_default_html( $h ) );
3075 + $values[ $h . '_html' ] = $post_values['options'][ $h . '_html' ] ?? FrmFormsHelper::get_default_html( $h );
2801 3076 }
2802 3077 unset( $h );
2803 3078 }
2804 3079 }
@@ -2810,46 +3085,57 @@
2810 3085 *
2811 3086 * @return bool|int
2812 3087 */
2813 3088 public static function custom_style_value( $post_values ) {
2814 - if ( ! empty( $post_values ) && isset( $post_values['options']['custom_style'] ) ) {
2815 - $custom_style = absint( $post_values['options']['custom_style'] );
2816 - } else {
2817 - $frm_settings = self::get_settings();
2818 - $custom_style = ( $frm_settings->load_style !== 'none' );
3089 + if ( $post_values && isset( $post_values['options']['custom_style'] ) ) {
3090 + return absint( $post_values['options']['custom_style'] );
2819 3091 }
2820 3092
2821 - return $custom_style;
3093 + $frm_settings = self::get_settings();
3094 + return $frm_settings->load_style !== 'none';
2822 3095 }
2823 3096
2824 3097 /**
3098 + * Truncate a string.
3099 + *
3100 + * Note: By default, this function will allow for a few additional characters more than $length.
3101 + * If a string has no spaces, it allows up to 50 additional characters. To force a true length limit,
3102 + * use $force_length_limit = true.
3103 + *
2825 3104 * @param mixed $original_string
2826 3105 * @param int|string $length
2827 3106 * @param int $minword
2828 3107 * @param string $continue
3108 + * @param bool $force_length_limit Force the final string to never exceed the length limit.
3109 + *
2829 3110 * @return string
2830 3111 */
2831 - public static function truncate( $original_string, $length, $minword = 3, $continue = '...' ) {
3112 + public static function truncate( $original_string, $length, $minword = 3, $continue = '...', $force_length_limit = false ) {
2832 3113 if ( ! is_string( $original_string ) && ! is_int( $original_string ) ) {
2833 3114 return '';
2834 3115 }
2835 3116
2836 - $length = (int) $length;
2837 - $str = wp_strip_all_tags( (string) $original_string );
2838 - $original_len = self::mb_function( array( 'mb_strlen', 'strlen' ), array( $str ) );
3117 + $length = (int) $length;
2839 3118
2840 - if ( $length == 0 ) {
3119 + if ( $length === 0 ) {
2841 3120 return '';
2842 3121 }
2843 3122
3123 + $str = wp_strip_all_tags( (string) $original_string );
3124 + $original_len = self::mb_function( array( 'mb_strlen', 'strlen' ), array( $str ) );
3125 +
2844 3126 if ( $length <= 10 ) {
2845 3127 $sub = self::mb_function( array( 'mb_substr', 'substr' ), array( $str, 0, $length ) );
3128 +
3129 + if ( $force_length_limit ) {
3130 + return $sub;
3131 + }
3132 +
2846 3133 return $sub . ( $length < $original_len ? $continue : '' );
2847 3134 }
2848 3135
2849 - $sub = '';
2850 - $len = 0;
2851 -
3136 + $sub = '';
3137 + $len = 0;
2852 3138 $words = self::mb_function( array( 'mb_split', 'explode' ), array( ' ', $str ) );
2853 3139
2854 3140 if ( ! is_array( $words ) ) {
2855 3141 return $original_string;
@@ -2855,10 +3141,11 @@
2855 3141 return $original_string;
2856 3142 }
2857 3143
2858 3144 foreach ( $words as $word ) {
2859 - $part = ( $sub != '' ? ' ' : '' ) . $word;
3145 + $part = ( $sub !== '' ? ' ' : '' ) . $word;
2860 3146 $total_len = self::mb_function( array( 'mb_strlen', 'strlen' ), array( $sub . $part ) );
3147 +
2861 3148 if ( $total_len > $length && substr_count( $sub, ' ' ) ) {
2862 3149 break;
2863 3150 }
2864 3151
@@ -2871,10 +3158,21 @@
2871 3158
2872 3159 unset( $total_len, $word );
2873 3160 }
2874 3161
2875 - $sub = self::maybe_force_truncate_on_string_with_no_spaces( $sub, $length );
3162 + $sub = self::maybe_force_truncate_on_string_with_no_spaces( $sub, $length, $force_length_limit );
2876 3163
3164 + if ( $force_length_limit ) {
3165 + // Ensure the final string doesn't exceed the length limit.
3166 + $final_len = self::mb_function( array( 'mb_strlen', 'strlen' ), array( $sub ) );
3167 +
3168 + if ( $final_len > $length ) {
3169 + return self::mb_function( array( 'mb_substr', 'substr' ), array( $sub, 0, $length ) );
3170 + }
3171 +
3172 + return $sub;
3173 + }
3174 +
2877 3175 return $sub . ( $len < $original_len ? $continue : '' );
2878 3176 }
2879 3177
2880 3178 /**
@@ -2881,30 +3179,51 @@
2881 3179 * If the string is still too long because there may not have been any spaces, force truncate.
2882 3180 *
2883 3181 * @since 6.5.4
2884 3182 *
2885 - * @param string $sub Current substring.
2886 - * @param int $length The length limit.
3183 + * @param string $sub Current substring.
3184 + * @param int $length The length limit.
3185 + * @param bool $force_length_limit Force the string to not exceed the length limit.
3186 + *
2887 3187 * @return string
2888 3188 */
2889 - private static function maybe_force_truncate_on_string_with_no_spaces( $sub, $length ) {
2890 - if ( strlen( $sub ) < $length + 50 ) {
2891 - // If the string isn't way over the limit, leave it.
2892 - return $sub;
3189 + private static function maybe_force_truncate_on_string_with_no_spaces( $sub, $length, $force_length_limit = false ) {
3190 + if ( ! $force_length_limit ) {
3191 + if ( strlen( $sub ) < $length + 50 ) {
3192 + // If the string isn't way over the limit, leave it.
3193 + return $sub;
3194 + }
3195 +
3196 + $first_space = strpos( $sub, ' ', $length );
3197 +
3198 + if ( false !== $first_space ) {
3199 + // Ignore anything with spaces.
3200 + return $sub;
3201 + }
3202 +
3203 + return substr( $sub, 0, $length + 10 );
2893 3204 }
2894 3205
2895 - $first_space = strpos( $sub, ' ', $length );
2896 - if ( false !== $first_space ) {
2897 - // Ignore anything with spaces.
2898 - return $sub;
3206 + // When force_length_limit is true, ensure the string doesn't exceed the length.
3207 + $final_len = self::mb_function( array( 'mb_strlen', 'strlen' ), array( $sub ) );
3208 +
3209 + if ( $final_len > $length ) {
3210 + return self::mb_function( array( 'mb_substr', 'substr' ), array( $sub, 0, $length ) );
2899 3211 }
2900 3212
2901 - return substr( $sub, 0, $length + 10 );
3213 + return $sub;
2902 3214 }
2903 3215
3216 + /**
3217 + * @param array $function_names
3218 + * @param array $args
3219 + *
3220 + * @return mixed
3221 + */
2904 3222 public static function mb_function( $function_names, $args ) {
2905 3223 $mb_function_name = $function_names[0];
2906 3224 $function_name = $function_names[1];
3225 +
2907 3226 if ( function_exists( $mb_function_name ) ) {
2908 3227 $function_name = $mb_function_name;
2909 3228 }
2910 3229
@@ -2910,14 +3229,21 @@
2910 3229
2911 3230 return call_user_func_array( $function_name, $args );
2912 3231 }
2913 3232
3233 + /**
3234 + * @param string $date
3235 + * @param string $date_format
3236 + * @param string $time_format
3237 + *
3238 + * @return string
3239 + */
2914 3240 public static function get_formatted_time( $date, $date_format = '', $time_format = '' ) {
2915 - if ( empty( $date ) ) {
3241 + if ( ! $date ) {
2916 3242 return $date;
2917 3243 }
2918 3244
2919 - if ( empty( $date_format ) ) {
3245 + if ( ! $date_format ) {
2920 3246 $date_format = get_option( 'date_format' );
2921 3247 }
2922 3248
2923 3249 if ( preg_match( '/^\d{1-2}\/\d{1-2}\/\d{4}$/', $date ) && self::pro_is_installed() ) {
@@ -2925,10 +3251,10 @@
2925 3251 $date = FrmProAppHelper::convert_date( $date, $frmpro_settings->date_format, 'Y-m-d' );
2926 3252 }
2927 3253
2928 3254 $formatted = self::get_localized_date( $date_format, $date );
3255 + $do_time = gmdate( 'H:i:s', strtotime( $date ) ) !== '00:00:00';
2929 3256
2930 - $do_time = ( gmdate( 'H:i:s', strtotime( $date ) ) != '00:00:00' );
2931 3257 if ( $do_time ) {
2932 3258 $formatted .= self::add_time_to_date( $time_format, $date );
2933 3259 }
2934 3260
@@ -2937,30 +3263,35 @@
2937 3263
2938 3264 /**
2939 3265 * @param string $time_format
2940 3266 * @param string $date
3267 + *
2941 3268 * @return string
2942 3269 */
2943 3270 private static function add_time_to_date( $time_format, $date ) {
2944 - if ( empty( $time_format ) ) {
3271 + if ( ! $time_format ) {
2945 3272 $time_format = get_option( 'time_format' );
2946 3273 }
2947 3274
2948 3275 $trimmed_format = trim( $time_format );
2949 - $time = '';
2950 - if ( $time_format && ! empty( $trimmed_format ) ) {
2951 - $time = ' ' . __( 'at', 'formidable' ) . ' ' . self::get_localized_date( $time_format, $date );
3276 +
3277 + if ( $time_format && $trimmed_format ) {
3278 + return ' ' . __( 'at', 'formidable' ) . ' ' . self::get_localized_date( $time_format, $date );
2952 3279 }
2953 3280
2954 - return $time;
3281 + return '';
2955 3282 }
2956 3283
2957 3284 /**
2958 3285 * @since 2.0.8
3286 + *
3287 + * @param string $date_format
3288 + * @param string $date
3289 + *
3290 + * @return string
2959 3291 */
2960 3292 public static function get_localized_date( $date_format, $date ) {
2961 3293 $date = get_date_from_gmt( $date );
2962 -
2963 3294 return date_i18n( $date_format, strtotime( $date ) );
2964 3295 }
2965 3296
2966 3297 /**
@@ -2965,19 +3296,16 @@
2965 3296
2966 3297 /**
2967 3298 * Gets the time ago in words.
2968 3299 *
2969 - * @param int $from In seconds.
2970 - * @param int|string $to In seconds.
3300 + * @param int $from In seconds.
3301 + * @param int|string $to In seconds.
3302 + * @param int|string $levels Number of time units to include or a specific unit.
2971 3303 *
2972 - * @return string $time_ago
3304 + * @return string Time ago.
2973 3305 */
2974 3306 public static function human_time_diff( $from, $to = '', $levels = 1 ) {
2975 - if ( empty( $to ) && 0 !== $to ) {
2976 - $now = new DateTime();
2977 - } else {
2978 - $now = new DateTime( '@' . $to );
2979 - }
3307 + $now = ! $to && 0 !== $to ? new DateTime() : new DateTime( '@' . $to );
2980 3308 $ago = new DateTime( '@' . $from );
2981 3309
2982 3310 // Get the time difference
2983 3311 $diff_object = $now->diff( $ago );
@@ -2991,8 +3319,9 @@
2991 3319
2992 3320 if ( ! is_numeric( $levels ) ) {
2993 3321 // Show time in specified unit.
2994 3322 $levels = self::get_unit( $levels );
3323 +
2995 3324 if ( isset( $time_strings[ $levels ] ) ) {
2996 3325 $diff = array(
2997 3326 $levels => self::time_format( $levels, $diff ),
2998 3327 );
@@ -2999,13 +3328,14 @@
2999 3328 $time_strings = array(
3000 3329 $levels => $time_strings[ $levels ],
3001 3330 );
3002 3331 }
3332 +
3003 3333 $levels = 1;
3004 3334 }
3005 3335
3006 3336 foreach ( $time_strings as $k => $v ) {
3007 - if ( isset( $diff[ $k ] ) && $diff[ $k ] ) {
3337 + if ( ! empty( $diff[ $k ] ) ) {
3008 3338 $time_strings[ $k ] = $diff[ $k ] . ' ' . ( $diff[ $k ] > 1 ? $v[1] : $v[0] );
3009 3339 } elseif ( isset( $diff[ $k ] ) && count( $time_strings ) === 1 ) {
3010 3340 // Account for 0.
3011 3341 $time_strings[ $k ] = $diff[ $k ] . ' ' . $v[1];
@@ -3013,17 +3343,21 @@
3013 3343 unset( $time_strings[ $k ] );
3014 3344 }
3015 3345 }
3016 3346
3017 - $levels_deep = apply_filters( 'frm_time_ago_levels', $levels, compact( 'time_strings', 'from', 'to' ) );
3018 - $time_strings = array_slice( $time_strings, 0, absint( $levels_deep ) );
3019 - $time_ago_string = implode( ' ', $time_strings );
3347 + $levels_deep = apply_filters( 'frm_time_ago_levels', $levels, compact( 'time_strings', 'from', 'to' ) );
3348 + $time_strings = array_slice( $time_strings, 0, absint( $levels_deep ) );
3020 3349
3021 - return $time_ago_string;
3350 + return implode( ' ', $time_strings );
3022 3351 }
3023 3352
3024 3353 /**
3025 3354 * @since 4.05.01
3355 + *
3356 + * @param string $unit
3357 + * @param array $diff
3358 + *
3359 + * @return int
3026 3360 */
3027 3361 private static function time_format( $unit, $diff ) {
3028 3362 $return = array(
3029 3363 'y' => 'y',
@@ -3028,14 +3362,14 @@
3028 3362 $return = array(
3029 3363 'y' => 'y',
3030 3364 'd' => 'days',
3031 3365 );
3366 +
3032 3367 if ( isset( $return[ $unit ] ) ) {
3033 3368 return $diff[ $return[ $unit ] ];
3034 3369 }
3035 3370
3036 3371 $total = $diff['days'] * self::convert_time( 'd', $unit );
3037 -
3038 3372 $times = array( 'h', 'i', 's' );
3039 3373
3040 3374 foreach ( $times as $time ) {
3041 3375 if ( ! isset( $diff[ $time ] ) ) {
@@ -3049,8 +3383,13 @@
3049 3383 }
3050 3384
3051 3385 /**
3052 3386 * @since 4.05.01
3387 + *
3388 + * @param string $from
3389 + * @param string $to
3390 + *
3391 + * @return int
3053 3392 */
3054 3393 private static function convert_time( $from, $to ) {
3055 3394 $convert = array(
3056 3395 's' => 1,
@@ -3066,20 +3405,26 @@
3066 3405 }
3067 3406
3068 3407 /**
3069 3408 * @since 4.05.01
3409 + *
3410 + * @param string $unit
3411 + *
3412 + * @return int|string
3070 3413 */
3071 3414 private static function get_unit( $unit ) {
3072 3415 $units = self::get_time_strings();
3416 +
3073 3417 if ( isset( $units[ $unit ] ) || is_numeric( $unit ) ) {
3074 3418 return $unit;
3075 3419 }
3076 3420
3077 3421 foreach ( $units as $u => $strings ) {
3078 - if ( in_array( $unit, $strings ) ) {
3422 + if ( in_array( $unit, $strings, true ) ) {
3079 3423 return $u;
3080 3424 }
3081 3425 }
3426 +
3082 3427 return 1;
3083 3428 }
3084 3429
3085 3430 /**
@@ -3086,8 +3431,9 @@
3086 3431 * Get the translatable time strings. The untranslated version is a failsafe
3087 3432 * in case languages are changing for the unit set in the shortcode.
3088 3433 *
3089 3434 * @since 2.0.20
3435 + *
3090 3436 * @return array
3091 3437 */
3092 3438 private static function get_time_strings() {
3093 3439 return array(
@@ -3134,12 +3480,13 @@
3134 3480 /**
3135 3481 * @param int $r_count
3136 3482 * @param int $current_p
3137 3483 * @param int $p_size
3484 + *
3138 3485 * @return int
3139 3486 */
3140 3487 public static function get_last_record_num( $r_count, $current_p, $p_size ) {
3141 - return ( $r_count < $current_p * $p_size ? $r_count : $current_p * $p_size );
3488 + return $r_count < $current_p * $p_size ? $r_count : $current_p * $p_size;
3142 3489 }
3143 3490
3144 3491 /**
3145 3492 * @param int $r_count
@@ -3144,11 +3491,13 @@
3144 3491 /**
3145 3492 * @param int $r_count
3146 3493 * @param int $current_p
3147 3494 * @param int $p_size
3495 + *
3148 3496 * @return int
3149 3497 */
3150 3498 public static function get_first_record_num( $r_count, $current_p, $p_size ) {
3499 + // phpcs:ignore Universal.Operators.StrictComparisons
3151 3500 if ( $current_p == 1 ) {
3152 3501 return 1;
3153 3502 }
3154 3503 return self::get_last_record_num( $r_count, $current_p - 1, $p_size ) + 1;
@@ -3154,17 +3503,22 @@
3154 3503 return self::get_last_record_num( $r_count, $current_p - 1, $p_size ) + 1;
3155 3504 }
3156 3505
3157 3506 /**
3507 + * @param array $json_vars
3508 + *
3158 3509 * @return array
3159 3510 */
3160 3511 public static function json_to_array( $json_vars ) {
3161 3512 $vars = array();
3513 +
3162 3514 foreach ( $json_vars as $jv ) {
3163 3515 $jv_name = explode( '[', $jv['name'] );
3164 3516 $last = count( $jv_name ) - 1;
3517 +
3165 3518 foreach ( $jv_name as $p => $n ) {
3166 3519 $name = trim( $n, ']' );
3520 +
3167 3521 if ( ! isset( $l1 ) ) {
3168 3522 $l1 = $name;
3169 3523 }
3170 3524
@@ -3175,9 +3529,9 @@
3175 3529 if ( ! isset( $l3 ) ) {
3176 3530 $l3 = $name;
3177 3531 }
3178 3532
3179 - $this_val = $p == $last ? $jv['value'] : array();
3533 + $this_val = $p === $last ? $jv['value'] : array();
3180 3534
3181 3535 switch ( $p ) {
3182 3536 case 0:
3183 3537 $l1 = $name;
@@ -3210,10 +3564,15 @@
3210 3564
3211 3565 /**
3212 3566 * @param string $name
3213 3567 * @param string $l1
3568 + * @param mixed $val
3569 + * @param array $vars
3570 + *
3571 + * @return void
3214 3572 */
3215 3573 public static function add_value_to_array( $name, $l1, $val, &$vars ) {
3574 + // phpcs:ignore Universal.Operators.StrictComparisons
3216 3575 if ( $name == '' ) {
3217 3576 $vars[] = $val;
3218 3577 } elseif ( ! isset( $vars[ $l1 ] ) ) {
3219 3578 $vars[ $l1 ] = $val;
@@ -3219,19 +3578,26 @@
3219 3578 $vars[ $l1 ] = $val;
3220 3579 }
3221 3580 }
3222 3581
3582 + /**
3583 + * @param string $name
3584 + * @param string $class
3585 + * @param string $form_name
3586 + *
3587 + * @return void
3588 + */
3223 3589 public static function maybe_add_tooltip( $name, $class = 'closed', $form_name = '' ) {
3224 3590 $tooltips = array(
3225 3591 'action_title' => __( 'Give this action a label for easy reference.', 'formidable' ),
3226 - 'email_to' => __( 'Add one or more recipient addresses separated by a ",". FORMAT: Name <[email protected]> or [email protected]. [default-email] is the address set in the global "Default Email Address" settings.', 'formidable' ),
3592 + 'email_to' => __( 'Add one or more recipient addresses separated by a ",". FORMAT: Name <[email protected]> or [email protected]. [default-email] is the address set in the global "Default Email Address" settings.', 'formidable' ), // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
3227 3593 'cc' => __( 'Add CC addresses separated by a ",". FORMAT: Name <[email protected]> or [email protected].', 'formidable' ),
3228 3594 'bcc' => __( 'Add BCC addresses separated by a ",". FORMAT: Name <[email protected]> or [email protected].', 'formidable' ),
3229 - 'reply_to' => __( 'If you would like a different reply to address than the "from" address, add a single address here. FORMAT: Name <[email protected]> or [email protected].', 'formidable' ),
3595 + 'reply_to' => __( 'If you would like a different reply to address than the "from" address, add a single address here. FORMAT: Name <[email protected]> or [email protected].', 'formidable' ), // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
3230 3596 'from' => __( 'Enter the name and/or email address of the sender. FORMAT: John Bates <[email protected]> or [email protected].', 'formidable' ),
3231 3597 /* translators: %1$s: Form name, %2$s: Date */
3232 - '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() ) ),
3233 - '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' ),
3598 + '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() ) ), // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
3599 + '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' ), // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
3234 3600 );
3235 3601
3236 3602 if ( ! isset( $tooltips[ $name ] ) ) {
3237 3603 return;
@@ -3236,9 +3602,9 @@
3236 3602 if ( ! isset( $tooltips[ $name ] ) ) {
3237 3603 return;
3238 3604 }
3239 3605
3240 - if ( 'open' == $class ) {
3606 + if ( 'open' === $class ) {
3241 3607 echo ' frm_help"';
3242 3608 } else {
3243 3609 echo ' class="frm_help"';
3244 3610 }
@@ -3244,9 +3610,9 @@
3244 3610 }
3245 3611
3246 3612 echo ' title="' . esc_attr( $tooltips[ $name ] );
3247 3613
3248 - if ( 'open' != $class ) {
3614 + if ( 'open' !== $class ) {
3249 3615 echo '"';
3250 3616 }
3251 3617 }
3252 3618
@@ -3251,20 +3617,28 @@
3251 3617 }
3252 3618
3253 3619 /**
3254 3620 * Add the current_page class to that page in the form nav
3621 + *
3622 + * @param int|string $page
3623 + * @param int|string $current_page
3624 + * @param array $action
3625 + *
3626 + * @return void
3255 3627 */
3256 3628 public static function select_current_page( $page, $current_page, $action = array() ) {
3629 + // phpcs:ignore Universal.Operators.StrictComparisons
3257 3630 if ( $current_page != $page ) {
3258 3631 return;
3259 3632 }
3260 3633
3261 3634 $frm_action = self::simple_get( 'frm_action', 'sanitize_title' );
3635 +
3262 3636 if ( 'lite-reports' === $frm_action ) {
3263 3637 $frm_action = 'reports';
3264 3638 }
3265 3639
3266 - if ( empty( $action ) || ( ! empty( $frm_action ) && in_array( $frm_action, $action ) ) ) {
3640 + if ( ! $action || ( $frm_action && in_array( $frm_action, $action, true ) ) ) {
3267 3641 echo ' class="current_page"';
3268 3642 }
3269 3643 }
3270 3644
@@ -3294,14 +3668,19 @@
3294 3668
3295 3669 // Add extra slashes for \r\n since WP strips them.
3296 3670 $post_content = str_replace( array( '\\r', '\\n', '\\u', '\\t' ), array( '\\\\r', '\\\\n', '\\\\u', '\\\\t' ), $post_content );
3297 3671
3298 - // allow for &quot
3299 - $post_content = str_replace( '&quot;', '\\"', $post_content );
3300 -
3301 - return $post_content;
3672 + // Allow for &quot
3673 + return str_replace( '&quot;', '\\"', $post_content );
3302 3674 }
3303 3675
3676 + /**
3677 + * @param array|string $val
3678 + * @param int|string $key
3679 + * @param array $post_content
3680 + *
3681 + * @return void
3682 + */
3304 3683 private static function prepare_action_slashes( $val, $key, &$post_content ) {
3305 3684 if ( ! isset( $post_content[ $key ] ) || is_numeric( $val ) ) {
3306 3685 return;
3307 3686 }
@@ -3310,15 +3689,17 @@
3310 3689 foreach ( $val as $k1 => $v1 ) {
3311 3690 self::prepare_action_slashes( $v1, $k1, $post_content[ $key ] );
3312 3691 unset( $k1, $v1 );
3313 3692 }
3314 - } else {
3315 - // Strip all slashes so everything is the same, no matter where the value is coming from
3316 - $val = stripslashes( $val );
3317 3693
3318 - // Add backslashes before double quotes and forward slashes only
3319 - $post_content[ $key ] = addcslashes( $val, '"\\/' );
3694 + return;
3320 3695 }
3696 +
3697 + // Strip all slashes so everything is the same, no matter where the value is coming from
3698 + $val = stripslashes( $val );
3699 +
3700 + // Add backslashes before double quotes and forward slashes only
3701 + $post_content[ $key ] = addcslashes( $val, '"\\/' );
3321 3702 }
3322 3703
3323 3704 /**
3324 3705 * Check for either json or serialized data. This is temporary while transitioning
@@ -3326,8 +3707,9 @@
3326 3707 *
3327 3708 * @since 4.02.03
3328 3709 *
3329 3710 * @param array|string $value
3711 + *
3330 3712 * @return void
3331 3713 */
3332 3714 public static function unserialize_or_decode( &$value ) {
3333 3715 if ( is_array( $value ) ) {
@@ -3333,13 +3715,9 @@
3333 3715 if ( is_array( $value ) ) {
3334 3716 return;
3335 3717 }
3336 3718
3337 - if ( is_serialized( $value ) ) {
3338 - $value = self::maybe_unserialize_array( $value );
3339 - } else {
3340 - $value = self::maybe_json_decode( $value, false );
3341 - }
3719 + $value = is_serialized( $value ) ? self::maybe_unserialize_array( $value ) : self::maybe_json_decode( $value, false );
3342 3720 }
3343 3721
3344 3722 /**
3345 3723 * Safely unserialize an array if necessary.
@@ -3347,8 +3725,9 @@
3347 3725 *
3348 3726 * @since 6.2
3349 3727 *
3350 3728 * @param mixed $value
3729 + *
3351 3730 * @return mixed
3352 3731 */
3353 3732 public static function maybe_unserialize_array( $value ) {
3354 3733 if ( ! is_string( $value ) ) {
@@ -3355,18 +3734,15 @@
3355 3734 return $value;
3356 3735 }
3357 3736
3358 3737 // Since we only expect an array, skip anything that doesn't start with a:.
3359 - if ( ! is_serialized( $value ) || 'a:' !== substr( $value, 0, 2 ) ) {
3738 + if ( ! is_serialized( $value ) || ! str_starts_with( $value, 'a:' ) ) {
3360 3739 return $value;
3361 3740 }
3362 3741
3363 3742 $parsed = FrmSerializedStringParserHelper::get()->parse( $value );
3364 - if ( is_array( $parsed ) ) {
3365 - $value = $parsed;
3366 - }
3367 3743
3368 - return $value;
3744 + return is_array( $parsed ) ? $parsed : $value;
3369 3745 }
3370 3746
3371 3747 /**
3372 3748 * Decode a JSON string.
@@ -3371,11 +3747,12 @@
3371 3747 /**
3372 3748 * Decode a JSON string.
3373 3749 * Do not switch shortcodes like [24] to array unless intentional ie XML values.
3374 3750 *
3375 - * @param mixed $string
3376 - * @param bool $single_to_array
3377 - * @return mixed
3751 + * @param array|string|null $string
3752 + * @param bool $single_to_array
3753 + *
3754 + * @return array|string|null
3378 3755 */
3379 3756 public static function maybe_json_decode( $string, $single_to_array = true ) {
3380 3757 if ( is_array( $string ) || is_null( $string ) ) {
3381 3758 return $string;
@@ -3380,20 +3757,19 @@
3380 3757 if ( is_array( $string ) || is_null( $string ) ) {
3381 3758 return $string;
3382 3759 }
3383 3760
3384 - $new_string = json_decode( $string, true );
3385 - if ( function_exists( 'json_last_error' ) ) {
3386 - // php 5.3+
3387 - $single_value = false;
3388 - if ( ! $single_to_array ) {
3389 - $single_value = is_array( $new_string ) && count( $new_string ) === 1 && isset( $new_string[0] );
3390 - }
3391 - if ( json_last_error() == JSON_ERROR_NONE && is_array( $new_string ) && ! $single_value ) {
3392 - $string = $new_string;
3393 - }
3761 + $new_string = json_decode( $string, true );
3762 + $single_value = false;
3763 +
3764 + if ( ! $single_to_array ) {
3765 + $single_value = is_array( $new_string ) && count( $new_string ) === 1 && isset( $new_string[0] );
3394 3766 }
3395 3767
3768 + if ( json_last_error() === JSON_ERROR_NONE && is_array( $new_string ) && ! $single_value ) {
3769 + return $new_string;
3770 + }
3771 +
3396 3772 return $string;
3397 3773 }
3398 3774
3399 3775 /**
@@ -3399,8 +3775,9 @@
3399 3775 /**
3400 3776 * @since 6.2.3
3401 3777 *
3402 3778 * @param string $value
3779 + *
3403 3780 * @return string
3404 3781 */
3405 3782 public static function maybe_utf8_encode( $value ) {
3406 3783 $from_format = 'ISO-8859-1';
@@ -3409,13 +3786,15 @@
3409 3786 if ( function_exists( 'mb_check_encoding' ) && function_exists( 'mb_convert_encoding' ) ) {
3410 3787 if ( mb_check_encoding( $value, $from_format ) ) {
3411 3788 return mb_convert_encoding( $value, $to_format, $from_format );
3412 3789 }
3790 +
3413 3791 return $value;
3414 3792 }
3415 3793
3416 3794 if ( function_exists( 'iconv' ) ) {
3417 3795 $converted = iconv( $from_format, $to_format, $value );
3796 +
3418 3797 // Value is false if $value is not ISO-8859-1.
3419 3798 if ( false !== $converted ) {
3420 3799 return $converted;
3421 3800 }
@@ -3427,8 +3806,12 @@
3427 3806 /**
3428 3807 * Reformat the json serialized array in name => value array.
3429 3808 *
3430 3809 * @since 4.02.03
3810 + *
3811 + * @param array $form
3812 + *
3813 + * @return void
3431 3814 */
3432 3815 public static function format_form_data( &$form ) {
3433 3816 $formatted = array();
3434 3817
@@ -3435,17 +3818,20 @@
3435 3818 foreach ( $form as $input ) {
3436 3819 if ( ! isset( $input['name'] ) ) {
3437 3820 continue;
3438 3821 }
3822 +
3439 3823 $key = $input['name'];
3440 - if ( isset( $formatted[ $key ] ) ) {
3441 - if ( is_array( $formatted[ $key ] ) ) {
3442 - $formatted[ $key ][] = $input['value'];
3443 - } else {
3444 - $formatted[ $key ] = array( $formatted[ $key ], $input['value'] );
3445 - }
3824 +
3825 + if ( ! isset( $formatted[ $key ] ) ) {
3826 + $formatted[ $key ] = $input['value'];
3827 + continue;
3828 + }
3829 +
3830 + if ( is_array( $formatted[ $key ] ) ) {
3831 + $formatted[ $key ][] = $input['value'];
3446 3832 } else {
3447 - $formatted[ $key ] = $input['value'];
3833 + $formatted[ $key ] = array( $formatted[ $key ], $input['value'] );
3448 3834 }
3449 3835 }
3450 3836
3451 3837 parse_str( http_build_query( $formatted ), $form );
@@ -3454,15 +3840,13 @@
3454 3840 /**
3455 3841 * @since 4.02.03
3456 3842 *
3457 3843 * @param array|string $value
3844 + *
3458 3845 * @return string
3459 3846 */
3460 3847 public static function maybe_json_encode( $value ) {
3461 - if ( is_array( $value ) ) {
3462 - $value = wp_json_encode( $value );
3463 - }
3464 - return $value;
3848 + return is_array( $value ) ? wp_json_encode( $value ) : $value;
3465 3849 }
3466 3850
3467 3851 /**
3468 3852 * Echo The javascript to open and highlight the Formidable menu
@@ -3469,18 +3853,19 @@
3469 3853 *
3470 3854 * @since 1.07.10
3471 3855 *
3472 3856 * @param string $post_type The name of the post type that may need to be highlighted.
3857 + *
3473 3858 * @return void
3474 3859 */
3475 3860 public static function maybe_highlight_menu( $post_type ) {
3476 3861 global $post;
3477 3862
3478 - if ( isset( $_REQUEST['post_type'] ) && $_REQUEST['post_type'] != $post_type ) {
3863 + if ( isset( $_REQUEST['post_type'] ) && $_REQUEST['post_type'] !== $post_type ) {
3479 3864 return;
3480 3865 }
3481 3866
3482 - if ( is_object( $post ) && $post->post_type != $post_type ) {
3867 + if ( is_object( $post ) && $post->post_type !== $post_type ) {
3483 3868 return;
3484 3869 }
3485 3870
3486 3871 self::load_admin_wide_js();
@@ -3492,13 +3877,13 @@
3492 3877 *
3493 3878 * @since 2.0
3494 3879 *
3495 3880 * @param bool $load
3881 + *
3496 3882 * @return void
3497 3883 */
3498 3884 public static function load_admin_wide_js( $load = true ) {
3499 - $version = self::plugin_version();
3500 - wp_register_script( 'formidable_admin_global', self::plugin_url() . '/js/formidable_admin_global.js', array( 'jquery' ), $version );
3885 + wp_register_script( 'formidable_admin_global', self::plugin_url() . '/js/formidable_admin_global.js', array( 'jquery' ), self::plugin_version() );
3501 3886
3502 3887 $global_strings = array(
3503 3888 'updating_msg' => __( 'Please wait while your site updates.', 'formidable' ),
3504 3889 'deauthorize' => __( 'Are you sure you want to deauthorize Formidable Forms on this site?', 'formidable' ),
@@ -3504,9 +3889,9 @@
3504 3889 'deauthorize' => __( 'Are you sure you want to deauthorize Formidable Forms on this site?', 'formidable' ),
3505 3890 'url' => self::plugin_url(),
3506 3891 'app_url' => 'https://formidableforms.com/',
3507 3892 'applicationsUrl' => admin_url( 'admin.php?page=formidable-applications' ),
3508 - 'canAccessApplicationDashboard' => current_user_can( is_callable( 'FrmProApplicationsHelper::get_required_templates_capability' ) ? FrmProApplicationsHelper::get_required_templates_capability() : 'frm_edit_forms' ),
3893 + 'canAccessApplicationDashboard' => current_user_can( is_callable( 'FrmProApplicationsHelper::get_required_templates_capability' ) ? FrmProApplicationsHelper::get_required_templates_capability() : 'frm_edit_forms' ), // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
3509 3894 'loading' => __( 'Loading&hellip;', 'formidable' ),
3510 3895 'nonce' => wp_create_nonce( 'frm_ajax' ),
3511 3896 'proIncludesSliderJs' => is_callable( 'FrmProFormsHelper::prepare_custom_currency' ),
3512 3897 'inboxSlideIn' => FrmInbox::get_inbox_slide_in_value_for_js(),
@@ -3519,8 +3904,9 @@
3519 3904 }
3520 3905
3521 3906 /**
3522 3907 * @since 2.0.9
3908 + *
3523 3909 * @return void
3524 3910 */
3525 3911 public static function load_font_style() {
3526 3912 wp_enqueue_style( 'frm_fonts', self::plugin_url() . '/css/frm_fonts.css', array(), self::plugin_version() );
@@ -3527,8 +3913,9 @@
3527 3913 }
3528 3914
3529 3915 /**
3530 3916 * @param string $location
3917 + *
3531 3918 * @return void
3532 3919 */
3533 3920 public static function localize_script( $location ) {
3534 3921 global $wp_scripts, $wp_version;
@@ -3551,8 +3938,9 @@
3551 3938 'include_resend_email' => false,
3552 3939 );
3553 3940
3554 3941 $data = $wp_scripts->get_data( 'formidable', 'data' );
3942 +
3555 3943 if ( ! $data ) {
3556 3944 wp_localize_script( 'formidable', 'frm_js', $script_strings );
3557 3945 }
3558 3946
@@ -3569,9 +3957,9 @@
3569 3957 'valid_default' => __( 'Default value will pass form validation', 'formidable' ),
3570 3958 'no_valid_default' => __( 'Default value will NOT pass form validation', 'formidable' ),
3571 3959 'confirm' => __( 'Are you sure?', 'formidable' ),
3572 3960 'conf_delete' => __( 'Are you sure you want to delete this field and all data associated with it?', 'formidable' ),
3573 - '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' ),
3961 + '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' ), // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
3574 3962 'conf_no_repeat' => __( 'Warning: If you have entries with multiple rows, all but the first row will be lost.', 'formidable' ),
3575 3963 'default_unique' => FrmFieldsHelper::default_unique_msg(),
3576 3964 'default_conf' => __( 'The entered values do not match', 'formidable' ),
3577 3965 'enter_email' => __( 'Enter Email', 'formidable' ),
@@ -3577,9 +3965,9 @@
3577 3965 'enter_email' => __( 'Enter Email', 'formidable' ),
3578 3966 'confirm_email' => __( 'Confirm Email', 'formidable' ),
3579 3967 'conditional_text' => __( 'Conditional content here', 'formidable' ),
3580 3968 'new_option' => __( 'New Option', 'formidable' ),
3581 - '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' ),
3969 + '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' ), // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
3582 3970 'enter_password' => __( 'Enter Password', 'formidable' ),
3583 3971 'confirm_password' => __( 'Confirm Password', 'formidable' ),
3584 3972 'import_complete' => __( 'Import Complete', 'formidable' ),
3585 3973 'updating' => __( 'Please wait while your site updates.', 'formidable' ),
@@ -3593,13 +3981,15 @@
3593 3981 'text_shortcodes' => __( 'This calculation may have shortcodes that work in text calculations but not numeric calculations.', 'formidable' ),
3594 3982 /* translators: %d is the number of allowed actions per form */
3595 3983 'only_one_action' => sprintf( __( 'This form action is limited to %d per form.', 'formidable' ), 1 ),
3596 3984 'edit_action_text' => __( 'Please edit the existing form action.', 'formidable' ),
3985 + 'only_one_payment_action' => __( 'This form already has a payment action, and is currently limited to a single payment action.', 'formidable' ),
3986 + 'only_one_stripe_action' => __( 'This form already has a payment action. Multiple Stripe actions are available when using the Stripe add-on, available for Formidable Business licenses and higher.', 'formidable' ), // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
3597 3987 'unsafe_params' => FrmFormsHelper::reserved_words(),
3598 3988 /* Translators: %s is the name of a Detail Page Slug that is a reserved word.*/
3599 - 'slug_is_reserved' => sprintf( __( 'The Detail Page Slug "%s" is reserved by WordPress. This may cause problems. Is this intentional?', 'formidable' ), '****' ),
3989 + 'slug_is_reserved' => sprintf( __( 'The Detail Page Slug "%s" is reserved by WordPress. This may cause problems. Is this intentional?', 'formidable' ), '****' ), // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
3600 3990 /* 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. */
3601 - 'param_is_reserved' => sprintf( __( 'The parameter "%s" is reserved by WordPress. This may cause problems when included in the URL. Is this intentional? ', 'formidable' ), '****' ),
3991 + 'param_is_reserved' => sprintf( __( 'The parameter "%s" is reserved by WordPress. This may cause problems when included in the URL. Is this intentional? ', 'formidable' ), '****' ), // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
3602 3992 'reserved_words' => __( 'See the list of reserved words in WordPress.', 'formidable' ),
3603 3993 'repeat_limit_min' => __( 'Please enter a Repeat Limit that is greater than 1.', 'formidable' ),
3604 3994 'checkbox_limit' => __( 'Please select a limit between 0 and 200.', 'formidable' ),
3605 3995 'install' => __( 'Install', 'formidable' ),
@@ -3620,11 +4010,14 @@
3620 4010 'holdShiftMsg' => esc_html__( 'You can hold %1$sShift%2$s on your keyboard to select multiple fields', 'formidable' ),
3621 4011 'noTitleText' => FrmFormsHelper::get_no_title_text(),
3622 4012
3623 4013 // In older versions this event listener causes the section to immediately close again
3624 - // when the h3 element is clicked. It's only required in WP 6.7+.
4014 + // When the h3 element is clicked. It's only required in WP 6.7+.
3625 4015 'requireAccordionTitleClickListener' => version_compare( $wp_version, '6.7', '>=' ),
3626 4016 );
4017 +
4018 + self::add_form_builder_modal_data( $admin_script_strings );
4019 +
3627 4020 /**
3628 4021 * @param array $admin_script_strings
3629 4022 */
3630 4023 $admin_script_strings = apply_filters( 'frm_admin_script_strings', $admin_script_strings );
@@ -3629,8 +4022,9 @@
3629 4022 */
3630 4023 $admin_script_strings = apply_filters( 'frm_admin_script_strings', $admin_script_strings );
3631 4024
3632 4025 $data = $wp_scripts->get_data( 'formidable_admin', 'data' );
4026 +
3633 4027 if ( ! $data ) {
3634 4028 wp_localize_script( 'formidable_admin', 'frm_admin_js', $admin_script_strings );
3635 4029 }
3636 4030 }//end if
@@ -3636,8 +4030,80 @@
3636 4030 }//end if
3637 4031 }
3638 4032
3639 4033 /**
4034 + * @param array $admin_script_strings
4035 + *
4036 + * @return void
4037 + */
4038 + private static function add_form_builder_modal_data( &$admin_script_strings ) {
4039 + if ( ! self::is_form_builder_page() || self::pro_is_installed() ) {
4040 + return;
4041 + }
4042 +
4043 + $stripe_connected = FrmStrpLiteConnectHelper::at_least_one_mode_is_setup();
4044 + $square_connected = FrmSquareLiteConnectHelper::at_least_one_mode_is_setup();
4045 + $paypal_connected = FrmPayPalLiteConnectHelper::at_least_one_mode_is_setup();
4046 + $gateway_connected = $stripe_connected || $square_connected || $paypal_connected;
4047 + $payments_settings_url = FrmStrpLiteAppController::get_payments_settings_url();
4048 +
4049 + if ( ! $gateway_connected ) {
4050 + // This modal shows when user clicks on one of the pricing fields and no payment gateways configured.
4051 + $admin_script_strings['paymentsSettingsModal'] = array(
4052 + 'title' => __( 'Setup a Payment Gateway first', 'formidable' ),
4053 + 'msg' => __( 'To use the payment fields, please install and configure a payment gateway in your account settings.', 'formidable' ),
4054 + 'closeText' => __( 'Close', 'formidable' ),
4055 + 'actionUrl' => $payments_settings_url,
4056 + 'actionText' => __( 'Go to Payment Settings', 'formidable' ),
4057 + 'noCenter' => true,
4058 + );
4059 + }
4060 +
4061 + // This modal shows on load once after upgrading the plugin.
4062 + $show_pricing_fields_modal = get_option( 'frm_show_pricing_fields_modal' );
4063 +
4064 + if ( ! $show_pricing_fields_modal ) {
4065 + return;
4066 + }
4067 +
4068 + $admin_script_strings['pricingFieldsModal'] = array(
4069 + 'title' => esc_html__( 'Start Accepting Payments Today!', 'formidable' ),
4070 + 'img' => esc_url( self::plugin_url() . '/images/upsell/pricing-fields.png' ),
4071 + 'noCenter' => true,
4072 + );
4073 +
4074 + if ( $gateway_connected ) {
4075 + $gateway_texts = array();
4076 +
4077 + if ( $stripe_connected ) {
4078 + $gateway_texts['stripe'] = esc_html__( 'Stripe', 'formidable' );
4079 + }
4080 +
4081 + if ( $square_connected ) {
4082 + $gateway_texts['square'] = esc_html__( 'Square', 'formidable' );
4083 + }
4084 +
4085 + if ( $paypal_connected ) {
4086 + $gateway_texts['paypal'] = esc_html__( 'PayPal', 'formidable' );
4087 + }
4088 +
4089 + $admin_script_strings['pricingFieldsModal']['msg'] = sprintf(
4090 + // translators: %s: Stripe, Square, or PayPal.
4091 + esc_html__( 'You already have %s connected, so these have already been unlocked.', 'formidable' ),
4092 + esc_html( implode( ' ' . esc_html__( 'and', 'formidable' ) . ' ', $gateway_texts ) )
4093 + );
4094 + } else {
4095 + $admin_script_strings['pricingFieldsModal']['closeText'] = __( 'I\'ll do it later!', 'formidable' );
4096 + $admin_script_strings['pricingFieldsModal']['actionText'] = __( 'Setup Payments Now', 'formidable' );
4097 + $admin_script_strings['pricingFieldsModal']['actionUrl'] = $payments_settings_url;
4098 + // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
4099 + $admin_script_strings['pricingFieldsModal']['msg'] = __( 'We\'ve unlocked Product, Quantity, and Total fields for Lite users! You can now transform your forms into checkout pages. To start collecting revenue, simply connect your preferred payment gateway (Stripe, Square, or PayPal) in your settings.', 'formidable' );
4100 + }//end if
4101 +
4102 + delete_option( 'frm_show_pricing_fields_modal' );
4103 + }
4104 +
4105 + /**
3640 4106 * Get the no label text.
3641 4107 *
3642 4108 * @since 6.25.1
3643 4109 *
@@ -3690,8 +4156,9 @@
3690 4156 *
3691 4157 * @since 1.07.10
3692 4158 *
3693 4159 * @param float $min_version The version the add-on requires.
4160 + *
3694 4161 * @return void
3695 4162 */
3696 4163 public static function min_version_notice( $min_version ) {
3697 4164 $frm_version = self::plugin_version();
@@ -3701,11 +4168,11 @@
3701 4168 return;
3702 4169 }
3703 4170
3704 4171 $wp_list_table = _get_list_table( 'WP_Plugins_List_Table' );
3705 - echo '<tr class="plugin-update-tr active"><th colspan="' . absint( $wp_list_table->get_column_count() ) . '" class="check-column plugin-update colspanchange"><div class="update-message">' .
3706 - esc_html__( 'You are running an outdated version of Formidable. This plugin may not work correctly if you do not update Formidable.', 'formidable' ) .
3707 - '</div></td></tr>';
4172 + echo '<tr class="plugin-update-tr active"><th colspan="' . absint( $wp_list_table->get_column_count() ) . '" class="check-column plugin-update colspanchange"><div class="update-message">' . // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
4173 + esc_html__( 'You are running an outdated version of Formidable. This plugin may not work correctly if you do not update Formidable.', 'formidable' ) .
4174 + '</div></td></tr>';
3708 4175 }
3709 4176
3710 4177 /**
3711 4178 * If Pro is far outdated, show a message.
@@ -3711,8 +4178,10 @@
3711 4178 * If Pro is far outdated, show a message.
3712 4179 *
3713 4180 * @since 4.0.01
3714 4181 *
4182 + * @param string $min_version
4183 + *
3715 4184 * @return void
3716 4185 */
3717 4186 public static function min_pro_version_notice( $min_version ) {
3718 4187 if ( ! self::is_formidable_admin() ) {
@@ -3722,8 +4191,9 @@
3722 4191
3723 4192 self::php_version_notice();
3724 4193
3725 4194 $is_pro = self::pro_is_installed() && class_exists( 'FrmProDb' );
4195 +
3726 4196 if ( ! $is_pro || self::meets_min_pro_version( $min_version ) ) {
3727 4197 return;
3728 4198 }
3729 4199
@@ -3735,8 +4205,9 @@
3735 4205 *
3736 4206 * @since 4.0.01
3737 4207 *
3738 4208 * @param string $min_version
4209 + *
3739 4210 * @return bool
3740 4211 */
3741 4212 public static function meets_min_pro_version( $min_version ) {
3742 4213 return ! class_exists( 'FrmProDb' ) || version_compare( FrmProDb::$plug_version, $min_version, '>=' );
@@ -3745,16 +4216,19 @@
3745 4216 /**
3746 4217 * Show a message if the PHP version is below the recommendations.
3747 4218 *
3748 4219 * @since 4.0.02
4220 + *
3749 4221 * @return void
3750 4222 */
3751 4223 private static function php_version_notice() {
3752 4224 $message = array();
4225 +
3753 4226 if ( version_compare( phpversion(), '7.0', '<' ) ) {
3754 - $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' );
4227 + $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' ); // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
3755 4228 }
3756 4229
4230 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
3757 4231 foreach ( $message as $m ) {
3758 4232 ?>
3759 4233 <div class="frm-banner-alert frm_error_style frm_previous_install">
3760 4234 <?php echo esc_html( $m ); ?>
@@ -3760,12 +4234,14 @@
3760 4234 <?php echo esc_html( $m ); ?>
3761 4235 </div>
3762 4236 <?php
3763 4237 }
4238 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
3764 4239 }
3765 4240
3766 4241 /**
3767 4242 * @param string $type
4243 + *
3768 4244 * @return array<string,string>
3769 4245 */
3770 4246 public static function locales( $type = 'date' ) {
3771 4247 $locales = array(
@@ -3859,12 +4335,12 @@
3859 4335 'zu' => __( 'Zulu', 'formidable' ),
3860 4336 );
3861 4337
3862 4338 if ( $type === 'captcha' ) {
3863 - // remove the languages unavailable for the captcha
4339 + // Remove the languages unavailable for the captcha
3864 4340 $unset = array( 'sq', 'bs', 'eo', 'fo', 'fr-CH', 'sr-SR', 'ar-DZ', 'be', 'cy-GB', 'kk', 'km', 'ky', 'lb', 'mk', 'nb', 'nn', 'rm', 'tj' );
3865 4341 } else {
3866 - // remove the languages unavailable for the datepicker
4342 + // Remove the languages unavailable for the datepicker
3867 4343 $unset = array( 'fil', 'fr-CA', 'de-AT', 'de-CH', 'iw', 'hi', 'pt', 'pt-PT', 'es-419', 'mr', 'lo', 'kn', 'si', 'gu', 'bn', 'zu', 'ur', 'te', 'sw', 'am' );
3868 4344 }
3869 4345
3870 4346 $locales = array_diff_key( $locales, array_flip( $unset ) );
@@ -3875,14 +4351,13 @@
3875 4351 * @since 5.4.5 Added $args parameter with type.
3876 4352 *
3877 4353 * @param array<string,string> $locales
3878 4354 * @param array $args {
4355 + *
3879 4356 * @type string $type
3880 4357 * }
3881 4358 */
3882 - $locales = apply_filters( 'frm_locales', $locales, compact( 'type' ) );
3883 -
3884 - return $locales;
4359 + return apply_filters( 'frm_locales', $locales, compact( 'type' ) );
3885 4360 }
3886 4361
3887 4362 /**
3888 4363 * @return string
@@ -3889,12 +4364,14 @@
3889 4364 */
3890 4365 public static function get_menu_icon_class() {
3891 4366 if ( is_callable( 'FrmProAppHelper::get_settings' ) ) {
3892 4367 $settings = FrmProAppHelper::get_settings();
4368 +
3893 4369 if ( is_object( $settings ) && ! empty( $settings->menu_icon ) ) {
3894 4370 return $settings->menu_icon;
3895 4371 }
3896 4372 }
4373 +
3897 4374 return 'frmfont frm_logo_icon';
3898 4375 }
3899 4376
3900 4377 /**
@@ -3912,10 +4389,9 @@
3912 4389 * @type array $input_attrs Attributes of value input.
3913 4390 * }
3914 4391 */
3915 4392 public static function images_dropdown( $args ) {
3916 - $args = self::fill_default_images_dropdown_args( $args );
3917 -
4393 + $args = self::fill_default_images_dropdown_args( $args );
3918 4394 $input_attrs_str = self::get_images_dropdown_input_attrs( $args );
3919 4395 ob_start();
3920 4396 include self::plugin_path() . '/classes/views/shared/images-dropdown.php';
3921 4397 $output = ob_get_clean();
@@ -3936,8 +4412,9 @@
3936 4412 *
3937 4413 * @since 5.0.04
3938 4414 *
3939 4415 * @param array $args The arguments.
4416 + *
3940 4417 * @return array
3941 4418 */
3942 4419 private static function fill_default_images_dropdown_args( $args ) {
3943 4420 $defaults = array(
@@ -3967,8 +4444,9 @@
3967 4444 *
3968 4445 * @since 5.0.04
3969 4446 *
3970 4447 * @param array $args The arguments.
4448 + *
3971 4449 * @return string
3972 4450 */
3973 4451 private static function get_images_dropdown_input_attrs( $args ) {
3974 4452 $input_attrs = $args['input_attrs'];
@@ -3975,8 +4453,9 @@
3975 4453 $input_attrs['type'] = 'radio';
3976 4454 $input_attrs['name'] = $args['name'];
3977 4455
3978 4456 $input_attrs_str = '';
4457 +
3979 4458 foreach ( $input_attrs as $key => $input_attr ) {
3980 4459 $input_attrs_str .= ' ' . sprintf( '%s="%s"', esc_attr( $key ), esc_attr( $input_attr ) );
3981 4460 }
3982 4461
@@ -3992,8 +4471,13 @@
3992 4471 }
3993 4472
3994 4473 /**
3995 4474 * @since 6.7.1
4475 + *
4476 + * @param array $option Option data.
4477 + * @param array $args The arguments of images_dropdown() method.
4478 + *
4479 + * @return array
3996 4480 */
3997 4481 public static function get_images_dropdown_atts( $option, $args ) {
3998 4482 $image = self::get_images_dropdown_option_image( $option, $args );
3999 4483 $classes = self::get_images_dropdown_option_classes( $option, $args );
@@ -4007,8 +4491,9 @@
4007 4491 * @since 5.0.04
4008 4492 *
4009 4493 * @param array $option Option data.
4010 4494 * @param array $args The arguments of images_dropdown() method.
4495 + *
4011 4496 * @return string
4012 4497 */
4013 4498 private static function get_images_dropdown_option_image( $option, $args ) {
4014 4499 $image = self::icon_by_class(
@@ -4037,8 +4522,9 @@
4037 4522 * @since 5.0.04
4038 4523 *
4039 4524 * @param array $option Option data.
4040 4525 * @param array $args The arguments of images_dropdown() method.
4526 + *
4041 4527 * @return string
4042 4528 */
4043 4529 private static function get_images_dropdown_option_classes( $option, $args ) {
4044 4530 $classes = '';
@@ -4066,17 +4552,19 @@
4066 4552 * @since 5.0.04
4067 4553 *
4068 4554 * @param array $option Option data.
4069 4555 * @param array $args The arguments of images_dropdown() method.
4556 + *
4070 4557 * @return string
4071 4558 */
4072 4559 private static function get_images_dropdown_option_html_attrs( $option, $args ) {
4073 4560 $html_attrs = '';
4561 +
4074 4562 if ( ! empty( $option['custom_attrs'] ) && is_array( $option['custom_attrs'] ) ) {
4075 4563 $html_attrs_arr = array();
4076 4564
4077 4565 foreach ( $option['custom_attrs'] as $key => $value ) {
4078 - if ( in_array( $key, array( 'type', 'class', 'data-value' ) ) ) {
4566 + if ( in_array( $key, array( 'type', 'class', 'data-value' ), true ) ) {
4079 4567 continue;
4080 4568 }
4081 4569
4082 4570 $html_attrs_arr[] = sprintf( '%s="%s"', esc_attr( $key ), esc_attr( $value ) );
@@ -4134,14 +4622,13 @@
4134 4622 * @since 5.0.07
4135 4623 *
4136 4624 * @param array $values
4137 4625 * @param array $keys
4626 + *
4138 4627 * @return array
4139 4628 */
4140 4629 public static function maybe_filter_array( $values, $keys ) {
4141 - $allow_unfiltered_html = self::allow_unfiltered_html();
4142 -
4143 - if ( $allow_unfiltered_html ) {
4630 + if ( self::allow_unfiltered_html() ) {
4144 4631 return $values;
4145 4632 }
4146 4633
4147 4634 foreach ( $keys as $key ) {
@@ -4160,13 +4647,14 @@
4160 4647 * @since 5.0.13
4161 4648 *
4162 4649 * @param string $value
4163 4650 * @param array|string $allowed 'all' for everything included as defaults.
4651 + *
4164 4652 * @return string
4165 4653 */
4166 4654 public static function maybe_kses( $value, $allowed = 'all' ) {
4167 4655 if ( self::should_never_allow_unfiltered_html() ) {
4168 - $value = self::kses( $value, $allowed );
4656 + return self::kses( $value, $allowed );
4169 4657 }
4170 4658 return $value;
4171 4659 }
4172 4660
@@ -4176,17 +4664,18 @@
4176 4664 * @since 6.11.2
4177 4665 *
4178 4666 * @param string $key
4179 4667 * @param string $context Either 'display' or 'update'. On update, we want to allow a few keys that are never displayed.
4668 + *
4180 4669 * @return bool
4181 4670 */
4182 4671 public static function input_key_is_safe( $key, $context = 'display' ) {
4183 4672 if ( 'update' === $context && in_array( $key, array( 'opt', 'label' ), true ) ) {
4184 4673 $safe = true;
4185 - } elseif ( 0 === strpos( $key, 'data-' ) ) {
4674 + } elseif ( str_starts_with( $key, 'data-' ) ) {
4186 4675 // Allow all data attributes.
4187 4676 $safe = true;
4188 - } elseif ( 0 === strpos( $key, 'aria-' ) ) {
4677 + } elseif ( str_starts_with( $key, 'aria-' ) ) {
4189 4678 // Allow all aria attributes.
4190 4679 $safe = true;
4191 4680 } else {
4192 4681 $safe_keys = array(
@@ -4228,16 +4717,9 @@
4228 4717
4229 4718 /**
4230 4719 * @since 5.0.16
4231 4720 *
4232 - * @return bool
4233 - */
4234 - public static function show_landing_pages() {
4235 - return self::show_new_feature( 'landing' );
4236 - }
4237 -
4238 - /**
4239 - * @since 5.0.16
4721 + * @param string $medium
4240 4722 *
4241 4723 * @return array
4242 4724 */
4243 4725 public static function get_landing_page_upgrade_data_params( $medium = 'landing' ) {
@@ -4245,8 +4727,9 @@
4245 4727 'medium' => $medium,
4246 4728 'upgrade' => __( 'Form Landing Pages', 'formidable' ),
4247 4729 'message' => __( 'Easily manage a landing page for your form. Upgrade to get form landing pages.', 'formidable' ),
4248 4730 'screenshot' => 'landing.png',
4731 + 'learn-more' => self::get_doc_url( 'landing-page-forms', 'form-landing-page-settings', false ),
4249 4732 );
4250 4733 return self::get_upgrade_data_params( 'landing', $params );
4251 4734 }
4252 4735
@@ -4253,8 +4736,9 @@
4253 4736 /**
4254 4737 * @since 5.0.17
4255 4738 *
4256 4739 * @param string $feature
4740 + *
4257 4741 * @return bool
4258 4742 */
4259 4743 public static function show_new_feature( $feature ) {
4260 4744 $link = FrmAddonsController::install_link( $feature );
@@ -4268,12 +4752,14 @@
4268 4752 *
4269 4753 * @param string $plugin The plugin slug to get installation data for.
4270 4754 * @param array $params Initial parameters for the upgrade data.
4271 4755 * @param bool $detailed Whether to include detailed information.
4756 + *
4272 4757 * @return array Modified parameters with installation data.
4273 4758 */
4274 4759 public static function get_upgrade_data_params( $plugin, $params, $detailed = false ) {
4275 4760 $link = FrmAddonsController::install_link( $plugin );
4761 +
4276 4762 if ( ! $link ) {
4277 4763 return $params;
4278 4764 }
4279 4765
@@ -4279,8 +4765,9 @@
4279 4765
4280 4766 if ( ! empty( $link['url'] ) && self::pro_is_installed() ) {
4281 4767 $params['oneclick'] = json_encode( $link );
4282 4768 unset( $params['message'] );
4769 +
4283 4770 if ( ! isset( $params['medium'] ) ) {
4284 4771 $params['medium'] = $plugin;
4285 4772 }
4286 4773 } else {
@@ -4300,8 +4787,9 @@
4300 4787 *
4301 4788 * @since 5.0.17
4302 4789 *
4303 4790 * @param string $text
4791 + *
4304 4792 * @return bool
4305 4793 */
4306 4794 public static function ctype_xdigit( $text ) {
4307 4795 if ( function_exists( 'ctype_xdigit' ) ) {
@@ -4316,12 +4804,14 @@
4316 4804 * @since 5.2.01
4317 4805 */
4318 4806 public static function set_current_screen_and_hook_suffix() {
4319 4807 global $hook_suffix;
4808 +
4320 4809 if ( is_null( $hook_suffix ) ) {
4321 4810 // $hook_suffix gets used in substr so make sure it's not null. PHP 8.1 deprecates null in substr.
4322 4811 $hook_suffix = ''; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
4323 4812 }
4813 +
4324 4814 set_current_screen();
4325 4815 }
4326 4816
4327 4817 /**
@@ -4328,9 +4818,9 @@
4328 4818 * Shows pill text.
4329 4819 *
4330 4820 * @since 5.2.02
4331 4821 *
4332 - * @param string $text Text in the pill. Default is NEW.
4822 + * @param string|null $text Text in the pill. Default is NEW.
4333 4823 */
4334 4824 public static function show_pill_text( $text = null ) {
4335 4825 if ( null === $text ) {
4336 4826 $text = __( 'NEW', 'formidable' );
@@ -4343,8 +4833,9 @@
4343 4833 *
4344 4834 * @since 5.2.07
4345 4835 *
4346 4836 * @param mixed $num Number.
4837 + *
4347 4838 * @return false|int Returns `false` if the passed parameter is not number.
4348 4839 */
4349 4840 public static function count_decimals( $num ) {
4350 4841 if ( ! is_numeric( $num ) ) {
@@ -4352,8 +4843,9 @@
4352 4843 }
4353 4844
4354 4845 $num = (string) $num;
4355 4846 $parts = explode( '.', $num );
4847 +
4356 4848 if ( 1 === count( $parts ) ) {
4357 4849 return 0;
4358 4850 }
4359 4851
@@ -4435,17 +4927,20 @@
4435 4927 * @since 5.5.5
4436 4928 *
4437 4929 * @param string $url
4438 4930 * @param string $expected_extension
4931 + *
4439 4932 * @return bool
4440 4933 */
4441 4934 public static function validate_url_is_in_s3_bucket( $url, $expected_extension ) {
4442 - $file_is_in_expected_s3_bucket = 0 === strpos( $url, 'https://s3.amazonaws.com/fp.strategy11.com' );
4935 + $file_is_in_expected_s3_bucket = str_starts_with( $url, 'https://s3.amazonaws.com/fp.strategy11.com' );
4936 +
4443 4937 if ( ! $file_is_in_expected_s3_bucket ) {
4444 4938 return false;
4445 4939 }
4446 4940
4447 4941 $parsed = parse_url( $url );
4942 +
4448 4943 if ( ! is_array( $parsed ) ) {
4449 4944 // URL is malformed.
4450 4945 return false;
4451 4946 }
@@ -4451,14 +4946,10 @@
4451 4946 }
4452 4947
4453 4948 $path = $parsed['path'];
4454 4949 $ext = pathinfo( $path, PATHINFO_EXTENSION );
4455 - if ( $expected_extension !== $ext ) {
4456 - // The URL isn't to an XML file.
4457 - return false;
4458 - }
4459 -
4460 - return true;
4950 + // The URL isn't to an XML file.
4951 + return $expected_extension === $ext;
4461 4952 }
4462 4953
4463 4954 /**
4464 4955 * Display a dismissable warning message and save its dismissal state.
@@ -4466,8 +4957,9 @@
4466 4957 * @since 6.3
4467 4958 *
4468 4959 * @param string $message The warning message to display.
4469 4960 * @param string $option The unique identifier for the dismissal state of the message and the WP Ajax action.
4961 + *
4470 4962 * @return void
4471 4963 */
4472 4964 public static function add_dismissable_warning_message( $message = '', $option = '' ) {
4473 4965 if ( ! $message || ! $option ) {
@@ -4510,8 +5002,9 @@
4510 5002 *
4511 5003 * @since 6.3
4512 5004 *
4513 5005 * @param string $option The unique identifier for the dismissal state of the message.
5006 + *
4514 5007 * @return void
4515 5008 */
4516 5009 public static function dismiss_warning_message( $option = '' ) {
4517 5010 self::permission_check( 'frm_change_settings' );
@@ -4517,9 +5010,9 @@
4517 5010 self::permission_check( 'frm_change_settings' );
4518 5011 check_ajax_referer( 'frm_ajax', 'nonce' );
4519 5012
4520 5013 if ( $option ) {
4521 - update_option( $option, true, 'no' );
5014 + update_option( $option, true, false );
4522 5015 }
4523 5016
4524 5017 wp_send_json_success();
4525 5018 }
@@ -4533,17 +5026,8 @@
4533 5026 * @return string
4534 5027 */
4535 5028 public static function copy_for_lite_license() {
4536 5029 $message = __( 'You\'re using Formidable Forms Lite - no license needed. Enjoy!', 'formidable' ) . ' 🙂';
4537 -
4538 - if ( is_callable( 'FrmProAddonsController::get_readable_license_type' ) && ! class_exists( 'FrmProDashboardController' ) ) {
4539 - // Manage PRO versions without PRO dashboard functionality.
4540 - $license_type = FrmProAddonsController::get_readable_license_type();
4541 - if ( 'lite' !== strtolower( $license_type ) ) {
4542 - $message = 'Formidable Pro ' . $license_type;
4543 - }
4544 - }
4545 -
4546 5030 return apply_filters( 'frm_license_type_text', $message );
4547 5031 }
4548 5032
4549 5033 /**
@@ -4549,8 +5033,9 @@
4549 5033 /**
4550 5034 * Removes scripts that are unnecessarily loaded across the pages!
4551 5035 *
4552 5036 * @since 6.9
5037 + *
4553 5038 * @return void
4554 5039 */
4555 5040 public static function dequeue_extra_global_scripts() {
4556 5041 wp_dequeue_script( 'frm-surveys-admin' );
@@ -4568,18 +5053,21 @@
4568 5053 * @return void
4569 5054 */
4570 5055 public static function tooltip_icon( $tooltip_text, $atts = array() ) {
4571 5056 $atts['title'] = $tooltip_text;
5057 +
4572 5058 if ( isset( $atts['class'] ) ) {
4573 5059 $atts['class'] .= ' frm_help';
4574 5060 } else {
4575 5061 $atts['class'] = 'frm_help';
4576 5062 }
5063 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
4577 5064 ?>
4578 5065 <span <?php self::array_to_html_params( $atts, true ); ?>>
4579 5066 <?php self::icon_by_class( 'frmfont frm_tooltip_icon' ); ?>
4580 5067 </span>
4581 5068 <?php
5069 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
4582 5070 }
4583 5071
4584 5072 /**
4585 5073 * Prints errors for settings in onboarding wizard or template settings.
@@ -4600,8 +5088,10 @@
4600 5088 )
4601 5089 );
4602 5090
4603 5091 $args['class'] .= ' frm-validation-error frm-mt-xs frm_hidden';
5092 +
5093 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
4604 5094 ?>
4605 5095 <span id="<?php echo esc_attr( $args['id'] ); ?>" class="<?php echo esc_attr( $args['class'] ); ?>">
4606 5096 <?php
4607 5097 if ( is_array( $args['errors'] ) ) {
@@ -4615,8 +5105,9 @@
4615 5105 }
4616 5106 ?>
4617 5107 </span>
4618 5108 <?php
5109 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
4619 5110 }
4620 5111
4621 5112 /**
4622 5113 * Check if GDPR is enabled.
@@ -4646,19 +5137,55 @@
4646 5137 * Check if a string is valid UTF-8.
4647 5138 *
4648 5139 * @since 6.24
4649 5140 *
4650 - * @param string $string The string to check.
5141 + * @param string|null $string The string to check.
5142 + *
4651 5143 * @return bool
4652 5144 */
4653 5145 public static function is_valid_utf8( $string ) {
5146 + if ( is_null( $string ) ) {
5147 + // Return true so we do not attempt to change encoding on a null value.
5148 + return true;
5149 + }
5150 +
4654 5151 // wp_is_valid_utf8 is added in WP 6.9.
4655 5152 if ( function_exists( 'wp_is_valid_utf8' ) ) {
4656 5153 return wp_is_valid_utf8( $string );
4657 5154 }
5155 +
4658 5156 // As of WP 6.9, seems_utf8 is deprecated.
4659 - if ( function_exists( 'seems_utf8' ) ) {
4660 - return seems_utf8( $string );
5157 + return function_exists( 'seems_utf8' ) ? seems_utf8( $string ) : false;
5158 + }
5159 +
5160 + /**
5161 + * Get a documentation URL with UTM parameters and affiliate tracking.
5162 + *
5163 + * @since 6.26
5164 + *
5165 + * @param string $path The relative path to append to the base URL.
5166 + * @param string $campaign The campaign to use for UTM parameters.
5167 + * @param bool $add_kb_base Whether to prepend 'knowledgebase/' to the path. Default true.
5168 + *
5169 + * @return string The processed URL with UTM parameters and affiliate tracking.
5170 + */
5171 + public static function get_doc_url( $path, $campaign, $add_kb_base = true ) {
5172 + $path = trim( $path, '/' );
5173 +
5174 + if ( $add_kb_base ) {
5175 + $path = 'knowledgebase/' . $path;
4661 5176 }
4662 - return false;
5177 +
5178 + return self::maybe_add_missing_utm( 'https://formidableforms.com/' . $path, array( 'campaign' => $campaign ) );
5179 + }
5180 +
5181 + /**
5182 + * @since 5.0.16
5183 + * @deprecated 6.26
5184 + *
5185 + * @return bool
5186 + */
5187 + public static function show_landing_pages() {
5188 + _deprecated_function( __METHOD__, '6.26' );
5189 + return true;
4663 5190 }
4664 5191 }