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 +1012 -438 6.25 → 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';
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
@@ -114,55 +117,124 @@
114 117 }
115 118
116 119 /**
117 120 * @since 3.04.02
118 - * @param array|string $args
121 + *
122 + * @param array|string $args If a string is passed, it is used for the utm_campaign attribute.
119 123 * @param string $page
120 124 */
121 125 public static function admin_upgrade_link( $args, $page = '' ) {
122 - if ( empty( $page ) ) {
123 - $page = 'https://formidableforms.com/lite-upgrade/';
124 - } else {
126 + if ( $page ) {
125 127 $page = str_replace( 'https://formidableforms.com/', '', $page );
126 128 $page = 'https://formidableforms.com/' . $page;
129 + } else {
130 + $page = 'https://formidableforms.com/lite-upgrade/';
127 131 }
128 132
129 - $anchor = '';
130 - if ( is_array( $args ) ) {
131 - $medium = $args['medium'] ?? '';
132 - if ( isset( $args['content'] ) ) {
133 - $content = $args['content'];
134 - }
135 - if ( isset( $args['anchor'] ) ) {
136 - $anchor = '#' . $args['anchor'];
137 - }
138 - } else {
139 - $medium = $args;
140 - }
133 + $args = is_array( $args ) ? self::adjust_legacy_utm_args( $args ) : array( 'campaign' => $args );
141 134
142 135 $query_args = array(
143 - 'utm_source' => 'WordPress',
144 - 'utm_medium' => $medium,
145 - 'utm_campaign' => 'liteplugin',
136 + 'utm_source' => 'plugin',
137 + 'utm_medium' => self::get_utm_medium(),
146 138 );
139 + $query_args = self::maybe_add_utm_license( $query_args );
147 140
148 - if ( isset( $content ) ) {
149 - $query_args['utm_content'] = $content;
141 + if ( isset( $args['campaign'] ) ) {
142 + $query_args['utm_campaign'] = $args['campaign'];
150 143 }
151 144
152 - if ( is_array( $args ) && isset( $args['param'] ) ) {
145 + if ( isset( $args['content'] ) ) {
146 + $query_args['utm_content'] = $args['content'];
147 + }
148 +
149 + if ( isset( $args['param'] ) ) {
153 150 $query_args['f'] = $args['param'];
154 151 }
155 152
156 - if ( is_array( $args ) && ! empty( $args['plan'] ) ) {
153 + if ( ! empty( $args['plan'] ) ) {
157 154 $query_args['plan'] = $args['plan'];
158 155 }
159 156
160 - $link = add_query_arg( $query_args, $page ) . $anchor;
157 + $link = add_query_arg( $query_args, $page );
158 +
159 + if ( isset( $args['anchor'] ) ) {
160 + $link .= '#' . $args['anchor'];
161 + }
162 +
161 163 return self::make_affiliate_url( $link );
162 164 }
163 165
164 166 /**
167 + * If medium is "pro", add an additional utm_license param with their active license type.
168 + *
169 + * @since 6.25.1
170 + *
171 + * @param array $query_args
172 + * @param string $link
173 + *
174 + * @return array
175 + */
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' ) ) {
180 + $query_args['utm_license'] = strtolower( FrmProAddonsController::get_readable_license_type() );
181 + }
182 +
183 + return $query_args;
184 + }
185 +
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 + /**
210 + * @since 6.25.1
211 + *
212 + * @return string
213 + */
214 + private static function get_utm_medium() {
215 + return self::pro_is_connected() ? 'pro' : 'lite';
216 + }
217 +
218 + /**
219 + * Change campaign from "liteplugin" to what we're currently using for medium.
220 + *
221 + * @since 6.25.1
222 + *
223 + * @param array $args
224 + *
225 + * @return array
226 + */
227 + private static function adjust_legacy_utm_args( $args ) {
228 + if ( isset( $args['medium'] ) ) {
229 + $args['campaign'] = $args['medium'];
230 + unset( $args['medium'] );
231 + }
232 +
233 + return $args;
234 + }
235 +
236 + /**
165 237 * @since 6.21
166 238 *
167 239 * @param string $cta_link
168 240 * @param array $utm
@@ -167,26 +239,29 @@
167 239 * @param string $cta_link
168 240 * @param array $utm
169 241 */
170 242 public static function maybe_add_missing_utm( $cta_link, $utm ) {
243 + $utm = self::adjust_legacy_utm_args( $utm );
171 244 $query_args = array();
172 245
173 - if ( false === strpos( $cta_link, 'utm_source' ) ) {
174 - $query_args['utm_source'] = 'WordPress';
246 + if ( ! str_contains( $cta_link, 'utm_source' ) ) {
247 + $query_args['utm_source'] = 'plugin';
175 248 }
176 249
177 - if ( false === strpos( $cta_link, 'utm_campaign' ) ) {
178 - $query_args['utm_campaign'] = 'liteplugin';
250 + if ( ! str_contains( $cta_link, 'utm_medium' ) ) {
251 + $query_args['utm_medium'] = self::get_utm_medium();
179 252 }
180 253
181 - if ( false === strpos( $cta_link, 'utm_medium' ) && isset( $utm['medium'] ) ) {
182 - $query_args['utm_medium'] = $utm['medium'];
254 + if ( ! str_contains( $cta_link, 'utm_campaign' ) && isset( $utm['campaign'] ) ) {
255 + $query_args['utm_campaign'] = $utm['campaign'];
183 256 }
184 257
185 - if ( false === strpos( $cta_link, 'utm_content' ) && isset( $utm['content'] ) ) {
258 + if ( ! str_contains( $cta_link, 'utm_content' ) && isset( $utm['content'] ) ) {
186 259 $query_args['utm_content'] = $utm['content'];
187 260 }
188 261
262 + $query_args = self::maybe_add_utm_license( $query_args, $cta_link );
263 +
189 264 return $query_args ? add_query_arg( $query_args, $cta_link ) : $cta_link;
190 265 }
191 266
192 267 /**
@@ -194,13 +269,15 @@
194 269 *
195 270 * @since 2.0
196 271 *
197 272 * @param array $args - May include the form id when values need translation.
198 - * @return FrmSettings $frm_settings
273 + *
274 + * @return FrmSettings
199 275 */
200 276 public static function get_settings( $args = array() ) {
201 277 global $frm_settings;
202 - if ( empty( $frm_settings ) ) {
278 +
279 + if ( ! $frm_settings ) {
203 280 $frm_settings = new FrmSettings( $args );
204 281 } elseif ( isset( $args['current_form'] ) ) {
205 282 // If the global has already been set, allow strings to be filtered.
206 283 $frm_settings->maybe_filter_for_form( $args );
@@ -212,11 +289,13 @@
212 289 /**
213 290 * @return string
214 291 */
215 292 public static function get_menu_name() {
216 - $frm_settings = self::get_settings();
293 + if ( ! self::pro_is_installed() || FrmAddonsController::is_license_expired() ) {
294 + return 'Formidable';
295 + }
217 296
218 - return FrmAddonsController::is_license_expired() || ! self::pro_is_installed() ? 'Formidable' : $frm_settings->menu;
297 + return self::get_settings()->menu;
219 298 }
220 299
221 300 /**
222 301 * Determine if the current branding is set to 'formidable'.
@@ -230,10 +309,9 @@
230 309 if ( ! self::pro_is_installed() ) {
231 310 return true;
232 311 }
233 312
234 - $menu_icon = self::get_menu_icon_class();
235 - return strpos( $menu_icon, 'frm_logo_icon' ) !== false;
313 + return str_contains( self::get_menu_icon_class(), 'frm_logo_icon' );
236 314 }
237 315
238 316 /**
239 317 * @since 3.05
@@ -238,8 +316,9 @@
238 316 /**
239 317 * @since 3.05
240 318 *
241 319 * @param array $atts
320 + *
242 321 * @return string
243 322 */
244 323 public static function svg_logo( $atts = array() ) {
245 324 $defaults = array(
@@ -249,12 +328,14 @@
249 328 'orange' => '#f05a24',
250 329 );
251 330 $atts = array_merge( $defaults, $atts );
252 331
332 + // phpcs:disable SlevomatCodingStandard.Files.LineLength.LineTooLong
253 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'] ) . '">
254 334 <path fill="' . esc_attr( $atts['orange'] ) . '" d="M289.6 384h140v76h-140z"/>
255 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"/>
256 336 </svg>';
337 + // phpcs:enable SlevomatCodingStandard.Files.LineLength.LineTooLong
257 338 }
258 339
259 340 /**
260 341 * @since 4.0
@@ -259,12 +340,13 @@
259 340 /**
260 341 * @since 4.0
261 342 *
262 343 * @param array $atts
344 + *
263 345 * @return void
264 346 */
265 347 public static function show_logo( $atts = array() ) {
266 - echo self::kses( self::svg_logo( $atts ), 'all' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
348 + self::kses_echo( self::svg_logo( $atts ), 'all' );
267 349 }
268 350
269 351 /**
270 352 * @since 4.03.02
@@ -279,10 +361,11 @@
279 361 )
280 362 );
281 363
282 364 $new_icon = apply_filters( 'frm_icon', $icon, true );
365 +
283 366 if ( $new_icon !== $icon ) {
284 - if ( strpos( $new_icon, '<svg' ) === 0 ) {
367 + if ( str_starts_with( $new_icon, '<svg' ) ) {
285 368 $icon = str_replace( 'viewBox="0 0 20', 'width="30" height="35" style="color:#929699" viewBox="0 0 20', $new_icon );
286 369 } else {
287 370 // Show nothing if it isn't an SVG.
288 371 $icon = '<div style="height:39px"></div>';
@@ -287,9 +370,9 @@
287 370 // Show nothing if it isn't an SVG.
288 371 $icon = '<div style="height:39px"></div>';
289 372 }
290 373 }
291 - echo self::kses( $icon, 'all' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
374 + self::kses_echo( $icon, 'all' );
292 375 }
293 376
294 377 /**
295 378 * @since 2.02.04
@@ -339,11 +422,13 @@
339 422 */
340 423 public static function is_form_builder_page( $check_for_settings = true ) {
341 424 $action = self::simple_get( 'frm_action', 'sanitize_title' );
342 425 $check_actions = array( 'edit', 'duplicate' );
426 +
343 427 if ( $check_for_settings ) {
344 428 $check_actions[] = 'settings';
345 429 }
430 +
346 431 return self::is_admin_page( 'formidable' ) && in_array( $action, $check_actions, true );
347 432 }
348 433
349 434 /**
@@ -349,15 +434,15 @@
349 434 /**
350 435 * @return bool
351 436 */
352 437 public static function is_formidable_admin() {
353 - $page = self::simple_get( 'page', 'sanitize_title' );
354 - $is_formidable = strpos( $page, 'formidable' ) !== false;
355 - if ( empty( $page ) ) {
356 - $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();
357 442 }
358 443
359 - return $is_formidable;
444 + return str_contains( $page, 'formidable' );
360 445 }
361 446
362 447 /**
363 448 * Checks if is a list page.
@@ -364,8 +449,9 @@
364 449 *
365 450 * @since 6.19
366 451 *
367 452 * @param string $page The name of the page to check.
453 + *
368 454 * @return bool
369 455 */
370 456 public static function is_admin_list_page( $page = 'formidable' ) {
371 457 if ( 'formidable' === $page ) {
@@ -377,8 +463,9 @@
377 463 }
378 464
379 465 if ( 'formidable-entries' === $page ) {
380 466 $action = self::simple_get( 'frm_action' );
467 +
381 468 if ( ! $action || in_array( $action, self::get_entries_listing_page_form_actions(), true ) ) {
382 469 return true;
383 470 }
384 471 }
@@ -407,11 +494,13 @@
407 494 */
408 495 public static function is_admin_page( $page = 'formidable' ) {
409 496 global $pagenow;
410 497 $get_page = self::simple_get( 'page', 'sanitize_title' );
498 +
411 499 if ( $pagenow ) {
412 - // 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
413 501 $is_page = ( $pagenow === 'admin.php' || $pagenow === 'admin-ajax.php' ) && $get_page === $page;
502 +
414 503 if ( $is_page ) {
415 504 return true;
416 505 }
417 506 }
@@ -429,15 +518,15 @@
429 518 */
430 519 public static function is_view_builder_page() {
431 520 global $pagenow;
432 521
433 - 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 ) ) {
434 523 return false;
435 524 }
436 525
437 526 $post_type = self::simple_get( 'post_type', 'sanitize_title' );
438 527
439 - if ( empty( $post_type ) ) {
528 + if ( ! $post_type ) {
440 529 $post_id = self::simple_get( 'post', 'absint' );
441 530 $post = get_post( $post_id );
442 531 $post_type = $post ? $post->post_type : '';
443 532 }
@@ -455,9 +544,9 @@
455 544 public static function is_preview_page() {
456 545 global $pagenow;
457 546 $action = self::simple_get( 'action', 'sanitize_title' );
458 547
459 - return $pagenow && $pagenow === 'admin-ajax.php' && $action === 'frm_forms_preview';
548 + return $pagenow === 'admin-ajax.php' && $action === 'frm_forms_preview';
460 549 }
461 550
462 551 /**
463 552 * Check for ajax except the form preview page
@@ -478,8 +567,9 @@
478 567 }
479 568
480 569 /**
481 570 * @since 2.0.8
571 + *
482 572 * @return bool
483 573 */
484 574 public static function prevent_caching() {
485 575 global $frm_vars;
@@ -497,8 +587,9 @@
497 587 $is_admin = is_admin() && ! wp_doing_ajax();
498 588
499 589 /**
500 590 * @since 6.0
591 + *
501 592 * @param bool $is_admin
502 593 */
503 594 return apply_filters( 'frm_is_admin', $is_admin );
504 595 }
@@ -513,14 +604,15 @@
513 604 *
514 605 * @return bool
515 606 */
516 607 public static function is_empty_value( $value, $empty = '' ) {
517 - return ( is_array( $value ) && empty( $value ) ) || $value === $empty;
608 + return $value === array() || $value === $empty;
518 609 }
519 610
520 611 /**
521 612 * @param mixed $value
522 613 * @param string $empty
614 + *
523 615 * @return bool
524 616 */
525 617 public static function is_not_empty_value( $value, $empty = '' ) {
526 618 return ! self::is_empty_value( $value, $empty );
@@ -574,8 +666,9 @@
574 666 continue;
575 667 }
576 668
577 669 $key = self::get_server_value( $key );
670 +
578 671 foreach ( explode( ',', $key ) as $ip ) {
579 672 // Just to be safe.
580 673 $ip = trim( $ip );
581 674
@@ -631,16 +724,26 @@
631 724 */
632 725 return apply_filters( 'frm_use_custom_header_ip', $should_use_custom_header_ip );
633 726 }
634 727
728 + /**
729 + * @param string $param
730 + * @param mixed $default
731 + * @param string $src
732 + * @param callable|string $sanitize
733 + *
734 + * @return mixed
735 + */
635 736 public static function get_param( $param, $default = '', $src = 'get', $sanitize = '' ) {
636 - if ( strpos( $param, '[' ) ) {
737 + if ( str_contains( $param, '[' ) ) {
637 738 $params = explode( '[', $param );
638 739 $param = $params[0];
639 740 }
640 741
641 742 if ( $src === 'get' ) {
642 - $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 +
643 746 if ( ! isset( $_POST[ $param ] ) && isset( $_GET[ $param ] ) && ! is_array( $value ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
644 747 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
645 748 $value = htmlspecialchars_decode( wp_unslash( $_GET[ $param ] ) );
646 749 }
@@ -655,17 +758,19 @@
655 758 )
656 759 );
657 760 }
658 761
659 - if ( isset( $params ) && is_array( $value ) && ! empty( $value ) ) {
660 - foreach ( $params as $k => $p ) {
661 - if ( ! $k || ! is_array( $value ) ) {
662 - continue;
663 - }
762 + if ( ! isset( $params ) || ! is_array( $value ) || ! $value ) {
763 + return $value;
764 + }
664 765
665 - $p = trim( $p, ']' );
666 - $value = $value[ $p ] ?? $default;
766 + foreach ( $params as $k => $p ) {
767 + if ( ! $k || ! is_array( $value ) ) {
768 + continue;
667 769 }
770 +
771 + $p = trim( $p, ']' );
772 + $value = $value[ $p ] ?? $default;
668 773 }
669 774
670 775 return $value;
671 776 }
@@ -676,8 +781,9 @@
676 781 * @param string $param The key we are trying to access data from in $_POST.
677 782 * @param mixed $default The default if nothing is being sent.
678 783 * @param callable|string $sanitize Make sure to pass a sanitize method here. This function will NOT sanitize by default.
679 784 * @param bool $serialized
785 + *
680 786 * @return mixed
681 787 */
682 788 public static function get_post_param( $param, $default = '', $sanitize = '', $serialized = false ) {
683 789 return self::get_simple_request(
@@ -697,9 +803,9 @@
697 803 * @param string $param
698 804 * @param string $sanitize
699 805 * @param string $default
700 806 *
701 - * @return array|string
807 + * @return array|int|string
702 808 */
703 809 public static function simple_get( $param, $sanitize = 'sanitize_text_field', $default = '' ) {
704 810 return self::get_simple_request(
705 811 array(
@@ -728,10 +834,10 @@
728 834 'sanitize' => 'sanitize_text_field',
729 835 'serialized' => false,
730 836 );
731 837 $args = wp_parse_args( $args, $defaults );
838 + $value = $args['default'];
732 839
733 - $value = $args['default'];
734 840 if ( $args['type'] === 'get' ) {
735 841 if ( $_GET && isset( $_GET[ $args['param'] ] ) ) {
736 842 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing
737 843 $value = wp_unslash( $_GET[ $args['param'] ] );
@@ -739,16 +845,16 @@
739 845 } elseif ( $args['type'] === 'post' ) {
740 846 if ( isset( $_POST[ $args['param'] ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
741 847 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing
742 848 $value = wp_unslash( $_POST[ $args['param'] ] );
849 +
743 850 if ( $args['serialized'] === true && is_serialized_string( $value ) && is_serialized( $value ) ) {
744 851 self::unserialize_or_decode( $value );
745 852 }
746 853 }
747 854 } elseif ( isset( $_REQUEST[ $args['param'] ] ) ) {
748 - // phpcs:ignore WordPress.Security.NonceVerification.Missing
749 - // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
750 - $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'] ] );
751 857 }
752 858
753 859 self::sanitize_value( $args['sanitize'], $value );
754 860
@@ -761,17 +867,13 @@
761 867 * @since 2.0.8
762 868 *
763 869 * @param string $value
764 870 *
765 - * @return string $value
871 + * @return string Value.
766 872 */
767 873 public static function preserve_backslashes( $value ) {
768 874 // If backslashes have already been added, don't add them again
769 - if ( strpos( $value, '\\\\' ) === false ) {
770 - $value = addslashes( $value );
771 - }
772 -
773 - return $value;
875 + return str_contains( $value, '\\\\' ) ? $value : addslashes( $value );
774 876 }
775 877
776 878 /**
777 879 * Sanitize a value in-place.
@@ -778,8 +880,9 @@
778 880 * If $value is an array, the sanitize function will get called for each item.
779 881 *
780 882 * @param callable $sanitize
781 883 * @param mixed $value
884 + *
782 885 * @return void
783 886 */
784 887 public static function sanitize_value( $sanitize, &$value ) {
785 888 if ( ! $sanitize ) {
@@ -792,11 +895,13 @@
792 895 }
793 896
794 897 if ( is_array( $value ) ) {
795 898 $temp_values = $value;
899 +
796 900 foreach ( $temp_values as $k => $v ) {
797 901 self::sanitize_value( $sanitize, $value[ $k ] );
798 902 }
903 +
799 904 return;
800 905 }
801 906
802 907 $value = call_user_func( $sanitize, $value );
@@ -801,10 +906,17 @@
801 906
802 907 $value = call_user_func( $sanitize, $value );
803 908 }
804 909
910 + /**
911 + * @param array $sanitize_method
912 + * @param array $values
913 + *
914 + * @return void
915 + */
805 916 public static function sanitize_request( $sanitize_method, &$values ) {
806 917 $temp_values = $values;
918 +
807 919 foreach ( $temp_values as $k => $val ) {
808 920 if ( isset( $sanitize_method[ $k ] ) ) {
809 921 $values[ $k ] = call_user_func( $sanitize_method[ $k ], $val );
810 922 }
@@ -814,8 +926,9 @@
814 926 /**
815 927 * @since 4.0.04
816 928 *
817 929 * @param mixed $value
930 + *
818 931 * @return void
819 932 */
820 933 public static function sanitize_with_html( &$value ) {
821 934 if ( current_user_can( 'frm_edit_entries' ) || current_user_can( 'administrator' ) ) {
@@ -824,8 +937,9 @@
824 937 } else {
825 938 self::sanitize_value( self::class . '::strip_most_html', $value );
826 939 }
827 940 self::decode_specialchars( $value );
941 + self::sanitize_value( 'FrmHtmlSanitizer::sanitize_url_attributes', $value );
828 942 }
829 943
830 944 /**
831 945 * Allow only a small set of very basic HTML for unprivileged users.
@@ -834,8 +948,12 @@
834 948 *
835 949 * @param string $value
836 950 */
837 951 public static function strip_most_html( $value ) {
952 + if ( '' === $value ) {
953 + return $value;
954 + }
955 +
838 956 $allowed_html = array(
839 957 'b' => array(),
840 958 'br' => array(),
841 959 'strong' => array(),
@@ -860,12 +978,15 @@
860 978 * Do wp_specialchars_decode to get back '&' that wp_kses_post might have turned to '&amp;'
861 979 * this MUST be done, else we'll be back to the '& entity' problem.
862 980 *
863 981 * @since 4.0.04
982 + *
983 + * @param mixed $value Value to decode, passed by reference.
864 984 */
865 985 public static function decode_specialchars( &$value ) {
866 986 if ( is_array( $value ) ) {
867 987 $temp_values = $value;
988 +
868 989 foreach ( $temp_values as $k => $v ) {
869 990 self::decode_specialchars( $value[ $k ] );
870 991 }
871 992 } else {
@@ -879,13 +1000,13 @@
879 1000 * Adapted from wp_specialchars_decode().
880 1001 *
881 1002 * @since 4.03.01
882 1003 *
883 - * @param string $string The string to prep.
1004 + * @param string $string The string to prep, passed by reference.
884 1005 */
885 1006 private static function decode_amp( &$string ) {
886 1007 // Don't bother if there are no entities - saves a lot of processing
887 - if ( empty( $string ) || strpos( $string, '&' ) === false ) {
1008 + if ( ! $string || ! str_contains( $string, '&' ) ) {
888 1009 return;
889 1010 }
890 1011
891 1012 $translation = array(
@@ -923,17 +1044,15 @@
923 1044 * Sanitize the value, and allow some HTML
924 1045 *
925 1046 * @since 2.0
926 1047 *
927 - * @param string $value
928 - * @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.
929 1050 *
930 1051 * @return string
931 1052 */
932 1053 public static function kses( $value, $allowed = array() ) {
933 - $allowed_html = self::allowed_html( $allowed );
934 -
935 - return wp_kses( $value, $allowed_html );
1054 + return wp_kses( $value, self::allowed_html( $allowed ) );
936 1055 }
937 1056
938 1057 /**
939 1058 * Sanitizes and echoes a given value.
@@ -941,8 +1060,9 @@
941 1060 * @since 6.18
942 1061 *
943 1062 * @param string $value The value to sanitize and output.
944 1063 * @param array|string $allowed Allowed HTML tags and attributes.
1064 + *
945 1065 * @return void
946 1066 */
947 1067 public static function kses_echo( $value, $allowed = array() ) {
948 1068 echo self::kses( $value, $allowed ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
@@ -953,21 +1073,23 @@
953 1073 *
954 1074 * @since 5.0.13
955 1075 *
956 1076 * @param string $html
1077 + *
957 1078 * @return string
958 1079 */
959 1080 public static function kses_submit_button( $html ) {
960 - $included_button_action = false !== strpos( $html, '[button_action]' );
961 - $included_back_hook = false !== strpos( $html, '[back_hook]' );
962 - $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]' );
963 1084 add_filter( 'safe_style_css', 'FrmAppHelper::allow_visibility_style' );
964 1085 add_filter( 'frm_striphtml_allowed_tags', 'FrmAppHelper::add_allowed_submit_button_tags' );
965 1086 $html = self::kses( $html, 'all' );
966 1087 remove_filter( 'safe_style_css', 'FrmAppHelper::allow_visibility_style' );
967 1088 remove_filter( 'frm_striphtml_allowed_tags', 'FrmAppHelper::add_allowed_submit_button_tags' );
1089 +
968 1090 if ( $included_button_action ) {
969 - if ( false !== strpos( $html, '<input type="submit"' ) ) {
1091 + if ( str_contains( $html, '<input type="submit"' ) ) {
970 1092 $pattern = '/(<input type="submit")([^>]*)(\/>)/';
971 1093 $html = preg_replace( $pattern, '$1$2[button_action] $3', $html, 1 );
972 1094 } else {
973 1095 $pattern = '/(<button)(.*)(class=")(.*)(frm_button_submit)(.*)(")(.*)([^>]+)(>)/';
@@ -973,14 +1095,17 @@
973 1095 $pattern = '/(<button)(.*)(class=")(.*)(frm_button_submit)(.*)(")(.*)([^>]+)(>)/';
974 1096 $html = preg_replace( $pattern, '$1$2$3$4$5$6$7 [button_action]$8$9$10', $html, 1 );
975 1097 }
976 1098 }
1099 +
977 1100 if ( $included_back_hook ) {
978 1101 $html = str_replace( 'class="frm_prev_page"', 'class="frm_prev_page" [back_hook]', $html );
979 1102 }
1103 +
980 1104 if ( $included_draft_hook ) {
981 - $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 );
982 1106 }
1107 +
983 1108 return $html;
984 1109 }
985 1110
986 1111 /**
@@ -986,8 +1111,9 @@
986 1111 /**
987 1112 * @since 5.0.13
988 1113 *
989 1114 * @param array $allowed_attr
1115 + *
990 1116 * @return array
991 1117 */
992 1118 public static function allow_visibility_style( $allowed_attr ) {
993 1119 $allowed_attr[] = 'visibility';
@@ -997,8 +1123,9 @@
997 1123 /**
998 1124 * @since 5.0.13
999 1125 *
1000 1126 * @param array $allowed_html
1127 + *
1001 1128 * @return array
1002 1129 */
1003 1130 public static function add_allowed_submit_button_tags( $allowed_html ) {
1004 1131 $allowed_html['input'] = array(
@@ -1015,15 +1142,20 @@
1015 1142 }
1016 1143
1017 1144 /**
1018 1145 * @since 2.05.03
1146 + *
1147 + * @param array|string $allowed
1148 + *
1149 + * @return array
1019 1150 */
1020 1151 private static function allowed_html( $allowed ) {
1021 1152 $html = self::safe_html();
1022 1153 $allowed_html = array();
1154 +
1023 1155 if ( $allowed === 'all' ) {
1024 1156 $allowed_html = $html;
1025 - } elseif ( ! empty( $allowed ) ) {
1157 + } elseif ( $allowed ) {
1026 1158 foreach ( (array) $allowed as $a ) {
1027 1159 $allowed_html[ $a ] = $html[ $a ] ?? array();
1028 1160 }
1029 1161 }
@@ -1032,8 +1164,10 @@
1032 1164 }
1033 1165
1034 1166 /**
1035 1167 * @since 2.05.03
1168 + *
1169 + * @return array
1036 1170 */
1037 1171 private static function safe_html() {
1038 1172 $allow_class = array(
1039 1173 'class' => true,
@@ -1185,14 +1319,16 @@
1185 1319 return;
1186 1320 }
1187 1321
1188 1322 $action_name = isset( $_GET['action'] ) ? 'action' : ( isset( $_GET['action2'] ) ? 'action2' : '' );
1189 - if ( empty( $action_name ) ) {
1323 +
1324 + if ( ! $action_name ) {
1190 1325 return;
1191 1326 }
1192 1327
1193 1328 $new_action = self::get_param( $action_name, '', 'get', 'sanitize_text_field' );
1194 - if ( ! empty( $new_action ) ) {
1329 +
1330 + if ( $new_action ) {
1195 1331 $_SERVER['REQUEST_URI'] = str_replace( '&action=' . $new_action, '', self::get_server_value( 'REQUEST_URI' ) );
1196 1332 }
1197 1333 }
1198 1334
@@ -1199,21 +1335,23 @@
1199 1335 /**
1200 1336 * Check the WP query for a parameter
1201 1337 *
1202 1338 * @since 2.0
1339 + *
1340 + * @param array|string $value
1341 + * @param string $param
1342 + *
1203 1343 * @return array|string
1204 1344 */
1205 1345 public static function get_query_var( $value, $param ) {
1346 + // phpcs:ignore Universal.Operators.StrictComparisons
1206 1347 if ( $value != '' ) {
1207 1348 return $value;
1208 1349 }
1209 1350
1210 1351 global $wp_query;
1211 - if ( isset( $wp_query->query_vars[ $param ] ) ) {
1212 - $value = $wp_query->query_vars[ $param ];
1213 - }
1214 1352
1215 - return $value;
1353 + return $wp_query->query_vars[ $param ] ?? $value;
1216 1354 }
1217 1355
1218 1356 /**
1219 1357 * Try to show the SVG if possible. Otherwise, use the font icon.
@@ -1221,18 +1359,18 @@
1221 1359 * @since 4.0.02
1222 1360 *
1223 1361 * @param string $class
1224 1362 * @param array $atts
1363 + *
1225 1364 * @return string|null
1226 1365 */
1227 1366 public static function icon_by_class( $class, $atts = array() ) {
1228 1367 $echo = ! isset( $atts['echo'] ) || $atts['echo'];
1368 +
1229 1369 if ( isset( $atts['echo'] ) ) {
1230 1370 unset( $atts['echo'] );
1231 1371 }
1232 1372
1233 - $html_atts = self::array_to_html_params( $atts );
1234 -
1235 1373 $icon = trim( str_replace( array( 'frm_icon_font', 'frmfont ' ), '', $class ) );
1236 1374
1237 1375 // Replace icons that have been removed or renamed.
1238 1376 $deprecated = array(
@@ -1239,29 +1377,61 @@
1239 1377 'frm_clone_solid_icon' => 'frm_clone_icon',
1240 1378 'frm_keyalt_icon' => 'frm_key_icon',
1241 1379 'frm_keyalt_solid_icon' => 'frm_key_solid_icon',
1242 1380 );
1381 +
1243 1382 if ( isset( $deprecated[ $icon ] ) ) {
1244 1383 $icon = $deprecated[ $icon ];
1245 1384 $class = str_replace( $icon, $deprecated[ $icon ], $class );
1246 1385 }
1247 1386
1248 - if ( $icon === $class ) {
1249 - $icon = '<i class="' . esc_attr( $class ) . '"' . $html_atts . '></i>';
1250 - } else {
1251 - $class = strpos( $icon, ' ' ) === false ? '' : ' ' . $icon;
1252 - 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, ' ' ) ) {
1253 1393 $icon = explode( ' ', $icon );
1254 1394 $icon = reset( $icon );
1255 1395 }
1256 - $icon = '<svg class="frmsvg' . esc_attr( $class ) . '"' . $html_atts . '><use xlink:href="#' . esc_attr( $icon ) . '" /></svg>';
1257 1396 }
1258 1397
1259 - if ( $echo ) {
1260 - echo self::kses_icon( $icon ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1261 - } else {
1262 - 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;
1263 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 );
1264 1434 }
1265 1435
1266 1436 /**
1267 1437 * Run kses for icons. It needs to add a few filters first in order to preserve some custom style values.
@@ -1268,8 +1438,9 @@
1268 1438 *
1269 1439 * @since 5.0.13
1270 1440 *
1271 1441 * @param string $icon
1442 + *
1272 1443 * @return string
1273 1444 */
1274 1445 public static function kses_icon( $icon ) {
1275 1446 add_filter( 'safe_style_css', 'FrmAppHelper::allow_vars_in_styles' );
@@ -1285,8 +1456,9 @@
1285 1456 /**
1286 1457 * @since 5.0.13.1
1287 1458 *
1288 1459 * @param array $allowed_html
1460 + *
1289 1461 * @return array
1290 1462 */
1291 1463 public static function add_allowed_icon_tags( $allowed_html ) {
1292 1464 $allowed_html['svg']['data-open'] = true;
@@ -1298,8 +1470,9 @@
1298 1470 /**
1299 1471 * @since 5.0.13
1300 1472 *
1301 1473 * @param array $allowed_attr
1474 + *
1302 1475 * @return array
1303 1476 */
1304 1477 public static function allow_vars_in_styles( $allowed_attr ) {
1305 1478 $allowed_attr[] = '--primary-700';
@@ -1312,9 +1485,9 @@
1312 1485 * @param bool $allow_css
1313 1486 * @param string $css_string
1314 1487 */
1315 1488 public static function allow_style( $allow_css, $css_string ) {
1316 - if ( ! $allow_css && 0 === strpos( $css_string, '--primary-700:' ) ) {
1489 + if ( ! $allow_css && str_starts_with( $css_string, '--primary-700:' ) ) {
1317 1490 $split = explode( ':', $css_string, 2 );
1318 1491 $allow_css = 2 === count( $split ) && self::is_a_valid_color( $split[1] );
1319 1492 }
1320 1493 return $allow_css;
@@ -1323,19 +1496,22 @@
1323 1496 /**
1324 1497 * @since 5.0.13
1325 1498 *
1326 1499 * @param string $value
1500 + *
1327 1501 * @return bool
1328 1502 */
1329 1503 private static function is_a_valid_color( $value ) {
1330 1504 $match = 0;
1331 - if ( 0 === strpos( $value, 'rgba(' ) ) {
1505 +
1506 + if ( str_starts_with( $value, 'rgba(' ) ) {
1332 1507 $match = preg_match( '/^rgba\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),\s*(\d*(?:\.\d+)?)\)$/', $value );
1333 - } elseif ( 0 === strpos( $value, 'rgb(' ) ) {
1508 + } elseif ( str_starts_with( $value, 'rgb(' ) ) {
1334 1509 $match = preg_match( '/^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/', $value );
1335 - } elseif ( 0 === strpos( $value, '#' ) ) {
1510 + } elseif ( str_starts_with( $value, '#' ) ) {
1336 1511 $match = preg_match( '/^#([a-f0-9]{6}|[a-f0-9]{3})\b$/', $value );
1337 1512 }
1513 +
1338 1514 return (bool) $match;
1339 1515 }
1340 1516
1341 1517 /**
@@ -1341,8 +1517,9 @@
1341 1517 /**
1342 1518 * Include svg images.
1343 1519 *
1344 1520 * @since 4.0.02
1521 + *
1345 1522 * @return void
1346 1523 */
1347 1524 public static function include_svg() {
1348 1525 if ( self::$included_svg ) {
@@ -1361,17 +1538,20 @@
1361 1538 * @since 5.0.13 added $echo parameter.
1362 1539 *
1363 1540 * @param array $atts
1364 1541 * @param bool $echo
1542 + *
1365 1543 * @return string|void
1366 1544 */
1367 1545 public static function array_to_html_params( $atts, $echo = false ) {
1368 1546 $callback = function () use ( $atts ) {
1369 - if ( $atts ) {
1370 - foreach ( $atts as $key => $value ) {
1371 - echo ' ' . esc_attr( $key ) . '="' . esc_attr( $value ) . '"';
1372 - }
1547 + if ( ! $atts ) {
1548 + return;
1373 1549 }
1550 +
1551 + foreach ( $atts as $key => $value ) {
1552 + echo ' ' . esc_attr( $key ) . '="' . esc_attr( $value ) . '"';
1553 + }
1374 1554 };
1375 1555 return self::clip( $callback, $echo );
1376 1556 }
1377 1557
@@ -1381,9 +1561,12 @@
1381 1561 * @since 5.0.13
1382 1562 *
1383 1563 * @param Closure $echo_function
1384 1564 * @param bool $echo
1565 + *
1385 1566 * @return string|null
1567 + *
1568 + * @psalm-return ($echo is true ? null : string)
1386 1569 */
1387 1570 public static function clip( $echo_function, $echo = false ) {
1388 1571 if ( ! $echo ) {
1389 1572 ob_start();
@@ -1392,13 +1575,9 @@
1392 1575 if ( is_callable( $echo_function ) ) {
1393 1576 $echo_function();
1394 1577 }
1395 1578
1396 - if ( ! $echo ) {
1397 - $return = ob_get_contents();
1398 - ob_end_clean();
1399 - return $return;
1400 - }
1579 + return $echo ? null : ob_get_clean();
1401 1580 }
1402 1581
1403 1582 /**
1404 1583 * @since 3.0
@@ -1403,15 +1582,18 @@
1403 1582 /**
1404 1583 * @since 3.0
1405 1584 *
1406 1585 * @param array $atts
1586 + *
1407 1587 * @return void
1408 1588 */
1409 1589 public static function get_admin_header( $atts ) {
1410 1590 $has_nav = ! empty( $atts['form'] ) && empty( $atts['is_template'] );
1591 +
1411 1592 if ( empty( $atts['close'] ) ) {
1412 1593 $atts['close'] = admin_url( 'admin.php?page=formidable' );
1413 1594 }
1595 +
1414 1596 if ( ! isset( $atts['import_link'] ) ) {
1415 1597 $atts['import_link'] = false;
1416 1598 }
1417 1599
@@ -1421,16 +1603,19 @@
1421 1603 /**
1422 1604 * @since 6.0
1423 1605 *
1424 1606 * @param string $type
1607 + *
1425 1608 * @return void
1426 1609 */
1427 1610 public static function import_link( $type = 'secondary' ) {
1611 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
1428 1612 ?>
1429 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">
1430 1614 <?php esc_html_e( 'Import', 'formidable' ); ?>
1431 1615 </a>
1432 1616 <?php
1617 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
1433 1618 }
1434 1619
1435 1620 /**
1436 1621 * Print applicable admin banner.
@@ -1437,8 +1622,9 @@
1437 1622 *
1438 1623 * @since 5.4.2
1439 1624 *
1440 1625 * @param bool $should_show_lite_upgrade
1626 + *
1441 1627 * @return void
1442 1628 */
1443 1629 public static function print_admin_banner( $should_show_lite_upgrade ) {
1444 1630 if ( ! current_user_can( 'administrator' ) ) {
@@ -1450,13 +1636,15 @@
1450 1636 // Print license warning or inbox banner and exit if either prints.
1451 1637 // And exit before printing the upgrade bar if it shouldn't be shown.
1452 1638 return;
1453 1639 }
1640 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
1454 1641 ?>
1455 1642 <div class="frm-upgrade-bar">
1456 1643 <div class="frm-upgrade-bar-inner">
1457 1644 <?php
1458 1645 $cta_text = FrmSalesApi::get_best_sale_value( 'lite_banner_cta_text' );
1646 +
1459 1647 if ( ! $cta_text ) {
1460 1648 $cta_text = __( 'upgrading to PRO', 'formidable' );
1461 1649 }
1462 1650
@@ -1461,22 +1649,18 @@
1461 1649 }
1462 1650
1463 1651 $upgrade_link = FrmSalesApi::get_best_sale_value( 'lite_banner_cta_link' );
1464 1652 $utm = array(
1465 - 'medium' => 'settings-license',
1466 - 'content' => 'lite-banner',
1653 + 'campaign' => 'settings-license',
1654 + 'content' => 'lite-banner',
1467 1655 );
1468 1656
1469 - if ( $upgrade_link ) {
1470 - $upgrade_link = self::maybe_add_missing_utm( $upgrade_link, $utm );
1471 - } else {
1472 - $upgrade_link = self::admin_upgrade_link( $utm );
1473 - }
1657 + $upgrade_link = $upgrade_link ? self::maybe_add_missing_utm( $upgrade_link, $utm ) : self::admin_upgrade_link( $utm );
1474 1658
1475 1659 printf(
1476 1660 /* translators: %1$s: Start link HTML, %2$s: CTA text ("upgrading to PRO" by default), %3$s: End link HTML */
1477 1661 esc_html__( 'You\'re using Formidable Forms Lite. To unlock more features consider %1$s%2$s%3$s.', 'formidable' ),
1478 - '<a href="' . esc_url( $upgrade_link ) . '">',
1662 + '<a href="' . esc_url( $upgrade_link ) . '" target="_blank" rel="noopener">',
1479 1663 esc_html( $cta_text ),
1480 1664 '</a>'
1481 1665 );
1482 1666 ?>
@@ -1482,8 +1666,9 @@
1482 1666 ?>
1483 1667 </div>
1484 1668 </div>
1485 1669 <?php
1670 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
1486 1671 }
1487 1672
1488 1673 /**
1489 1674 * @since 5.4.2
@@ -1497,8 +1682,9 @@
1497 1682 /**
1498 1683 * Render a button for a new item (Form, Application, etc).
1499 1684 *
1500 1685 * @since 3.0
1686 + *
1501 1687 * @param array $atts {
1502 1688 * Details about the button.
1503 1689 *
1504 1690 * @type array $link_hook Custom link hook, calls do_action and exits early.
@@ -1505,8 +1691,9 @@
1505 1691 * @type string $new_link Href value, default #.
1506 1692 * @type string $class Custom class names, space separated.
1507 1693 * @type string $button_text Button text. Default "Add New".
1508 1694 * }
1695 + *
1509 1696 * @return void
1510 1697 */
1511 1698 public static function add_new_item_link( $atts ) {
1512 1699 if ( isset( $atts['link_hook'] ) ) {
@@ -1532,8 +1719,12 @@
1532 1719 }
1533 1720
1534 1721 /**
1535 1722 * @since 3.06
1723 + *
1724 + * @param array $atts
1725 + *
1726 + * @return void
1536 1727 */
1537 1728 public static function show_search_box( $atts ) {
1538 1729 $defaults = array(
1539 1730 'placeholder' => '',
@@ -1549,8 +1740,9 @@
1549 1740 $atts['tosearch'] = 'frm-card';
1550 1741 }
1551 1742
1552 1743 $class = 'frm-search-input';
1744 +
1553 1745 if ( ! empty( $atts['tosearch'] ) ) {
1554 1746 $class .= ' frm-auto-search';
1555 1747 }
1556 1748
@@ -1574,14 +1766,15 @@
1574 1766
1575 1767 if ( ! empty( $atts['tosearch'] ) ) {
1576 1768 $input_atts['autocomplete'] = 'off';
1577 1769 }
1770 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
1578 1771 ?>
1579 1772 <p class="frm-search <?php echo esc_attr( $atts['class'] ); ?>">
1580 1773 <label class="screen-reader-text" for="<?php echo esc_attr( $input_id ); ?>">
1581 1774 <?php echo esc_html( $atts['text'] ); ?>:
1582 1775 </label>
1583 - <?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' ); ?>
1584 1777 <input <?php self::array_to_html_params( $input_atts, true ); ?> />
1585 1778 <?php
1586 1779 if ( empty( $atts['tosearch'] ) ) {
1587 1780 submit_button( $atts['text'], 'button-secondary', '', false, array( 'id' => 'search-submit' ) );
@@ -1588,17 +1781,21 @@
1588 1781 }
1589 1782 ?>
1590 1783 </p>
1591 1784 <?php
1785 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
1592 1786 }
1593 1787
1594 1788 /**
1595 - * @param string $type
1789 + * @param string $type Hook type slug.
1790 + * @param object|null $object Optional related object.
1791 + *
1596 1792 * @return void
1597 1793 */
1598 1794 public static function trigger_hook_load( $type, $object = null ) {
1599 1795 // Only load the form hooks once.
1600 1796 $hooks_loaded = apply_filters( 'frm_' . $type . '_hooks_loaded', false, $object );
1797 +
1601 1798 if ( ! $hooks_loaded ) {
1602 1799 do_action( 'frm_load_' . $type . '_hooks' );
1603 1800 }
1604 1801 }
@@ -1657,14 +1854,14 @@
1657 1854 * True when value is 1, true, 'true', 'yes'
1658 1855 *
1659 1856 * @since 1.07.10
1660 1857 *
1661 - * @param string $value The value to compare.
1858 + * @param bool|int|string $value The value to compare.
1662 1859 *
1663 1860 * @return bool
1664 1861 */
1665 1862 public static function is_true( $value ) {
1666 - return true === $value || 1 == $value || 'true' === $value || 'yes' === $value;
1863 + return true === $value || '1' === (string) $value || 'true' === $value || 'yes' === $value;
1667 1864 }
1668 1865
1669 1866 /**
1670 1867 * Gets all post from a specific post type.
@@ -1672,8 +1869,9 @@
1672 1869 *
1673 1870 * @since 4.10.01 Add `$post_type` argument.
1674 1871 *
1675 1872 * @param string $post_type Post type to query. Default is `page`.
1873 + *
1676 1874 * @return WP_Post[]
1677 1875 */
1678 1876 public static function get_pages( $post_type = 'page' ) {
1679 1877 $query = array(
@@ -1692,8 +1890,9 @@
1692 1890 *
1693 1891 * @since 5.0.09
1694 1892 *
1695 1893 * @param string $post_type Post type to query. Default is `page`.
1894 + *
1696 1895 * @return array
1697 1896 */
1698 1897 public static function get_post_ids_and_titles( $post_type = 'page' ) {
1699 1898 return FrmDb::get_results(
@@ -1718,10 +1917,9 @@
1718 1917 *
1719 1918 * @param array $args Selection arguments.
1720 1919 */
1721 1920 public static function maybe_autocomplete_pages_options( $args ) {
1722 - $args = self::preformat_selection_args( $args );
1723 -
1921 + $args = self::preformat_selection_args( $args );
1724 1922 $pages_count = wp_count_posts( $args['post_type'] );
1725 1923
1726 1924 if ( ! isset( $pages_count->publish ) || $pages_count->publish <= 50 ) {
1727 1925 self::wp_pages_dropdown( $args );
@@ -1768,11 +1966,11 @@
1768 1966 'value_key' => 'value',
1769 1967 'label_key' => 'label',
1770 1968 );
1771 1969
1772 - $args = wp_parse_args( $args, $defaults );
1970 + $args = wp_parse_args( $args, $defaults );
1971 + $html_attrs = array();
1773 1972
1774 - $html_attrs = array();
1775 1973 if ( ! empty( $args['name'] ) ) {
1776 1974 $html_attrs['name'] = $args['name'];
1777 1975 }
1778 1976
@@ -1779,8 +1977,9 @@
1779 1977 if ( ! empty( $args['id'] ) ) {
1780 1978 $html_attrs['id'] = $args['id'];
1781 1979 }
1782 1980
1981 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
1783 1982 if ( count( $args['source'] ) <= $args['dropdown_limit'] ) {
1784 1983 ?>
1785 1984 <select <?php self::array_to_html_params( $html_attrs, true ); ?>>
1786 1985 <option value=""><?php echo esc_html( $args['placeholder'] ); ?></option>
@@ -1786,40 +1985,44 @@
1786 1985 <option value=""><?php echo esc_html( $args['placeholder'] ); ?></option>
1787 1986 <?php
1788 1987 foreach ( $args['source'] as $key => $source ) :
1789 1988 $value_label = self::get_dropdown_value_and_label_from_option( $source, $key, $args );
1989 +
1790 1990 if ( ! empty( $args['truncate'] ) ) {
1791 1991 $value_label['label'] = self::truncate( $value_label['label'], $args['truncate'] );
1792 1992 }
1793 1993 ?>
1794 - <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 ?>
1795 1995 <?php endforeach; ?>
1796 1996 </select>
1797 1997 <?php
1798 - } else {
1799 - $options = array();
1800 - $autocomplete_value = '';
1801 - foreach ( $args['source'] as $key => $source ) {
1802 - $value_label = self::get_dropdown_value_and_label_from_option( $source, $key, $args );
1998 + return;
1999 + }
2000 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
1803 2001
1804 - if ( $value_label['value'] === $args['selected'] ) {
1805 - $autocomplete_value = $value_label['label'];
1806 - }
2002 + $options = array();
2003 + $autocomplete_value = '';
1807 2004
1808 - $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'];
1809 2010 }
1810 2011
1811 - $html_attrs['type'] = 'hidden';
1812 - $html_attrs['class'] = 'frm_autocomplete_value_input';
1813 - $html_attrs['value'] = $args['selected'];
1814 - ?>
1815 - <input type="text" class="frm-custom-search"
1816 - data-source="<?php echo esc_attr( wp_json_encode( $options ) ); ?>"
1817 - placeholder="<?php echo esc_attr( $args['autocomplete_placeholder'] ); ?>"
1818 - value="<?php echo esc_attr( $autocomplete_value ); ?>" />
1819 - <input <?php self::array_to_html_params( $html_attrs, true ); ?> />
1820 - <?php
1821 - }//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
1822 2025 }
1823 2026
1824 2027 /**
1825 2028 * Gets dropdown value and label from autodropdown option.
@@ -1828,8 +2031,9 @@
1828 2031 *
1829 2032 * @param array|string $option Autocomplete option.
1830 2033 * @param string $key Array key of the option.
1831 2034 * @param array $args See {@see FrmAppHelper::maybe_autocomplete_options()}.
2035 + *
1832 2036 * @return array
1833 2037 */
1834 2038 private static function get_dropdown_value_and_label_from_option( $option, $key, $args ) {
1835 2039 if ( is_array( $option ) ) {
@@ -1852,8 +2056,9 @@
1852 2056 self::prep_page_dropdown_params( $page_id, $truncate, $args );
1853 2057
1854 2058 $pages = self::get_post_ids_and_titles( $args['post_type'] );
1855 2059 $selected = self::get_post_param( $args['field_name'], $args['page_id'], 'absint' );
2060 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
1856 2061 ?>
1857 2062 <select name="<?php echo esc_attr( $args['field_name'] ); ?>" id="<?php echo esc_attr( $args['field_name'] ); ?>" class="frm-pages-dropdown">
1858 2063 <option value=""><?php echo esc_html( $args['placeholder'] ); ?></option>
1859 2064 <?php foreach ( $pages as $page ) { ?>
@@ -1862,8 +2067,9 @@
1862 2067 </option>
1863 2068 <?php } ?>
1864 2069 </select>
1865 2070 <?php
2071 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
1866 2072 }
1867 2073
1868 2074 /**
1869 2075 * Fill in missing parameters passed to wp_pages_dropdown().
@@ -1869,8 +2075,14 @@
1869 2075 * Fill in missing parameters passed to wp_pages_dropdown().
1870 2076 * This is for reverse compatibility with switching 3 params to 1.
1871 2077 *
1872 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
1873 2085 */
1874 2086 private static function prep_page_dropdown_params( $page_id, $truncate, &$args ) {
1875 2087 if ( ! is_array( $args ) ) {
1876 2088 $args = array(
@@ -1887,8 +2099,12 @@
1887 2099 * Filter to format args for page dropdown or autocomplete
1888 2100 *
1889 2101 * @since 4.03.06
1890 2102 * @since 4.10.01 Added `post_type` and `autocomplete_placeholder` to the arguments array.
2103 + *
2104 + * @param array $args
2105 + *
2106 + * @return array
1891 2107 */
1892 2108 private static function preformat_selection_args( $args ) {
1893 2109 $defaults = array(
1894 2110 'truncate' => false,
@@ -1901,13 +2117,18 @@
1901 2117
1902 2118 return array_merge( $defaults, $args );
1903 2119 }
1904 2120
2121 + /**
2122 + * @param int $post_id
2123 + *
2124 + * @return string
2125 + */
1905 2126 public static function post_edit_link( $post_id ) {
1906 2127 $post = get_post( $post_id );
2128 +
1907 2129 if ( $post ) {
1908 2130 $post_url = admin_url( 'post.php?post=' . $post_id . '&action=edit' );
1909 -
1910 2131 return '<a href="' . esc_url( $post_url ) . '">' . self::truncate( $post->post_title, 50 ) . '</a>';
1911 2132 }
1912 2133
1913 2134 return '';
@@ -1920,11 +2141,9 @@
1920 2141 *
1921 2142 * @return bool
1922 2143 */
1923 2144 public static function is_full_screen() {
1924 - return self::is_form_builder_page() ||
1925 - self::is_style_editor_page() ||
1926 - 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();
1927 2146 }
1928 2147
1929 2148 /**
1930 2149 * Check if user is on the style editor or its alternative URL.
@@ -1934,8 +2153,9 @@
1934 2153 * @since 5.5.3
1935 2154 * @since 6.0 Added the $view parameter. Previously there was only a 'edit' view.
1936 2155 *
1937 2156 * @param string $view Supports 'edit', 'list', and ''. If '', both 'edit' and 'list' will match.
2157 + *
1938 2158 * @return bool
1939 2159 */
1940 2160 public static function is_style_editor_page( $view = '' ) {
1941 2161 if ( ! self::is_admin_page( 'formidable-styles' ) && ! self::is_admin_page( 'formidable-styles2' ) ) {
@@ -1972,8 +2192,9 @@
1972 2192 * @param array|string $capability
1973 2193 * @param string $multiple 'single' and 'multiple'.
1974 2194 */
1975 2195 public static function wp_roles_dropdown( $field_name, $capability, $multiple = 'single' ) {
2196 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
1976 2197 ?>
1977 2198 <select name="<?php echo esc_attr( $field_name ); ?>" id="<?php echo esc_attr( $field_name ); ?>"
1978 2199 <?php echo 'multiple' === $multiple ? 'multiple="multiple"' : ''; ?>
1979 2200 class="frm_multiselect">
@@ -1979,12 +2200,14 @@
1979 2200 class="frm_multiselect">
1980 2201 <?php self::roles_options( $capability ); ?>
1981 2202 </select>
1982 2203 <?php
2204 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
1983 2205 }
1984 2206
1985 2207 /**
1986 2208 * @since 4.07
2209 + *
1987 2210 * @param array|string $selected
1988 2211 * @param string $current
1989 2212 */
1990 2213 private static function selected( $selected, $current ) {
@@ -1999,8 +2222,9 @@
1999 2222 * @param array|string $capability
2000 2223 */
2001 2224 public static function roles_options( $capability ) {
2002 2225 global $frm_vars;
2226 +
2003 2227 if ( isset( $frm_vars['editable_roles'] ) ) {
2004 2228 $editable_roles = $frm_vars['editable_roles'];
2005 2229 } else {
2006 2230 $editable_roles = get_editable_roles();
@@ -2021,8 +2245,9 @@
2021 2245 *
2022 2246 * @since 5.0 Parameter `$type` supports `pro_only` value.
2023 2247 *
2024 2248 * @param string $type Supports `auto`, `pro`, or `pro_only`.
2249 + *
2025 2250 * @return array
2026 2251 */
2027 2252 public static function frm_capabilities( $type = 'auto' ) {
2028 2253 if ( ! self::pro_is_installed() && ! in_array( $type, array( 'pro', 'pro_only' ), true ) ) {
@@ -2087,21 +2312,18 @@
2087 2312 if ( $role === 'loggedin' || ! $role ) {
2088 2313 return is_user_logged_in();
2089 2314 }
2090 2315
2091 - if ( $role == 1 ) {
2316 + if ( (int) $role === 1 ) {
2092 2317 $role = 'administrator';
2093 2318 }
2094 2319
2095 - if ( ! is_user_logged_in() ) {
2096 - return false;
2097 - }
2098 -
2099 - return current_user_can( $role );
2320 + return is_user_logged_in() ? current_user_can( $role ) : false;
2100 2321 }
2101 2322
2102 2323 /**
2103 2324 * @param array|string $needed_role
2325 + *
2104 2326 * @return bool
2105 2327 */
2106 2328 public static function user_has_permission( $needed_role ) {
2107 2329 if ( is_array( $needed_role ) ) {
@@ -2115,18 +2337,20 @@
2115 2337 }
2116 2338
2117 2339 $can = self::current_user_can( $needed_role );
2118 2340
2119 - if ( $can || in_array( $needed_role, array( '-1', 'loggedout' ) ) ) {
2341 + if ( $can || in_array( $needed_role, array( '-1', 'loggedout' ), true ) ) {
2120 2342 return $can;
2121 2343 }
2122 2344
2123 2345 $roles = array( 'administrator', 'editor', 'author', 'contributor', 'subscriber' );
2346 +
2124 2347 foreach ( $roles as $role ) {
2125 2348 if ( current_user_can( $role ) ) {
2126 2349 return true;
2127 2350 }
2128 - if ( $role == $needed_role ) {
2351 +
2352 + if ( $role === $needed_role ) {
2129 2353 break;
2130 2354 }
2131 2355 }
2132 2356
@@ -2144,11 +2368,11 @@
2144 2368 if ( ! current_user_can( 'administrator' ) || current_user_can( 'frm_view_forms' ) ) {
2145 2369 return;
2146 2370 }
2147 2371
2148 - $user_id = get_current_user_id();
2149 - $user = new WP_User( $user_id );
2372 + $user = new WP_User( get_current_user_id() );
2150 2373 $frm_roles = self::frm_capabilities();
2374 +
2151 2375 foreach ( $frm_roles as $frm_role => $frm_role_description ) {
2152 2376 $user->add_cap( $frm_role );
2153 2377 unset( $frm_role, $frm_role_description );
2154 2378 }
@@ -2159,18 +2383,22 @@
2159 2383 *
2160 2384 * @since 2.0.6
2161 2385 *
2162 2386 * @param string $cap
2387 + *
2163 2388 * @return void
2164 2389 */
2165 2390 public static function force_capability( $cap = 'frm_change_settings' ) {
2166 - if ( current_user_can( 'administrator' ) && ! current_user_can( $cap ) ) {
2167 - $role = get_role( 'administrator' );
2168 - $frm_roles = self::frm_capabilities();
2169 - foreach ( $frm_roles as $frm_role => $frm_role_description ) {
2170 - $role->add_cap( $frm_role );
2171 - }
2391 + if ( ! current_user_can( 'administrator' ) || current_user_can( $cap ) ) {
2392 + return;
2172 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 + }
2173 2401 }
2174 2402
2175 2403 /**
2176 2404 * Check if the user has permission for action.
@@ -2178,17 +2406,22 @@
2178 2406 *
2179 2407 * @since 2.0
2180 2408 *
2181 2409 * @param string $permission
2410 + * @param string $show_message
2182 2411 */
2183 2412 public static function permission_check( $permission, $show_message = 'show' ) {
2184 2413 $permission_error = self::permission_nonce_error( $permission );
2185 - if ( $permission_error !== false ) {
2186 - if ( 'hide' == $show_message ) {
2187 - $permission_error = '';
2188 - }
2189 - wp_die( esc_html( $permission_error ) );
2414 +
2415 + if ( $permission_error === false ) {
2416 + return;
2190 2417 }
2418 +
2419 + if ( 'hide' === $show_message ) {
2420 + $permission_error = '';
2421 + }
2422 +
2423 + wp_die( esc_html( $permission_error ) );
2191 2424 }
2192 2425
2193 2426 /**
2194 2427 * Check user permission and nonce
@@ -2195,24 +2428,27 @@
2195 2428 *
2196 2429 * @since 2.0
2197 2430 *
2198 2431 * @param string $permission
2432 + * @param string $nonce_name
2433 + * @param string $nonce
2199 2434 *
2200 2435 * @return false|string The permission message or false if allowed
2201 2436 */
2202 2437 public static function permission_nonce_error( $permission, $nonce_name = '', $nonce = '' ) {
2203 - if ( ! empty( $permission ) && ! current_user_can( $permission ) && ! current_user_can( 'administrator' ) ) {
2438 + if ( $permission && ! current_user_can( $permission ) && ! current_user_can( 'administrator' ) ) {
2204 2439 $frm_settings = self::get_settings();
2205 -
2206 2440 return $frm_settings->admin_permission;
2207 2441 }
2208 2442
2209 2443 $error = false;
2210 - if ( empty( $nonce_name ) ) {
2444 +
2445 + if ( ! $nonce_name ) {
2211 2446 return $error;
2212 2447 }
2213 2448
2214 2449 $nonce_value = $_REQUEST && isset( $_REQUEST[ $nonce_name ] ) ? sanitize_text_field( wp_unslash( $_REQUEST[ $nonce_name ] ) ) : '';
2450 +
2215 2451 if ( $_REQUEST && ( ! isset( $_REQUEST[ $nonce_name ] ) || ! wp_verify_nonce( $nonce_value, $nonce ) ) ) {
2216 2452 $frm_settings = self::get_settings();
2217 2453 $error = $frm_settings->admin_permission;
2218 2454 }
@@ -2219,8 +2455,14 @@
2219 2455
2220 2456 return $error;
2221 2457 }
2222 2458
2459 + /**
2460 + * @param array|string $values
2461 + * @param string $current
2462 + *
2463 + * @return void
2464 + */
2223 2465 public static function checked( $values, $current ) {
2224 2466 if ( self::check_selected( $values, $current ) ) {
2225 2467 echo ' checked="checked"';
2226 2468 }
@@ -2225,38 +2467,47 @@
2225 2467 echo ' checked="checked"';
2226 2468 }
2227 2469 }
2228 2470
2471 + /**
2472 + * @param array|string $values
2473 + * @param string|null $current
2474 + *
2475 + * @return bool
2476 + */
2229 2477 public static function check_selected( $values, $current ) {
2230 - $values = self::recursive_function_map( $values, 'trim' );
2231 - $values = self::recursive_function_map( $values, 'htmlspecialchars_decode' );
2232 -
2478 + $values = self::recursive_function_map( $values, 'trim' );
2479 + $values = self::recursive_function_map( $values, 'htmlspecialchars_decode' );
2233 2480 $current = is_null( $current ) ? '' : htmlspecialchars_decode( trim( $current ) );
2234 2481
2235 - 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;
2236 2484 }
2237 2485
2486 + /**
2487 + * @param array|string $value
2488 + * @param callable|string $function
2489 + *
2490 + * @return array|string
2491 + */
2238 2492 public static function recursive_function_map( $value, $function ) {
2239 2493 if ( is_array( $value ) ) {
2240 2494 $original_function = $function;
2241 - if ( count( $value ) ) {
2242 - $function = explode( ', ', FrmDb::prepare_array_values( $value, $function ) );
2243 - } else {
2244 - $function = array( $function );
2245 - }
2246 - if ( ! self::is_assoc( $value ) ) {
2247 - $value = array_map( array( 'FrmAppHelper', 'recursive_function_map' ), $value, $function );
2248 - } else {
2495 + $function = count( $value ) ? explode( ', ', FrmDb::prepare_array_values( $value, $function ) ) : array( $function );
2496 +
2497 + if ( self::is_assoc( $value ) ) {
2249 2498 foreach ( $value as $k => $v ) {
2250 2499 if ( ! is_array( $v ) ) {
2251 2500 $value[ $k ] = call_user_func( $original_function, $v );
2252 2501 }
2253 2502 }
2503 + } else {
2504 + $value = array_map( array( 'FrmAppHelper', 'recursive_function_map' ), $value, $function );
2254 2505 }
2255 2506 } else {
2256 2507 $value = self::maybe_update_value_if_null( $value, $function );
2257 2508 $value = call_user_func( $function, $value );
2258 - }
2509 + }//end if
2259 2510
2260 2511 return $value;
2261 2512 }
2262 2513
@@ -2263,20 +2514,27 @@
2263 2514 /**
2264 2515 * Updates value to empty string if it is null and being passed to a string function.
2265 2516 *
2266 2517 * @since 6.8.4
2518 + *
2267 2519 * @param mixed $value
2268 2520 * @param string $function
2521 + *
2269 2522 * @return mixed
2270 2523 */
2271 2524 private static function maybe_update_value_if_null( $value, $function ) {
2272 2525 if ( null === $value && in_array( $function, array( 'trim', 'strlen' ), true ) ) {
2273 - $value = '';
2526 + return '';
2274 2527 }
2275 2528
2276 2529 return $value;
2277 2530 }
2278 2531
2532 + /**
2533 + * @param array $array
2534 + *
2535 + * @return bool
2536 + */
2279 2537 public static function is_assoc( $array ) {
2280 2538 return (bool) count( array_filter( array_keys( $array ), 'is_string' ) );
2281 2539 }
2282 2540
@@ -2281,16 +2539,22 @@
2281 2539 }
2282 2540
2283 2541 /**
2284 2542 * Flatten a multi-dimensional array
2543 + *
2544 + * @param array $array
2545 + * @param string $keys
2546 + *
2547 + * @return array
2285 2548 */
2286 2549 public static function array_flatten( $array, $keys = 'keep' ) {
2287 2550 $return = array();
2551 +
2288 2552 foreach ( $array as $key => $value ) {
2289 2553 if ( is_array( $value ) ) {
2290 2554 $return = array_merge( $return, self::array_flatten( $value, $keys ) );
2291 2555 } elseif ( $keys === 'keep' ) {
2292 - $return[ $key ] = $value;
2556 + $return[ $key ] = $value;
2293 2557 } else {
2294 2558 $return[] = $value;
2295 2559 }
2296 2560 }
@@ -2304,8 +2568,9 @@
2304 2568 * @since 6.16.1
2305 2569 *
2306 2570 * @param string $sep
2307 2571 * @param array $array
2572 + *
2308 2573 * @return string
2309 2574 */
2310 2575 public static function safe_implode( $sep, $array ) {
2311 2576 $array = self::array_flatten( $array );
@@ -2314,15 +2579,18 @@
2314 2579
2315 2580 /**
2316 2581 * @param string $text
2317 2582 * @param bool $is_rich_text
2583 + *
2318 2584 * @return string
2319 2585 */
2320 2586 public static function esc_textarea( $text, $is_rich_text = false ) {
2321 2587 $safe_text = str_replace( '&quot;', '"', $text );
2588 +
2322 2589 if ( ! $is_rich_text ) {
2323 2590 $safe_text = htmlspecialchars( $safe_text, ENT_NOQUOTES );
2324 2591 }
2592 +
2325 2593 $safe_text = str_replace( '&amp; ', '& ', $safe_text );
2326 2594
2327 2595 /**
2328 2596 * @param string $safe_text
@@ -2334,44 +2602,59 @@
2334 2602 /**
2335 2603 * Add auto paragraphs to text areas
2336 2604 *
2337 2605 * @since 2.0
2606 + *
2607 + * @param mixed $content
2608 + *
2609 + * @return mixed
2338 2610 */
2339 2611 public static function use_wpautop( $content ) {
2340 2612 if ( apply_filters( 'frm_use_wpautop', true ) && is_string( $content ) ) {
2341 - $content = wpautop( str_replace( '<br>', '<br />', $content ) );
2613 + return wpautop( str_replace( '<br>', '<br />', $content ) );
2342 2614 }
2343 2615
2344 2616 return $content;
2345 2617 }
2346 2618
2619 + /**
2620 + * Replace quotes with their HTML entities.
2621 + *
2622 + * @param string $val
2623 + *
2624 + * @return string
2625 + */
2347 2626 public static function replace_quotes( $val ) {
2348 2627 // Replace double quotes.
2349 2628 $val = str_replace( array( '&#8220;', '&#8221;', '&#8243;' ), '"', $val );
2350 2629
2351 2630 // Replace single quotes.
2352 - $val = str_replace( array( '&#8216;', '&#8217;', '&#8242;', '&prime;', '&rsquo;', '&lsquo;' ), "'", $val );
2353 -
2354 - return $val;
2631 + return str_replace( array( '&#8216;', '&#8217;', '&#8242;', '&prime;', '&rsquo;', '&lsquo;' ), "'", $val );
2355 2632 }
2356 2633
2357 2634 /**
2358 - * @param string $handle
2635 + * @param string $handle
2636 + * @param int|string $default
2637 + *
2638 + * @return int|string
2359 2639 */
2360 2640 public static function script_version( $handle, $default = 0 ) {
2361 2641 global $wp_scripts;
2642 +
2362 2643 if ( ! $wp_scripts ) {
2363 2644 return $default;
2364 2645 }
2365 2646
2366 2647 $ver = $default;
2648 +
2367 2649 if ( ! isset( $wp_scripts->registered[ $handle ] ) ) {
2368 2650 return $ver;
2369 2651 }
2370 2652
2371 2653 $query = $wp_scripts->registered[ $handle ];
2654 +
2372 2655 if ( is_object( $query ) && ! empty( $query->ver ) ) {
2373 - $ver = $query->ver;
2656 + return $query->ver;
2374 2657 }
2375 2658
2376 2659 return $ver;
2377 2660 }
@@ -2380,8 +2663,9 @@
2380 2663 * @since 5.0.13 added $echo param.
2381 2664 *
2382 2665 * @param string $url
2383 2666 * @param bool $echo
2667 + *
2384 2668 * @return string|null
2385 2669 */
2386 2670 public static function js_redirect( $url, $echo = false ) {
2387 2671 $callback = function () use ( $url ) {
@@ -2389,8 +2673,13 @@
2389 2673 };
2390 2674 return self::clip( $callback, $echo );
2391 2675 }
2392 2676
2677 + /**
2678 + * @param int|string $user_id
2679 + *
2680 + * @return int|string
2681 + */
2393 2682 public static function get_user_id_param( $user_id ) {
2394 2683 if ( ! $user_id || is_numeric( $user_id ) ) {
2395 2684 return $user_id;
2396 2685 }
@@ -2395,16 +2684,13 @@
2395 2684 return $user_id;
2396 2685 }
2397 2686
2398 2687 $user_id = sanitize_text_field( $user_id );
2688 +
2399 2689 if ( $user_id === 'current' ) {
2400 2690 $user_id = get_current_user_id();
2401 2691 } else {
2402 - if ( is_email( $user_id ) ) {
2403 - $user = get_user_by( 'email', $user_id );
2404 - } else {
2405 - $user = get_user_by( 'login', $user_id );
2406 - }
2692 + $user = is_email( $user_id ) ? get_user_by( 'email', $user_id ) : get_user_by( 'login', $user_id );
2407 2693
2408 2694 if ( $user ) {
2409 2695 $user_id = $user->ID;
2410 2696 }
@@ -2416,8 +2702,9 @@
2416 2702
2417 2703 /**
2418 2704 * @param string $filename
2419 2705 * @param array $atts
2706 + *
2420 2707 * @return false|string
2421 2708 */
2422 2709 public static function get_file_contents( $filename, $atts = array() ) {
2423 2710 if ( ! is_file( $filename ) ) {
@@ -2426,12 +2713,9 @@
2426 2713
2427 2714 extract( $atts ); // phpcs:ignore WordPress.PHP.DontExtract
2428 2715 ob_start();
2429 2716 include $filename;
2430 - $contents = ob_get_contents();
2431 - ob_end_clean();
2432 -
2433 - return $contents;
2717 + return ob_get_clean();
2434 2718 }
2435 2719
2436 2720 /**
2437 2721 * @param string $name
@@ -2441,8 +2725,9 @@
2441 2725 * @param int $num_chars
2442 2726 */
2443 2727 public static function get_unique_key( $name, $table_name, $column, $id = 0, $num_chars = 5 ) {
2444 2728 $key = '';
2729 +
2445 2730 if ( $name ) {
2446 2731 $key = sanitize_key( $name );
2447 2732 $key = self::maybe_clear_long_key( $key, $column );
2448 2733 }
@@ -2462,31 +2747,31 @@
2462 2747 $column
2463 2748 );
2464 2749
2465 2750 // Create a unique field id if it has already been used.
2466 - if ( in_array( $key, $similar_keys, true ) ) {
2467 - $key = self::maybe_truncate_key_before_appending( $column, $key );
2751 + if ( ! in_array( $key, $similar_keys, true ) ) {
2752 + return $key;
2753 + }
2468 2754
2469 - /**
2470 - * Allow for a custom separator between the attempted key and the generated suffix.
2471 - *
2472 - * @since 5.2.03
2473 - *
2474 - * @param string $separator. Default empty.
2475 - * @param string $key the key without the added suffix.
2476 - */
2477 - $separator = apply_filters( 'frm_unique_' . $column . '_separator', '', $key );
2755 + $key = self::maybe_truncate_key_before_appending( $column, $key );
2478 2756
2479 - $suffix = 2;
2480 - do {
2481 - $key_check = $key . $separator . $suffix;
2482 - ++$suffix;
2483 - } 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 );
2484 2766
2485 - $key = $key_check;
2486 - }//end if
2767 + $suffix = 2;
2768 + do {
2769 + $key_check = $key . $separator . $suffix;
2770 + ++$suffix;
2771 + } while ( in_array( $key_check, $similar_keys, true ) );
2487 2772
2488 - return $key;
2773 + return $key_check;
2489 2774 }
2490 2775
2491 2776 /**
2492 2777 * Avoid trying to append to a really long key,
@@ -2493,20 +2778,26 @@
2493 2778 * The database limit is 100 for form and field keys so we want to avoid getting too close.
2494 2779 *
2495 2780 * @param string $column
2496 2781 * @param string $key
2782 + *
2497 2783 * @return string
2498 2784 */
2499 2785 private static function maybe_truncate_key_before_appending( $column, $key ) {
2500 - if ( in_array( $column, array( 'form_key', 'field_key' ), true ) ) {
2501 - $max_key_length_before_truncating = 60;
2502 - if ( strlen( $key ) > $max_key_length_before_truncating ) {
2503 - $key = substr( $key, 0, $max_key_length_before_truncating );
2504 - if ( is_numeric( $key ) ) {
2505 - $key .= 'a';
2506 - }
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';
2507 2797 }
2508 2798 }
2799 +
2509 2800 return $key;
2510 2801 }
2511 2802
2512 2803 /**
@@ -2513,13 +2804,14 @@
2513 2804 * Possibly reset a key to avoid conflicts with column size limits.
2514 2805 *
2515 2806 * @param string $key
2516 2807 * @param string $column
2808 + *
2517 2809 * @return string either the original key value, or an empty string if the key was too long.
2518 2810 */
2519 2811 private static function maybe_clear_long_key( $key, $column ) {
2520 2812 if ( 'field_key' === $column && strlen( $key ) >= 70 ) {
2521 - $key = '';
2813 + return '';
2522 2814 }
2523 2815 return $key;
2524 2816 }
2525 2817
@@ -2526,8 +2818,9 @@
2526 2818 /**
2527 2819 * @since 6.21 This is changed from `private` to `public`.
2528 2820 *
2529 2821 * @param int $num_chars
2822 + *
2530 2823 * @return string
2531 2824 */
2532 2825 public static function generate_new_key( $num_chars ) {
2533 2826 $max_slug_value = 36 ** $num_chars;
@@ -2538,8 +2831,9 @@
2538 2831 }
2539 2832
2540 2833 /**
2541 2834 * @param string $key
2835 + *
2542 2836 * @return string
2543 2837 */
2544 2838 private static function prevent_numeric_and_reserved_keys( $key ) {
2545 2839 if ( is_numeric( $key ) ) {
@@ -2553,12 +2847,14 @@
2553 2847 'editlink',
2554 2848 'siteurl',
2555 2849 'evenodd',
2556 2850 );
2851 +
2557 2852 if ( in_array( $key, $not_allowed, true ) ) {
2558 2853 $key .= 'a';
2559 2854 }
2560 2855 }
2856 +
2561 2857 return $key;
2562 2858 }
2563 2859
2564 2860 /**
@@ -2577,9 +2873,9 @@
2577 2873 if ( ! $record ) {
2578 2874 return false;
2579 2875 }
2580 2876
2581 - if ( empty( $post_values ) ) {
2877 + if ( ! $post_values ) {
2582 2878 $post_values = wp_unslash( $_POST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
2583 2879 }
2584 2880
2585 2881 $values = array(
@@ -2607,46 +2903,67 @@
2607 2903
2608 2904 return $values;
2609 2905 }
2610 2906
2907 + /**
2908 + * @param array|string $fields
2909 + * @param object $record
2910 + * @param array $values
2911 + * @param array $args
2912 + *
2913 + * @return void
2914 + */
2611 2915 private static function prepare_field_arrays( $fields, $record, array &$values, $args ) {
2612 - if ( ! empty( $fields ) ) {
2613 - foreach ( (array) $fields as $field ) {
2614 - if ( ! self::is_admin_page() ) {
2615 - // Don't prep default values on the form settings page.
2616 - $field->default_value = apply_filters( 'frm_get_default_value', $field->default_value, $field, true );
2617 - }
2618 - $args['parent_form_id'] = $args['parent_form_id'] ?? $field->form_id;
2619 - 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 );
2620 2924 }
2925 +
2926 + $args['parent_form_id'] = $args['parent_form_id'] ?? $field->form_id;
2927 + self::fill_field_defaults( $field, $record, $values, $args );
2621 2928 }
2622 2929 }
2623 2930
2931 + /**
2932 + * @param object $field
2933 + * @param object $record
2934 + * @param array $values
2935 + * @param array $args
2936 + *
2937 + * @return void
2938 + */
2624 2939 private static function fill_field_defaults( $field, $record, array &$values, $args ) {
2625 2940 $post_values = $args['post_values'];
2626 2941
2627 2942 if ( $args['default'] ) {
2628 2943 $meta_value = $field->default_value;
2629 - } 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'] ) ) {
2630 2945 if ( ! isset( $field->field_options['custom_field'] ) ) {
2631 2946 $field->field_options['custom_field'] = '';
2632 2947 }
2633 - $meta_value = FrmProEntryMetaHelper::get_post_value(
2634 - $record->post_id,
2635 - $field->field_options['post_field'],
2636 - $field->field_options['custom_field'],
2637 - array(
2638 - 'truncate' => false,
2639 - 'type' => $field->type,
2640 - 'form_id' => $field->form_id,
2641 - 'field' => $field,
2642 - )
2643 - );
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 + );
2644 2960 } else {
2645 2961 $meta_value = FrmEntryMeta::get_meta_value( $record, $field->id );
2646 2962 }//end if
2647 2963
2648 2964 $field_type = $post_values['field_options'][ 'type_' . $field->id ] ?? $field->type;
2965 +
2649 2966 if ( isset( $post_values['item_meta'][ $field->id ] ) ) {
2650 2967 $new_value = $post_values['item_meta'][ $field->id ];
2651 2968 self::unserialize_or_decode( $new_value );
2652 2969 } else {
@@ -2661,9 +2978,9 @@
2661 2978 $args['field_type'] = $field_type;
2662 2979
2663 2980 FrmFieldsHelper::prepare_edit_front_field( $field_array, $field, $values['id'], $args );
2664 2981
2665 - if ( ! isset( $field_array['unique'] ) || ! $field_array['unique'] ) {
2982 + if ( empty( $field_array['unique'] ) ) {
2666 2983 $field_array['unique_msg'] = '';
2667 2984 }
2668 2985
2669 2986 $field_array = array_merge( (array) $field->field_options, $field_array );
@@ -2730,15 +3047,21 @@
2730 3047 }
2731 3048
2732 3049 /**
2733 3050 * Set to POST value or default
3051 + *
3052 + * @param array $post_values
3053 + * @param array $values
3054 + *
3055 + * @return void
2734 3056 */
2735 3057 private static function fill_form_defaults( $post_values, array &$values ) {
2736 3058 $form_defaults = FrmFormsHelper::get_default_opts();
2737 3059
2738 3060 foreach ( $form_defaults as $opt => $default ) {
3061 + // phpcs:ignore Universal.Operators.StrictComparisons
2739 3062 if ( ! isset( $values[ $opt ] ) || $values[ $opt ] == '' ) {
2740 - $values[ $opt ] = $post_values && isset( $post_values['options'][ $opt ] ) ? $post_values['options'][ $opt ] : $default;
3063 + $values[ $opt ] = $post_values['options'][ $opt ] ?? $default;
2741 3064 }
2742 3065
2743 3066 unset( $opt, $default );
2744 3067 }
@@ -2748,9 +3071,9 @@
2748 3071 }
2749 3072
2750 3073 foreach ( array( 'before', 'after', 'submit' ) as $h ) {
2751 3074 if ( ! isset( $values[ $h . '_html' ] ) ) {
2752 - $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 );
2753 3076 }
2754 3077 unset( $h );
2755 3078 }
2756 3079 }
@@ -2762,46 +3085,57 @@
2762 3085 *
2763 3086 * @return bool|int
2764 3087 */
2765 3088 public static function custom_style_value( $post_values ) {
2766 - if ( ! empty( $post_values ) && isset( $post_values['options']['custom_style'] ) ) {
2767 - $custom_style = absint( $post_values['options']['custom_style'] );
2768 - } else {
2769 - $frm_settings = self::get_settings();
2770 - $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'] );
2771 3091 }
2772 3092
2773 - return $custom_style;
3093 + $frm_settings = self::get_settings();
3094 + return $frm_settings->load_style !== 'none';
2774 3095 }
2775 3096
2776 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 + *
2777 3104 * @param mixed $original_string
2778 3105 * @param int|string $length
2779 3106 * @param int $minword
2780 3107 * @param string $continue
3108 + * @param bool $force_length_limit Force the final string to never exceed the length limit.
3109 + *
2781 3110 * @return string
2782 3111 */
2783 - 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 ) {
2784 3113 if ( ! is_string( $original_string ) && ! is_int( $original_string ) ) {
2785 3114 return '';
2786 3115 }
2787 3116
2788 - $length = (int) $length;
2789 - $str = wp_strip_all_tags( (string) $original_string );
2790 - $original_len = self::mb_function( array( 'mb_strlen', 'strlen' ), array( $str ) );
3117 + $length = (int) $length;
2791 3118
2792 - if ( $length == 0 ) {
3119 + if ( $length === 0 ) {
2793 3120 return '';
2794 3121 }
2795 3122
3123 + $str = wp_strip_all_tags( (string) $original_string );
3124 + $original_len = self::mb_function( array( 'mb_strlen', 'strlen' ), array( $str ) );
3125 +
2796 3126 if ( $length <= 10 ) {
2797 3127 $sub = self::mb_function( array( 'mb_substr', 'substr' ), array( $str, 0, $length ) );
3128 +
3129 + if ( $force_length_limit ) {
3130 + return $sub;
3131 + }
3132 +
2798 3133 return $sub . ( $length < $original_len ? $continue : '' );
2799 3134 }
2800 3135
2801 - $sub = '';
2802 - $len = 0;
2803 -
3136 + $sub = '';
3137 + $len = 0;
2804 3138 $words = self::mb_function( array( 'mb_split', 'explode' ), array( ' ', $str ) );
2805 3139
2806 3140 if ( ! is_array( $words ) ) {
2807 3141 return $original_string;
@@ -2807,10 +3141,11 @@
2807 3141 return $original_string;
2808 3142 }
2809 3143
2810 3144 foreach ( $words as $word ) {
2811 - $part = ( $sub != '' ? ' ' : '' ) . $word;
3145 + $part = ( $sub !== '' ? ' ' : '' ) . $word;
2812 3146 $total_len = self::mb_function( array( 'mb_strlen', 'strlen' ), array( $sub . $part ) );
3147 +
2813 3148 if ( $total_len > $length && substr_count( $sub, ' ' ) ) {
2814 3149 break;
2815 3150 }
2816 3151
@@ -2823,10 +3158,21 @@
2823 3158
2824 3159 unset( $total_len, $word );
2825 3160 }
2826 3161
2827 - $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 );
2828 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 +
2829 3175 return $sub . ( $len < $original_len ? $continue : '' );
2830 3176 }
2831 3177
2832 3178 /**
@@ -2833,30 +3179,51 @@
2833 3179 * If the string is still too long because there may not have been any spaces, force truncate.
2834 3180 *
2835 3181 * @since 6.5.4
2836 3182 *
2837 - * @param string $sub Current substring.
2838 - * @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 + *
2839 3187 * @return string
2840 3188 */
2841 - private static function maybe_force_truncate_on_string_with_no_spaces( $sub, $length ) {
2842 - if ( strlen( $sub ) < $length + 50 ) {
2843 - // If the string isn't way over the limit, leave it.
2844 - 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 );
2845 3204 }
2846 3205
2847 - $first_space = strpos( $sub, ' ', $length );
2848 - if ( false !== $first_space ) {
2849 - // Ignore anything with spaces.
2850 - 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 ) );
2851 3211 }
2852 3212
2853 - return substr( $sub, 0, $length + 10 );
3213 + return $sub;
2854 3214 }
2855 3215
3216 + /**
3217 + * @param array $function_names
3218 + * @param array $args
3219 + *
3220 + * @return mixed
3221 + */
2856 3222 public static function mb_function( $function_names, $args ) {
2857 3223 $mb_function_name = $function_names[0];
2858 3224 $function_name = $function_names[1];
3225 +
2859 3226 if ( function_exists( $mb_function_name ) ) {
2860 3227 $function_name = $mb_function_name;
2861 3228 }
2862 3229
@@ -2862,14 +3229,21 @@
2862 3229
2863 3230 return call_user_func_array( $function_name, $args );
2864 3231 }
2865 3232
3233 + /**
3234 + * @param string $date
3235 + * @param string $date_format
3236 + * @param string $time_format
3237 + *
3238 + * @return string
3239 + */
2866 3240 public static function get_formatted_time( $date, $date_format = '', $time_format = '' ) {
2867 - if ( empty( $date ) ) {
3241 + if ( ! $date ) {
2868 3242 return $date;
2869 3243 }
2870 3244
2871 - if ( empty( $date_format ) ) {
3245 + if ( ! $date_format ) {
2872 3246 $date_format = get_option( 'date_format' );
2873 3247 }
2874 3248
2875 3249 if ( preg_match( '/^\d{1-2}\/\d{1-2}\/\d{4}$/', $date ) && self::pro_is_installed() ) {
@@ -2877,10 +3251,10 @@
2877 3251 $date = FrmProAppHelper::convert_date( $date, $frmpro_settings->date_format, 'Y-m-d' );
2878 3252 }
2879 3253
2880 3254 $formatted = self::get_localized_date( $date_format, $date );
3255 + $do_time = gmdate( 'H:i:s', strtotime( $date ) ) !== '00:00:00';
2881 3256
2882 - $do_time = ( gmdate( 'H:i:s', strtotime( $date ) ) != '00:00:00' );
2883 3257 if ( $do_time ) {
2884 3258 $formatted .= self::add_time_to_date( $time_format, $date );
2885 3259 }
2886 3260
@@ -2889,30 +3263,35 @@
2889 3263
2890 3264 /**
2891 3265 * @param string $time_format
2892 3266 * @param string $date
3267 + *
2893 3268 * @return string
2894 3269 */
2895 3270 private static function add_time_to_date( $time_format, $date ) {
2896 - if ( empty( $time_format ) ) {
3271 + if ( ! $time_format ) {
2897 3272 $time_format = get_option( 'time_format' );
2898 3273 }
2899 3274
2900 3275 $trimmed_format = trim( $time_format );
2901 - $time = '';
2902 - if ( $time_format && ! empty( $trimmed_format ) ) {
2903 - $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 );
2904 3279 }
2905 3280
2906 - return $time;
3281 + return '';
2907 3282 }
2908 3283
2909 3284 /**
2910 3285 * @since 2.0.8
3286 + *
3287 + * @param string $date_format
3288 + * @param string $date
3289 + *
3290 + * @return string
2911 3291 */
2912 3292 public static function get_localized_date( $date_format, $date ) {
2913 3293 $date = get_date_from_gmt( $date );
2914 -
2915 3294 return date_i18n( $date_format, strtotime( $date ) );
2916 3295 }
2917 3296
2918 3297 /**
@@ -2917,19 +3296,16 @@
2917 3296
2918 3297 /**
2919 3298 * Gets the time ago in words.
2920 3299 *
2921 - * @param int $from In seconds.
2922 - * @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.
2923 3303 *
2924 - * @return string $time_ago
3304 + * @return string Time ago.
2925 3305 */
2926 3306 public static function human_time_diff( $from, $to = '', $levels = 1 ) {
2927 - if ( empty( $to ) && 0 !== $to ) {
2928 - $now = new DateTime();
2929 - } else {
2930 - $now = new DateTime( '@' . $to );
2931 - }
3307 + $now = ! $to && 0 !== $to ? new DateTime() : new DateTime( '@' . $to );
2932 3308 $ago = new DateTime( '@' . $from );
2933 3309
2934 3310 // Get the time difference
2935 3311 $diff_object = $now->diff( $ago );
@@ -2943,8 +3319,9 @@
2943 3319
2944 3320 if ( ! is_numeric( $levels ) ) {
2945 3321 // Show time in specified unit.
2946 3322 $levels = self::get_unit( $levels );
3323 +
2947 3324 if ( isset( $time_strings[ $levels ] ) ) {
2948 3325 $diff = array(
2949 3326 $levels => self::time_format( $levels, $diff ),
2950 3327 );
@@ -2951,13 +3328,14 @@
2951 3328 $time_strings = array(
2952 3329 $levels => $time_strings[ $levels ],
2953 3330 );
2954 3331 }
3332 +
2955 3333 $levels = 1;
2956 3334 }
2957 3335
2958 3336 foreach ( $time_strings as $k => $v ) {
2959 - if ( isset( $diff[ $k ] ) && $diff[ $k ] ) {
3337 + if ( ! empty( $diff[ $k ] ) ) {
2960 3338 $time_strings[ $k ] = $diff[ $k ] . ' ' . ( $diff[ $k ] > 1 ? $v[1] : $v[0] );
2961 3339 } elseif ( isset( $diff[ $k ] ) && count( $time_strings ) === 1 ) {
2962 3340 // Account for 0.
2963 3341 $time_strings[ $k ] = $diff[ $k ] . ' ' . $v[1];
@@ -2965,17 +3343,21 @@
2965 3343 unset( $time_strings[ $k ] );
2966 3344 }
2967 3345 }
2968 3346
2969 - $levels_deep = apply_filters( 'frm_time_ago_levels', $levels, compact( 'time_strings', 'from', 'to' ) );
2970 - $time_strings = array_slice( $time_strings, 0, absint( $levels_deep ) );
2971 - $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 ) );
2972 3349
2973 - return $time_ago_string;
3350 + return implode( ' ', $time_strings );
2974 3351 }
2975 3352
2976 3353 /**
2977 3354 * @since 4.05.01
3355 + *
3356 + * @param string $unit
3357 + * @param array $diff
3358 + *
3359 + * @return int
2978 3360 */
2979 3361 private static function time_format( $unit, $diff ) {
2980 3362 $return = array(
2981 3363 'y' => 'y',
@@ -2980,14 +3362,14 @@
2980 3362 $return = array(
2981 3363 'y' => 'y',
2982 3364 'd' => 'days',
2983 3365 );
3366 +
2984 3367 if ( isset( $return[ $unit ] ) ) {
2985 3368 return $diff[ $return[ $unit ] ];
2986 3369 }
2987 3370
2988 3371 $total = $diff['days'] * self::convert_time( 'd', $unit );
2989 -
2990 3372 $times = array( 'h', 'i', 's' );
2991 3373
2992 3374 foreach ( $times as $time ) {
2993 3375 if ( ! isset( $diff[ $time ] ) ) {
@@ -3001,8 +3383,13 @@
3001 3383 }
3002 3384
3003 3385 /**
3004 3386 * @since 4.05.01
3387 + *
3388 + * @param string $from
3389 + * @param string $to
3390 + *
3391 + * @return int
3005 3392 */
3006 3393 private static function convert_time( $from, $to ) {
3007 3394 $convert = array(
3008 3395 's' => 1,
@@ -3018,20 +3405,26 @@
3018 3405 }
3019 3406
3020 3407 /**
3021 3408 * @since 4.05.01
3409 + *
3410 + * @param string $unit
3411 + *
3412 + * @return int|string
3022 3413 */
3023 3414 private static function get_unit( $unit ) {
3024 3415 $units = self::get_time_strings();
3416 +
3025 3417 if ( isset( $units[ $unit ] ) || is_numeric( $unit ) ) {
3026 3418 return $unit;
3027 3419 }
3028 3420
3029 3421 foreach ( $units as $u => $strings ) {
3030 - if ( in_array( $unit, $strings ) ) {
3422 + if ( in_array( $unit, $strings, true ) ) {
3031 3423 return $u;
3032 3424 }
3033 3425 }
3426 +
3034 3427 return 1;
3035 3428 }
3036 3429
3037 3430 /**
@@ -3038,8 +3431,9 @@
3038 3431 * Get the translatable time strings. The untranslated version is a failsafe
3039 3432 * in case languages are changing for the unit set in the shortcode.
3040 3433 *
3041 3434 * @since 2.0.20
3435 + *
3042 3436 * @return array
3043 3437 */
3044 3438 private static function get_time_strings() {
3045 3439 return array(
@@ -3086,12 +3480,13 @@
3086 3480 /**
3087 3481 * @param int $r_count
3088 3482 * @param int $current_p
3089 3483 * @param int $p_size
3484 + *
3090 3485 * @return int
3091 3486 */
3092 3487 public static function get_last_record_num( $r_count, $current_p, $p_size ) {
3093 - 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;
3094 3489 }
3095 3490
3096 3491 /**
3097 3492 * @param int $r_count
@@ -3096,11 +3491,13 @@
3096 3491 /**
3097 3492 * @param int $r_count
3098 3493 * @param int $current_p
3099 3494 * @param int $p_size
3495 + *
3100 3496 * @return int
3101 3497 */
3102 3498 public static function get_first_record_num( $r_count, $current_p, $p_size ) {
3499 + // phpcs:ignore Universal.Operators.StrictComparisons
3103 3500 if ( $current_p == 1 ) {
3104 3501 return 1;
3105 3502 }
3106 3503 return self::get_last_record_num( $r_count, $current_p - 1, $p_size ) + 1;
@@ -3106,17 +3503,22 @@
3106 3503 return self::get_last_record_num( $r_count, $current_p - 1, $p_size ) + 1;
3107 3504 }
3108 3505
3109 3506 /**
3507 + * @param array $json_vars
3508 + *
3110 3509 * @return array
3111 3510 */
3112 3511 public static function json_to_array( $json_vars ) {
3113 3512 $vars = array();
3513 +
3114 3514 foreach ( $json_vars as $jv ) {
3115 3515 $jv_name = explode( '[', $jv['name'] );
3116 3516 $last = count( $jv_name ) - 1;
3517 +
3117 3518 foreach ( $jv_name as $p => $n ) {
3118 3519 $name = trim( $n, ']' );
3520 +
3119 3521 if ( ! isset( $l1 ) ) {
3120 3522 $l1 = $name;
3121 3523 }
3122 3524
@@ -3127,9 +3529,9 @@
3127 3529 if ( ! isset( $l3 ) ) {
3128 3530 $l3 = $name;
3129 3531 }
3130 3532
3131 - $this_val = $p == $last ? $jv['value'] : array();
3533 + $this_val = $p === $last ? $jv['value'] : array();
3132 3534
3133 3535 switch ( $p ) {
3134 3536 case 0:
3135 3537 $l1 = $name;
@@ -3162,10 +3564,15 @@
3162 3564
3163 3565 /**
3164 3566 * @param string $name
3165 3567 * @param string $l1
3568 + * @param mixed $val
3569 + * @param array $vars
3570 + *
3571 + * @return void
3166 3572 */
3167 3573 public static function add_value_to_array( $name, $l1, $val, &$vars ) {
3574 + // phpcs:ignore Universal.Operators.StrictComparisons
3168 3575 if ( $name == '' ) {
3169 3576 $vars[] = $val;
3170 3577 } elseif ( ! isset( $vars[ $l1 ] ) ) {
3171 3578 $vars[ $l1 ] = $val;
@@ -3171,19 +3578,26 @@
3171 3578 $vars[ $l1 ] = $val;
3172 3579 }
3173 3580 }
3174 3581
3582 + /**
3583 + * @param string $name
3584 + * @param string $class
3585 + * @param string $form_name
3586 + *
3587 + * @return void
3588 + */
3175 3589 public static function maybe_add_tooltip( $name, $class = 'closed', $form_name = '' ) {
3176 3590 $tooltips = array(
3177 3591 'action_title' => __( 'Give this action a label for easy reference.', 'formidable' ),
3178 - '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
3179 3593 'cc' => __( 'Add CC addresses separated by a ",". FORMAT: Name <[email protected]> or [email protected].', 'formidable' ),
3180 3594 'bcc' => __( 'Add BCC addresses separated by a ",". FORMAT: Name <[email protected]> or [email protected].', 'formidable' ),
3181 - '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
3182 3596 'from' => __( 'Enter the name and/or email address of the sender. FORMAT: John Bates <[email protected]> or [email protected].', 'formidable' ),
3183 3597 /* translators: %1$s: Form name, %2$s: Date */
3184 - '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() ) ),
3185 - '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
3186 3600 );
3187 3601
3188 3602 if ( ! isset( $tooltips[ $name ] ) ) {
3189 3603 return;
@@ -3188,9 +3602,9 @@
3188 3602 if ( ! isset( $tooltips[ $name ] ) ) {
3189 3603 return;
3190 3604 }
3191 3605
3192 - if ( 'open' == $class ) {
3606 + if ( 'open' === $class ) {
3193 3607 echo ' frm_help"';
3194 3608 } else {
3195 3609 echo ' class="frm_help"';
3196 3610 }
@@ -3196,9 +3610,9 @@
3196 3610 }
3197 3611
3198 3612 echo ' title="' . esc_attr( $tooltips[ $name ] );
3199 3613
3200 - if ( 'open' != $class ) {
3614 + if ( 'open' !== $class ) {
3201 3615 echo '"';
3202 3616 }
3203 3617 }
3204 3618
@@ -3203,20 +3617,28 @@
3203 3617 }
3204 3618
3205 3619 /**
3206 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
3207 3627 */
3208 3628 public static function select_current_page( $page, $current_page, $action = array() ) {
3629 + // phpcs:ignore Universal.Operators.StrictComparisons
3209 3630 if ( $current_page != $page ) {
3210 3631 return;
3211 3632 }
3212 3633
3213 3634 $frm_action = self::simple_get( 'frm_action', 'sanitize_title' );
3635 +
3214 3636 if ( 'lite-reports' === $frm_action ) {
3215 3637 $frm_action = 'reports';
3216 3638 }
3217 3639
3218 - if ( empty( $action ) || ( ! empty( $frm_action ) && in_array( $frm_action, $action ) ) ) {
3640 + if ( ! $action || ( $frm_action && in_array( $frm_action, $action, true ) ) ) {
3219 3641 echo ' class="current_page"';
3220 3642 }
3221 3643 }
3222 3644
@@ -3246,14 +3668,19 @@
3246 3668
3247 3669 // Add extra slashes for \r\n since WP strips them.
3248 3670 $post_content = str_replace( array( '\\r', '\\n', '\\u', '\\t' ), array( '\\\\r', '\\\\n', '\\\\u', '\\\\t' ), $post_content );
3249 3671
3250 - // allow for &quot
3251 - $post_content = str_replace( '&quot;', '\\"', $post_content );
3252 -
3253 - return $post_content;
3672 + // Allow for &quot
3673 + return str_replace( '&quot;', '\\"', $post_content );
3254 3674 }
3255 3675
3676 + /**
3677 + * @param array|string $val
3678 + * @param int|string $key
3679 + * @param array $post_content
3680 + *
3681 + * @return void
3682 + */
3256 3683 private static function prepare_action_slashes( $val, $key, &$post_content ) {
3257 3684 if ( ! isset( $post_content[ $key ] ) || is_numeric( $val ) ) {
3258 3685 return;
3259 3686 }
@@ -3262,15 +3689,17 @@
3262 3689 foreach ( $val as $k1 => $v1 ) {
3263 3690 self::prepare_action_slashes( $v1, $k1, $post_content[ $key ] );
3264 3691 unset( $k1, $v1 );
3265 3692 }
3266 - } else {
3267 - // Strip all slashes so everything is the same, no matter where the value is coming from
3268 - $val = stripslashes( $val );
3269 3693
3270 - // Add backslashes before double quotes and forward slashes only
3271 - $post_content[ $key ] = addcslashes( $val, '"\\/' );
3694 + return;
3272 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, '"\\/' );
3273 3702 }
3274 3703
3275 3704 /**
3276 3705 * Check for either json or serialized data. This is temporary while transitioning
@@ -3278,8 +3707,9 @@
3278 3707 *
3279 3708 * @since 4.02.03
3280 3709 *
3281 3710 * @param array|string $value
3711 + *
3282 3712 * @return void
3283 3713 */
3284 3714 public static function unserialize_or_decode( &$value ) {
3285 3715 if ( is_array( $value ) ) {
@@ -3285,13 +3715,9 @@
3285 3715 if ( is_array( $value ) ) {
3286 3716 return;
3287 3717 }
3288 3718
3289 - if ( is_serialized( $value ) ) {
3290 - $value = self::maybe_unserialize_array( $value );
3291 - } else {
3292 - $value = self::maybe_json_decode( $value, false );
3293 - }
3719 + $value = is_serialized( $value ) ? self::maybe_unserialize_array( $value ) : self::maybe_json_decode( $value, false );
3294 3720 }
3295 3721
3296 3722 /**
3297 3723 * Safely unserialize an array if necessary.
@@ -3299,8 +3725,9 @@
3299 3725 *
3300 3726 * @since 6.2
3301 3727 *
3302 3728 * @param mixed $value
3729 + *
3303 3730 * @return mixed
3304 3731 */
3305 3732 public static function maybe_unserialize_array( $value ) {
3306 3733 if ( ! is_string( $value ) ) {
@@ -3307,18 +3734,15 @@
3307 3734 return $value;
3308 3735 }
3309 3736
3310 3737 // Since we only expect an array, skip anything that doesn't start with a:.
3311 - if ( ! is_serialized( $value ) || 'a:' !== substr( $value, 0, 2 ) ) {
3738 + if ( ! is_serialized( $value ) || ! str_starts_with( $value, 'a:' ) ) {
3312 3739 return $value;
3313 3740 }
3314 3741
3315 3742 $parsed = FrmSerializedStringParserHelper::get()->parse( $value );
3316 - if ( is_array( $parsed ) ) {
3317 - $value = $parsed;
3318 - }
3319 3743
3320 - return $value;
3744 + return is_array( $parsed ) ? $parsed : $value;
3321 3745 }
3322 3746
3323 3747 /**
3324 3748 * Decode a JSON string.
@@ -3323,11 +3747,12 @@
3323 3747 /**
3324 3748 * Decode a JSON string.
3325 3749 * Do not switch shortcodes like [24] to array unless intentional ie XML values.
3326 3750 *
3327 - * @param mixed $string
3328 - * @param bool $single_to_array
3329 - * @return mixed
3751 + * @param array|string|null $string
3752 + * @param bool $single_to_array
3753 + *
3754 + * @return array|string|null
3330 3755 */
3331 3756 public static function maybe_json_decode( $string, $single_to_array = true ) {
3332 3757 if ( is_array( $string ) || is_null( $string ) ) {
3333 3758 return $string;
@@ -3332,20 +3757,19 @@
3332 3757 if ( is_array( $string ) || is_null( $string ) ) {
3333 3758 return $string;
3334 3759 }
3335 3760
3336 - $new_string = json_decode( $string, true );
3337 - if ( function_exists( 'json_last_error' ) ) {
3338 - // php 5.3+
3339 - $single_value = false;
3340 - if ( ! $single_to_array ) {
3341 - $single_value = is_array( $new_string ) && count( $new_string ) === 1 && isset( $new_string[0] );
3342 - }
3343 - if ( json_last_error() == JSON_ERROR_NONE && is_array( $new_string ) && ! $single_value ) {
3344 - $string = $new_string;
3345 - }
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] );
3346 3766 }
3347 3767
3768 + if ( json_last_error() === JSON_ERROR_NONE && is_array( $new_string ) && ! $single_value ) {
3769 + return $new_string;
3770 + }
3771 +
3348 3772 return $string;
3349 3773 }
3350 3774
3351 3775 /**
@@ -3351,8 +3775,9 @@
3351 3775 /**
3352 3776 * @since 6.2.3
3353 3777 *
3354 3778 * @param string $value
3779 + *
3355 3780 * @return string
3356 3781 */
3357 3782 public static function maybe_utf8_encode( $value ) {
3358 3783 $from_format = 'ISO-8859-1';
@@ -3361,13 +3786,15 @@
3361 3786 if ( function_exists( 'mb_check_encoding' ) && function_exists( 'mb_convert_encoding' ) ) {
3362 3787 if ( mb_check_encoding( $value, $from_format ) ) {
3363 3788 return mb_convert_encoding( $value, $to_format, $from_format );
3364 3789 }
3790 +
3365 3791 return $value;
3366 3792 }
3367 3793
3368 3794 if ( function_exists( 'iconv' ) ) {
3369 3795 $converted = iconv( $from_format, $to_format, $value );
3796 +
3370 3797 // Value is false if $value is not ISO-8859-1.
3371 3798 if ( false !== $converted ) {
3372 3799 return $converted;
3373 3800 }
@@ -3379,8 +3806,12 @@
3379 3806 /**
3380 3807 * Reformat the json serialized array in name => value array.
3381 3808 *
3382 3809 * @since 4.02.03
3810 + *
3811 + * @param array $form
3812 + *
3813 + * @return void
3383 3814 */
3384 3815 public static function format_form_data( &$form ) {
3385 3816 $formatted = array();
3386 3817
@@ -3387,17 +3818,20 @@
3387 3818 foreach ( $form as $input ) {
3388 3819 if ( ! isset( $input['name'] ) ) {
3389 3820 continue;
3390 3821 }
3822 +
3391 3823 $key = $input['name'];
3392 - if ( isset( $formatted[ $key ] ) ) {
3393 - if ( is_array( $formatted[ $key ] ) ) {
3394 - $formatted[ $key ][] = $input['value'];
3395 - } else {
3396 - $formatted[ $key ] = array( $formatted[ $key ], $input['value'] );
3397 - }
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'];
3398 3832 } else {
3399 - $formatted[ $key ] = $input['value'];
3833 + $formatted[ $key ] = array( $formatted[ $key ], $input['value'] );
3400 3834 }
3401 3835 }
3402 3836
3403 3837 parse_str( http_build_query( $formatted ), $form );
@@ -3406,15 +3840,13 @@
3406 3840 /**
3407 3841 * @since 4.02.03
3408 3842 *
3409 3843 * @param array|string $value
3844 + *
3410 3845 * @return string
3411 3846 */
3412 3847 public static function maybe_json_encode( $value ) {
3413 - if ( is_array( $value ) ) {
3414 - $value = wp_json_encode( $value );
3415 - }
3416 - return $value;
3848 + return is_array( $value ) ? wp_json_encode( $value ) : $value;
3417 3849 }
3418 3850
3419 3851 /**
3420 3852 * Echo The javascript to open and highlight the Formidable menu
@@ -3421,18 +3853,19 @@
3421 3853 *
3422 3854 * @since 1.07.10
3423 3855 *
3424 3856 * @param string $post_type The name of the post type that may need to be highlighted.
3857 + *
3425 3858 * @return void
3426 3859 */
3427 3860 public static function maybe_highlight_menu( $post_type ) {
3428 3861 global $post;
3429 3862
3430 - if ( isset( $_REQUEST['post_type'] ) && $_REQUEST['post_type'] != $post_type ) {
3863 + if ( isset( $_REQUEST['post_type'] ) && $_REQUEST['post_type'] !== $post_type ) {
3431 3864 return;
3432 3865 }
3433 3866
3434 - if ( is_object( $post ) && $post->post_type != $post_type ) {
3867 + if ( is_object( $post ) && $post->post_type !== $post_type ) {
3435 3868 return;
3436 3869 }
3437 3870
3438 3871 self::load_admin_wide_js();
@@ -3444,13 +3877,13 @@
3444 3877 *
3445 3878 * @since 2.0
3446 3879 *
3447 3880 * @param bool $load
3881 + *
3448 3882 * @return void
3449 3883 */
3450 3884 public static function load_admin_wide_js( $load = true ) {
3451 - $version = self::plugin_version();
3452 - 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() );
3453 3886
3454 3887 $global_strings = array(
3455 3888 'updating_msg' => __( 'Please wait while your site updates.', 'formidable' ),
3456 3889 'deauthorize' => __( 'Are you sure you want to deauthorize Formidable Forms on this site?', 'formidable' ),
@@ -3456,9 +3889,9 @@
3456 3889 'deauthorize' => __( 'Are you sure you want to deauthorize Formidable Forms on this site?', 'formidable' ),
3457 3890 'url' => self::plugin_url(),
3458 3891 'app_url' => 'https://formidableforms.com/',
3459 3892 'applicationsUrl' => admin_url( 'admin.php?page=formidable-applications' ),
3460 - '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
3461 3894 'loading' => __( 'Loading&hellip;', 'formidable' ),
3462 3895 'nonce' => wp_create_nonce( 'frm_ajax' ),
3463 3896 'proIncludesSliderJs' => is_callable( 'FrmProFormsHelper::prepare_custom_currency' ),
3464 3897 'inboxSlideIn' => FrmInbox::get_inbox_slide_in_value_for_js(),
@@ -3471,8 +3904,9 @@
3471 3904 }
3472 3905
3473 3906 /**
3474 3907 * @since 2.0.9
3908 + *
3475 3909 * @return void
3476 3910 */
3477 3911 public static function load_font_style() {
3478 3912 wp_enqueue_style( 'frm_fonts', self::plugin_url() . '/css/frm_fonts.css', array(), self::plugin_version() );
@@ -3479,8 +3913,9 @@
3479 3913 }
3480 3914
3481 3915 /**
3482 3916 * @param string $location
3917 + *
3483 3918 * @return void
3484 3919 */
3485 3920 public static function localize_script( $location ) {
3486 3921 global $wp_scripts, $wp_version;
@@ -3503,8 +3938,9 @@
3503 3938 'include_resend_email' => false,
3504 3939 );
3505 3940
3506 3941 $data = $wp_scripts->get_data( 'formidable', 'data' );
3942 +
3507 3943 if ( ! $data ) {
3508 3944 wp_localize_script( 'formidable', 'frm_js', $script_strings );
3509 3945 }
3510 3946
@@ -3511,9 +3947,9 @@
3511 3947 if ( $location === 'admin' ) {
3512 3948 $admin_script_strings = array(
3513 3949 'desc' => __( '(Click to add description)', 'formidable' ),
3514 3950 'blank' => __( '(Blank)', 'formidable' ),
3515 - 'no_label' => __( '(no label)', 'formidable' ),
3951 + 'no_label' => self::get_no_label_text(),
3516 3952 'ok' => __( 'OK', 'formidable' ),
3517 3953 'cancel' => __( 'Cancel', 'formidable' ),
3518 3954 'default_label' => __( 'Default', 'formidable' ),
3519 3955 'clear_default' => __( 'Clear default value when typing', 'formidable' ),
@@ -3521,9 +3957,9 @@
3521 3957 'valid_default' => __( 'Default value will pass form validation', 'formidable' ),
3522 3958 'no_valid_default' => __( 'Default value will NOT pass form validation', 'formidable' ),
3523 3959 'confirm' => __( 'Are you sure?', 'formidable' ),
3524 3960 'conf_delete' => __( 'Are you sure you want to delete this field and all data associated with it?', 'formidable' ),
3525 - '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
3526 3962 'conf_no_repeat' => __( 'Warning: If you have entries with multiple rows, all but the first row will be lost.', 'formidable' ),
3527 3963 'default_unique' => FrmFieldsHelper::default_unique_msg(),
3528 3964 'default_conf' => __( 'The entered values do not match', 'formidable' ),
3529 3965 'enter_email' => __( 'Enter Email', 'formidable' ),
@@ -3529,9 +3965,9 @@
3529 3965 'enter_email' => __( 'Enter Email', 'formidable' ),
3530 3966 'confirm_email' => __( 'Confirm Email', 'formidable' ),
3531 3967 'conditional_text' => __( 'Conditional content here', 'formidable' ),
3532 3968 'new_option' => __( 'New Option', 'formidable' ),
3533 - '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
3534 3970 'enter_password' => __( 'Enter Password', 'formidable' ),
3535 3971 'confirm_password' => __( 'Confirm Password', 'formidable' ),
3536 3972 'import_complete' => __( 'Import Complete', 'formidable' ),
3537 3973 'updating' => __( 'Please wait while your site updates.', 'formidable' ),
@@ -3545,13 +3981,15 @@
3545 3981 'text_shortcodes' => __( 'This calculation may have shortcodes that work in text calculations but not numeric calculations.', 'formidable' ),
3546 3982 /* translators: %d is the number of allowed actions per form */
3547 3983 'only_one_action' => sprintf( __( 'This form action is limited to %d per form.', 'formidable' ), 1 ),
3548 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
3549 3987 'unsafe_params' => FrmFormsHelper::reserved_words(),
3550 3988 /* Translators: %s is the name of a Detail Page Slug that is a reserved word.*/
3551 - '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
3552 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. */
3553 - '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
3554 3992 'reserved_words' => __( 'See the list of reserved words in WordPress.', 'formidable' ),
3555 3993 'repeat_limit_min' => __( 'Please enter a Repeat Limit that is greater than 1.', 'formidable' ),
3556 3994 'checkbox_limit' => __( 'Please select a limit between 0 and 200.', 'formidable' ),
3557 3995 'install' => __( 'Install', 'formidable' ),
@@ -3572,11 +4010,14 @@
3572 4010 'holdShiftMsg' => esc_html__( 'You can hold %1$sShift%2$s on your keyboard to select multiple fields', 'formidable' ),
3573 4011 'noTitleText' => FrmFormsHelper::get_no_title_text(),
3574 4012
3575 4013 // In older versions this event listener causes the section to immediately close again
3576 - // 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+.
3577 4015 'requireAccordionTitleClickListener' => version_compare( $wp_version, '6.7', '>=' ),
3578 4016 );
4017 +
4018 + self::add_form_builder_modal_data( $admin_script_strings );
4019 +
3579 4020 /**
3580 4021 * @param array $admin_script_strings
3581 4022 */
3582 4023 $admin_script_strings = apply_filters( 'frm_admin_script_strings', $admin_script_strings );
@@ -3581,8 +4022,9 @@
3581 4022 */
3582 4023 $admin_script_strings = apply_filters( 'frm_admin_script_strings', $admin_script_strings );
3583 4024
3584 4025 $data = $wp_scripts->get_data( 'formidable_admin', 'data' );
4026 +
3585 4027 if ( ! $data ) {
3586 4028 wp_localize_script( 'formidable_admin', 'frm_admin_js', $admin_script_strings );
3587 4029 }
3588 4030 }//end if
@@ -3588,8 +4030,91 @@
3588 4030 }//end if
3589 4031 }
3590 4032
3591 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 + /**
4106 + * Get the no label text.
4107 + *
4108 + * @since 6.25.1
4109 + *
4110 + * @return string
4111 + */
4112 + public static function get_no_label_text() {
4113 + return __( '(no label)', 'formidable' );
4114 + }
4115 +
4116 + /**
3592 4117 * @since 6.5
3593 4118 *
3594 4119 * @return string
3595 4120 */
@@ -3631,8 +4156,9 @@
3631 4156 *
3632 4157 * @since 1.07.10
3633 4158 *
3634 4159 * @param float $min_version The version the add-on requires.
4160 + *
3635 4161 * @return void
3636 4162 */
3637 4163 public static function min_version_notice( $min_version ) {
3638 4164 $frm_version = self::plugin_version();
@@ -3642,11 +4168,11 @@
3642 4168 return;
3643 4169 }
3644 4170
3645 4171 $wp_list_table = _get_list_table( 'WP_Plugins_List_Table' );
3646 - 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">' .
3647 - esc_html__( 'You are running an outdated version of Formidable. This plugin may not work correctly if you do not update Formidable.', 'formidable' ) .
3648 - '</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>';
3649 4175 }
3650 4176
3651 4177 /**
3652 4178 * If Pro is far outdated, show a message.
@@ -3652,8 +4178,10 @@
3652 4178 * If Pro is far outdated, show a message.
3653 4179 *
3654 4180 * @since 4.0.01
3655 4181 *
4182 + * @param string $min_version
4183 + *
3656 4184 * @return void
3657 4185 */
3658 4186 public static function min_pro_version_notice( $min_version ) {
3659 4187 if ( ! self::is_formidable_admin() ) {
@@ -3663,25 +4191,14 @@
3663 4191
3664 4192 self::php_version_notice();
3665 4193
3666 4194 $is_pro = self::pro_is_installed() && class_exists( 'FrmProDb' );
4195 +
3667 4196 if ( ! $is_pro || self::meets_min_pro_version( $min_version ) ) {
3668 4197 return;
3669 4198 }
3670 4199
3671 - $expired = FrmAddonsController::is_license_expired();
3672 - ?>
3673 - <div class="frm-banner-alert frm_error_style frm_previous_install">
3674 - <?php
3675 - esc_html_e( 'You are running a version of Formidable Forms that may not be compatible with your version of Formidable Forms Pro.', 'formidable' );
3676 - if ( empty( $expired ) ) {
3677 - echo ' Please <a href="' . esc_url( admin_url( 'plugins.php?s=formidable%20forms%20pro' ) ) . '">update now</a>.';
3678 - } else {
3679 - echo '<br/>Please <a href="https://formidableforms.com/account/downloads/?utm_source=WordPress&utm_medium=outdated">renew now</a> to get the latest version.';
3680 - }
3681 - ?>
3682 - </div>
3683 - <?php
4200 + include self::plugin_path() . '/classes/views/addons/min-version-notice.php';
3684 4201 }
3685 4202
3686 4203 /**
3687 4204 * If Pro is installed, check the version number.
@@ -3688,8 +4205,9 @@
3688 4205 *
3689 4206 * @since 4.0.01
3690 4207 *
3691 4208 * @param string $min_version
4209 + *
3692 4210 * @return bool
3693 4211 */
3694 4212 public static function meets_min_pro_version( $min_version ) {
3695 4213 return ! class_exists( 'FrmProDb' ) || version_compare( FrmProDb::$plug_version, $min_version, '>=' );
@@ -3698,16 +4216,19 @@
3698 4216 /**
3699 4217 * Show a message if the PHP version is below the recommendations.
3700 4218 *
3701 4219 * @since 4.0.02
4220 + *
3702 4221 * @return void
3703 4222 */
3704 4223 private static function php_version_notice() {
3705 4224 $message = array();
4225 +
3706 4226 if ( version_compare( phpversion(), '7.0', '<' ) ) {
3707 - $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
3708 4228 }
3709 4229
4230 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
3710 4231 foreach ( $message as $m ) {
3711 4232 ?>
3712 4233 <div class="frm-banner-alert frm_error_style frm_previous_install">
3713 4234 <?php echo esc_html( $m ); ?>
@@ -3713,12 +4234,14 @@
3713 4234 <?php echo esc_html( $m ); ?>
3714 4235 </div>
3715 4236 <?php
3716 4237 }
4238 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
3717 4239 }
3718 4240
3719 4241 /**
3720 4242 * @param string $type
4243 + *
3721 4244 * @return array<string,string>
3722 4245 */
3723 4246 public static function locales( $type = 'date' ) {
3724 4247 $locales = array(
@@ -3812,12 +4335,12 @@
3812 4335 'zu' => __( 'Zulu', 'formidable' ),
3813 4336 );
3814 4337
3815 4338 if ( $type === 'captcha' ) {
3816 - // remove the languages unavailable for the captcha
4339 + // Remove the languages unavailable for the captcha
3817 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' );
3818 4341 } else {
3819 - // remove the languages unavailable for the datepicker
4342 + // Remove the languages unavailable for the datepicker
3820 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' );
3821 4344 }
3822 4345
3823 4346 $locales = array_diff_key( $locales, array_flip( $unset ) );
@@ -3828,14 +4351,13 @@
3828 4351 * @since 5.4.5 Added $args parameter with type.
3829 4352 *
3830 4353 * @param array<string,string> $locales
3831 4354 * @param array $args {
4355 + *
3832 4356 * @type string $type
3833 4357 * }
3834 4358 */
3835 - $locales = apply_filters( 'frm_locales', $locales, compact( 'type' ) );
3836 -
3837 - return $locales;
4359 + return apply_filters( 'frm_locales', $locales, compact( 'type' ) );
3838 4360 }
3839 4361
3840 4362 /**
3841 4363 * @return string
@@ -3842,12 +4364,14 @@
3842 4364 */
3843 4365 public static function get_menu_icon_class() {
3844 4366 if ( is_callable( 'FrmProAppHelper::get_settings' ) ) {
3845 4367 $settings = FrmProAppHelper::get_settings();
4368 +
3846 4369 if ( is_object( $settings ) && ! empty( $settings->menu_icon ) ) {
3847 4370 return $settings->menu_icon;
3848 4371 }
3849 4372 }
4373 +
3850 4374 return 'frmfont frm_logo_icon';
3851 4375 }
3852 4376
3853 4377 /**
@@ -3865,10 +4389,9 @@
3865 4389 * @type array $input_attrs Attributes of value input.
3866 4390 * }
3867 4391 */
3868 4392 public static function images_dropdown( $args ) {
3869 - $args = self::fill_default_images_dropdown_args( $args );
3870 -
4393 + $args = self::fill_default_images_dropdown_args( $args );
3871 4394 $input_attrs_str = self::get_images_dropdown_input_attrs( $args );
3872 4395 ob_start();
3873 4396 include self::plugin_path() . '/classes/views/shared/images-dropdown.php';
3874 4397 $output = ob_get_clean();
@@ -3889,8 +4412,9 @@
3889 4412 *
3890 4413 * @since 5.0.04
3891 4414 *
3892 4415 * @param array $args The arguments.
4416 + *
3893 4417 * @return array
3894 4418 */
3895 4419 private static function fill_default_images_dropdown_args( $args ) {
3896 4420 $defaults = array(
@@ -3920,8 +4444,9 @@
3920 4444 *
3921 4445 * @since 5.0.04
3922 4446 *
3923 4447 * @param array $args The arguments.
4448 + *
3924 4449 * @return string
3925 4450 */
3926 4451 private static function get_images_dropdown_input_attrs( $args ) {
3927 4452 $input_attrs = $args['input_attrs'];
@@ -3928,8 +4453,9 @@
3928 4453 $input_attrs['type'] = 'radio';
3929 4454 $input_attrs['name'] = $args['name'];
3930 4455
3931 4456 $input_attrs_str = '';
4457 +
3932 4458 foreach ( $input_attrs as $key => $input_attr ) {
3933 4459 $input_attrs_str .= ' ' . sprintf( '%s="%s"', esc_attr( $key ), esc_attr( $input_attr ) );
3934 4460 }
3935 4461
@@ -3945,8 +4471,13 @@
3945 4471 }
3946 4472
3947 4473 /**
3948 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
3949 4480 */
3950 4481 public static function get_images_dropdown_atts( $option, $args ) {
3951 4482 $image = self::get_images_dropdown_option_image( $option, $args );
3952 4483 $classes = self::get_images_dropdown_option_classes( $option, $args );
@@ -3960,8 +4491,9 @@
3960 4491 * @since 5.0.04
3961 4492 *
3962 4493 * @param array $option Option data.
3963 4494 * @param array $args The arguments of images_dropdown() method.
4495 + *
3964 4496 * @return string
3965 4497 */
3966 4498 private static function get_images_dropdown_option_image( $option, $args ) {
3967 4499 $image = self::icon_by_class(
@@ -3990,8 +4522,9 @@
3990 4522 * @since 5.0.04
3991 4523 *
3992 4524 * @param array $option Option data.
3993 4525 * @param array $args The arguments of images_dropdown() method.
4526 + *
3994 4527 * @return string
3995 4528 */
3996 4529 private static function get_images_dropdown_option_classes( $option, $args ) {
3997 4530 $classes = '';
@@ -4019,17 +4552,19 @@
4019 4552 * @since 5.0.04
4020 4553 *
4021 4554 * @param array $option Option data.
4022 4555 * @param array $args The arguments of images_dropdown() method.
4556 + *
4023 4557 * @return string
4024 4558 */
4025 4559 private static function get_images_dropdown_option_html_attrs( $option, $args ) {
4026 4560 $html_attrs = '';
4561 +
4027 4562 if ( ! empty( $option['custom_attrs'] ) && is_array( $option['custom_attrs'] ) ) {
4028 4563 $html_attrs_arr = array();
4029 4564
4030 4565 foreach ( $option['custom_attrs'] as $key => $value ) {
4031 - if ( in_array( $key, array( 'type', 'class', 'data-value' ) ) ) {
4566 + if ( in_array( $key, array( 'type', 'class', 'data-value' ), true ) ) {
4032 4567 continue;
4033 4568 }
4034 4569
4035 4570 $html_attrs_arr[] = sprintf( '%s="%s"', esc_attr( $key ), esc_attr( $value ) );
@@ -4087,14 +4622,13 @@
4087 4622 * @since 5.0.07
4088 4623 *
4089 4624 * @param array $values
4090 4625 * @param array $keys
4626 + *
4091 4627 * @return array
4092 4628 */
4093 4629 public static function maybe_filter_array( $values, $keys ) {
4094 - $allow_unfiltered_html = self::allow_unfiltered_html();
4095 -
4096 - if ( $allow_unfiltered_html ) {
4630 + if ( self::allow_unfiltered_html() ) {
4097 4631 return $values;
4098 4632 }
4099 4633
4100 4634 foreach ( $keys as $key ) {
@@ -4113,13 +4647,14 @@
4113 4647 * @since 5.0.13
4114 4648 *
4115 4649 * @param string $value
4116 4650 * @param array|string $allowed 'all' for everything included as defaults.
4651 + *
4117 4652 * @return string
4118 4653 */
4119 4654 public static function maybe_kses( $value, $allowed = 'all' ) {
4120 4655 if ( self::should_never_allow_unfiltered_html() ) {
4121 - $value = self::kses( $value, $allowed );
4656 + return self::kses( $value, $allowed );
4122 4657 }
4123 4658 return $value;
4124 4659 }
4125 4660
@@ -4129,17 +4664,18 @@
4129 4664 * @since 6.11.2
4130 4665 *
4131 4666 * @param string $key
4132 4667 * @param string $context Either 'display' or 'update'. On update, we want to allow a few keys that are never displayed.
4668 + *
4133 4669 * @return bool
4134 4670 */
4135 4671 public static function input_key_is_safe( $key, $context = 'display' ) {
4136 4672 if ( 'update' === $context && in_array( $key, array( 'opt', 'label' ), true ) ) {
4137 4673 $safe = true;
4138 - } elseif ( 0 === strpos( $key, 'data-' ) ) {
4674 + } elseif ( str_starts_with( $key, 'data-' ) ) {
4139 4675 // Allow all data attributes.
4140 4676 $safe = true;
4141 - } elseif ( 0 === strpos( $key, 'aria-' ) ) {
4677 + } elseif ( str_starts_with( $key, 'aria-' ) ) {
4142 4678 // Allow all aria attributes.
4143 4679 $safe = true;
4144 4680 } else {
4145 4681 $safe_keys = array(
@@ -4181,16 +4717,9 @@
4181 4717
4182 4718 /**
4183 4719 * @since 5.0.16
4184 4720 *
4185 - * @return bool
4186 - */
4187 - public static function show_landing_pages() {
4188 - return self::show_new_feature( 'landing' );
4189 - }
4190 -
4191 - /**
4192 - * @since 5.0.16
4721 + * @param string $medium
4193 4722 *
4194 4723 * @return array
4195 4724 */
4196 4725 public static function get_landing_page_upgrade_data_params( $medium = 'landing' ) {
@@ -4198,8 +4727,9 @@
4198 4727 'medium' => $medium,
4199 4728 'upgrade' => __( 'Form Landing Pages', 'formidable' ),
4200 4729 'message' => __( 'Easily manage a landing page for your form. Upgrade to get form landing pages.', 'formidable' ),
4201 4730 'screenshot' => 'landing.png',
4731 + 'learn-more' => self::get_doc_url( 'landing-page-forms', 'form-landing-page-settings', false ),
4202 4732 );
4203 4733 return self::get_upgrade_data_params( 'landing', $params );
4204 4734 }
4205 4735
@@ -4206,8 +4736,9 @@
4206 4736 /**
4207 4737 * @since 5.0.17
4208 4738 *
4209 4739 * @param string $feature
4740 + *
4210 4741 * @return bool
4211 4742 */
4212 4743 public static function show_new_feature( $feature ) {
4213 4744 $link = FrmAddonsController::install_link( $feature );
@@ -4221,12 +4752,14 @@
4221 4752 *
4222 4753 * @param string $plugin The plugin slug to get installation data for.
4223 4754 * @param array $params Initial parameters for the upgrade data.
4224 4755 * @param bool $detailed Whether to include detailed information.
4756 + *
4225 4757 * @return array Modified parameters with installation data.
4226 4758 */
4227 4759 public static function get_upgrade_data_params( $plugin, $params, $detailed = false ) {
4228 4760 $link = FrmAddonsController::install_link( $plugin );
4761 +
4229 4762 if ( ! $link ) {
4230 4763 return $params;
4231 4764 }
4232 4765
@@ -4232,8 +4765,9 @@
4232 4765
4233 4766 if ( ! empty( $link['url'] ) && self::pro_is_installed() ) {
4234 4767 $params['oneclick'] = json_encode( $link );
4235 4768 unset( $params['message'] );
4769 +
4236 4770 if ( ! isset( $params['medium'] ) ) {
4237 4771 $params['medium'] = $plugin;
4238 4772 }
4239 4773 } else {
@@ -4253,8 +4787,9 @@
4253 4787 *
4254 4788 * @since 5.0.17
4255 4789 *
4256 4790 * @param string $text
4791 + *
4257 4792 * @return bool
4258 4793 */
4259 4794 public static function ctype_xdigit( $text ) {
4260 4795 if ( function_exists( 'ctype_xdigit' ) ) {
@@ -4269,12 +4804,14 @@
4269 4804 * @since 5.2.01
4270 4805 */
4271 4806 public static function set_current_screen_and_hook_suffix() {
4272 4807 global $hook_suffix;
4808 +
4273 4809 if ( is_null( $hook_suffix ) ) {
4274 4810 // $hook_suffix gets used in substr so make sure it's not null. PHP 8.1 deprecates null in substr.
4275 4811 $hook_suffix = ''; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
4276 4812 }
4813 +
4277 4814 set_current_screen();
4278 4815 }
4279 4816
4280 4817 /**
@@ -4281,9 +4818,9 @@
4281 4818 * Shows pill text.
4282 4819 *
4283 4820 * @since 5.2.02
4284 4821 *
4285 - * @param string $text Text in the pill. Default is NEW.
4822 + * @param string|null $text Text in the pill. Default is NEW.
4286 4823 */
4287 4824 public static function show_pill_text( $text = null ) {
4288 4825 if ( null === $text ) {
4289 4826 $text = __( 'NEW', 'formidable' );
@@ -4296,8 +4833,9 @@
4296 4833 *
4297 4834 * @since 5.2.07
4298 4835 *
4299 4836 * @param mixed $num Number.
4837 + *
4300 4838 * @return false|int Returns `false` if the passed parameter is not number.
4301 4839 */
4302 4840 public static function count_decimals( $num ) {
4303 4841 if ( ! is_numeric( $num ) ) {
@@ -4305,8 +4843,9 @@
4305 4843 }
4306 4844
4307 4845 $num = (string) $num;
4308 4846 $parts = explode( '.', $num );
4847 +
4309 4848 if ( 1 === count( $parts ) ) {
4310 4849 return 0;
4311 4850 }
4312 4851
@@ -4388,17 +4927,20 @@
4388 4927 * @since 5.5.5
4389 4928 *
4390 4929 * @param string $url
4391 4930 * @param string $expected_extension
4931 + *
4392 4932 * @return bool
4393 4933 */
4394 4934 public static function validate_url_is_in_s3_bucket( $url, $expected_extension ) {
4395 - $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 +
4396 4937 if ( ! $file_is_in_expected_s3_bucket ) {
4397 4938 return false;
4398 4939 }
4399 4940
4400 4941 $parsed = parse_url( $url );
4942 +
4401 4943 if ( ! is_array( $parsed ) ) {
4402 4944 // URL is malformed.
4403 4945 return false;
4404 4946 }
@@ -4404,14 +4946,10 @@
4404 4946 }
4405 4947
4406 4948 $path = $parsed['path'];
4407 4949 $ext = pathinfo( $path, PATHINFO_EXTENSION );
4408 - if ( $expected_extension !== $ext ) {
4409 - // The URL isn't to an XML file.
4410 - return false;
4411 - }
4412 -
4413 - return true;
4950 + // The URL isn't to an XML file.
4951 + return $expected_extension === $ext;
4414 4952 }
4415 4953
4416 4954 /**
4417 4955 * Display a dismissable warning message and save its dismissal state.
@@ -4419,8 +4957,9 @@
4419 4957 * @since 6.3
4420 4958 *
4421 4959 * @param string $message The warning message to display.
4422 4960 * @param string $option The unique identifier for the dismissal state of the message and the WP Ajax action.
4961 + *
4423 4962 * @return void
4424 4963 */
4425 4964 public static function add_dismissable_warning_message( $message = '', $option = '' ) {
4426 4965 if ( ! $message || ! $option ) {
@@ -4463,8 +5002,9 @@
4463 5002 *
4464 5003 * @since 6.3
4465 5004 *
4466 5005 * @param string $option The unique identifier for the dismissal state of the message.
5006 + *
4467 5007 * @return void
4468 5008 */
4469 5009 public static function dismiss_warning_message( $option = '' ) {
4470 5010 self::permission_check( 'frm_change_settings' );
@@ -4470,9 +5010,9 @@
4470 5010 self::permission_check( 'frm_change_settings' );
4471 5011 check_ajax_referer( 'frm_ajax', 'nonce' );
4472 5012
4473 5013 if ( $option ) {
4474 - update_option( $option, true, 'no' );
5014 + update_option( $option, true, false );
4475 5015 }
4476 5016
4477 5017 wp_send_json_success();
4478 5018 }
@@ -4486,17 +5026,8 @@
4486 5026 * @return string
4487 5027 */
4488 5028 public static function copy_for_lite_license() {
4489 5029 $message = __( 'You\'re using Formidable Forms Lite - no license needed. Enjoy!', 'formidable' ) . ' 🙂';
4490 -
4491 - if ( is_callable( 'FrmProAddonsController::get_readable_license_type' ) && ! class_exists( 'FrmProDashboardController' ) ) {
4492 - // Manage PRO versions without PRO dashboard functionality.
4493 - $license_type = FrmProAddonsController::get_readable_license_type();
4494 - if ( 'lite' !== strtolower( $license_type ) ) {
4495 - $message = 'Formidable Pro ' . $license_type;
4496 - }
4497 - }
4498 -
4499 5030 return apply_filters( 'frm_license_type_text', $message );
4500 5031 }
4501 5032
4502 5033 /**
@@ -4502,8 +5033,9 @@
4502 5033 /**
4503 5034 * Removes scripts that are unnecessarily loaded across the pages!
4504 5035 *
4505 5036 * @since 6.9
5037 + *
4506 5038 * @return void
4507 5039 */
4508 5040 public static function dequeue_extra_global_scripts() {
4509 5041 wp_dequeue_script( 'frm-surveys-admin' );
@@ -4521,18 +5053,21 @@
4521 5053 * @return void
4522 5054 */
4523 5055 public static function tooltip_icon( $tooltip_text, $atts = array() ) {
4524 5056 $atts['title'] = $tooltip_text;
5057 +
4525 5058 if ( isset( $atts['class'] ) ) {
4526 5059 $atts['class'] .= ' frm_help';
4527 5060 } else {
4528 5061 $atts['class'] = 'frm_help';
4529 5062 }
5063 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
4530 5064 ?>
4531 5065 <span <?php self::array_to_html_params( $atts, true ); ?>>
4532 5066 <?php self::icon_by_class( 'frmfont frm_tooltip_icon' ); ?>
4533 5067 </span>
4534 5068 <?php
5069 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
4535 5070 }
4536 5071
4537 5072 /**
4538 5073 * Prints errors for settings in onboarding wizard or template settings.
@@ -4553,8 +5088,10 @@
4553 5088 )
4554 5089 );
4555 5090
4556 5091 $args['class'] .= ' frm-validation-error frm-mt-xs frm_hidden';
5092 +
5093 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
4557 5094 ?>
4558 5095 <span id="<?php echo esc_attr( $args['id'] ); ?>" class="<?php echo esc_attr( $args['class'] ); ?>">
4559 5096 <?php
4560 5097 if ( is_array( $args['errors'] ) ) {
@@ -4568,8 +5105,9 @@
4568 5105 }
4569 5106 ?>
4570 5107 </span>
4571 5108 <?php
5109 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
4572 5110 }
4573 5111
4574 5112 /**
4575 5113 * Check if GDPR is enabled.
@@ -4599,19 +5137,55 @@
4599 5137 * Check if a string is valid UTF-8.
4600 5138 *
4601 5139 * @since 6.24
4602 5140 *
4603 - * @param string $string The string to check.
5141 + * @param string|null $string The string to check.
5142 + *
4604 5143 * @return bool
4605 5144 */
4606 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 +
4607 5151 // wp_is_valid_utf8 is added in WP 6.9.
4608 5152 if ( function_exists( 'wp_is_valid_utf8' ) ) {
4609 5153 return wp_is_valid_utf8( $string );
4610 5154 }
5155 +
4611 5156 // As of WP 6.9, seems_utf8 is deprecated.
4612 - if ( function_exists( 'seems_utf8' ) ) {
4613 - 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;
4614 5176 }
4615 - 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;
4616 5190 }
4617 5191 }