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 +1882 -694 6.5.3 → trunk View file →
@@ -3,15 +3,26 @@
3 3 die( 'You are not allowed to call this page directly.' );
4 4 }
5 5
6 6 class FrmAppHelper {
7 - public static $db_version = 98; // Version of the database we are moving to.
8 - public static $pro_db_version = 37; //deprecated
9 - public static $font_version = 7;
10 7
11 8 /**
12 - * @var bool $added_gmt_offset_filter
9 + * Version of the database we are moving to.
10 + *
11 + * @var int
13 12 */
13 + public static $db_version = 106;
14 +
15 + /**
16 + * Used by the API add-on.
17 + *
18 + * @var float
19 + */
20 + public static $font_version = 7;
21 +
22 + /**
23 + * @var bool
24 + */
14 25 private static $added_gmt_offset_filter = false;
15 26
16 27 /**
17 28 * @since 2.0
@@ -17,9 +28,9 @@
17 28 * @since 2.0
18 29 *
19 30 * @var string
20 31 */
21 - public static $plug_version = '6.5.3';
32 + public static $plug_version = '6.35';
22 33
23 34 /**
24 35 * @var bool
25 36 */
@@ -27,10 +38,8 @@
27 38
28 39 /**
29 40 * @since 1.07.02
30 41 *
31 - * @param none
32 - *
33 42 * @return string The version of this plugin
34 43 */
35 44 public static function plugin_version() {
36 45 return self::$plug_version;
@@ -35,21 +44,33 @@
35 44 public static function plugin_version() {
36 45 return self::$plug_version;
37 46 }
38 47
48 + /**
49 + * @return string
50 + */
39 51 public static function plugin_folder() {
40 52 return basename( self::plugin_path() );
41 53 }
42 54
55 + /**
56 + * @return string
57 + */
43 58 public static function plugin_path() {
44 - return dirname( dirname( dirname( __FILE__ ) ) );
59 + return dirname( __DIR__, 2 );
45 60 }
46 61
62 + /**
63 + * @return string
64 + */
47 65 public static function plugin_url() {
48 - // Prevously FRM_URL constant.
66 + // Previously FRM_URL constant.
49 67 return plugins_url( '', self::plugin_path() . '/formidable.php' );
50 68 }
51 69
70 + /**
71 + * @return string
72 + */
52 73 public static function relative_plugin_url() {
53 74 return str_replace( array( 'https:', 'http:' ), '', self::plugin_url() );
54 75 }
55 76
@@ -64,8 +85,9 @@
64 85 * Get the name of this site
65 86 * Used for [sitename] shortcode
66 87 *
67 88 * @since 2.0
89 + *
68 90 * @return string
69 91 */
70 92 public static function site_name() {
71 93 return get_option( 'blogname' );
@@ -70,11 +92,17 @@
70 92 public static function site_name() {
71 93 return get_option( 'blogname' );
72 94 }
73 95
96 + /**
97 + * @param string $url
98 + *
99 + * @return string
100 + */
74 101 public static function make_affiliate_url( $url ) {
75 102 $affiliate_id = self::get_affiliate();
76 - if ( ! empty( $affiliate_id ) ) {
103 +
104 + if ( $affiliate_id ) {
77 105 $url = str_replace( array( 'http://', 'https://' ), '', $url );
78 106 $url = 'http://www.shareasale.com/r.cfm?u=' . absint( $affiliate_id ) . '&b=841990&m=64739&afftrack=plugin&urllink=' . urlencode( $url );
79 107 }
80 108
@@ -80,8 +108,11 @@
80 108
81 109 return $url;
82 110 }
83 111
112 + /**
113 + * @return int
114 + */
84 115 public static function get_affiliate() {
85 116 return absint( apply_filters( 'frm_affiliate_id', 0 ) );
86 117 }
87 118
@@ -86,65 +117,167 @@
86 117 }
87 118
88 119 /**
89 120 * @since 3.04.02
90 - * @param array|string $args
121 + *
122 + * @param array|string $args If a string is passed, it is used for the utm_campaign attribute.
91 123 * @param string $page
92 124 */
93 125 public static function admin_upgrade_link( $args, $page = '' ) {
94 - if ( empty( $page ) ) {
95 - $page = 'https://formidableforms.com/lite-upgrade/';
96 - } else {
126 + if ( $page ) {
97 127 $page = str_replace( 'https://formidableforms.com/', '', $page );
98 128 $page = 'https://formidableforms.com/' . $page;
129 + } else {
130 + $page = 'https://formidableforms.com/lite-upgrade/';
99 131 }
100 132
101 - $anchor = '';
102 - if ( is_array( $args ) ) {
103 - $medium = $args['medium'];
104 - if ( isset( $args['content'] ) ) {
105 - $content = $args['content'];
106 - }
107 - if ( isset( $args['anchor'] ) ) {
108 - $anchor = '#' . $args['anchor'];
109 - }
110 - } else {
111 - $medium = $args;
112 - }
133 + $args = is_array( $args ) ? self::adjust_legacy_utm_args( $args ) : array( 'campaign' => $args );
113 134
114 135 $query_args = array(
115 - 'utm_source' => 'WordPress',
116 - 'utm_medium' => $medium,
117 - 'utm_campaign' => 'liteplugin',
136 + 'utm_source' => 'plugin',
137 + 'utm_medium' => self::get_utm_medium(),
118 138 );
139 + $query_args = self::maybe_add_utm_license( $query_args );
119 140
120 - if ( isset( $content ) ) {
121 - $query_args['utm_content'] = $content;
141 + if ( isset( $args['campaign'] ) ) {
142 + $query_args['utm_campaign'] = $args['campaign'];
122 143 }
123 144
124 - 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'] ) ) {
125 150 $query_args['f'] = $args['param'];
126 151 }
127 152
128 - if ( is_array( $args ) && ! empty( $args['plan'] ) ) {
153 + if ( ! empty( $args['plan'] ) ) {
129 154 $query_args['plan'] = $args['plan'];
130 155 }
131 156
132 - $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 +
133 163 return self::make_affiliate_url( $link );
134 164 }
135 165
136 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 + /**
237 + * @since 6.21
238 + *
239 + * @param string $cta_link
240 + * @param array $utm
241 + */
242 + public static function maybe_add_missing_utm( $cta_link, $utm ) {
243 + $utm = self::adjust_legacy_utm_args( $utm );
244 + $query_args = array();
245 +
246 + if ( ! str_contains( $cta_link, 'utm_source' ) ) {
247 + $query_args['utm_source'] = 'plugin';
248 + }
249 +
250 + if ( ! str_contains( $cta_link, 'utm_medium' ) ) {
251 + $query_args['utm_medium'] = self::get_utm_medium();
252 + }
253 +
254 + if ( ! str_contains( $cta_link, 'utm_campaign' ) && isset( $utm['campaign'] ) ) {
255 + $query_args['utm_campaign'] = $utm['campaign'];
256 + }
257 +
258 + if ( ! str_contains( $cta_link, 'utm_content' ) && isset( $utm['content'] ) ) {
259 + $query_args['utm_content'] = $utm['content'];
260 + }
261 +
262 + $query_args = self::maybe_add_utm_license( $query_args, $cta_link );
263 +
264 + return $query_args ? add_query_arg( $query_args, $cta_link ) : $cta_link;
265 + }
266 +
267 + /**
137 268 * Get the Formidable settings
138 269 *
139 270 * @since 2.0
140 271 *
141 272 * @param array $args - May include the form id when values need translation.
142 - * @return FrmSettings $frm_setings
273 + *
274 + * @return FrmSettings
143 275 */
144 276 public static function get_settings( $args = array() ) {
145 277 global $frm_settings;
146 - if ( empty( $frm_settings ) ) {
278 +
279 + if ( ! $frm_settings ) {
147 280 $frm_settings = new FrmSettings( $args );
148 281 } elseif ( isset( $args['current_form'] ) ) {
149 282 // If the global has already been set, allow strings to be filtered.
150 283 $frm_settings->maybe_filter_for_form( $args );
@@ -152,32 +285,41 @@
152 285
153 286 return $frm_settings;
154 287 }
155 288
289 + /**
290 + * @return string
291 + */
156 292 public static function get_menu_name() {
157 - $frm_settings = self::get_settings();
293 + if ( ! self::pro_is_installed() || FrmAddonsController::is_license_expired() ) {
294 + return 'Formidable';
295 + }
158 296
159 - return FrmAddonsController::is_license_expired() ? 'Formidable' : $frm_settings->menu;
297 + return self::get_settings()->menu;
160 298 }
161 299
162 300 /**
163 301 * Determine if the current branding is set to 'formidable'.
302 + * Checks the menu icon, and verifies if it matches the formidable branding.
164 303 *
165 - * Checks the menu title, retrieved through get_menu_name,
166 - * and verifies if it matches the 'formidable' branding.
167 - *
168 304 * @since 6.4.2
169 305 *
170 - * @return bool True if the menu title is 'formidable', false otherwise.
306 + * @return bool True if the menu icon is the logo, false otherwise.
171 307 */
172 308 public static function is_formidable_branding() {
173 - $menu_title = self::get_menu_name();
309 + if ( ! self::pro_is_installed() ) {
310 + return true;
311 + }
174 312
175 - return 'formidable' === strtolower( trim( $menu_title ) );
313 + return str_contains( self::get_menu_icon_class(), 'frm_logo_icon' );
176 314 }
177 315
178 316 /**
179 317 * @since 3.05
318 + *
319 + * @param array $atts
320 + *
321 + * @return string
180 322 */
181 323 public static function svg_logo( $atts = array() ) {
182 324 $defaults = array(
183 325 'height' => 18,
@@ -186,23 +328,31 @@
186 328 'orange' => '#f05a24',
187 329 );
188 330 $atts = array_merge( $defaults, $atts );
189 331
332 + // phpcs:disable SlevomatCodingStandard.Files.LineLength.LineTooLong
190 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'] ) . '">
191 334 <path fill="' . esc_attr( $atts['orange'] ) . '" d="M289.6 384h140v76h-140z"/>
192 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"/>
193 336 </svg>';
337 + // phpcs:enable SlevomatCodingStandard.Files.LineLength.LineTooLong
194 338 }
195 339
196 340 /**
197 341 * @since 4.0
342 + *
343 + * @param array $atts
344 + *
345 + * @return void
198 346 */
199 347 public static function show_logo( $atts = array() ) {
200 - echo self::kses( self::svg_logo( $atts ), 'all' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
348 + self::kses_echo( self::svg_logo( $atts ), 'all' );
201 349 }
202 350
203 351 /**
204 352 * @since 4.03.02
353 + *
354 + * @return void
205 355 */
206 356 public static function show_header_logo() {
207 357 $icon = self::svg_logo(
208 358 array(
@@ -211,10 +361,11 @@
211 361 )
212 362 );
213 363
214 364 $new_icon = apply_filters( 'frm_icon', $icon, true );
365 +
215 366 if ( $new_icon !== $icon ) {
216 - if ( strpos( $new_icon, '<svg' ) === 0 ) {
367 + if ( str_starts_with( $new_icon, '<svg' ) ) {
217 368 $icon = str_replace( 'viewBox="0 0 20', 'width="30" height="35" style="color:#929699" viewBox="0 0 20', $new_icon );
218 369 } else {
219 370 // Show nothing if it isn't an SVG.
220 371 $icon = '<div style="height:39px"></div>';
@@ -219,26 +370,43 @@
219 370 // Show nothing if it isn't an SVG.
220 371 $icon = '<div style="height:39px"></div>';
221 372 }
222 373 }
223 - echo self::kses( $icon, 'all' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
374 + self::kses_echo( $icon, 'all' );
224 375 }
225 376
226 377 /**
227 378 * @since 2.02.04
379 + *
380 + * @return bool
228 381 */
229 382 public static function ips_saved() {
230 383 $frm_settings = self::get_settings();
231 -
232 384 return ! $frm_settings->no_ips;
233 385 }
234 386
387 + /**
388 + * @return bool
389 + */
235 390 public static function pro_is_installed() {
236 - return apply_filters( 'frm_pro_installed', false );
391 + return (bool) apply_filters( 'frm_pro_installed', false );
237 392 }
238 393
239 394 /**
395 + * Check if the Pro plugin is installed, whether authorized or not.
396 + *
397 + * @since 6.8.3
398 + *
399 + * @return bool
400 + */
401 + public static function pro_is_included() {
402 + return function_exists( 'load_formidable_pro' );
403 + }
404 +
405 + /**
240 406 * @since 4.06.02
407 + *
408 + * @return bool
241 409 */
242 410 public static function pro_is_connected() {
243 411 global $frm_vars;
244 412 return self::pro_is_installed() && $frm_vars['pro_is_authorized'];
@@ -245,39 +413,94 @@
245 413 }
246 414
247 415 /**
248 416 * @since 4.06
417 + * @since 6.16.2 Added $check_for_settings parameter
418 + *
419 + * @param bool $check_for_settings
420 + *
421 + * @return bool
249 422 */
250 - public static function is_form_builder_page() {
251 - $action = self::simple_get( 'frm_action', 'sanitize_title' );
252 - return self::is_admin_page( 'formidable' ) && ( $action === 'edit' || $action === 'settings' || $action === 'duplicate' );
423 + public static function is_form_builder_page( $check_for_settings = true ) {
424 + $action = self::simple_get( 'frm_action', 'sanitize_title' );
425 + $check_actions = array( 'edit', 'duplicate' );
426 +
427 + if ( $check_for_settings ) {
428 + $check_actions[] = 'settings';
429 + }
430 +
431 + return self::is_admin_page( 'formidable' ) && in_array( $action, $check_actions, true );
253 432 }
254 433
434 + /**
435 + * @return bool
436 + */
255 437 public static function is_formidable_admin() {
256 - $page = self::simple_get( 'page', 'sanitize_title' );
257 - $is_formidable = strpos( $page, 'formidable' ) !== false;
258 - if ( empty( $page ) ) {
259 - $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();
260 442 }
261 443
262 - return $is_formidable;
444 + return str_contains( $page, 'formidable' );
263 445 }
264 446
265 447 /**
448 + * Checks if is a list page.
449 + *
450 + * @since 6.19
451 + *
452 + * @param string $page The name of the page to check.
453 + *
454 + * @return bool
455 + */
456 + public static function is_admin_list_page( $page = 'formidable' ) {
457 + if ( 'formidable' === $page ) {
458 + return self::on_form_listing_page();
459 + }
460 +
461 + if ( ! self::is_admin_page( $page ) ) {
462 + return false;
463 + }
464 +
465 + if ( 'formidable-entries' === $page ) {
466 + $action = self::simple_get( 'frm_action' );
467 +
468 + if ( ! $action || in_array( $action, self::get_entries_listing_page_form_actions(), true ) ) {
469 + return true;
470 + }
471 + }
472 +
473 + // Check edit or settings page.
474 + return ! self::simple_get( 'frm_action' );
475 + }
476 +
477 + /**
478 + * @since 6.20
479 + *
480 + * @return array<string>
481 + */
482 + private static function get_entries_listing_page_form_actions() {
483 + return array( 'list', 'destroy' );
484 + }
485 +
486 + /**
266 487 * Check for certain page in Formidable settings
267 488 *
268 489 * @since 2.0
269 490 *
270 - * @param string $page The name of the page to check
491 + * @param string $page The name of the page to check.
271 492 *
272 - * @return boolean
493 + * @return bool
273 494 */
274 495 public static function is_admin_page( $page = 'formidable' ) {
275 496 global $pagenow;
276 497 $get_page = self::simple_get( 'page', 'sanitize_title' );
498 +
277 499 if ( $pagenow ) {
278 - // 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
279 501 $is_page = ( $pagenow === 'admin.php' || $pagenow === 'admin-ajax.php' ) && $get_page === $page;
502 +
280 503 if ( $is_page ) {
281 504 return true;
282 505 }
283 506 }
@@ -289,21 +512,23 @@
289 512 * If the current page is for editing or creating a view.
290 513 * Returns false for the views listing page.
291 514 *
292 515 * @since 4.0
516 + *
517 + * @return bool
293 518 */
294 519 public static function is_view_builder_page() {
295 520 global $pagenow;
296 521
297 - 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 ) ) {
298 523 return false;
299 524 }
300 525
301 526 $post_type = self::simple_get( 'post_type', 'sanitize_title' );
302 527
303 - if ( empty( $post_type ) ) {
304 - $post_id = self::simple_get( 'post', 'absint' );
305 - $post = get_post( $post_id );
528 + if ( ! $post_type ) {
529 + $post_id = self::simple_get( 'post', 'absint' );
530 + $post = get_post( $post_id );
306 531 $post_type = $post ? $post->post_type : '';
307 532 }
308 533
309 534 return $post_type === 'frm_display';
@@ -313,17 +538,15 @@
313 538 * Check for the form preview page
314 539 *
315 540 * @since 2.0
316 541 *
317 - * @param None
318 - *
319 - * @return boolean
542 + * @return bool
320 543 */
321 544 public static function is_preview_page() {
322 545 global $pagenow;
323 546 $action = self::simple_get( 'action', 'sanitize_title' );
324 547
325 - return $pagenow && $pagenow == 'admin-ajax.php' && $action == 'frm_forms_preview';
548 + return $pagenow === 'admin-ajax.php' && $action === 'frm_forms_preview';
326 549 }
327 550
328 551 /**
329 552 * Check for ajax except the form preview page
@@ -329,16 +552,17 @@
329 552 * Check for ajax except the form preview page
330 553 *
331 554 * @since 2.0
332 555 *
333 - * @param None
334 - *
335 - * @return boolean
556 + * @return bool
336 557 */
337 558 public static function doing_ajax() {
338 559 return wp_doing_ajax() && ! self::is_preview_page();
339 560 }
340 561
562 + /**
563 + * @return string
564 + */
341 565 public static function js_suffix() {
342 566 return defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
343 567 }
344 568
@@ -343,13 +567,14 @@
343 567 }
344 568
345 569 /**
346 570 * @since 2.0.8
571 + *
572 + * @return bool
347 573 */
348 574 public static function prevent_caching() {
349 575 global $frm_vars;
350 -
351 - return isset( $frm_vars['prevent_caching'] ) && $frm_vars['prevent_caching'];
576 + return ! empty( $frm_vars['prevent_caching'] );
352 577 }
353 578
354 579 /**
355 580 * Check if on an admin page
@@ -355,9 +580,9 @@
355 580 * Check if on an admin page
356 581 *
357 582 * @since 2.0
358 583 *
359 - * @return boolean
584 + * @return bool
360 585 */
361 586 public static function is_admin() {
362 587 $is_admin = is_admin() && ! wp_doing_ajax();
363 588
@@ -362,8 +587,9 @@
362 587 $is_admin = is_admin() && ! wp_doing_ajax();
363 588
364 589 /**
365 590 * @since 6.0
591 + *
366 592 * @param bool $is_admin
367 593 */
368 594 return apply_filters( 'frm_is_admin', $is_admin );
369 595 }
@@ -372,17 +598,23 @@
372 598 * Check if value contains blank value or empty array
373 599 *
374 600 * @since 2.0
375 601 *
376 - * @param mixed $value - value to check
377 - * @param string
602 + * @param mixed $value Value to check.
603 + * @param string $empty
378 604 *
379 - * @return boolean
605 + * @return bool
380 606 */
381 607 public static function is_empty_value( $value, $empty = '' ) {
382 - return ( is_array( $value ) && empty( $value ) ) || $value === $empty;
608 + return $value === array() || $value === $empty;
383 609 }
384 610
611 + /**
612 + * @param mixed $value
613 + * @param string $empty
614 + *
615 + * @return bool
616 + */
385 617 public static function is_not_empty_value( $value, $empty = '' ) {
386 618 return ! self::is_empty_value( $value, $empty );
387 619 }
388 620
@@ -434,10 +666,12 @@
434 666 continue;
435 667 }
436 668
437 669 $key = self::get_server_value( $key );
670 +
438 671 foreach ( explode( ',', $key ) as $ip ) {
439 - $ip = trim( $ip ); // Just to be safe.
672 + // Just to be safe.
673 + $ip = trim( $ip );
440 674
441 675 if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) !== false ) {
442 676 return sanitize_text_field( $ip );
443 677 }
@@ -490,16 +724,26 @@
490 724 */
491 725 return apply_filters( 'frm_use_custom_header_ip', $should_use_custom_header_ip );
492 726 }
493 727
728 + /**
729 + * @param string $param
730 + * @param mixed $default
731 + * @param string $src
732 + * @param callable|string $sanitize
733 + *
734 + * @return mixed
735 + */
494 736 public static function get_param( $param, $default = '', $src = 'get', $sanitize = '' ) {
495 - if ( strpos( $param, '[' ) ) {
737 + if ( str_contains( $param, '[' ) ) {
496 738 $params = explode( '[', $param );
497 739 $param = $params[0];
498 740 }
499 741
500 742 if ( $src === 'get' ) {
501 - $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 +
502 746 if ( ! isset( $_POST[ $param ] ) && isset( $_GET[ $param ] ) && ! is_array( $value ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
503 747 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
504 748 $value = htmlspecialchars_decode( wp_unslash( $_GET[ $param ] ) );
505 749 }
@@ -514,29 +758,41 @@
514 758 )
515 759 );
516 760 }
517 761
518 - if ( isset( $params ) && is_array( $value ) && ! empty( $value ) ) {
519 - foreach ( $params as $k => $p ) {
520 - if ( ! $k || ! is_array( $value ) ) {
521 - continue;
522 - }
762 + if ( ! isset( $params ) || ! is_array( $value ) || ! $value ) {
763 + return $value;
764 + }
523 765
524 - $p = trim( $p, ']' );
525 - $value = isset( $value[ $p ] ) ? $value[ $p ] : $default;
766 + foreach ( $params as $k => $p ) {
767 + if ( ! $k || ! is_array( $value ) ) {
768 + continue;
526 769 }
770 +
771 + $p = trim( $p, ']' );
772 + $value = $value[ $p ] ?? $default;
527 773 }
528 774
529 775 return $value;
530 776 }
531 777
778 + /**
779 + * Get a value from $_POST data.
780 + *
781 + * @param string $param The key we are trying to access data from in $_POST.
782 + * @param mixed $default The default if nothing is being sent.
783 + * @param callable|string $sanitize Make sure to pass a sanitize method here. This function will NOT sanitize by default.
784 + * @param bool $serialized
785 + *
786 + * @return mixed
787 + */
532 788 public static function get_post_param( $param, $default = '', $sanitize = '', $serialized = false ) {
533 789 return self::get_simple_request(
534 790 array(
535 - 'type' => 'post',
536 - 'param' => $param,
537 - 'default' => $default,
538 - 'sanitize' => $sanitize,
791 + 'type' => 'post',
792 + 'param' => $param,
793 + 'default' => $default,
794 + 'sanitize' => $sanitize,
539 795 'serialized' => $serialized,
540 796 )
541 797 );
542 798 }
@@ -547,9 +803,9 @@
547 803 * @param string $param
548 804 * @param string $sanitize
549 805 * @param string $default
550 806 *
551 - * @return string|array
807 + * @return array|int|string
552 808 */
553 809 public static function simple_get( $param, $sanitize = 'sanitize_text_field', $default = '' ) {
554 810 return self::get_simple_request(
555 811 array(
@@ -567,21 +823,21 @@
567 823 * @since 2.0.6
568 824 *
569 825 * @param array $args
570 826 *
571 - * @return string|array
827 + * @return array|string
572 828 */
573 829 public static function get_simple_request( $args ) {
574 830 $defaults = array(
575 - 'param' => '',
576 - 'default' => '',
577 - 'type' => 'get',
578 - 'sanitize' => 'sanitize_text_field',
831 + 'param' => '',
832 + 'default' => '',
833 + 'type' => 'get',
834 + 'sanitize' => 'sanitize_text_field',
579 835 'serialized' => false,
580 836 );
581 837 $args = wp_parse_args( $args, $defaults );
838 + $value = $args['default'];
582 839
583 - $value = $args['default'];
584 840 if ( $args['type'] === 'get' ) {
585 841 if ( $_GET && isset( $_GET[ $args['param'] ] ) ) {
586 842 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing
587 843 $value = wp_unslash( $_GET[ $args['param'] ] );
@@ -589,17 +845,16 @@
589 845 } elseif ( $args['type'] === 'post' ) {
590 846 if ( isset( $_POST[ $args['param'] ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
591 847 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing
592 848 $value = wp_unslash( $_POST[ $args['param'] ] );
849 +
593 850 if ( $args['serialized'] === true && is_serialized_string( $value ) && is_serialized( $value ) ) {
594 851 self::unserialize_or_decode( $value );
595 852 }
596 853 }
597 - } else {
598 - if ( isset( $_REQUEST[ $args['param'] ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
599 - // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
600 - $value = wp_unslash( $_REQUEST[ $args['param'] ] );
601 - }
854 + } elseif ( isset( $_REQUEST[ $args['param'] ] ) ) {
855 + // phpcs:ignore phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
856 + $value = wp_unslash( $_REQUEST[ $args['param'] ] );
602 857 }
603 858
604 859 self::sanitize_value( $args['sanitize'], $value );
605 860
@@ -612,34 +867,56 @@
612 867 * @since 2.0.8
613 868 *
614 869 * @param string $value
615 870 *
616 - * @return string $value
871 + * @return string Value.
617 872 */
618 873 public static function preserve_backslashes( $value ) {
619 874 // If backslashes have already been added, don't add them again
620 - if ( strpos( $value, '\\\\' ) === false ) {
621 - $value = addslashes( $value );
875 + return str_contains( $value, '\\\\' ) ? $value : addslashes( $value );
876 + }
877 +
878 + /**
879 + * Sanitize a value in-place.
880 + * If $value is an array, the sanitize function will get called for each item.
881 + *
882 + * @param callable $sanitize
883 + * @param mixed $value
884 + *
885 + * @return void
886 + */
887 + public static function sanitize_value( $sanitize, &$value ) {
888 + if ( ! $sanitize ) {
889 + return;
622 890 }
623 891
624 - return $value;
625 - }
892 + if ( is_object( $value ) ) {
893 + $value = '';
894 + return;
895 + }
626 896
627 - public static function sanitize_value( $sanitize, &$value ) {
628 - if ( ! empty( $sanitize ) ) {
629 - if ( is_array( $value ) ) {
630 - $temp_values = $value;
631 - foreach ( $temp_values as $k => $v ) {
632 - self::sanitize_value( $sanitize, $value[ $k ] );
633 - }
634 - } else {
635 - $value = call_user_func( $sanitize, $value );
897 + if ( is_array( $value ) ) {
898 + $temp_values = $value;
899 +
900 + foreach ( $temp_values as $k => $v ) {
901 + self::sanitize_value( $sanitize, $value[ $k ] );
636 902 }
903 +
904 + return;
637 905 }
906 +
907 + $value = call_user_func( $sanitize, $value );
638 908 }
639 909
910 + /**
911 + * @param array $sanitize_method
912 + * @param array $values
913 + *
914 + * @return void
915 + */
640 916 public static function sanitize_request( $sanitize_method, &$values ) {
641 917 $temp_values = $values;
918 +
642 919 foreach ( $temp_values as $k => $val ) {
643 920 if ( isset( $sanitize_method[ $k ] ) ) {
644 921 $values[ $k ] = call_user_func( $sanitize_method[ $k ], $val );
645 922 }
@@ -647,23 +924,69 @@
647 924 }
648 925
649 926 /**
650 927 * @since 4.0.04
928 + *
929 + * @param mixed $value
930 + *
931 + * @return void
651 932 */
652 933 public static function sanitize_with_html( &$value ) {
653 - self::sanitize_value( 'wp_kses_post', $value );
934 + if ( current_user_can( 'frm_edit_entries' ) || current_user_can( 'administrator' ) ) {
935 + // Only strip unsafe HTML like scripts for a privileged user submitting a form.
936 + self::sanitize_value( 'wp_kses_post', $value );
937 + } else {
938 + self::sanitize_value( self::class . '::strip_most_html', $value );
939 + }
654 940 self::decode_specialchars( $value );
941 + self::sanitize_value( 'FrmHtmlSanitizer::sanitize_url_attributes', $value );
655 942 }
656 943
657 944 /**
945 + * Allow only a small set of very basic HTML for unprivileged users.
946 + *
947 + * @since 6.7.1
948 + *
949 + * @param string $value
950 + */
951 + public static function strip_most_html( $value ) {
952 + if ( '' === $value ) {
953 + return $value;
954 + }
955 +
956 + $allowed_html = array(
957 + 'b' => array(),
958 + 'br' => array(),
959 + 'strong' => array(),
960 + 'p' => array(),
961 + 'i' => array(),
962 + 'ul' => array(),
963 + 'ol' => array(),
964 + 'li' => array(),
965 + );
966 +
967 + /**
968 + * @since 6.7.1
969 + *
970 + * @param array $allowed_html
971 + */
972 + $allowed_html = apply_filters( 'frm_allowed_form_input_html', $allowed_html );
973 +
974 + return wp_kses( $value, $allowed_html );
975 + }
976 +
977 + /**
658 978 * Do wp_specialchars_decode to get back '&' that wp_kses_post might have turned to '&amp;'
659 979 * this MUST be done, else we'll be back to the '& entity' problem.
660 980 *
661 981 * @since 4.0.04
982 + *
983 + * @param mixed $value Value to decode, passed by reference.
662 984 */
663 985 public static function decode_specialchars( &$value ) {
664 986 if ( is_array( $value ) ) {
665 987 $temp_values = $value;
988 +
666 989 foreach ( $temp_values as $k => $v ) {
667 990 self::decode_specialchars( $value[ $k ] );
668 991 }
669 992 } else {
@@ -677,13 +1000,13 @@
677 1000 * Adapted from wp_specialchars_decode().
678 1001 *
679 1002 * @since 4.03.01
680 1003 *
681 - * @param string $string The string to prep.
1004 + * @param string $string The string to prep, passed by reference.
682 1005 */
683 1006 private static function decode_amp( &$string ) {
684 1007 // Don't bother if there are no entities - saves a lot of processing
685 - if ( empty( $string ) || strpos( $string, '&' ) === false ) {
1008 + if ( ! $string || ! str_contains( $string, '&' ) ) {
686 1009 return;
687 1010 }
688 1011
689 1012 $translation = array(
@@ -689,10 +1012,12 @@
689 1012 $translation = array(
690 1013 '&quot;' => '"',
691 1014 '&#034;' => '"',
692 1015 '&#x22;' => '"',
693 - '&lt; ' => '< ', // The space preserves the HTML.
694 - '&#060; ' => '< ', // The space preserves the HTML.
1016 + // The space preserves the HTML.
1017 + '&lt; ' => '< ',
1018 + // The space preserves the HTML.
1019 + '&#060; ' => '< ',
695 1020 '&gt;' => '>',
696 1021 '&#062;' => '>',
697 1022 '&amp;' => '&',
698 1023 '&#038;' => '&',
@@ -719,17 +1044,29 @@
719 1044 * Sanitize the value, and allow some HTML
720 1045 *
721 1046 * @since 2.0
722 1047 *
723 - * @param string $value
724 - * @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.
725 1050 *
726 1051 * @return string
727 1052 */
728 1053 public static function kses( $value, $allowed = array() ) {
729 - $allowed_html = self::allowed_html( $allowed );
1054 + return wp_kses( $value, self::allowed_html( $allowed ) );
1055 + }
730 1056
731 - return wp_kses( $value, $allowed_html );
1057 + /**
1058 + * Sanitizes and echoes a given value.
1059 + *
1060 + * @since 6.18
1061 + *
1062 + * @param string $value The value to sanitize and output.
1063 + * @param array|string $allowed Allowed HTML tags and attributes.
1064 + *
1065 + * @return void
1066 + */
1067 + public static function kses_echo( $value, $allowed = array() ) {
1068 + echo self::kses( $value, $allowed ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
732 1069 }
733 1070
734 1071 /**
735 1072 * The regular kses function strips [button_action] from submit button HTML.
@@ -736,21 +1073,23 @@
736 1073 *
737 1074 * @since 5.0.13
738 1075 *
739 1076 * @param string $html
1077 + *
740 1078 * @return string
741 1079 */
742 1080 public static function kses_submit_button( $html ) {
743 - $included_button_action = false !== strpos( $html, '[button_action]' );
744 - $included_back_hook = false !== strpos( $html, '[back_hook]' );
745 - $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]' );
746 1084 add_filter( 'safe_style_css', 'FrmAppHelper::allow_visibility_style' );
747 1085 add_filter( 'frm_striphtml_allowed_tags', 'FrmAppHelper::add_allowed_submit_button_tags' );
748 1086 $html = self::kses( $html, 'all' );
749 1087 remove_filter( 'safe_style_css', 'FrmAppHelper::allow_visibility_style' );
750 1088 remove_filter( 'frm_striphtml_allowed_tags', 'FrmAppHelper::add_allowed_submit_button_tags' );
1089 +
751 1090 if ( $included_button_action ) {
752 - if ( false !== strpos( $html, '<input type="submit"' ) ) {
1091 + if ( str_contains( $html, '<input type="submit"' ) ) {
753 1092 $pattern = '/(<input type="submit")([^>]*)(\/>)/';
754 1093 $html = preg_replace( $pattern, '$1$2[button_action] $3', $html, 1 );
755 1094 } else {
756 1095 $pattern = '/(<button)(.*)(class=")(.*)(frm_button_submit)(.*)(")(.*)([^>]+)(>)/';
@@ -756,14 +1095,17 @@
756 1095 $pattern = '/(<button)(.*)(class=")(.*)(frm_button_submit)(.*)(")(.*)([^>]+)(>)/';
757 1096 $html = preg_replace( $pattern, '$1$2$3$4$5$6$7 [button_action]$8$9$10', $html, 1 );
758 1097 }
759 1098 }
1099 +
760 1100 if ( $included_back_hook ) {
761 1101 $html = str_replace( 'class="frm_prev_page"', 'class="frm_prev_page" [back_hook]', $html );
762 1102 }
1103 +
763 1104 if ( $included_draft_hook ) {
764 - $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 );
765 1106 }
1107 +
766 1108 return $html;
767 1109 }
768 1110
769 1111 /**
@@ -769,8 +1111,9 @@
769 1111 /**
770 1112 * @since 5.0.13
771 1113 *
772 1114 * @param array $allowed_attr
1115 + *
773 1116 * @return array
774 1117 */
775 1118 public static function allow_visibility_style( $allowed_attr ) {
776 1119 $allowed_attr[] = 'visibility';
@@ -780,8 +1123,9 @@
780 1123 /**
781 1124 * @since 5.0.13
782 1125 *
783 1126 * @param array $allowed_html
1127 + *
784 1128 * @return array
785 1129 */
786 1130 public static function add_allowed_submit_button_tags( $allowed_html ) {
787 1131 $allowed_html['input'] = array(
@@ -798,17 +1142,22 @@
798 1142 }
799 1143
800 1144 /**
801 1145 * @since 2.05.03
1146 + *
1147 + * @param array|string $allowed
1148 + *
1149 + * @return array
802 1150 */
803 1151 private static function allowed_html( $allowed ) {
804 1152 $html = self::safe_html();
805 1153 $allowed_html = array();
1154 +
806 1155 if ( $allowed === 'all' ) {
807 1156 $allowed_html = $html;
808 - } elseif ( ! empty( $allowed ) ) {
1157 + } elseif ( $allowed ) {
809 1158 foreach ( (array) $allowed as $a ) {
810 - $allowed_html[ $a ] = isset( $html[ $a ] ) ? $html[ $a ] : array();
1159 + $allowed_html[ $a ] = $html[ $a ] ?? array();
811 1160 }
812 1161 }
813 1162
814 1163 return apply_filters( 'frm_striphtml_allowed_tags', $allowed_html );
@@ -815,8 +1164,10 @@
815 1164 }
816 1165
817 1166 /**
818 1167 * @since 2.05.03
1168 + *
1169 + * @return array
819 1170 */
820 1171 private static function safe_html() {
821 1172 $allow_class = array(
822 1173 'class' => true,
@@ -823,9 +1174,9 @@
823 1174 'id' => true,
824 1175 );
825 1176
826 1177 return array(
827 - 'a' => array(
1178 + 'a' => array(
828 1179 'class' => true,
829 1180 'href' => true,
830 1181 'id' => true,
831 1182 'rel' => true,
@@ -894,16 +1245,16 @@
894 1245 'cite' => true,
895 1246 'title' => true,
896 1247 ),
897 1248 'rect' => array(
898 - 'class' => true,
899 - 'fill' => true,
900 - 'height' => true,
901 - 'width' => true,
902 - 'x' => true,
903 - 'y' => true,
904 - 'rx' => true,
905 - 'stroke' => true,
1249 + 'class' => true,
1250 + 'fill' => true,
1251 + 'height' => true,
1252 + 'width' => true,
1253 + 'x' => true,
1254 + 'y' => true,
1255 + 'rx' => true,
1256 + 'stroke' => true,
906 1257 'stroke-opacity' => true,
907 1258 'stroke-width' => true,
908 1259 ),
909 1260 'section' => $allow_class,
@@ -938,9 +1289,9 @@
938 1289 'xlink:href' => true,
939 1290 ),
940 1291 'ul' => $allow_class,
941 1292 'label' => array(
942 - 'for' => true,
1293 + 'for' => true,
943 1294 'class' => true,
944 1295 'id' => true,
945 1296 ),
946 1297 'button' => array(
@@ -949,8 +1300,13 @@
949 1300 ),
950 1301 'legend' => array(
951 1302 'class' => true,
952 1303 ),
1304 + 'option' => array(
1305 + 'class' => true,
1306 + 'value' => true,
1307 + 'selected' => true,
1308 + ),
953 1309 );
954 1310 }
955 1311
956 1312 /**
@@ -958,19 +1314,21 @@
958 1314 *
959 1315 * @since 2.0
960 1316 */
961 1317 public static function remove_get_action() {
962 - if ( ! isset( $_GET ) ) {
1318 + if ( empty( $_GET ) ) {
963 1319 return;
964 1320 }
965 1321
966 1322 $action_name = isset( $_GET['action'] ) ? 'action' : ( isset( $_GET['action2'] ) ? 'action2' : '' );
967 - if ( empty( $action_name ) ) {
1323 +
1324 + if ( ! $action_name ) {
968 1325 return;
969 1326 }
970 1327
971 1328 $new_action = self::get_param( $action_name, '', 'get', 'sanitize_text_field' );
972 - if ( ! empty( $new_action ) ) {
1329 +
1330 + if ( $new_action ) {
973 1331 $_SERVER['REQUEST_URI'] = str_replace( '&action=' . $new_action, '', self::get_server_value( 'REQUEST_URI' ) );
974 1332 }
975 1333 }
976 1334
@@ -977,21 +1335,23 @@
977 1335 /**
978 1336 * Check the WP query for a parameter
979 1337 *
980 1338 * @since 2.0
981 - * @return string|array
1339 + *
1340 + * @param array|string $value
1341 + * @param string $param
1342 + *
1343 + * @return array|string
982 1344 */
983 1345 public static function get_query_var( $value, $param ) {
1346 + // phpcs:ignore Universal.Operators.StrictComparisons
984 1347 if ( $value != '' ) {
985 1348 return $value;
986 1349 }
987 1350
988 1351 global $wp_query;
989 - if ( isset( $wp_query->query_vars[ $param ] ) ) {
990 - $value = $wp_query->query_vars[ $param ];
991 - }
992 1352
993 - return $value;
1353 + return $wp_query->query_vars[ $param ] ?? $value;
994 1354 }
995 1355
996 1356 /**
997 1357 * Try to show the SVG if possible. Otherwise, use the font icon.
@@ -996,48 +1356,82 @@
996 1356 /**
997 1357 * Try to show the SVG if possible. Otherwise, use the font icon.
998 1358 *
999 1359 * @since 4.0.02
1360 + *
1000 1361 * @param string $class
1001 1362 * @param array $atts
1363 + *
1364 + * @return string|null
1002 1365 */
1003 1366 public static function icon_by_class( $class, $atts = array() ) {
1004 1367 $echo = ! isset( $atts['echo'] ) || $atts['echo'];
1368 +
1005 1369 if ( isset( $atts['echo'] ) ) {
1006 1370 unset( $atts['echo'] );
1007 1371 }
1008 1372
1009 - $html_atts = self::array_to_html_params( $atts );
1010 -
1011 1373 $icon = trim( str_replace( array( 'frm_icon_font', 'frmfont ' ), '', $class ) );
1012 1374
1013 - // Replace icons that have been removed.
1375 + // Replace icons that have been removed or renamed.
1014 1376 $deprecated = array(
1015 - 'frm_clone_solid_icon' => 'frm_clone_icon',
1377 + 'frm_clone_solid_icon' => 'frm_clone_icon',
1378 + 'frm_keyalt_icon' => 'frm_key_icon',
1379 + 'frm_keyalt_solid_icon' => 'frm_key_solid_icon',
1016 1380 );
1381 +
1017 1382 if ( isset( $deprecated[ $icon ] ) ) {
1018 - $icon = $deprecated[ $icon ];
1383 + $icon = $deprecated[ $icon ];
1019 1384 $class = str_replace( $icon, $deprecated[ $icon ], $class );
1020 1385 }
1021 1386
1022 - if ( $icon === $class ) {
1023 - $icon = '<i class="' . esc_attr( $class ) . '"' . $html_atts . '></i>';
1024 - } else {
1025 - $class = strpos( $icon, ' ' ) === false ? '' : ' ' . $icon;
1026 - 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, ' ' ) ) {
1027 1393 $icon = explode( ' ', $icon );
1028 1394 $icon = reset( $icon );
1029 1395 }
1030 - $icon = '<svg class="frmsvg' . esc_attr( $class ) . '"' . $html_atts . '>
1031 - <use xlink:href="#' . esc_attr( $icon ) . '" />
1032 - </svg>';
1033 1396 }
1034 1397
1035 - if ( $echo ) {
1036 - echo self::kses_icon( $icon ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1037 - } else {
1038 - 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;
1039 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 );
1040 1434 }
1041 1435
1042 1436 /**
1043 1437 * Run kses for icons. It needs to add a few filters first in order to preserve some custom style values.
@@ -1044,8 +1438,9 @@
1044 1438 *
1045 1439 * @since 5.0.13
1046 1440 *
1047 1441 * @param string $icon
1442 + *
1048 1443 * @return string
1049 1444 */
1050 1445 public static function kses_icon( $icon ) {
1051 1446 add_filter( 'safe_style_css', 'FrmAppHelper::allow_vars_in_styles' );
@@ -1061,13 +1456,15 @@
1061 1456 /**
1062 1457 * @since 5.0.13.1
1063 1458 *
1064 1459 * @param array $allowed_html
1460 + *
1065 1461 * @return array
1066 1462 */
1067 1463 public static function add_allowed_icon_tags( $allowed_html ) {
1068 1464 $allowed_html['svg']['data-open'] = true;
1069 1465 $allowed_html['svg']['title'] = true;
1466 + $allowed_html['svg']['tabindex'] = true;
1070 1467 return $allowed_html;
1071 1468 }
1072 1469
1073 1470 /**
@@ -1073,8 +1470,9 @@
1073 1470 /**
1074 1471 * @since 5.0.13
1075 1472 *
1076 1473 * @param array $allowed_attr
1474 + *
1077 1475 * @return array
1078 1476 */
1079 1477 public static function allow_vars_in_styles( $allowed_attr ) {
1080 1478 $allowed_attr[] = '--primary-700';
@@ -1087,9 +1485,9 @@
1087 1485 * @param bool $allow_css
1088 1486 * @param string $css_string
1089 1487 */
1090 1488 public static function allow_style( $allow_css, $css_string ) {
1091 - if ( ! $allow_css && 0 === strpos( $css_string, '--primary-700:' ) ) {
1489 + if ( ! $allow_css && str_starts_with( $css_string, '--primary-700:' ) ) {
1092 1490 $split = explode( ':', $css_string, 2 );
1093 1491 $allow_css = 2 === count( $split ) && self::is_a_valid_color( $split[1] );
1094 1492 }
1095 1493 return $allow_css;
@@ -1098,19 +1496,22 @@
1098 1496 /**
1099 1497 * @since 5.0.13
1100 1498 *
1101 1499 * @param string $value
1500 + *
1102 1501 * @return bool
1103 1502 */
1104 1503 private static function is_a_valid_color( $value ) {
1105 1504 $match = 0;
1106 - if ( 0 === strpos( $value, 'rgba(' ) ) {
1505 +
1506 + if ( str_starts_with( $value, 'rgba(' ) ) {
1107 1507 $match = preg_match( '/^rgba\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),\s*(\d*(?:\.\d+)?)\)$/', $value );
1108 - } elseif ( 0 === strpos( $value, 'rgb(' ) ) {
1508 + } elseif ( str_starts_with( $value, 'rgb(' ) ) {
1109 1509 $match = preg_match( '/^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/', $value );
1110 - } elseif ( 0 === strpos( $value, '#' ) ) {
1510 + } elseif ( str_starts_with( $value, '#' ) ) {
1111 1511 $match = preg_match( '/^#([a-f0-9]{6}|[a-f0-9]{3})\b$/', $value );
1112 1512 }
1513 +
1113 1514 return (bool) $match;
1114 1515 }
1115 1516
1116 1517 /**
@@ -1116,8 +1517,9 @@
1116 1517 /**
1117 1518 * Include svg images.
1118 1519 *
1119 1520 * @since 4.0.02
1521 + *
1120 1522 * @return void
1121 1523 */
1122 1524 public static function include_svg() {
1123 1525 if ( self::$included_svg ) {
@@ -1136,17 +1538,20 @@
1136 1538 * @since 5.0.13 added $echo parameter.
1137 1539 *
1138 1540 * @param array $atts
1139 1541 * @param bool $echo
1542 + *
1140 1543 * @return string|void
1141 1544 */
1142 1545 public static function array_to_html_params( $atts, $echo = false ) {
1143 - $callback = function() use ( $atts ) {
1144 - if ( $atts ) {
1145 - foreach ( $atts as $key => $value ) {
1146 - echo ' ' . esc_attr( $key ) . '="' . esc_attr( $value ) . '"';
1147 - }
1546 + $callback = function () use ( $atts ) {
1547 + if ( ! $atts ) {
1548 + return;
1148 1549 }
1550 +
1551 + foreach ( $atts as $key => $value ) {
1552 + echo ' ' . esc_attr( $key ) . '="' . esc_attr( $value ) . '"';
1553 + }
1149 1554 };
1150 1555 return self::clip( $callback, $echo );
1151 1556 }
1152 1557
@@ -1156,9 +1561,12 @@
1156 1561 * @since 5.0.13
1157 1562 *
1158 1563 * @param Closure $echo_function
1159 1564 * @param bool $echo
1160 - * @return string|void
1565 + *
1566 + * @return string|null
1567 + *
1568 + * @psalm-return ($echo is true ? null : string)
1161 1569 */
1162 1570 public static function clip( $echo_function, $echo = false ) {
1163 1571 if ( ! $echo ) {
1164 1572 ob_start();
@@ -1167,28 +1575,30 @@
1167 1575 if ( is_callable( $echo_function ) ) {
1168 1576 $echo_function();
1169 1577 }
1170 1578
1171 - if ( ! $echo ) {
1172 - $return = ob_get_contents();
1173 - ob_end_clean();
1174 - return $return;
1175 - }
1579 + return $echo ? null : ob_get_clean();
1176 1580 }
1177 1581
1178 1582 /**
1179 1583 * @since 3.0
1584 + *
1585 + * @param array $atts
1586 + *
1587 + * @return void
1180 1588 */
1181 1589 public static function get_admin_header( $atts ) {
1182 - $has_nav = ( isset( $atts['form'] ) && ! empty( $atts['form'] ) && ( ! isset( $atts['is_template'] ) || ! $atts['is_template'] ) );
1183 - if ( ! isset( $atts['close'] ) || empty( $atts['close'] ) ) {
1590 + $has_nav = ! empty( $atts['form'] ) && empty( $atts['is_template'] );
1591 +
1592 + if ( empty( $atts['close'] ) ) {
1184 1593 $atts['close'] = admin_url( 'admin.php?page=formidable' );
1185 1594 }
1595 +
1186 1596 if ( ! isset( $atts['import_link'] ) ) {
1187 1597 $atts['import_link'] = false;
1188 1598 }
1189 1599
1190 - include( self::plugin_path() . '/classes/views/shared/admin-header.php' );
1600 + include self::plugin_path() . '/classes/views/shared/admin-header.php';
1191 1601 }
1192 1602
1193 1603 /**
1194 1604 * @since 6.0
@@ -1193,16 +1603,19 @@
1193 1603 /**
1194 1604 * @since 6.0
1195 1605 *
1196 1606 * @param string $type
1607 + *
1197 1608 * @return void
1198 1609 */
1199 1610 public static function import_link( $type = 'secondary' ) {
1611 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
1200 1612 ?>
1201 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">
1202 1614 <?php esc_html_e( 'Import', 'formidable' ); ?>
1203 1615 </a>
1204 1616 <?php
1617 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
1205 1618 }
1206 1619
1207 1620 /**
1208 1621 * Print applicable admin banner.
@@ -1209,8 +1622,9 @@
1209 1622 *
1210 1623 * @since 5.4.2
1211 1624 *
1212 1625 * @param bool $should_show_lite_upgrade
1626 + *
1213 1627 * @return void
1214 1628 */
1215 1629 public static function print_admin_banner( $should_show_lite_upgrade ) {
1216 1630 if ( ! current_user_can( 'administrator' ) ) {
@@ -1217,19 +1631,44 @@
1217 1631 FrmInbox::maybe_show_banner();
1218 1632 return;
1219 1633 }
1220 1634
1221 - if ( self::maybe_show_license_warning() || FrmInbox::maybe_show_banner() || ! $should_show_lite_upgrade || self::pro_is_installed() ) {
1635 + if ( FrmSalesApi::maybe_show_banner() || self::maybe_show_license_warning() || FrmInbox::maybe_show_banner() || ! $should_show_lite_upgrade || self::pro_is_installed() ) {
1222 1636 // Print license warning or inbox banner and exit if either prints.
1223 1637 // And exit before printing the upgrade bar if it shouldn't be shown.
1224 1638 return;
1225 1639 }
1640 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
1226 1641 ?>
1227 1642 <div class="frm-upgrade-bar">
1228 - <span>You're using Formidable Forms Lite. To unlock more features consider</span>
1229 - <a href="<?php echo esc_url( self::admin_upgrade_link( 'top-bar' ) ); ?>" target="_blank" rel="noopener">upgrading to Pro</a>.
1643 + <div class="frm-upgrade-bar-inner">
1644 + <?php
1645 + $cta_text = FrmSalesApi::get_best_sale_value( 'lite_banner_cta_text' );
1646 +
1647 + if ( ! $cta_text ) {
1648 + $cta_text = __( 'upgrading to PRO', 'formidable' );
1649 + }
1650 +
1651 + $upgrade_link = FrmSalesApi::get_best_sale_value( 'lite_banner_cta_link' );
1652 + $utm = array(
1653 + 'campaign' => 'settings-license',
1654 + 'content' => 'lite-banner',
1655 + );
1656 +
1657 + $upgrade_link = $upgrade_link ? self::maybe_add_missing_utm( $upgrade_link, $utm ) : self::admin_upgrade_link( $utm );
1658 +
1659 + printf(
1660 + /* translators: %1$s: Start link HTML, %2$s: CTA text ("upgrading to PRO" by default), %3$s: End link HTML */
1661 + esc_html__( 'You\'re using Formidable Forms Lite. To unlock more features consider %1$s%2$s%3$s.', 'formidable' ),
1662 + '<a href="' . esc_url( $upgrade_link ) . '" target="_blank" rel="noopener">',
1663 + esc_html( $cta_text ),
1664 + '</a>'
1665 + );
1666 + ?>
1667 + </div>
1230 1668 </div>
1231 1669 <?php
1670 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
1232 1671 }
1233 1672
1234 1673 /**
1235 1674 * @since 5.4.2
@@ -1243,14 +1682,18 @@
1243 1682 /**
1244 1683 * Render a button for a new item (Form, Application, etc).
1245 1684 *
1246 1685 * @since 3.0
1686 + *
1247 1687 * @param array $atts {
1688 + * Details about the button.
1689 + *
1248 1690 * @type array $link_hook Custom link hook, calls do_action and exits early.
1249 1691 * @type string $new_link Href value, default #.
1250 1692 * @type string $class Custom class names, space separated.
1251 1693 * @type string $button_text Button text. Default "Add New".
1252 1694 * }
1695 + *
1253 1696 * @return void
1254 1697 */
1255 1698 public static function add_new_item_link( $atts ) {
1256 1699 if ( isset( $atts['link_hook'] ) ) {
@@ -1257,9 +1700,9 @@
1257 1700 do_action( $atts['link_hook']['hook'], $atts['link_hook']['param'] );
1258 1701 return;
1259 1702 }
1260 1703
1261 - if ( empty( $atts['new_link'] ) && empty( $atts['trigger_new_form_modal'] ) && empty( $atts['class'] ) ) {
1704 + if ( empty( $atts['new_link'] ) && empty( $atts['create_form'] ) && empty( $atts['class'] ) ) {
1262 1705 // Do not render a button if none of these attributes are set.
1263 1706 return;
1264 1707 }
1265 1708
@@ -1265,12 +1708,8 @@
1265 1708
1266 1709 $href = ! empty( $atts['new_link'] ) ? esc_url( $atts['new_link'] ) : '#';
1267 1710 $class = 'button button-primary frm-button-primary';
1268 1711
1269 - if ( ! empty( $atts['trigger_new_form_modal'] ) ) {
1270 - $class .= ' frm-trigger-new-form-modal';
1271 - }
1272 -
1273 1712 if ( ! empty( $atts['class'] ) ) {
1274 1713 $class .= ' ' . $atts['class'];
1275 1714 }
1276 1715
@@ -1280,8 +1719,12 @@
1280 1719 }
1281 1720
1282 1721 /**
1283 1722 * @since 3.06
1723 + *
1724 + * @param array $atts
1725 + *
1726 + * @return void
1284 1727 */
1285 1728 public static function show_search_box( $atts ) {
1286 1729 $defaults = array(
1287 1730 'placeholder' => '',
@@ -1287,10 +1730,12 @@
1287 1730 'placeholder' => '',
1288 1731 'tosearch' => '',
1289 1732 'text' => __( 'Search', 'formidable' ),
1290 1733 'input_id' => '',
1734 + 'value' => false,
1735 + 'class' => '',
1291 1736 );
1292 - $atts = array_merge( $defaults, $atts );
1737 + $atts = array_merge( $defaults, $atts );
1293 1738
1294 1739 if ( $atts['input_id'] === 'template' && empty( $atts['tosearch'] ) ) {
1295 1740 $atts['tosearch'] = 'frm-card';
1296 1741 }
@@ -1295,8 +1740,9 @@
1295 1740 $atts['tosearch'] = 'frm-card';
1296 1741 }
1297 1742
1298 1743 $class = 'frm-search-input';
1744 +
1299 1745 if ( ! empty( $atts['tosearch'] ) ) {
1300 1746 $class .= ' frm-auto-search';
1301 1747 }
1302 1748
@@ -1301,21 +1747,35 @@
1301 1747 }
1302 1748
1303 1749 $input_id = $atts['input_id'] . '-search-input';
1304 1750
1751 + $input_atts = array(
1752 + 'type' => 'search',
1753 + 'id' => $input_id,
1754 + 'name' => 's',
1755 + 'placeholder' => $atts['placeholder'],
1756 + 'class' => $class,
1757 + 'data-tosearch' => $atts['tosearch'],
1758 + );
1759 +
1760 + if ( is_string( $atts['value'] ) ) {
1761 + $input_atts['value'] = $atts['value'];
1762 + } elseif ( isset( $_REQUEST['s'] ) ) {
1763 + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1764 + $input_atts['value'] = wp_unslash( $_REQUEST['s'] );
1765 + }
1766 +
1767 + if ( ! empty( $atts['tosearch'] ) ) {
1768 + $input_atts['autocomplete'] = 'off';
1769 + }
1770 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
1305 1771 ?>
1306 - <p class="frm-search">
1772 + <p class="frm-search <?php echo esc_attr( $atts['class'] ); ?>">
1307 1773 <label class="screen-reader-text" for="<?php echo esc_attr( $input_id ); ?>">
1308 1774 <?php echo esc_html( $atts['text'] ); ?>:
1309 1775 </label>
1310 - <span class="frmfont frm_search_icon"></span>
1311 - <input type="search" id="<?php echo esc_attr( $input_id ); ?>" name="s"
1312 - value="<?php _admin_search_query(); ?>" placeholder="<?php echo esc_attr( $atts['placeholder'] ); ?>"
1313 - class="<?php echo esc_attr( $class ); ?>" data-tosearch="<?php echo esc_attr( $atts['tosearch'] ); ?>"
1314 - <?php if ( ! empty( $atts['tosearch'] ) ) { ?>
1315 - autocomplete="off"
1316 - <?php } ?>
1317 - />
1776 + <?php self::icon_by_class( 'frmfont frm_search_icon frm_svg20' ); ?>
1777 + <input <?php self::array_to_html_params( $input_atts, true ); ?> />
1318 1778 <?php
1319 1779 if ( empty( $atts['tosearch'] ) ) {
1320 1780 submit_button( $atts['text'], 'button-secondary', '', false, array( 'id' => 'search-submit' ) );
1321 1781 }
@@ -1321,16 +1781,21 @@
1321 1781 }
1322 1782 ?>
1323 1783 </p>
1324 1784 <?php
1785 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
1325 1786 }
1326 1787
1327 1788 /**
1328 - * @param string $type
1789 + * @param string $type Hook type slug.
1790 + * @param object|null $object Optional related object.
1791 + *
1792 + * @return void
1329 1793 */
1330 1794 public static function trigger_hook_load( $type, $object = null ) {
1331 1795 // Only load the form hooks once.
1332 1796 $hooks_loaded = apply_filters( 'frm_' . $type . '_hooks_loaded', false, $object );
1797 +
1333 1798 if ( ! $hooks_loaded ) {
1334 1799 do_action( 'frm_load_' . $type . '_hooks' );
1335 1800 }
1336 1801 }
@@ -1370,9 +1835,9 @@
1370 1835 'new_file_path' => self::plugin_path() . '/js',
1371 1836 )
1372 1837 );
1373 1838 $new_file = new FrmCreateFile( $file_atts );
1374 - $files = array(
1839 + $files = array(
1375 1840 FrmStrpLiteAppHelper::plugin_path() . 'js/frmstrp.min.js',
1376 1841 );
1377 1842
1378 1843 /**
@@ -1389,14 +1854,14 @@
1389 1854 * True when value is 1, true, 'true', 'yes'
1390 1855 *
1391 1856 * @since 1.07.10
1392 1857 *
1393 - * @param string $value The value to compare
1858 + * @param bool|int|string $value The value to compare.
1394 1859 *
1395 - * @return boolean True or False
1860 + * @return bool
1396 1861 */
1397 1862 public static function is_true( $value ) {
1398 - return ( true === $value || 1 == $value || 'true' == $value || 'yes' == $value );
1863 + return true === $value || '1' === (string) $value || 'true' === $value || 'yes' === $value;
1399 1864 }
1400 1865
1401 1866 /**
1402 1867 * Gets all post from a specific post type.
@@ -1404,8 +1869,9 @@
1404 1869 *
1405 1870 * @since 4.10.01 Add `$post_type` argument.
1406 1871 *
1407 1872 * @param string $post_type Post type to query. Default is `page`.
1873 + *
1408 1874 * @return WP_Post[]
1409 1875 */
1410 1876 public static function get_pages( $post_type = 'page' ) {
1411 1877 $query = array(
@@ -1424,8 +1890,9 @@
1424 1890 *
1425 1891 * @since 5.0.09
1426 1892 *
1427 1893 * @param string $post_type Post type to query. Default is `page`.
1894 + *
1428 1895 * @return array
1429 1896 */
1430 1897 public static function get_post_ids_and_titles( $post_type = 'page' ) {
1431 1898 return FrmDb::get_results(
@@ -1450,13 +1917,12 @@
1450 1917 *
1451 1918 * @param array $args Selection arguments.
1452 1919 */
1453 1920 public static function maybe_autocomplete_pages_options( $args ) {
1454 - $args = self::preformat_selection_args( $args );
1455 -
1921 + $args = self::preformat_selection_args( $args );
1456 1922 $pages_count = wp_count_posts( $args['post_type'] );
1457 1923
1458 - if ( $pages_count->publish <= 50 ) {
1924 + if ( ! isset( $pages_count->publish ) || $pages_count->publish <= 50 ) {
1459 1925 self::wp_pages_dropdown( $args );
1460 1926 return;
1461 1927 }
1462 1928
@@ -1462,9 +1928,9 @@
1462 1928
1463 1929 wp_enqueue_script( 'jquery-ui-autocomplete' );
1464 1930
1465 1931 $selected = self::get_post_param( $args['field_name'], $args['page_id'], 'absint' );
1466 - $title = '';
1932 + $title = '';
1467 1933
1468 1934 if ( $selected ) {
1469 1935 $title = get_the_title( $selected );
1470 1936 }
@@ -1480,18 +1946,119 @@
1480 1946 <?php
1481 1947 }
1482 1948
1483 1949 /**
1484 - * @param array $args
1485 - * @param string $page_id Deprecated.
1486 - * @param boolean $truncate Deprecated.
1950 + * Maybe show an HTML select or autocomplete input based on the number of options.
1951 + *
1952 + * @since 6.21
1953 + *
1954 + * @param array $args Args. See the method for details.
1487 1955 */
1956 + public static function maybe_autocomplete_options( $args ) {
1957 + $defaults = array(
1958 + 'truncate' => false,
1959 + 'placeholder' => ' ',
1960 + 'name' => '',
1961 + 'id' => '',
1962 + 'selected' => '',
1963 + 'source' => array(),
1964 + 'dropdown_limit' => 50,
1965 + 'autocomplete_placeholder' => __( 'Select an option', 'formidable' ),
1966 + 'value_key' => 'value',
1967 + 'label_key' => 'label',
1968 + );
1969 +
1970 + $args = wp_parse_args( $args, $defaults );
1971 + $html_attrs = array();
1972 +
1973 + if ( ! empty( $args['name'] ) ) {
1974 + $html_attrs['name'] = $args['name'];
1975 + }
1976 +
1977 + if ( ! empty( $args['id'] ) ) {
1978 + $html_attrs['id'] = $args['id'];
1979 + }
1980 +
1981 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
1982 + if ( count( $args['source'] ) <= $args['dropdown_limit'] ) {
1983 + ?>
1984 + <select <?php self::array_to_html_params( $html_attrs, true ); ?>>
1985 + <option value=""><?php echo esc_html( $args['placeholder'] ); ?></option>
1986 + <?php
1987 + foreach ( $args['source'] as $key => $source ) :
1988 + $value_label = self::get_dropdown_value_and_label_from_option( $source, $key, $args );
1989 +
1990 + if ( ! empty( $args['truncate'] ) ) {
1991 + $value_label['label'] = self::truncate( $value_label['label'], $args['truncate'] );
1992 + }
1993 + ?>
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 ?>
1995 + <?php endforeach; ?>
1996 + </select>
1997 + <?php
1998 + return;
1999 + }
2000 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
2001 +
2002 + $options = array();
2003 + $autocomplete_value = '';
2004 +
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'];
2010 + }
2011 +
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
2025 + }
2026 +
2027 + /**
2028 + * Gets dropdown value and label from autodropdown option.
2029 + *
2030 + * @since 6.21
2031 + *
2032 + * @param array|string $option Autocomplete option.
2033 + * @param string $key Array key of the option.
2034 + * @param array $args See {@see FrmAppHelper::maybe_autocomplete_options()}.
2035 + *
2036 + * @return array
2037 + */
2038 + private static function get_dropdown_value_and_label_from_option( $option, $key, $args ) {
2039 + if ( is_array( $option ) ) {
2040 + $value = $option[ $args['value_key'] ] ?? '';
2041 + $label = $option[ $args['label_key'] ] ?? '';
2042 + } else {
2043 + $value = $key;
2044 + $label = $option;
2045 + }
2046 +
2047 + return compact( 'value', 'label' );
2048 + }
2049 +
2050 + /**
2051 + * @param array $args
2052 + * @param string $page_id Deprecated.
2053 + * @param bool $truncate Deprecated.
2054 + */
1488 2055 public static function wp_pages_dropdown( $args = array(), $page_id = '', $truncate = false ) {
1489 2056 self::prep_page_dropdown_params( $page_id, $truncate, $args );
1490 2057
1491 2058 $pages = self::get_post_ids_and_titles( $args['post_type'] );
1492 2059 $selected = self::get_post_param( $args['field_name'], $args['page_id'], 'absint' );
1493 -
2060 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
1494 2061 ?>
1495 2062 <select name="<?php echo esc_attr( $args['field_name'] ); ?>" id="<?php echo esc_attr( $args['field_name'] ); ?>" class="frm-pages-dropdown">
1496 2063 <option value=""><?php echo esc_html( $args['placeholder'] ); ?></option>
1497 2064 <?php foreach ( $pages as $page ) { ?>
@@ -1500,8 +2067,9 @@
1500 2067 </option>
1501 2068 <?php } ?>
1502 2069 </select>
1503 2070 <?php
2071 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
1504 2072 }
1505 2073
1506 2074 /**
1507 2075 * Fill in missing parameters passed to wp_pages_dropdown().
@@ -1507,8 +2075,14 @@
1507 2075 * Fill in missing parameters passed to wp_pages_dropdown().
1508 2076 * This is for reverse compatibility with switching 3 params to 1.
1509 2077 *
1510 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
1511 2085 */
1512 2086 private static function prep_page_dropdown_params( $page_id, $truncate, &$args ) {
1513 2087 if ( ! is_array( $args ) ) {
1514 2088 $args = array(
@@ -1525,16 +2099,20 @@
1525 2099 * Filter to format args for page dropdown or autocomplete
1526 2100 *
1527 2101 * @since 4.03.06
1528 2102 * @since 4.10.01 Added `post_type` and `autocomplete_placeholder` to the arguments array.
2103 + *
2104 + * @param array $args
2105 + *
2106 + * @return array
1529 2107 */
1530 2108 private static function preformat_selection_args( $args ) {
1531 2109 $defaults = array(
1532 - 'truncate' => false,
1533 - 'placeholder' => ' ',
1534 - 'field_name' => '',
1535 - 'page_id' => '',
1536 - 'post_type' => 'page',
2110 + 'truncate' => false,
2111 + 'placeholder' => ' ',
2112 + 'field_name' => '',
2113 + 'page_id' => '',
2114 + 'post_type' => 'page',
1537 2115 'autocomplete_placeholder' => __( 'Select a Page', 'formidable' ),
1538 2116 );
1539 2117
1540 2118 return array_merge( $defaults, $args );
@@ -1539,13 +2117,18 @@
1539 2117
1540 2118 return array_merge( $defaults, $args );
1541 2119 }
1542 2120
2121 + /**
2122 + * @param int $post_id
2123 + *
2124 + * @return string
2125 + */
1543 2126 public static function post_edit_link( $post_id ) {
1544 2127 $post = get_post( $post_id );
2128 +
1545 2129 if ( $post ) {
1546 2130 $post_url = admin_url( 'post.php?post=' . $post_id . '&action=edit' );
1547 -
1548 2131 return '<a href="' . esc_url( $post_url ) . '">' . self::truncate( $post->post_title, 50 ) . '</a>';
1549 2132 }
1550 2133
1551 2134 return '';
@@ -1558,11 +2141,9 @@
1558 2141 *
1559 2142 * @return bool
1560 2143 */
1561 2144 public static function is_full_screen() {
1562 - return self::is_form_builder_page() ||
1563 - self::is_style_editor_page() ||
1564 - 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();
1565 2146 }
1566 2147
1567 2148 /**
1568 2149 * Check if user is on the style editor or its alternative URL.
@@ -1572,8 +2153,9 @@
1572 2153 * @since 5.5.3
1573 2154 * @since 6.0 Added the $view parameter. Previously there was only a 'edit' view.
1574 2155 *
1575 2156 * @param string $view Supports 'edit', 'list', and ''. If '', both 'edit' and 'list' will match.
2157 + *
1576 2158 * @return bool
1577 2159 */
1578 2160 public static function is_style_editor_page( $view = '' ) {
1579 2161 if ( ! self::is_admin_page( 'formidable-styles' ) && ! self::is_admin_page( 'formidable-styles2' ) ) {
@@ -1605,35 +2187,29 @@
1605 2187 return self::is_admin_page( 'formidable-views-editor' );
1606 2188 }
1607 2189
1608 2190 /**
1609 - * @since 4.0
1610 - * @deprecated 5.5.3
2191 + * @param string $field_name
2192 + * @param array|string $capability
2193 + * @param string $multiple 'single' and 'multiple'.
1611 2194 */
1612 - public static function maybe_full_screen_link( $link ) {
1613 - _deprecated_function( __METHOD__, '5.5.3' );
1614 - return $link;
1615 - }
1616 -
1617 - /**
1618 - * @param string $field_name
1619 - * @param string|array $capability
1620 - * @param string $multiple 'single' and 'multiple'
1621 - */
1622 2195 public static function wp_roles_dropdown( $field_name, $capability, $multiple = 'single' ) {
2196 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
1623 2197 ?>
1624 2198 <select name="<?php echo esc_attr( $field_name ); ?>" id="<?php echo esc_attr( $field_name ); ?>"
1625 - <?php echo ( 'multiple' === $multiple ) ? 'multiple="multiple"' : ''; ?>
2199 + <?php echo 'multiple' === $multiple ? 'multiple="multiple"' : ''; ?>
1626 2200 class="frm_multiselect">
1627 2201 <?php self::roles_options( $capability ); ?>
1628 2202 </select>
1629 2203 <?php
2204 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
1630 2205 }
1631 2206
1632 2207 /**
1633 2208 * @since 4.07
2209 + *
1634 2210 * @param array|string $selected
1635 - * @param string $current
2211 + * @param string $current
1636 2212 */
1637 2213 private static function selected( $selected, $current ) {
1638 2214 if ( is_callable( 'FrmProAppHelper::selected' ) ) {
1639 2215 FrmProAppHelper::selected( $selected, $current );
@@ -1642,12 +2218,13 @@
1642 2218 }
1643 2219 }
1644 2220
1645 2221 /**
1646 - * @param string|array $capability
2222 + * @param array|string $capability
1647 2223 */
1648 2224 public static function roles_options( $capability ) {
1649 2225 global $frm_vars;
2226 +
1650 2227 if ( isset( $frm_vars['editable_roles'] ) ) {
1651 2228 $editable_roles = $frm_vars['editable_roles'];
1652 2229 } else {
1653 2230 $editable_roles = get_editable_roles();
@@ -1656,9 +2233,9 @@
1656 2233
1657 2234 foreach ( $editable_roles as $role => $details ) {
1658 2235 $name = translate_user_role( $details['name'] );
1659 2236 ?>
1660 - <option value="<?php echo esc_attr( $role ); ?>" <?php self::selected( $capability, $role ); ?>><?php echo esc_attr( $name ); ?> </option>
2237 + <option value="<?php echo esc_attr( $role ); ?>" <?php self::selected( $capability, $role ); ?>><?php echo esc_html( $name ); ?></option>
1661 2238 <?php
1662 2239 unset( $role, $details );
1663 2240 }
1664 2241 }
@@ -1668,8 +2245,9 @@
1668 2245 *
1669 2246 * @since 5.0 Parameter `$type` supports `pro_only` value.
1670 2247 *
1671 2248 * @param string $type Supports `auto`, `pro`, or `pro_only`.
2249 + *
1672 2250 * @return array
1673 2251 */
1674 2252 public static function frm_capabilities( $type = 'auto' ) {
1675 2253 if ( ! self::pro_is_installed() && ! in_array( $type, array( 'pro', 'pro_only' ), true ) ) {
@@ -1679,9 +2257,8 @@
1679 2257 $pro_cap = array(
1680 2258 'frm_create_entries' => __( 'Add Entries from Admin Area', 'formidable' ),
1681 2259 'frm_edit_entries' => __( 'Edit Entries from Admin Area', 'formidable' ),
1682 2260 'frm_view_reports' => __( 'View Reports', 'formidable' ),
1683 - 'frm_edit_displays' => __( 'Add/Edit Views', 'formidable' ),
1684 2261 );
1685 2262 /**
1686 2263 * @since 5.3.1
1687 2264 *
@@ -1704,9 +2281,9 @@
1704 2281 * @return array<string,string>
1705 2282 */
1706 2283 private static function get_lite_capabilities() {
1707 2284 return array(
1708 - 'frm_view_forms' => __( 'View Forms', 'formidable' ),
2285 + 'frm_view_forms' => __( 'View Forms List', 'formidable' ),
1709 2286 'frm_edit_forms' => __( 'Add and Edit Forms', 'formidable' ),
1710 2287 'frm_delete_forms' => __( 'Delete Forms', 'formidable' ),
1711 2288 'frm_change_settings' => __( 'Access this Settings Page', 'formidable' ),
1712 2289 'frm_view_entries' => __( 'View Entries from Admin Area', 'formidable' ),
@@ -1735,21 +2312,18 @@
1735 2312 if ( $role === 'loggedin' || ! $role ) {
1736 2313 return is_user_logged_in();
1737 2314 }
1738 2315
1739 - if ( $role == 1 ) {
2316 + if ( (int) $role === 1 ) {
1740 2317 $role = 'administrator';
1741 2318 }
1742 2319
1743 - if ( ! is_user_logged_in() ) {
1744 - return false;
1745 - }
1746 -
1747 - return current_user_can( $role );
2320 + return is_user_logged_in() ? current_user_can( $role ) : false;
1748 2321 }
1749 2322
1750 2323 /**
1751 - * @param string|array $needed_role
2324 + * @param array|string $needed_role
2325 + *
1752 2326 * @return bool
1753 2327 */
1754 2328 public static function user_has_permission( $needed_role ) {
1755 2329 if ( is_array( $needed_role ) ) {
@@ -1763,18 +2337,20 @@
1763 2337 }
1764 2338
1765 2339 $can = self::current_user_can( $needed_role );
1766 2340
1767 - if ( $can || in_array( $needed_role, array( '-1', 'loggedout' ) ) ) {
2341 + if ( $can || in_array( $needed_role, array( '-1', 'loggedout' ), true ) ) {
1768 2342 return $can;
1769 2343 }
1770 2344
1771 2345 $roles = array( 'administrator', 'editor', 'author', 'contributor', 'subscriber' );
2346 +
1772 2347 foreach ( $roles as $role ) {
1773 2348 if ( current_user_can( $role ) ) {
1774 2349 return true;
1775 2350 }
1776 - if ( $role == $needed_role ) {
2351 +
2352 + if ( $role === $needed_role ) {
1777 2353 break;
1778 2354 }
1779 2355 }
1780 2356
@@ -1792,11 +2368,11 @@
1792 2368 if ( ! current_user_can( 'administrator' ) || current_user_can( 'frm_view_forms' ) ) {
1793 2369 return;
1794 2370 }
1795 2371
1796 - $user_id = get_current_user_id();
1797 - $user = new WP_User( $user_id );
2372 + $user = new WP_User( get_current_user_id() );
1798 2373 $frm_roles = self::frm_capabilities();
2374 +
1799 2375 foreach ( $frm_roles as $frm_role => $frm_role_description ) {
1800 2376 $user->add_cap( $frm_role );
1801 2377 unset( $frm_role, $frm_role_description );
1802 2378 }
@@ -1805,35 +2381,47 @@
1805 2381 /**
1806 2382 * Make sure admins have permission to see the menu items
1807 2383 *
1808 2384 * @since 2.0.6
2385 + *
2386 + * @param string $cap
2387 + *
2388 + * @return void
1809 2389 */
1810 2390 public static function force_capability( $cap = 'frm_change_settings' ) {
1811 - if ( current_user_can( 'administrator' ) && ! current_user_can( $cap ) ) {
1812 - $role = get_role( 'administrator' );
1813 - $frm_roles = self::frm_capabilities();
1814 - foreach ( $frm_roles as $frm_role => $frm_role_description ) {
1815 - $role->add_cap( $frm_role );
1816 - }
2391 + if ( ! current_user_can( 'administrator' ) || current_user_can( $cap ) ) {
2392 + return;
1817 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 + }
1818 2401 }
1819 2402
1820 2403 /**
1821 - * Check if the user has permision for action.
2404 + * Check if the user has permission for action.
1822 2405 * Return permission message and stop the action if no permission
1823 2406 *
1824 2407 * @since 2.0
1825 2408 *
1826 2409 * @param string $permission
2410 + * @param string $show_message
1827 2411 */
1828 2412 public static function permission_check( $permission, $show_message = 'show' ) {
1829 2413 $permission_error = self::permission_nonce_error( $permission );
1830 - if ( $permission_error !== false ) {
1831 - if ( 'hide' == $show_message ) {
1832 - $permission_error = '';
1833 - }
1834 - wp_die( esc_html( $permission_error ) );
2414 +
2415 + if ( $permission_error === false ) {
2416 + return;
1835 2417 }
2418 +
2419 + if ( 'hide' === $show_message ) {
2420 + $permission_error = '';
2421 + }
2422 +
2423 + wp_die( esc_html( $permission_error ) );
1836 2424 }
1837 2425
1838 2426 /**
1839 2427 * Check user permission and nonce
@@ -1840,24 +2428,27 @@
1840 2428 *
1841 2429 * @since 2.0
1842 2430 *
1843 2431 * @param string $permission
2432 + * @param string $nonce_name
2433 + * @param string $nonce
1844 2434 *
1845 2435 * @return false|string The permission message or false if allowed
1846 2436 */
1847 2437 public static function permission_nonce_error( $permission, $nonce_name = '', $nonce = '' ) {
1848 - if ( ! empty( $permission ) && ! current_user_can( $permission ) && ! current_user_can( 'administrator' ) ) {
2438 + if ( $permission && ! current_user_can( $permission ) && ! current_user_can( 'administrator' ) ) {
1849 2439 $frm_settings = self::get_settings();
1850 -
1851 2440 return $frm_settings->admin_permission;
1852 2441 }
1853 2442
1854 2443 $error = false;
1855 - if ( empty( $nonce_name ) ) {
2444 +
2445 + if ( ! $nonce_name ) {
1856 2446 return $error;
1857 2447 }
1858 2448
1859 - $nonce_value = ( $_REQUEST && isset( $_REQUEST[ $nonce_name ] ) ) ? sanitize_text_field( wp_unslash( $_REQUEST[ $nonce_name ] ) ) : '';
2449 + $nonce_value = $_REQUEST && isset( $_REQUEST[ $nonce_name ] ) ? sanitize_text_field( wp_unslash( $_REQUEST[ $nonce_name ] ) ) : '';
2450 +
1860 2451 if ( $_REQUEST && ( ! isset( $_REQUEST[ $nonce_name ] ) || ! wp_verify_nonce( $nonce_value, $nonce ) ) ) {
1861 2452 $frm_settings = self::get_settings();
1862 2453 $error = $frm_settings->admin_permission;
1863 2454 }
@@ -1864,8 +2455,14 @@
1864 2455
1865 2456 return $error;
1866 2457 }
1867 2458
2459 + /**
2460 + * @param array|string $values
2461 + * @param string $current
2462 + *
2463 + * @return void
2464 + */
1868 2465 public static function checked( $values, $current ) {
1869 2466 if ( self::check_selected( $values, $current ) ) {
1870 2467 echo ' checked="checked"';
1871 2468 }
@@ -1870,40 +2467,74 @@
1870 2467 echo ' checked="checked"';
1871 2468 }
1872 2469 }
1873 2470
2471 + /**
2472 + * @param array|string $values
2473 + * @param string|null $current
2474 + *
2475 + * @return bool
2476 + */
1874 2477 public static function check_selected( $values, $current ) {
1875 2478 $values = self::recursive_function_map( $values, 'trim' );
1876 2479 $values = self::recursive_function_map( $values, 'htmlspecialchars_decode' );
1877 - $current = htmlspecialchars_decode( trim( $current ) );
2480 + $current = is_null( $current ) ? '' : htmlspecialchars_decode( trim( $current ) );
1878 2481
1879 - 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;
1880 2484 }
1881 2485
2486 + /**
2487 + * @param array|string $value
2488 + * @param callable|string $function
2489 + *
2490 + * @return array|string
2491 + */
1882 2492 public static function recursive_function_map( $value, $function ) {
1883 2493 if ( is_array( $value ) ) {
1884 2494 $original_function = $function;
1885 - if ( count( $value ) ) {
1886 - $function = explode( ', ', FrmDb::prepare_array_values( $value, $function ) );
1887 - } else {
1888 - $function = array( $function );
1889 - }
1890 - if ( ! self::is_assoc( $value ) ) {
1891 - $value = array_map( array( 'FrmAppHelper', 'recursive_function_map' ), $value, $function );
1892 - } else {
2495 + $function = count( $value ) ? explode( ', ', FrmDb::prepare_array_values( $value, $function ) ) : array( $function );
2496 +
2497 + if ( self::is_assoc( $value ) ) {
1893 2498 foreach ( $value as $k => $v ) {
1894 2499 if ( ! is_array( $v ) ) {
1895 2500 $value[ $k ] = call_user_func( $original_function, $v );
1896 2501 }
1897 2502 }
2503 + } else {
2504 + $value = array_map( array( 'FrmAppHelper', 'recursive_function_map' ), $value, $function );
1898 2505 }
1899 2506 } else {
2507 + $value = self::maybe_update_value_if_null( $value, $function );
1900 2508 $value = call_user_func( $function, $value );
2509 + }//end if
2510 +
2511 + return $value;
2512 + }
2513 +
2514 + /**
2515 + * Updates value to empty string if it is null and being passed to a string function.
2516 + *
2517 + * @since 6.8.4
2518 + *
2519 + * @param mixed $value
2520 + * @param string $function
2521 + *
2522 + * @return mixed
2523 + */
2524 + private static function maybe_update_value_if_null( $value, $function ) {
2525 + if ( null === $value && in_array( $function, array( 'trim', 'strlen' ), true ) ) {
2526 + return '';
1901 2527 }
1902 2528
1903 2529 return $value;
1904 2530 }
1905 2531
2532 + /**
2533 + * @param array $array
2534 + *
2535 + * @return bool
2536 + */
1906 2537 public static function is_assoc( $array ) {
1907 2538 return (bool) count( array_filter( array_keys( $array ), 'is_string' ) );
1908 2539 }
1909 2540
@@ -1908,20 +2539,24 @@
1908 2539 }
1909 2540
1910 2541 /**
1911 2542 * Flatten a multi-dimensional array
2543 + *
2544 + * @param array $array
2545 + * @param string $keys
2546 + *
2547 + * @return array
1912 2548 */
1913 2549 public static function array_flatten( $array, $keys = 'keep' ) {
1914 2550 $return = array();
2551 +
1915 2552 foreach ( $array as $key => $value ) {
1916 2553 if ( is_array( $value ) ) {
1917 2554 $return = array_merge( $return, self::array_flatten( $value, $keys ) );
2555 + } elseif ( $keys === 'keep' ) {
2556 + $return[ $key ] = $value;
1918 2557 } else {
1919 - if ( $keys === 'keep' ) {
1920 - $return[ $key ] = $value;
1921 - } else {
1922 - $return[] = $value;
1923 - }
2558 + $return[] = $value;
1924 2559 }
1925 2560 }
1926 2561
1927 2562 return $return;
@@ -1926,16 +2561,43 @@
1926 2561
1927 2562 return $return;
1928 2563 }
1929 2564
2565 + /**
2566 + * Flatten an array before imploding it to avoid Array to string conversion warnings.
2567 + *
2568 + * @since 6.16.1
2569 + *
2570 + * @param string $sep
2571 + * @param array $array
2572 + *
2573 + * @return string
2574 + */
2575 + public static function safe_implode( $sep, $array ) {
2576 + $array = self::array_flatten( $array );
2577 + return implode( $sep, $array );
2578 + }
2579 +
2580 + /**
2581 + * @param string $text
2582 + * @param bool $is_rich_text
2583 + *
2584 + * @return string
2585 + */
1930 2586 public static function esc_textarea( $text, $is_rich_text = false ) {
1931 2587 $safe_text = str_replace( '&quot;', '"', $text );
2588 +
1932 2589 if ( ! $is_rich_text ) {
1933 2590 $safe_text = htmlspecialchars( $safe_text, ENT_NOQUOTES );
1934 2591 }
2592 +
1935 2593 $safe_text = str_replace( '&amp; ', '& ', $safe_text );
1936 2594
1937 - return apply_filters( 'esc_textarea', $safe_text, $text );
2595 + /**
2596 + * @param string $safe_text
2597 + * @param string $text
2598 + */
2599 + return (string) apply_filters( 'esc_textarea', $safe_text, $text );
1938 2600 }
1939 2601
1940 2602 /**
1941 2603 * Add auto paragraphs to text areas
@@ -1940,44 +2602,59 @@
1940 2602 /**
1941 2603 * Add auto paragraphs to text areas
1942 2604 *
1943 2605 * @since 2.0
2606 + *
2607 + * @param mixed $content
2608 + *
2609 + * @return mixed
1944 2610 */
1945 2611 public static function use_wpautop( $content ) {
1946 - if ( apply_filters( 'frm_use_wpautop', true ) && ! is_array( $content ) ) {
1947 - $content = wpautop( str_replace( '<br>', '<br />', $content ) );
2612 + if ( apply_filters( 'frm_use_wpautop', true ) && is_string( $content ) ) {
2613 + return wpautop( str_replace( '<br>', '<br />', $content ) );
1948 2614 }
1949 2615
1950 2616 return $content;
1951 2617 }
1952 2618
2619 + /**
2620 + * Replace quotes with their HTML entities.
2621 + *
2622 + * @param string $val
2623 + *
2624 + * @return string
2625 + */
1953 2626 public static function replace_quotes( $val ) {
1954 2627 // Replace double quotes.
1955 2628 $val = str_replace( array( '&#8220;', '&#8221;', '&#8243;' ), '"', $val );
1956 2629
1957 2630 // Replace single quotes.
1958 - $val = str_replace( array( '&#8216;', '&#8217;', '&#8242;', '&prime;', '&rsquo;', '&lsquo;' ), "'", $val );
1959 -
1960 - return $val;
2631 + return str_replace( array( '&#8216;', '&#8217;', '&#8242;', '&prime;', '&rsquo;', '&lsquo;' ), "'", $val );
1961 2632 }
1962 2633
1963 2634 /**
1964 - * @param string $handle
2635 + * @param string $handle
2636 + * @param int|string $default
2637 + *
2638 + * @return int|string
1965 2639 */
1966 2640 public static function script_version( $handle, $default = 0 ) {
1967 2641 global $wp_scripts;
2642 +
1968 2643 if ( ! $wp_scripts ) {
1969 2644 return $default;
1970 2645 }
1971 2646
1972 2647 $ver = $default;
2648 +
1973 2649 if ( ! isset( $wp_scripts->registered[ $handle ] ) ) {
1974 2650 return $ver;
1975 2651 }
1976 2652
1977 2653 $query = $wp_scripts->registered[ $handle ];
2654 +
1978 2655 if ( is_object( $query ) && ! empty( $query->ver ) ) {
1979 - $ver = $query->ver;
2656 + return $query->ver;
1980 2657 }
1981 2658
1982 2659 return $ver;
1983 2660 }
@@ -1986,17 +2663,23 @@
1986 2663 * @since 5.0.13 added $echo param.
1987 2664 *
1988 2665 * @param string $url
1989 2666 * @param bool $echo
1990 - * @return string|void
2667 + *
2668 + * @return string|null
1991 2669 */
1992 2670 public static function js_redirect( $url, $echo = false ) {
1993 - $callback = function() use ( $url ) {
2671 + $callback = function () use ( $url ) {
1994 2672 echo '<script type="text/javascript">window.location="' . esc_url_raw( $url ) . '"</script>';
1995 2673 };
1996 2674 return self::clip( $callback, $echo );
1997 2675 }
1998 2676
2677 + /**
2678 + * @param int|string $user_id
2679 + *
2680 + * @return int|string
2681 + */
1999 2682 public static function get_user_id_param( $user_id ) {
2000 2683 if ( ! $user_id || is_numeric( $user_id ) ) {
2001 2684 return $user_id;
2002 2685 }
@@ -2001,16 +2684,13 @@
2001 2684 return $user_id;
2002 2685 }
2003 2686
2004 2687 $user_id = sanitize_text_field( $user_id );
2688 +
2005 2689 if ( $user_id === 'current' ) {
2006 2690 $user_id = get_current_user_id();
2007 2691 } else {
2008 - if ( is_email( $user_id ) ) {
2009 - $user = get_user_by( 'email', $user_id );
2010 - } else {
2011 - $user = get_user_by( 'login', $user_id );
2012 - }
2692 + $user = is_email( $user_id ) ? get_user_by( 'email', $user_id ) : get_user_by( 'login', $user_id );
2013 2693
2014 2694 if ( $user ) {
2015 2695 $user_id = $user->ID;
2016 2696 }
@@ -2019,8 +2699,14 @@
2019 2699
2020 2700 return $user_id;
2021 2701 }
2022 2702
2703 + /**
2704 + * @param string $filename
2705 + * @param array $atts
2706 + *
2707 + * @return false|string
2708 + */
2023 2709 public static function get_file_contents( $filename, $atts = array() ) {
2024 2710 if ( ! is_file( $filename ) ) {
2025 2711 return false;
2026 2712 }
@@ -2026,13 +2712,10 @@
2026 2712 }
2027 2713
2028 2714 extract( $atts ); // phpcs:ignore WordPress.PHP.DontExtract
2029 2715 ob_start();
2030 - include( $filename );
2031 - $contents = ob_get_contents();
2032 - ob_end_clean();
2033 -
2034 - return $contents;
2716 + include $filename;
2717 + return ob_get_clean();
2035 2718 }
2036 2719
2037 2720 /**
2038 2721 * @param string $name
@@ -2042,8 +2725,9 @@
2042 2725 * @param int $num_chars
2043 2726 */
2044 2727 public static function get_unique_key( $name, $table_name, $column, $id = 0, $num_chars = 5 ) {
2045 2728 $key = '';
2729 +
2046 2730 if ( $name ) {
2047 2731 $key = sanitize_key( $name );
2048 2732 $key = self::maybe_clear_long_key( $key, $column );
2049 2733 }
@@ -2063,31 +2747,31 @@
2063 2747 $column
2064 2748 );
2065 2749
2066 2750 // Create a unique field id if it has already been used.
2067 - if ( in_array( $key, $similar_keys, true ) ) {
2068 - $key = self::maybe_truncate_key_before_appending( $column, $key );
2751 + if ( ! in_array( $key, $similar_keys, true ) ) {
2752 + return $key;
2753 + }
2069 2754
2070 - /**
2071 - * Allow for a custom separator between the attempted key and the generated suffix.
2072 - *
2073 - * @since 5.2.03
2074 - *
2075 - * @param string $separator. Default empty.
2076 - * @param string $key the key without the added suffix.
2077 - */
2078 - $separator = apply_filters( 'frm_unique_' . $column . '_separator', '', $key );
2755 + $key = self::maybe_truncate_key_before_appending( $column, $key );
2079 2756
2080 - $suffix = 2;
2081 - do {
2082 - $key_check = $key . $separator . $suffix;
2083 - ++$suffix;
2084 - } 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 );
2085 2766
2086 - $key = $key_check;
2087 - }
2767 + $suffix = 2;
2768 + do {
2769 + $key_check = $key . $separator . $suffix;
2770 + ++$suffix;
2771 + } while ( in_array( $key_check, $similar_keys, true ) );
2088 2772
2089 - return $key;
2773 + return $key_check;
2090 2774 }
2091 2775
2092 2776 /**
2093 2777 * Avoid trying to append to a really long key,
@@ -2094,20 +2778,26 @@
2094 2778 * The database limit is 100 for form and field keys so we want to avoid getting too close.
2095 2779 *
2096 2780 * @param string $column
2097 2781 * @param string $key
2782 + *
2098 2783 * @return string
2099 2784 */
2100 2785 private static function maybe_truncate_key_before_appending( $column, $key ) {
2101 - if ( in_array( $column, array( 'form_key', 'field_key' ), true ) ) {
2102 - $max_key_length_before_truncating = 60;
2103 - if ( strlen( $key ) > $max_key_length_before_truncating ) {
2104 - $key = substr( $key, 0, $max_key_length_before_truncating );
2105 - if ( is_numeric( $key ) ) {
2106 - $key .= 'a';
2107 - }
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';
2108 2797 }
2109 2798 }
2799 +
2110 2800 return $key;
2111 2801 }
2112 2802
2113 2803 /**
@@ -2114,29 +2804,36 @@
2114 2804 * Possibly reset a key to avoid conflicts with column size limits.
2115 2805 *
2116 2806 * @param string $key
2117 2807 * @param string $column
2808 + *
2118 2809 * @return string either the original key value, or an empty string if the key was too long.
2119 2810 */
2120 2811 private static function maybe_clear_long_key( $key, $column ) {
2121 2812 if ( 'field_key' === $column && strlen( $key ) >= 70 ) {
2122 - $key = '';
2813 + return '';
2123 2814 }
2124 2815 return $key;
2125 2816 }
2126 2817
2127 2818 /**
2819 + * @since 6.21 This is changed from `private` to `public`.
2820 + *
2128 2821 * @param int $num_chars
2822 + *
2129 2823 * @return string
2130 2824 */
2131 - private static function generate_new_key( $num_chars ) {
2132 - $max_slug_value = pow( 36, $num_chars );
2133 - $min_slug_value = 37; // we want to have at least 2 characters in the slug
2134 - return base_convert( rand( $min_slug_value, $max_slug_value ), 10, 36 );
2825 + public static function generate_new_key( $num_chars ) {
2826 + $max_slug_value = 36 ** $num_chars;
2827 +
2828 + // We want to have at least 2 characters in the slug.
2829 + $min_slug_value = 37;
2830 + return base_convert( random_int( $min_slug_value, $max_slug_value ), 10, 36 );
2135 2831 }
2136 2832
2137 2833 /**
2138 2834 * @param string $key
2835 + *
2139 2836 * @return string
2140 2837 */
2141 2838 private static function prevent_numeric_and_reserved_keys( $key ) {
2142 2839 if ( is_numeric( $key ) ) {
@@ -2150,12 +2847,14 @@
2150 2847 'editlink',
2151 2848 'siteurl',
2152 2849 'evenodd',
2153 2850 );
2851 +
2154 2852 if ( in_array( $key, $not_allowed, true ) ) {
2155 2853 $key .= 'a';
2156 2854 }
2157 2855 }
2856 +
2158 2857 return $key;
2159 2858 }
2160 2859
2161 2860 /**
@@ -2160,11 +2859,16 @@
2160 2859
2161 2860 /**
2162 2861 * Editing a Form or Entry
2163 2862 *
2164 - * @param string $table
2863 + * @param object $record
2864 + * @param string $table
2865 + * @param array|string $fields
2866 + * @param bool $default
2867 + * @param array $post_values
2868 + * @param array $args
2165 2869 *
2166 - * @return bool|array
2870 + * @return array|bool
2167 2871 */
2168 2872 public static function setup_edit_vars( $record, $table, $fields = '', $default = false, $post_values = array(), $args = array() ) {
2169 2873 if ( ! $record ) {
2170 2874 return false;
@@ -2169,9 +2873,9 @@
2169 2873 if ( ! $record ) {
2170 2874 return false;
2171 2875 }
2172 2876
2173 - if ( empty( $post_values ) ) {
2877 + if ( ! $post_values ) {
2174 2878 $post_values = wp_unslash( $_POST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
2175 2879 }
2176 2880
2177 2881 $values = array(
@@ -2179,9 +2883,9 @@
2179 2883 'fields' => array(),
2180 2884 );
2181 2885
2182 2886 foreach ( array( 'name', 'description' ) as $var ) {
2183 - $default_val = isset( $record->{$var} ) ? $record->{$var} : '';
2887 + $default_val = $record->{$var} ?? '';
2184 2888 $values[ $var ] = self::get_param( $var, $default_val, 'get', 'wp_kses_post' );
2185 2889 unset( $var, $default_val );
2186 2890 }
2187 2891
@@ -2199,48 +2903,67 @@
2199 2903
2200 2904 return $values;
2201 2905 }
2202 2906
2907 + /**
2908 + * @param array|string $fields
2909 + * @param object $record
2910 + * @param array $values
2911 + * @param array $args
2912 + *
2913 + * @return void
2914 + */
2203 2915 private static function prepare_field_arrays( $fields, $record, array &$values, $args ) {
2204 - if ( ! empty( $fields ) ) {
2205 - foreach ( (array) $fields as $field ) {
2206 - if ( ! self::is_admin_page() ) {
2207 - // Don't prep default values on the form settings page.
2208 - $field->default_value = apply_filters( 'frm_get_default_value', $field->default_value, $field, true );
2209 - }
2210 - $args['parent_form_id'] = isset( $args['parent_form_id'] ) ? $args['parent_form_id'] : $field->form_id;
2211 - 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 );
2212 2924 }
2925 +
2926 + $args['parent_form_id'] = $args['parent_form_id'] ?? $field->form_id;
2927 + self::fill_field_defaults( $field, $record, $values, $args );
2213 2928 }
2214 2929 }
2215 2930
2931 + /**
2932 + * @param object $field
2933 + * @param object $record
2934 + * @param array $values
2935 + * @param array $args
2936 + *
2937 + * @return void
2938 + */
2216 2939 private static function fill_field_defaults( $field, $record, array &$values, $args ) {
2217 2940 $post_values = $args['post_values'];
2218 2941
2219 2942 if ( $args['default'] ) {
2220 2943 $meta_value = $field->default_value;
2944 + } elseif ( $record->post_id && self::pro_is_installed() && ! empty( $field->field_options['post_field'] ) ) {
2945 + if ( ! isset( $field->field_options['custom_field'] ) ) {
2946 + $field->field_options['custom_field'] = '';
2947 + }
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 + );
2221 2960 } else {
2222 - if ( $record->post_id && self::pro_is_installed() && isset( $field->field_options['post_field'] ) && $field->field_options['post_field'] ) {
2223 - if ( ! isset( $field->field_options['custom_field'] ) ) {
2224 - $field->field_options['custom_field'] = '';
2225 - }
2226 - $meta_value = FrmProEntryMetaHelper::get_post_value(
2227 - $record->post_id,
2228 - $field->field_options['post_field'],
2229 - $field->field_options['custom_field'],
2230 - array(
2231 - 'truncate' => false,
2232 - 'type' => $field->type,
2233 - 'form_id' => $field->form_id,
2234 - 'field' => $field,
2235 - )
2236 - );
2237 - } else {
2238 - $meta_value = FrmEntryMeta::get_meta_value( $record, $field->id );
2239 - }
2240 - }
2961 + $meta_value = FrmEntryMeta::get_meta_value( $record, $field->id );
2962 + }//end if
2241 2963
2242 - $field_type = isset( $post_values['field_options'][ 'type_' . $field->id ] ) ? $post_values['field_options'][ 'type_' . $field->id ] : $field->type;
2964 + $field_type = $post_values['field_options'][ 'type_' . $field->id ] ?? $field->type;
2965 +
2243 2966 if ( isset( $post_values['item_meta'][ $field->id ] ) ) {
2244 2967 $new_value = $post_values['item_meta'][ $field->id ];
2245 2968 self::unserialize_or_decode( $new_value );
2246 2969 } else {
@@ -2255,9 +2978,9 @@
2255 2978 $args['field_type'] = $field_type;
2256 2979
2257 2980 FrmFieldsHelper::prepare_edit_front_field( $field_array, $field, $values['id'], $args );
2258 2981
2259 - if ( ! isset( $field_array['unique'] ) || ! $field_array['unique'] ) {
2982 + if ( empty( $field_array['unique'] ) ) {
2260 2983 $field_array['unique_msg'] = '';
2261 2984 }
2262 2985
2263 2986 $field_array = array_merge( (array) $field->field_options, $field_array );
@@ -2286,12 +3009,15 @@
2286 3009 );
2287 3010 }
2288 3011
2289 3012 /**
3013 + * @param object $record
2290 3014 * @param string $table
3015 + * @param array $post_values
3016 + * @param array $values
2291 3017 */
2292 3018 private static function fill_form_opts( $record, $table, $post_values, array &$values ) {
2293 - if ( $table == 'entries' ) {
3019 + if ( $table === 'entries' ) {
2294 3020 $form = $record->form_id;
2295 3021 FrmForm::maybe_get_form( $form );
2296 3022 } else {
2297 3023 $form = $record;
@@ -2321,15 +3047,21 @@
2321 3047 }
2322 3048
2323 3049 /**
2324 3050 * Set to POST value or default
3051 + *
3052 + * @param array $post_values
3053 + * @param array $values
3054 + *
3055 + * @return void
2325 3056 */
2326 3057 private static function fill_form_defaults( $post_values, array &$values ) {
2327 3058 $form_defaults = FrmFormsHelper::get_default_opts();
2328 3059
2329 3060 foreach ( $form_defaults as $opt => $default ) {
3061 + // phpcs:ignore Universal.Operators.StrictComparisons
2330 3062 if ( ! isset( $values[ $opt ] ) || $values[ $opt ] == '' ) {
2331 - $values[ $opt ] = ( $post_values && isset( $post_values['options'][ $opt ] ) ) ? $post_values['options'][ $opt ] : $default;
3063 + $values[ $opt ] = $post_values['options'][ $opt ] ?? $default;
2332 3064 }
2333 3065
2334 3066 unset( $opt, $default );
2335 3067 }
@@ -2339,9 +3071,9 @@
2339 3071 }
2340 3072
2341 3073 foreach ( array( 'before', 'after', 'submit' ) as $h ) {
2342 3074 if ( ! isset( $values[ $h . '_html' ] ) ) {
2343 - $values[ $h . '_html' ] = ( isset( $post_values['options'][ $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 );
2344 3076 }
2345 3077 unset( $h );
2346 3078 }
2347 3079 }
@@ -2350,46 +3082,70 @@
2350 3082 * @since 2.2.10
2351 3083 *
2352 3084 * @param array $post_values
2353 3085 *
2354 - * @return boolean|int
3086 + * @return bool|int
2355 3087 */
2356 3088 public static function custom_style_value( $post_values ) {
2357 - if ( ! empty( $post_values ) && isset( $post_values['options']['custom_style'] ) ) {
2358 - $custom_style = absint( $post_values['options']['custom_style'] );
2359 - } else {
2360 - $frm_settings = self::get_settings();
2361 - $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'] );
2362 3091 }
2363 3092
2364 - return $custom_style;
3093 + $frm_settings = self::get_settings();
3094 + return $frm_settings->load_style !== 'none';
2365 3095 }
2366 3096
2367 - public static function truncate( $str, $length, $minword = 3, $continue = '...' ) {
2368 - if ( is_array( $str ) ) {
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 + *
3104 + * @param mixed $original_string
3105 + * @param int|string $length
3106 + * @param int $minword
3107 + * @param string $continue
3108 + * @param bool $force_length_limit Force the final string to never exceed the length limit.
3109 + *
3110 + * @return string
3111 + */
3112 + public static function truncate( $original_string, $length, $minword = 3, $continue = '...', $force_length_limit = false ) {
3113 + if ( ! is_string( $original_string ) && ! is_int( $original_string ) ) {
2369 3114 return '';
2370 3115 }
2371 3116
2372 - $length = (int) $length;
2373 - $str = wp_strip_all_tags( $str );
3117 + $length = (int) $length;
3118 +
3119 + if ( $length === 0 ) {
3120 + return '';
3121 + }
3122 +
3123 + $str = wp_strip_all_tags( (string) $original_string );
2374 3124 $original_len = self::mb_function( array( 'mb_strlen', 'strlen' ), array( $str ) );
2375 3125
2376 - if ( $length == 0 ) {
2377 - return '';
2378 - } elseif ( $length <= 10 ) {
3126 + if ( $length <= 10 ) {
2379 3127 $sub = self::mb_function( array( 'mb_substr', 'substr' ), array( $str, 0, $length ) );
2380 3128
2381 - return $sub . ( ( $length < $original_len ) ? $continue : '' );
3129 + if ( $force_length_limit ) {
3130 + return $sub;
3131 + }
3132 +
3133 + return $sub . ( $length < $original_len ? $continue : '' );
2382 3134 }
2383 3135
2384 - $sub = '';
2385 - $len = 0;
3136 + $sub = '';
3137 + $len = 0;
3138 + $words = self::mb_function( array( 'mb_split', 'explode' ), array( ' ', $str ) );
2386 3139
2387 - $words = self::mb_function( array( 'mb_split', 'explode' ), array( ' ', $str ) );
3140 + if ( ! is_array( $words ) ) {
3141 + return $original_string;
3142 + }
2388 3143
2389 3144 foreach ( $words as $word ) {
2390 - $part = ( ( $sub != '' ) ? ' ' : '' ) . $word;
3145 + $part = ( $sub !== '' ? ' ' : '' ) . $word;
2391 3146 $total_len = self::mb_function( array( 'mb_strlen', 'strlen' ), array( $sub . $part ) );
3147 +
2392 3148 if ( $total_len > $length && substr_count( $sub, ' ' ) ) {
2393 3149 break;
2394 3150 }
2395 3151
@@ -2402,14 +3158,72 @@
2402 3158
2403 3159 unset( $total_len, $word );
2404 3160 }
2405 3161
2406 - return $sub . ( ( $len < $original_len ) ? $continue : '' );
3162 + $sub = self::maybe_force_truncate_on_string_with_no_spaces( $sub, $length, $force_length_limit );
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 +
3175 + return $sub . ( $len < $original_len ? $continue : '' );
2407 3176 }
2408 3177
3178 + /**
3179 + * If the string is still too long because there may not have been any spaces, force truncate.
3180 + *
3181 + * @since 6.5.4
3182 + *
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 + *
3187 + * @return string
3188 + */
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 );
3204 + }
3205 +
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 ) );
3211 + }
3212 +
3213 + return $sub;
3214 + }
3215 +
3216 + /**
3217 + * @param array $function_names
3218 + * @param array $args
3219 + *
3220 + * @return mixed
3221 + */
2409 3222 public static function mb_function( $function_names, $args ) {
2410 3223 $mb_function_name = $function_names[0];
2411 3224 $function_name = $function_names[1];
3225 +
2412 3226 if ( function_exists( $mb_function_name ) ) {
2413 3227 $function_name = $mb_function_name;
2414 3228 }
2415 3229
@@ -2415,14 +3229,21 @@
2415 3229
2416 3230 return call_user_func_array( $function_name, $args );
2417 3231 }
2418 3232
3233 + /**
3234 + * @param string $date
3235 + * @param string $date_format
3236 + * @param string $time_format
3237 + *
3238 + * @return string
3239 + */
2419 3240 public static function get_formatted_time( $date, $date_format = '', $time_format = '' ) {
2420 - if ( empty( $date ) ) {
3241 + if ( ! $date ) {
2421 3242 return $date;
2422 3243 }
2423 3244
2424 - if ( empty( $date_format ) ) {
3245 + if ( ! $date_format ) {
2425 3246 $date_format = get_option( 'date_format' );
2426 3247 }
2427 3248
2428 3249 if ( preg_match( '/^\d{1-2}\/\d{1-2}\/\d{4}$/', $date ) && self::pro_is_installed() ) {
@@ -2430,10 +3251,10 @@
2430 3251 $date = FrmProAppHelper::convert_date( $date, $frmpro_settings->date_format, 'Y-m-d' );
2431 3252 }
2432 3253
2433 3254 $formatted = self::get_localized_date( $date_format, $date );
3255 + $do_time = gmdate( 'H:i:s', strtotime( $date ) ) !== '00:00:00';
2434 3256
2435 - $do_time = ( gmdate( 'H:i:s', strtotime( $date ) ) != '00:00:00' );
2436 3257 if ( $do_time ) {
2437 3258 $formatted .= self::add_time_to_date( $time_format, $date );
2438 3259 }
2439 3260
@@ -2439,45 +3260,52 @@
2439 3260
2440 3261 return $formatted;
2441 3262 }
2442 3263
3264 + /**
3265 + * @param string $time_format
3266 + * @param string $date
3267 + *
3268 + * @return string
3269 + */
2443 3270 private static function add_time_to_date( $time_format, $date ) {
2444 - if ( empty( $time_format ) ) {
3271 + if ( ! $time_format ) {
2445 3272 $time_format = get_option( 'time_format' );
2446 3273 }
2447 3274
2448 3275 $trimmed_format = trim( $time_format );
2449 - $time = '';
2450 - if ( $time_format && ! empty( $trimmed_format ) ) {
2451 - $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 );
2452 3279 }
2453 3280
2454 - return $time;
3281 + return '';
2455 3282 }
2456 3283
2457 3284 /**
2458 3285 * @since 2.0.8
3286 + *
3287 + * @param string $date_format
3288 + * @param string $date
3289 + *
3290 + * @return string
2459 3291 */
2460 3292 public static function get_localized_date( $date_format, $date ) {
2461 3293 $date = get_date_from_gmt( $date );
2462 -
2463 3294 return date_i18n( $date_format, strtotime( $date ) );
2464 3295 }
2465 3296
2466 3297 /**
2467 - * Gets the time ago in words
3298 + * Gets the time ago in words.
2468 3299 *
2469 - * @param int $from in seconds
2470 - * @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.
2471 3303 *
2472 - * @return string $time_ago
3304 + * @return string Time ago.
2473 3305 */
2474 3306 public static function human_time_diff( $from, $to = '', $levels = 1 ) {
2475 - if ( empty( $to ) && 0 !== $to ) {
2476 - $now = new DateTime();
2477 - } else {
2478 - $now = new DateTime( '@' . $to );
2479 - }
3307 + $now = ! $to && 0 !== $to ? new DateTime() : new DateTime( '@' . $to );
2480 3308 $ago = new DateTime( '@' . $from );
2481 3309
2482 3310 // Get the time difference
2483 3311 $diff_object = $now->diff( $ago );
@@ -2483,9 +3311,9 @@
2483 3311 $diff_object = $now->diff( $ago );
2484 3312 $diff = get_object_vars( $diff_object );
2485 3313
2486 3314 // Add week amount and update day amount
2487 - $diff['w'] = floor( $diff['d'] / 7 );
3315 + $diff['w'] = floor( $diff['d'] / 7 );
2488 3316 $diff['d'] -= $diff['w'] * 7;
2489 3317
2490 3318 $time_strings = self::get_time_strings();
2491 3319
@@ -2491,10 +3319,11 @@
2491 3319
2492 3320 if ( ! is_numeric( $levels ) ) {
2493 3321 // Show time in specified unit.
2494 3322 $levels = self::get_unit( $levels );
3323 +
2495 3324 if ( isset( $time_strings[ $levels ] ) ) {
2496 - $diff = array(
3325 + $diff = array(
2497 3326 $levels => self::time_format( $levels, $diff ),
2498 3327 );
2499 3328 $time_strings = array(
2500 3329 $levels => $time_strings[ $levels ],
@@ -2499,13 +3328,14 @@
2499 3328 $time_strings = array(
2500 3329 $levels => $time_strings[ $levels ],
2501 3330 );
2502 3331 }
3332 +
2503 3333 $levels = 1;
2504 3334 }
2505 3335
2506 3336 foreach ( $time_strings as $k => $v ) {
2507 - if ( isset( $diff[ $k ] ) && $diff[ $k ] ) {
3337 + if ( ! empty( $diff[ $k ] ) ) {
2508 3338 $time_strings[ $k ] = $diff[ $k ] . ' ' . ( $diff[ $k ] > 1 ? $v[1] : $v[0] );
2509 3339 } elseif ( isset( $diff[ $k ] ) && count( $time_strings ) === 1 ) {
2510 3340 // Account for 0.
2511 3341 $time_strings[ $k ] = $diff[ $k ] . ' ' . $v[1];
@@ -2513,17 +3343,21 @@
2513 3343 unset( $time_strings[ $k ] );
2514 3344 }
2515 3345 }
2516 3346
2517 - $levels_deep = apply_filters( 'frm_time_ago_levels', $levels, compact( 'time_strings', 'from', 'to' ) );
2518 - $time_strings = array_slice( $time_strings, 0, absint( $levels_deep ) );
2519 - $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 ) );
2520 3349
2521 - return $time_ago_string;
3350 + return implode( ' ', $time_strings );
2522 3351 }
2523 3352
2524 3353 /**
2525 3354 * @since 4.05.01
3355 + *
3356 + * @param string $unit
3357 + * @param array $diff
3358 + *
3359 + * @return int
2526 3360 */
2527 3361 private static function time_format( $unit, $diff ) {
2528 3362 $return = array(
2529 3363 'y' => 'y',
@@ -2528,14 +3362,14 @@
2528 3362 $return = array(
2529 3363 'y' => 'y',
2530 3364 'd' => 'days',
2531 3365 );
3366 +
2532 3367 if ( isset( $return[ $unit ] ) ) {
2533 3368 return $diff[ $return[ $unit ] ];
2534 3369 }
2535 3370
2536 3371 $total = $diff['days'] * self::convert_time( 'd', $unit );
2537 -
2538 3372 $times = array( 'h', 'i', 's' );
2539 3373
2540 3374 foreach ( $times as $time ) {
2541 3375 if ( ! isset( $diff[ $time ] ) ) {
@@ -2549,8 +3383,13 @@
2549 3383 }
2550 3384
2551 3385 /**
2552 3386 * @since 4.05.01
3387 + *
3388 + * @param string $from
3389 + * @param string $to
3390 + *
3391 + * @return int
2553 3392 */
2554 3393 private static function convert_time( $from, $to ) {
2555 3394 $convert = array(
2556 3395 's' => 1,
@@ -2566,28 +3405,35 @@
2566 3405 }
2567 3406
2568 3407 /**
2569 3408 * @since 4.05.01
3409 + *
3410 + * @param string $unit
3411 + *
3412 + * @return int|string
2570 3413 */
2571 3414 private static function get_unit( $unit ) {
2572 3415 $units = self::get_time_strings();
3416 +
2573 3417 if ( isset( $units[ $unit ] ) || is_numeric( $unit ) ) {
2574 3418 return $unit;
2575 3419 }
2576 3420
2577 3421 foreach ( $units as $u => $strings ) {
2578 - if ( in_array( $unit, $strings ) ) {
3422 + if ( in_array( $unit, $strings, true ) ) {
2579 3423 return $u;
2580 3424 }
2581 3425 }
3426 +
2582 3427 return 1;
2583 3428 }
2584 3429
2585 3430 /**
2586 3431 * Get the translatable time strings. The untranslated version is a failsafe
2587 - * in case langauges are changing for the unit set in the shortcode.
3432 + * in case languages are changing for the unit set in the shortcode.
2588 3433 *
2589 3434 * @since 2.0.20
3435 + *
2590 3436 * @return array
2591 3437 */
2592 3438 private static function get_time_strings() {
2593 3439 return array(
@@ -2631,35 +3477,48 @@
2631 3477
2632 3478 // Pagination Methods.
2633 3479
2634 3480 /**
2635 - * @param integer $current_p
3481 + * @param int $r_count
3482 + * @param int $current_p
3483 + * @param int $p_size
3484 + *
3485 + * @return int
2636 3486 */
2637 3487 public static function get_last_record_num( $r_count, $current_p, $p_size ) {
2638 - 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;
2639 3489 }
2640 3490
2641 3491 /**
2642 - * @param integer $current_p
3492 + * @param int $r_count
3493 + * @param int $current_p
3494 + * @param int $p_size
3495 + *
3496 + * @return int
2643 3497 */
2644 3498 public static function get_first_record_num( $r_count, $current_p, $p_size ) {
3499 + // phpcs:ignore Universal.Operators.StrictComparisons
2645 3500 if ( $current_p == 1 ) {
2646 3501 return 1;
2647 - } else {
2648 - return ( self::get_last_record_num( $r_count, ( $current_p - 1 ), $p_size ) + 1 );
2649 3502 }
3503 + return self::get_last_record_num( $r_count, $current_p - 1, $p_size ) + 1;
2650 3504 }
2651 3505
2652 3506 /**
3507 + * @param array $json_vars
3508 + *
2653 3509 * @return array
2654 3510 */
2655 3511 public static function json_to_array( $json_vars ) {
2656 3512 $vars = array();
3513 +
2657 3514 foreach ( $json_vars as $jv ) {
2658 3515 $jv_name = explode( '[', $jv['name'] );
2659 3516 $last = count( $jv_name ) - 1;
3517 +
2660 3518 foreach ( $jv_name as $p => $n ) {
2661 3519 $name = trim( $n, ']' );
3520 +
2662 3521 if ( ! isset( $l1 ) ) {
2663 3522 $l1 = $name;
2664 3523 }
2665 3524
@@ -2670,9 +3529,9 @@
2670 3529 if ( ! isset( $l3 ) ) {
2671 3530 $l3 = $name;
2672 3531 }
2673 3532
2674 - $this_val = ( $p == $last ) ? $jv['value'] : array();
3533 + $this_val = $p === $last ? $jv['value'] : array();
2675 3534
2676 3535 switch ( $p ) {
2677 3536 case 0:
2678 3537 $l1 = $name;
@@ -2694,12 +3553,12 @@
2694 3553 self::add_value_to_array( $name, $l4, $this_val, $vars[ $l1 ][ $l2 ][ $l3 ] );
2695 3554 }
2696 3555
2697 3556 unset( $this_val, $n );
2698 - }
3557 + }//end foreach
2699 3558
2700 3559 unset( $last, $jv );
2701 - }
3560 + }//end foreach
2702 3561
2703 3562 return $vars;
2704 3563 }
2705 3564
@@ -2705,10 +3564,15 @@
2705 3564
2706 3565 /**
2707 3566 * @param string $name
2708 3567 * @param string $l1
3568 + * @param mixed $val
3569 + * @param array $vars
3570 + *
3571 + * @return void
2709 3572 */
2710 3573 public static function add_value_to_array( $name, $l1, $val, &$vars ) {
3574 + // phpcs:ignore Universal.Operators.StrictComparisons
2711 3575 if ( $name == '' ) {
2712 3576 $vars[] = $val;
2713 3577 } elseif ( ! isset( $vars[ $l1 ] ) ) {
2714 3578 $vars[ $l1 ] = $val;
@@ -2714,19 +3578,26 @@
2714 3578 $vars[ $l1 ] = $val;
2715 3579 }
2716 3580 }
2717 3581
3582 + /**
3583 + * @param string $name
3584 + * @param string $class
3585 + * @param string $form_name
3586 + *
3587 + * @return void
3588 + */
2718 3589 public static function maybe_add_tooltip( $name, $class = 'closed', $form_name = '' ) {
2719 3590 $tooltips = array(
2720 3591 'action_title' => __( 'Give this action a label for easy reference.', 'formidable' ),
2721 - 'email_to' => __( 'Add one or more recipient addresses separated by a ",". FORMAT: Name <[email protected]> or [email protected]. [admin_email] is the address set in WP General 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
2722 3593 'cc' => __( 'Add CC addresses separated by a ",". FORMAT: Name <[email protected]> or [email protected].', 'formidable' ),
2723 3594 'bcc' => __( 'Add BCC addresses separated by a ",". FORMAT: Name <[email protected]> or [email protected].', 'formidable' ),
2724 - '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
2725 3596 'from' => __( 'Enter the name and/or email address of the sender. FORMAT: John Bates <[email protected]> or [email protected].', 'formidable' ),
2726 3597 /* translators: %1$s: Form name, %2$s: Date */
2727 - '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() ) ),
2728 - '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
2729 3600 );
2730 3601
2731 3602 if ( ! isset( $tooltips[ $name ] ) ) {
2732 3603 return;
@@ -2731,9 +3602,9 @@
2731 3602 if ( ! isset( $tooltips[ $name ] ) ) {
2732 3603 return;
2733 3604 }
2734 3605
2735 - if ( 'open' == $class ) {
3606 + if ( 'open' === $class ) {
2736 3607 echo ' frm_help"';
2737 3608 } else {
2738 3609 echo ' class="frm_help"';
2739 3610 }
@@ -2739,9 +3610,9 @@
2739 3610 }
2740 3611
2741 3612 echo ' title="' . esc_attr( $tooltips[ $name ] );
2742 3613
2743 - if ( 'open' != $class ) {
3614 + if ( 'open' !== $class ) {
2744 3615 echo '"';
2745 3616 }
2746 3617 }
2747 3618
@@ -2746,20 +3617,28 @@
2746 3617 }
2747 3618
2748 3619 /**
2749 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
2750 3627 */
2751 3628 public static function select_current_page( $page, $current_page, $action = array() ) {
3629 + // phpcs:ignore Universal.Operators.StrictComparisons
2752 3630 if ( $current_page != $page ) {
2753 3631 return;
2754 3632 }
2755 3633
2756 3634 $frm_action = self::simple_get( 'frm_action', 'sanitize_title' );
3635 +
2757 3636 if ( 'lite-reports' === $frm_action ) {
2758 3637 $frm_action = 'reports';
2759 3638 }
2760 3639
2761 - if ( empty( $action ) || ( ! empty( $frm_action ) && in_array( $frm_action, $action ) ) ) {
3640 + if ( ! $action || ( $frm_action && in_array( $frm_action, $action, true ) ) ) {
2762 3641 echo ' class="current_page"';
2763 3642 }
2764 3643 }
2765 3644
@@ -2789,14 +3668,19 @@
2789 3668
2790 3669 // Add extra slashes for \r\n since WP strips them.
2791 3670 $post_content = str_replace( array( '\\r', '\\n', '\\u', '\\t' ), array( '\\\\r', '\\\\n', '\\\\u', '\\\\t' ), $post_content );
2792 3671
2793 - // allow for &quot
2794 - $post_content = str_replace( '&quot;', '\\"', $post_content );
2795 -
2796 - return $post_content;
3672 + // Allow for &quot
3673 + return str_replace( '&quot;', '\\"', $post_content );
2797 3674 }
2798 3675
3676 + /**
3677 + * @param array|string $val
3678 + * @param int|string $key
3679 + * @param array $post_content
3680 + *
3681 + * @return void
3682 + */
2799 3683 private static function prepare_action_slashes( $val, $key, &$post_content ) {
2800 3684 if ( ! isset( $post_content[ $key ] ) || is_numeric( $val ) ) {
2801 3685 return;
2802 3686 }
@@ -2805,15 +3689,17 @@
2805 3689 foreach ( $val as $k1 => $v1 ) {
2806 3690 self::prepare_action_slashes( $v1, $k1, $post_content[ $key ] );
2807 3691 unset( $k1, $v1 );
2808 3692 }
2809 - } else {
2810 - // Strip all slashes so everything is the same, no matter where the value is coming from
2811 - $val = stripslashes( $val );
2812 3693
2813 - // Add backslashes before double quotes and forward slashes only
2814 - $post_content[ $key ] = addcslashes( $val, '"\\/' );
3694 + return;
2815 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, '"\\/' );
2816 3702 }
2817 3703
2818 3704 /**
2819 3705 * Check for either json or serialized data. This is temporary while transitioning
@@ -2821,8 +3707,9 @@
2821 3707 *
2822 3708 * @since 4.02.03
2823 3709 *
2824 3710 * @param array|string $value
3711 + *
2825 3712 * @return void
2826 3713 */
2827 3714 public static function unserialize_or_decode( &$value ) {
2828 3715 if ( is_array( $value ) ) {
@@ -2828,13 +3715,9 @@
2828 3715 if ( is_array( $value ) ) {
2829 3716 return;
2830 3717 }
2831 3718
2832 - if ( is_serialized( $value ) ) {
2833 - $value = self::maybe_unserialize_array( $value );
2834 - } else {
2835 - $value = self::maybe_json_decode( $value, false );
2836 - }
3719 + $value = is_serialized( $value ) ? self::maybe_unserialize_array( $value ) : self::maybe_json_decode( $value, false );
2837 3720 }
2838 3721
2839 3722 /**
2840 3723 * Safely unserialize an array if necessary.
@@ -2842,8 +3725,9 @@
2842 3725 *
2843 3726 * @since 6.2
2844 3727 *
2845 3728 * @param mixed $value
3729 + *
2846 3730 * @return mixed
2847 3731 */
2848 3732 public static function maybe_unserialize_array( $value ) {
2849 3733 if ( ! is_string( $value ) ) {
@@ -2850,18 +3734,15 @@
2850 3734 return $value;
2851 3735 }
2852 3736
2853 3737 // Since we only expect an array, skip anything that doesn't start with a:.
2854 - if ( ! is_serialized( $value ) || 'a:' !== substr( $value, 0, 2 ) ) {
3738 + if ( ! is_serialized( $value ) || ! str_starts_with( $value, 'a:' ) ) {
2855 3739 return $value;
2856 3740 }
2857 3741
2858 3742 $parsed = FrmSerializedStringParserHelper::get()->parse( $value );
2859 - if ( is_array( $parsed ) ) {
2860 - $value = $parsed;
2861 - }
2862 3743
2863 - return $value;
3744 + return is_array( $parsed ) ? $parsed : $value;
2864 3745 }
2865 3746
2866 3747 /**
2867 3748 * Decode a JSON string.
@@ -2866,11 +3747,12 @@
2866 3747 /**
2867 3748 * Decode a JSON string.
2868 3749 * Do not switch shortcodes like [24] to array unless intentional ie XML values.
2869 3750 *
2870 - * @param mixed $string
2871 - * @param bool $single_to_array
2872 - * @return mixed
3751 + * @param array|string|null $string
3752 + * @param bool $single_to_array
3753 + *
3754 + * @return array|string|null
2873 3755 */
2874 3756 public static function maybe_json_decode( $string, $single_to_array = true ) {
2875 3757 if ( is_array( $string ) || is_null( $string ) ) {
2876 3758 return $string;
@@ -2875,20 +3757,19 @@
2875 3757 if ( is_array( $string ) || is_null( $string ) ) {
2876 3758 return $string;
2877 3759 }
2878 3760
2879 - $new_string = json_decode( $string, true );
2880 - if ( function_exists( 'json_last_error' ) ) {
2881 - // php 5.3+
2882 - $single_value = false;
2883 - if ( ! $single_to_array ) {
2884 - $single_value = is_array( $new_string ) && count( $new_string ) === 1 && isset( $new_string[0] );
2885 - }
2886 - if ( json_last_error() == JSON_ERROR_NONE && is_array( $new_string ) && ! $single_value ) {
2887 - $string = $new_string;
2888 - }
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] );
2889 3766 }
2890 3767
3768 + if ( json_last_error() === JSON_ERROR_NONE && is_array( $new_string ) && ! $single_value ) {
3769 + return $new_string;
3770 + }
3771 +
2891 3772 return $string;
2892 3773 }
2893 3774
2894 3775 /**
@@ -2894,8 +3775,9 @@
2894 3775 /**
2895 3776 * @since 6.2.3
2896 3777 *
2897 3778 * @param string $value
3779 + *
2898 3780 * @return string
2899 3781 */
2900 3782 public static function maybe_utf8_encode( $value ) {
2901 3783 $from_format = 'ISO-8859-1';
@@ -2904,13 +3786,15 @@
2904 3786 if ( function_exists( 'mb_check_encoding' ) && function_exists( 'mb_convert_encoding' ) ) {
2905 3787 if ( mb_check_encoding( $value, $from_format ) ) {
2906 3788 return mb_convert_encoding( $value, $to_format, $from_format );
2907 3789 }
3790 +
2908 3791 return $value;
2909 3792 }
2910 3793
2911 3794 if ( function_exists( 'iconv' ) ) {
2912 3795 $converted = iconv( $from_format, $to_format, $value );
3796 +
2913 3797 // Value is false if $value is not ISO-8859-1.
2914 3798 if ( false !== $converted ) {
2915 3799 return $converted;
2916 3800 }
@@ -2922,8 +3806,12 @@
2922 3806 /**
2923 3807 * Reformat the json serialized array in name => value array.
2924 3808 *
2925 3809 * @since 4.02.03
3810 + *
3811 + * @param array $form
3812 + *
3813 + * @return void
2926 3814 */
2927 3815 public static function format_form_data( &$form ) {
2928 3816 $formatted = array();
2929 3817
@@ -2930,17 +3818,20 @@
2930 3818 foreach ( $form as $input ) {
2931 3819 if ( ! isset( $input['name'] ) ) {
2932 3820 continue;
2933 3821 }
3822 +
2934 3823 $key = $input['name'];
2935 - if ( isset( $formatted[ $key ] ) ) {
2936 - if ( is_array( $formatted[ $key ] ) ) {
2937 - $formatted[ $key ][] = $input['value'];
2938 - } else {
2939 - $formatted[ $key ] = array( $formatted[ $key ], $input['value'] );
2940 - }
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'];
2941 3832 } else {
2942 - $formatted[ $key ] = $input['value'];
3833 + $formatted[ $key ] = array( $formatted[ $key ], $input['value'] );
2943 3834 }
2944 3835 }
2945 3836
2946 3837 parse_str( http_build_query( $formatted ), $form );
@@ -2947,30 +3838,34 @@
2947 3838 }
2948 3839
2949 3840 /**
2950 3841 * @since 4.02.03
3842 + *
3843 + * @param array|string $value
3844 + *
3845 + * @return string
2951 3846 */
2952 3847 public static function maybe_json_encode( $value ) {
2953 - if ( is_array( $value ) ) {
2954 - $value = wp_json_encode( $value );
2955 - }
2956 - return $value;
3848 + return is_array( $value ) ? wp_json_encode( $value ) : $value;
2957 3849 }
2958 3850
2959 3851 /**
3852 + * Echo The javascript to open and highlight the Formidable menu
3853 + *
2960 3854 * @since 1.07.10
2961 3855 *
2962 - * @param string $post_type The name of the post type that may need to be highlighted
2963 - * echo The javascript to open and highlight the Formidable menu
3856 + * @param string $post_type The name of the post type that may need to be highlighted.
3857 + *
3858 + * @return void
2964 3859 */
2965 3860 public static function maybe_highlight_menu( $post_type ) {
2966 3861 global $post;
2967 3862
2968 - if ( isset( $_REQUEST['post_type'] ) && $_REQUEST['post_type'] != $post_type ) {
3863 + if ( isset( $_REQUEST['post_type'] ) && $_REQUEST['post_type'] !== $post_type ) {
2969 3864 return;
2970 3865 }
2971 3866
2972 - if ( is_object( $post ) && $post->post_type != $post_type ) {
3867 + if ( is_object( $post ) && $post->post_type !== $post_type ) {
2973 3868 return;
2974 3869 }
2975 3870
2976 3871 self::load_admin_wide_js();
@@ -2980,12 +3875,15 @@
2980 3875 /**
2981 3876 * Load the JS file on non-Formidable pages in the admin area
2982 3877 *
2983 3878 * @since 2.0
3879 + *
3880 + * @param bool $load
3881 + *
3882 + * @return void
2984 3883 */
2985 3884 public static function load_admin_wide_js( $load = true ) {
2986 - $version = self::plugin_version();
2987 - 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() );
2988 3886
2989 3887 $global_strings = array(
2990 3888 'updating_msg' => __( 'Please wait while your site updates.', 'formidable' ),
2991 3889 'deauthorize' => __( 'Are you sure you want to deauthorize Formidable Forms on this site?', 'formidable' ),
@@ -2991,12 +3889,13 @@
2991 3889 'deauthorize' => __( 'Are you sure you want to deauthorize Formidable Forms on this site?', 'formidable' ),
2992 3890 'url' => self::plugin_url(),
2993 3891 'app_url' => 'https://formidableforms.com/',
2994 3892 'applicationsUrl' => admin_url( 'admin.php?page=formidable-applications' ),
2995 - '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
2996 3894 'loading' => __( 'Loading&hellip;', 'formidable' ),
2997 3895 'nonce' => wp_create_nonce( 'frm_ajax' ),
2998 3896 'proIncludesSliderJs' => is_callable( 'FrmProFormsHelper::prepare_custom_currency' ),
3897 + 'inboxSlideIn' => FrmInbox::get_inbox_slide_in_value_for_js(),
2999 3898 );
3000 3899 wp_localize_script( 'formidable_admin_global', 'frmGlobal', $global_strings );
3001 3900
3002 3901 if ( $load ) {
@@ -3005,8 +3904,10 @@
3005 3904 }
3006 3905
3007 3906 /**
3008 3907 * @since 2.0.9
3908 + *
3909 + * @return void
3009 3910 */
3010 3911 public static function load_font_style() {
3011 3912 wp_enqueue_style( 'frm_fonts', self::plugin_url() . '/css/frm_fonts.css', array(), self::plugin_version() );
3012 3913 }
@@ -3012,86 +3913,111 @@
3012 3913 }
3013 3914
3014 3915 /**
3015 3916 * @param string $location
3917 + *
3918 + * @return void
3016 3919 */
3017 3920 public static function localize_script( $location ) {
3018 - global $wp_scripts;
3921 + global $wp_scripts, $wp_version;
3019 3922
3020 3923 $script_strings = array(
3021 - 'ajax_url' => esc_url_raw( self::get_ajax_url() ),
3022 - 'images_url' => self::plugin_url() . '/images',
3023 - 'loading' => __( 'Loading&hellip;', 'formidable' ),
3024 - 'remove' => __( 'Remove', 'formidable' ),
3025 - 'offset' => apply_filters( 'frm_scroll_offset', 4 ),
3026 - 'nonce' => wp_create_nonce( 'frm_ajax' ),
3027 - 'id' => __( 'ID', 'formidable' ),
3028 - 'no_results' => __( 'No results match', 'formidable' ),
3029 - 'file_spam' => __( 'That file looks like Spam.', 'formidable' ),
3030 - 'calc_error' => __( 'There is an error in the calculation in the field with key', 'formidable' ),
3031 - 'empty_fields' => __( 'Please complete the preceding required fields before uploading a file.', 'formidable' ),
3032 - 'focus_first_error' => self::should_focus_first_error(),
3033 - 'include_alert_role' => self::should_include_alert_role_on_field_errors(),
3924 + 'ajax_url' => esc_url_raw( self::get_ajax_url() ),
3925 + 'images_url' => self::plugin_url() . '/images',
3926 + 'loading' => __( 'Loading&hellip;', 'formidable' ),
3927 + 'remove' => __( 'Remove', 'formidable' ),
3928 + 'offset' => apply_filters( 'frm_scroll_offset', 4 ),
3929 + 'nonce' => wp_create_nonce( 'frm_ajax' ),
3930 + 'id' => __( 'ID', 'formidable' ),
3931 + 'no_results' => __( 'No results match', 'formidable' ),
3932 + 'file_spam' => __( 'That file looks like Spam.', 'formidable' ),
3933 + 'calc_error' => __( 'There is an error in the calculation in the field with key', 'formidable' ),
3934 + 'empty_fields' => __( 'Please complete the preceding required fields before uploading a file.', 'formidable' ),
3935 + 'focus_first_error' => self::should_focus_first_error(),
3936 + 'include_alert_role' => self::should_include_alert_role_on_field_errors(),
3937 + // We need to keep this setting for a few versions because Pro checks for this.
3938 + 'include_resend_email' => false,
3034 3939 );
3035 3940
3036 3941 $data = $wp_scripts->get_data( 'formidable', 'data' );
3942 +
3037 3943 if ( ! $data ) {
3038 3944 wp_localize_script( 'formidable', 'frm_js', $script_strings );
3039 3945 }
3040 3946
3041 3947 if ( $location === 'admin' ) {
3042 - $frm_settings = self::get_settings();
3043 3948 $admin_script_strings = array(
3044 - 'desc' => __( '(Click to add description)', 'formidable' ),
3045 - 'blank' => __( '(Blank)', 'formidable' ),
3046 - 'no_label' => __( '(no label)', 'formidable' ),
3047 - 'ok' => __( 'OK', 'formidable' ),
3048 - 'cancel' => __( 'Cancel', 'formidable' ),
3049 - 'default_label' => __( 'Default', 'formidable' ),
3050 - 'clear_default' => __( 'Clear default value when typing', 'formidable' ),
3051 - 'no_clear_default' => __( 'Do not clear default value when typing', 'formidable' ),
3052 - 'valid_default' => __( 'Default value will pass form validation', 'formidable' ),
3053 - 'no_valid_default' => __( 'Default value will NOT pass form validation', 'formidable' ),
3054 - 'confirm' => __( 'Are you sure?', 'formidable' ),
3055 - 'conf_delete' => __( 'Are you sure you want to delete this field and all data associated with it?', 'formidable' ),
3056 - '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' ),
3057 - 'conf_no_repeat' => __( 'Warning: If you have entries with multiple rows, all but the first row will be lost.', 'formidable' ),
3058 - 'default_unique' => $frm_settings->unique_msg,
3059 - 'default_conf' => __( 'The entered values do not match', 'formidable' ),
3060 - 'enter_email' => __( 'Enter Email', 'formidable' ),
3061 - 'confirm_email' => __( 'Confirm Email', 'formidable' ),
3062 - 'conditional_text' => __( 'Conditional content here', 'formidable' ),
3063 - 'new_option' => __( 'New Option', 'formidable' ),
3064 - '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' ),
3065 - 'enter_password' => __( 'Enter Password', 'formidable' ),
3066 - 'confirm_password' => __( 'Confirm Password', 'formidable' ),
3067 - 'import_complete' => __( 'Import Complete', 'formidable' ),
3068 - 'updating' => __( 'Please wait while your site updates.', 'formidable' ),
3069 - 'no_save_warning' => __( 'Warning: There is no way to retrieve unsaved entries.', 'formidable' ),
3070 - 'private_label' => __( 'Private', 'formidable' ),
3071 - 'jquery_ui_url' => '',
3072 - 'pro_url' => is_callable( 'FrmProAppHelper::plugin_url' ) ? FrmProAppHelper::plugin_url() : '',
3073 - 'no_licenses' => __( 'No new licenses were found', 'formidable' ),
3074 - 'unmatched_parens' => __( 'This calculation has at least one unmatched ( ) { } [ ].', 'formidable' ),
3075 - 'view_shortcodes' => __( 'This calculation may have shortcodes that work in Views but not forms.', 'formidable' ),
3076 - 'text_shortcodes' => __( 'This calculation may have shortcodes that work in text calculations but not numeric calculations.', 'formidable' ),
3077 - 'only_one_action' => __( 'This form action is limited to one per form. Please edit the existing form action.', 'formidable' ),
3078 - 'unsafe_params' => FrmFormsHelper::reserved_words(),
3949 + 'desc' => __( '(Click to add description)', 'formidable' ),
3950 + 'blank' => __( '(Blank)', 'formidable' ),
3951 + 'no_label' => self::get_no_label_text(),
3952 + 'ok' => __( 'OK', 'formidable' ),
3953 + 'cancel' => __( 'Cancel', 'formidable' ),
3954 + 'default_label' => __( 'Default', 'formidable' ),
3955 + 'clear_default' => __( 'Clear default value when typing', 'formidable' ),
3956 + 'no_clear_default' => __( 'Do not clear default value when typing', 'formidable' ),
3957 + 'valid_default' => __( 'Default value will pass form validation', 'formidable' ),
3958 + 'no_valid_default' => __( 'Default value will NOT pass form validation', 'formidable' ),
3959 + 'confirm' => __( 'Are you sure?', 'formidable' ),
3960 + 'conf_delete' => __( 'Are you sure you want to delete this field and all data associated with it?', '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
3962 + 'conf_no_repeat' => __( 'Warning: If you have entries with multiple rows, all but the first row will be lost.', 'formidable' ),
3963 + 'default_unique' => FrmFieldsHelper::default_unique_msg(),
3964 + 'default_conf' => __( 'The entered values do not match', 'formidable' ),
3965 + 'enter_email' => __( 'Enter Email', 'formidable' ),
3966 + 'confirm_email' => __( 'Confirm Email', 'formidable' ),
3967 + 'conditional_text' => __( 'Conditional content here', 'formidable' ),
3968 + 'new_option' => __( 'New Option', '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
3970 + 'enter_password' => __( 'Enter Password', 'formidable' ),
3971 + 'confirm_password' => __( 'Confirm Password', 'formidable' ),
3972 + 'import_complete' => __( 'Import Complete', 'formidable' ),
3973 + 'updating' => __( 'Please wait while your site updates.', 'formidable' ),
3974 + 'no_save_warning' => __( 'Warning: There is no way to retrieve unsaved entries.', 'formidable' ),
3975 + 'private_label' => __( 'Private', 'formidable' ),
3976 + 'jquery_ui_url' => '',
3977 + 'pro_url' => is_callable( 'FrmProAppHelper::plugin_url' ) ? FrmProAppHelper::plugin_url() : '',
3978 + 'no_licenses' => __( 'No new licenses were found', 'formidable' ),
3979 + 'unmatched_parens' => __( 'This calculation has at least one unmatched ( ) { } [ ].', 'formidable' ),
3980 + 'view_shortcodes' => __( 'This calculation may have shortcodes that work in Views but not forms.', 'formidable' ),
3981 + 'text_shortcodes' => __( 'This calculation may have shortcodes that work in text calculations but not numeric calculations.', 'formidable' ),
3982 + /* translators: %d is the number of allowed actions per form */
3983 + 'only_one_action' => sprintf( __( 'This form action is limited to %d per form.', 'formidable' ), 1 ),
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
3987 + 'unsafe_params' => FrmFormsHelper::reserved_words(),
3079 3988 /* Translators: %s is the name of a Detail Page Slug that is a reserved word.*/
3080 - '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
3081 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. */
3082 - 'param_is_reserved' => sprintf( __( 'The parameter "%s" is reserved by WordPress. This may cause problems when included in the URL. Is this intentional? ', 'formidable' ), '****' ),
3083 - 'reserved_words' => __( 'See the list of reserved words in WordPress.', 'formidable' ),
3084 - 'repeat_limit_min' => __( 'Please enter a Repeat Limit that is greater than 1.', 'formidable' ),
3085 - 'checkbox_limit' => __( 'Please select a limit between 0 and 200.', 'formidable' ),
3086 - 'install' => __( 'Install', 'formidable' ),
3087 - 'active' => __( 'Active', 'formidable' ),
3088 - 'select_a_field' => __( 'Select a Field', 'formidable' ),
3089 - 'no_items_found' => __( 'No items found.', 'formidable' ),
3090 - 'field_already_used' => __( 'Oops. You have already used that field.', 'formidable' ),
3091 - 'saving' => '', // Deprecated in 6.0.
3092 - 'saved' => '', // Deprecated in 6.0.
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
3992 + 'reserved_words' => __( 'See the list of reserved words in WordPress.', 'formidable' ),
3993 + 'repeat_limit_min' => __( 'Please enter a Repeat Limit that is greater than 1.', 'formidable' ),
3994 + 'checkbox_limit' => __( 'Please select a limit between 0 and 200.', 'formidable' ),
3995 + 'install' => __( 'Install', 'formidable' ),
3996 + 'active' => __( 'Active', 'formidable' ),
3997 + 'installed' => __( 'Installed', 'formidable' ),
3998 + 'not_installed' => __( 'Not Installed', 'formidable' ),
3999 + 'select_a_field' => __( 'Select a Field', 'formidable' ),
4000 + 'no_items_found' => __( 'No items found.', 'formidable' ),
4001 + 'field_already_used' => __( 'Oops. You have already used that field.', 'formidable' ),
4002 +
4003 + // Deprecated in 6.0.
4004 + 'saving' => '',
4005 +
4006 + // Deprecated in 6.0.
4007 + 'saved' => '',
4008 +
4009 + // translators: %1$s: HTML open tag, %2$s: HTML end tag.
4010 + 'holdShiftMsg' => esc_html__( 'You can hold %1$sShift%2$s on your keyboard to select multiple fields', 'formidable' ),
4011 + 'noTitleText' => FrmFormsHelper::get_no_title_text(),
4012 +
4013 + // In older versions this event listener causes the section to immediately close again
4014 + // When the h3 element is clicked. It's only required in WP 6.7+.
4015 + 'requireAccordionTitleClickListener' => version_compare( $wp_version, '6.7', '>=' ),
3093 4016 );
4017 +
4018 + self::add_form_builder_modal_data( $admin_script_strings );
4019 +
3094 4020 /**
3095 4021 * @param array $admin_script_strings
3096 4022 */
3097 4023 $admin_script_strings = apply_filters( 'frm_admin_script_strings', $admin_script_strings );
@@ -3096,15 +4022,99 @@
3096 4022 */
3097 4023 $admin_script_strings = apply_filters( 'frm_admin_script_strings', $admin_script_strings );
3098 4024
3099 4025 $data = $wp_scripts->get_data( 'formidable_admin', 'data' );
4026 +
3100 4027 if ( ! $data ) {
3101 4028 wp_localize_script( 'formidable_admin', 'frm_admin_js', $admin_script_strings );
3102 4029 }
4030 + }//end if
4031 + }
4032 +
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;
3103 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' );
3104 4103 }
3105 4104
3106 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 + /**
3107 4117 * @since 6.5
3108 4118 *
3109 4119 * @return string
3110 4120 */
@@ -3145,9 +4155,11 @@
3145 4155 * Echo the message on the plugins listing page
3146 4156 *
3147 4157 * @since 1.07.10
3148 4158 *
3149 - * @param float $min_version The version the add-on requires
4159 + * @param float $min_version The version the add-on requires.
4160 + *
4161 + * @return void
3150 4162 */
3151 4163 public static function min_version_notice( $min_version ) {
3152 4164 $frm_version = self::plugin_version();
3153 4165
@@ -3156,11 +4168,11 @@
3156 4168 return;
3157 4169 }
3158 4170
3159 4171 $wp_list_table = _get_list_table( 'WP_Plugins_List_Table' );
3160 - 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">' .
3161 - esc_html__( 'You are running an outdated version of Formidable. This plugin may not work correctly if you do not update Formidable.', 'formidable' ) .
3162 - '</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>';
3163 4175 }
3164 4176
3165 4177 /**
3166 4178 * If Pro is far outdated, show a message.
@@ -3166,8 +4178,10 @@
3166 4178 * If Pro is far outdated, show a message.
3167 4179 *
3168 4180 * @since 4.0.01
3169 4181 *
4182 + * @param string $min_version
4183 + *
3170 4184 * @return void
3171 4185 */
3172 4186 public static function min_pro_version_notice( $min_version ) {
3173 4187 if ( ! self::is_formidable_admin() ) {
@@ -3177,26 +4191,14 @@
3177 4191
3178 4192 self::php_version_notice();
3179 4193
3180 4194 $is_pro = self::pro_is_installed() && class_exists( 'FrmProDb' );
4195 +
3181 4196 if ( ! $is_pro || self::meets_min_pro_version( $min_version ) ) {
3182 4197 return;
3183 4198 }
3184 4199
3185 - $pro_version = FrmProDb::$plug_version;
3186 - $expired = FrmAddonsController::is_license_expired();
3187 - ?>
3188 - <div class="frm-banner-alert frm_error_style frm_previous_install">
3189 - <?php
3190 - esc_html_e( 'You are running a version of Formidable Forms that may not be compatible with your version of Formidable Forms Pro.', 'formidable' );
3191 - if ( empty( $expired ) ) {
3192 - echo ' Please <a href="' . esc_url( admin_url( 'plugins.php?s=formidable%20forms%20pro' ) ) . '">update now</a>.';
3193 - } else {
3194 - echo '<br/>Please <a href="https://formidableforms.com/account/downloads/?utm_source=WordPress&utm_medium=outdated">renew now</a> to get the latest version.';
3195 - }
3196 - ?>
3197 - </div>
3198 - <?php
4200 + include self::plugin_path() . '/classes/views/addons/min-version-notice.php';
3199 4201 }
3200 4202
3201 4203 /**
3202 4204 * If Pro is installed, check the version number.
@@ -3201,8 +4203,12 @@
3201 4203 /**
3202 4204 * If Pro is installed, check the version number.
3203 4205 *
3204 4206 * @since 4.0.01
4207 + *
4208 + * @param string $min_version
4209 + *
4210 + * @return bool
3205 4211 */
3206 4212 public static function meets_min_pro_version( $min_version ) {
3207 4213 return ! class_exists( 'FrmProDb' ) || version_compare( FrmProDb::$plug_version, $min_version, '>=' );
3208 4214 }
@@ -3207,24 +4213,22 @@
3207 4213 return ! class_exists( 'FrmProDb' ) || version_compare( FrmProDb::$plug_version, $min_version, '>=' );
3208 4214 }
3209 4215
3210 4216 /**
3211 - * Show a message if the browser or PHP version is below the recommendations.
4217 + * Show a message if the PHP version is below the recommendations.
3212 4218 *
3213 4219 * @since 4.0.02
4220 + *
4221 + * @return void
3214 4222 */
3215 4223 private static function php_version_notice() {
3216 4224 $message = array();
3217 - if ( version_compare( phpversion(), '5.6', '<' ) ) {
3218 - $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' );
3219 - }
3220 4225
3221 - $browser = self::get_server_value( 'HTTP_USER_AGENT' );
3222 - $is_ie = strpos( $browser, 'MSIE' ) !== false;
3223 - if ( $is_ie ) {
3224 - $message[] = __( 'You are using an outdated browser that is not compatible with Formidable Forms. Please update to a more current browser (we recommend Chrome).', 'formidable' );
4226 + if ( version_compare( phpversion(), '7.0', '<' ) ) {
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
3225 4228 }
3226 4229
4230 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
3227 4231 foreach ( $message as $m ) {
3228 4232 ?>
3229 4233 <div class="frm-banner-alert frm_error_style frm_previous_install">
3230 4234 <?php echo esc_html( $m ); ?>
@@ -3230,12 +4234,14 @@
3230 4234 <?php echo esc_html( $m ); ?>
3231 4235 </div>
3232 4236 <?php
3233 4237 }
4238 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
3234 4239 }
3235 4240
3236 4241 /**
3237 4242 * @param string $type
4243 + *
3238 4244 * @return array<string,string>
3239 4245 */
3240 4246 public static function locales( $type = 'date' ) {
3241 4247 $locales = array(
@@ -3329,12 +4335,12 @@
3329 4335 'zu' => __( 'Zulu', 'formidable' ),
3330 4336 );
3331 4337
3332 4338 if ( $type === 'captcha' ) {
3333 - // remove the languages unavailable for the captcha
4339 + // Remove the languages unavailable for the captcha
3334 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' );
3335 4341 } else {
3336 - // remove the languages unavailable for the datepicker
4342 + // Remove the languages unavailable for the datepicker
3337 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' );
3338 4344 }
3339 4345
3340 4346 $locales = array_diff_key( $locales, array_flip( $unset ) );
@@ -3345,32 +4351,27 @@
3345 4351 * @since 5.4.5 Added $args parameter with type.
3346 4352 *
3347 4353 * @param array<string,string> $locales
3348 4354 * @param array $args {
4355 + *
3349 4356 * @type string $type
3350 4357 * }
3351 4358 */
3352 - $locales = apply_filters( 'frm_locales', $locales, compact( 'type' ) );
3353 -
3354 - return $locales;
4359 + return apply_filters( 'frm_locales', $locales, compact( 'type' ) );
3355 4360 }
3356 4361
3357 4362 /**
3358 - * Output HTML containing reference text for accessibility
3359 - *
3360 - * @deprecated 5.1
4363 + * @return string
3361 4364 */
3362 - public static function multiselect_accessibility() {
3363 - _deprecated_function( __METHOD__, '5.1' );
3364 - }
3365 -
3366 4365 public static function get_menu_icon_class() {
3367 4366 if ( is_callable( 'FrmProAppHelper::get_settings' ) ) {
3368 4367 $settings = FrmProAppHelper::get_settings();
4368 +
3369 4369 if ( is_object( $settings ) && ! empty( $settings->menu_icon ) ) {
3370 4370 return $settings->menu_icon;
3371 4371 }
3372 4372 }
4373 +
3373 4374 return 'frmfont frm_logo_icon';
3374 4375 }
3375 4376
3376 4377 /**
@@ -3388,10 +4389,9 @@
3388 4389 * @type array $input_attrs Attributes of value input.
3389 4390 * }
3390 4391 */
3391 4392 public static function images_dropdown( $args ) {
3392 - $args = self::fill_default_images_dropdown_args( $args );
3393 -
4393 + $args = self::fill_default_images_dropdown_args( $args );
3394 4394 $input_attrs_str = self::get_images_dropdown_input_attrs( $args );
3395 4395 ob_start();
3396 4396 include self::plugin_path() . '/classes/views/shared/images-dropdown.php';
3397 4397 $output = ob_get_clean();
@@ -3412,8 +4412,9 @@
3412 4412 *
3413 4413 * @since 5.0.04
3414 4414 *
3415 4415 * @param array $args The arguments.
4416 + *
3416 4417 * @return array
3417 4418 */
3418 4419 private static function fill_default_images_dropdown_args( $args ) {
3419 4420 $defaults = array(
@@ -3426,14 +4427,8 @@
3426 4427
3427 4428 $new_args['options'] = (array) $new_args['options'];
3428 4429 $new_args['input_attrs'] = (array) $new_args['input_attrs'];
3429 4430
3430 - // Set the number of columns.
3431 - $new_args['col_class'] = ceil( 12 / count( $new_args['options'] ) );
3432 - if ( $new_args['col_class'] > 6 ) {
3433 - $new_args['col_class'] = ceil( $new_args['col_class'] / 2 );
3434 - }
3435 -
3436 4431 /**
3437 4432 * Allows modifying the arguments of images_dropdown() method.
3438 4433 *
3439 4434 * @since 5.0.04
@@ -3449,8 +4444,9 @@
3449 4444 *
3450 4445 * @since 5.0.04
3451 4446 *
3452 4447 * @param array $args The arguments.
4448 + *
3453 4449 * @return string
3454 4450 */
3455 4451 private static function get_images_dropdown_input_attrs( $args ) {
3456 4452 $input_attrs = $args['input_attrs'];
@@ -3457,8 +4453,9 @@
3457 4453 $input_attrs['type'] = 'radio';
3458 4454 $input_attrs['name'] = $args['name'];
3459 4455
3460 4456 $input_attrs_str = '';
4457 +
3461 4458 foreach ( $input_attrs as $key => $input_attr ) {
3462 4459 $input_attrs_str .= ' ' . sprintf( '%s="%s"', esc_attr( $key ), esc_attr( $input_attr ) );
3463 4460 }
3464 4461
@@ -3473,8 +4470,23 @@
3473 4470 return apply_filters( 'frm_images_dropdown_input_attrs', $input_attrs_str, $args );
3474 4471 }
3475 4472
3476 4473 /**
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
4480 + */
4481 + public static function get_images_dropdown_atts( $option, $args ) {
4482 + $image = self::get_images_dropdown_option_image( $option, $args );
4483 + $classes = self::get_images_dropdown_option_classes( $option, $args );
4484 + $custom_attrs = self::get_images_dropdown_option_html_attrs( $option, $args );
4485 + return compact( 'image', 'classes', 'custom_attrs' );
4486 + }
4487 +
4488 + /**
3477 4489 * Gets the image of each option in images_dropdown() method.
3478 4490 *
3479 4491 * @since 5.0.04
3480 4492 *
@@ -3479,8 +4491,9 @@
3479 4491 * @since 5.0.04
3480 4492 *
3481 4493 * @param array $option Option data.
3482 4494 * @param array $args The arguments of images_dropdown() method.
4495 + *
3483 4496 * @return string
3484 4497 */
3485 4498 private static function get_images_dropdown_option_image( $option, $args ) {
3486 4499 $image = self::icon_by_class(
@@ -3485,9 +4498,9 @@
3485 4498 private static function get_images_dropdown_option_image( $option, $args ) {
3486 4499 $image = self::icon_by_class(
3487 4500 'frmfont ' . $option['svg'],
3488 4501 array(
3489 - 'echo' => false,
4502 + 'echo' => false,
3490 4503 )
3491 4504 );
3492 4505
3493 4506 $args['option'] = $option;
@@ -3509,8 +4522,9 @@
3509 4522 * @since 5.0.04
3510 4523 *
3511 4524 * @param array $option Option data.
3512 4525 * @param array $args The arguments of images_dropdown() method.
4526 + *
3513 4527 * @return string
3514 4528 */
3515 4529 private static function get_images_dropdown_option_classes( $option, $args ) {
3516 4530 $classes = '';
@@ -3538,17 +4552,19 @@
3538 4552 * @since 5.0.04
3539 4553 *
3540 4554 * @param array $option Option data.
3541 4555 * @param array $args The arguments of images_dropdown() method.
4556 + *
3542 4557 * @return string
3543 4558 */
3544 4559 private static function get_images_dropdown_option_html_attrs( $option, $args ) {
3545 4560 $html_attrs = '';
4561 +
3546 4562 if ( ! empty( $option['custom_attrs'] ) && is_array( $option['custom_attrs'] ) ) {
3547 4563 $html_attrs_arr = array();
3548 4564
3549 4565 foreach ( $option['custom_attrs'] as $key => $value ) {
3550 - if ( in_array( $key, array( 'type', 'class', 'data-value' ) ) ) {
4566 + if ( in_array( $key, array( 'type', 'class', 'data-value' ), true ) ) {
3551 4567 continue;
3552 4568 }
3553 4569
3554 4570 $html_attrs_arr[] = sprintf( '%s="%s"', esc_attr( $key ), esc_attr( $value ) );
@@ -3606,14 +4622,13 @@
3606 4622 * @since 5.0.07
3607 4623 *
3608 4624 * @param array $values
3609 4625 * @param array $keys
4626 + *
3610 4627 * @return array
3611 4628 */
3612 4629 public static function maybe_filter_array( $values, $keys ) {
3613 - $allow_unfiltered_html = self::allow_unfiltered_html();
3614 -
3615 - if ( $allow_unfiltered_html ) {
4630 + if ( self::allow_unfiltered_html() ) {
3616 4631 return $values;
3617 4632 }
3618 4633
3619 4634 foreach ( $keys as $key ) {
@@ -3625,36 +4640,87 @@
3625 4640 return $values;
3626 4641 }
3627 4642
3628 4643 /**
3629 - * Some back end fields allow privleged users to add scripts.
4644 + * Some back end fields allow privileged users to add scripts.
3630 4645 * A site that uses the DISALLOW_UNFILTERED_HTML always remove scripts on echo.
3631 4646 *
3632 4647 * @since 5.0.13
3633 4648 *
3634 4649 * @param string $value
3635 - * @param array|string $allowed 'all' for everything included as defaults
4650 + * @param array|string $allowed 'all' for everything included as defaults.
4651 + *
3636 4652 * @return string
3637 4653 */
3638 4654 public static function maybe_kses( $value, $allowed = 'all' ) {
3639 4655 if ( self::should_never_allow_unfiltered_html() ) {
3640 - $value = self::kses( $value, $allowed );
4656 + return self::kses( $value, $allowed );
3641 4657 }
3642 4658 return $value;
3643 4659 }
3644 4660
3645 4661 /**
3646 - * @since 5.0.16
4662 + * Check if an option attribute used in an [input] shortcode is safe.
3647 4663 *
4664 + * @since 6.11.2
4665 + *
4666 + * @param string $key
4667 + * @param string $context Either 'display' or 'update'. On update, we want to allow a few keys that are never displayed.
4668 + *
3648 4669 * @return bool
3649 4670 */
3650 - public static function show_landing_pages() {
3651 - return self::show_new_feature( 'landing' );
4671 + public static function input_key_is_safe( $key, $context = 'display' ) {
4672 + if ( 'update' === $context && in_array( $key, array( 'opt', 'label' ), true ) ) {
4673 + $safe = true;
4674 + } elseif ( str_starts_with( $key, 'data-' ) ) {
4675 + // Allow all data attributes.
4676 + $safe = true;
4677 + } elseif ( str_starts_with( $key, 'aria-' ) ) {
4678 + // Allow all aria attributes.
4679 + $safe = true;
4680 + } else {
4681 + $safe_keys = array(
4682 + 'class',
4683 + 'required',
4684 + 'title',
4685 + 'placeholder',
4686 + 'value',
4687 + 'readonly',
4688 + 'disabled',
4689 + 'size',
4690 + 'maxlength',
4691 + 'min',
4692 + 'max',
4693 + 'pattern',
4694 + 'step',
4695 + 'autofocus',
4696 + 'width',
4697 + 'height',
4698 + 'autocomplete',
4699 + 'tabindex',
4700 + 'role',
4701 + 'style',
4702 + );
4703 + $safe = in_array( $key, $safe_keys, true );
4704 + }//end if
4705 +
4706 + /**
4707 + * Filter the $safe value so additional keys can be allowed or disallowed.
4708 + *
4709 + * @since 6.11.2
4710 + *
4711 + * @param bool $safe True if the key is considered safe.
4712 + * @param string $key
4713 + * @param string $context Either 'display' or 'update'.
4714 + */
4715 + return (bool) apply_filters( 'frm_input_key_is_safe', $safe, $key, $context );
3652 4716 }
3653 4717
3654 4718 /**
3655 4719 * @since 5.0.16
3656 4720 *
4721 + * @param string $medium
4722 + *
3657 4723 * @return array
3658 4724 */
3659 4725 public static function get_landing_page_upgrade_data_params( $medium = 'landing' ) {
3660 4726 $params = array(
@@ -3661,8 +4727,9 @@
3661 4727 'medium' => $medium,
3662 4728 'upgrade' => __( 'Form Landing Pages', 'formidable' ),
3663 4729 'message' => __( 'Easily manage a landing page for your form. Upgrade to get form landing pages.', 'formidable' ),
3664 4730 'screenshot' => 'landing.png',
4731 + 'learn-more' => self::get_doc_url( 'landing-page-forms', 'form-landing-page-settings', false ),
3665 4732 );
3666 4733 return self::get_upgrade_data_params( 'landing', $params );
3667 4734 }
3668 4735
@@ -3668,20 +4735,10 @@
3668 4735
3669 4736 /**
3670 4737 * @since 5.0.17
3671 4738 *
3672 - * @param string $plugin
3673 - * @return string|false
3674 - */
3675 - private static function get_plan_required( $plugin ) {
3676 - $link = FrmAddonsController::install_link( $plugin );
3677 - return FrmFormsHelper::get_plan_required( $link );
3678 - }
3679 -
3680 - /**
3681 - * @since 5.0.17
4739 + * @param string $feature
3682 4740 *
3683 - * @param string
3684 4741 * @return bool
3685 4742 */
3686 4743 public static function show_new_feature( $feature ) {
3687 4744 $link = FrmAddonsController::install_link( $feature );
@@ -3688,16 +4745,21 @@
3688 4745 return array_key_exists( 'status', $link ) || array_key_exists( 'class', $link );
3689 4746 }
3690 4747
3691 4748 /**
4749 + * Enhances upgrade data parameters with installation link and plan requirement information.
4750 + *
3692 4751 * @since 5.0.17
3693 4752 *
3694 - * @param string $plugin
3695 - * @param array $params
3696 - * @return array
4753 + * @param string $plugin The plugin slug to get installation data for.
4754 + * @param array $params Initial parameters for the upgrade data.
4755 + * @param bool $detailed Whether to include detailed information.
4756 + *
4757 + * @return array Modified parameters with installation data.
3697 4758 */
3698 - public static function get_upgrade_data_params( $plugin, $params ) {
4759 + public static function get_upgrade_data_params( $plugin, $params, $detailed = false ) {
3699 4760 $link = FrmAddonsController::install_link( $plugin );
4761 +
3700 4762 if ( ! $link ) {
3701 4763 return $params;
3702 4764 }
3703 4765
@@ -3703,15 +4765,20 @@
3703 4765
3704 4766 if ( ! empty( $link['url'] ) && self::pro_is_installed() ) {
3705 4767 $params['oneclick'] = json_encode( $link );
3706 4768 unset( $params['message'] );
4769 +
3707 4770 if ( ! isset( $params['medium'] ) ) {
3708 4771 $params['medium'] = $plugin;
3709 4772 }
3710 4773 } else {
3711 - $params['requires'] = FrmFormsHelper::get_plan_required( $link );
4774 + $params['requires'] = $params['requires'] ?? FrmFormsHelper::get_plan_required( $link );
3712 4775 }
3713 4776
4777 + if ( $detailed ) {
4778 + $params['plugin-status'] = $link['status'] ?? '';
4779 + }
4780 +
3714 4781 return $params;
3715 4782 }
3716 4783
3717 4784 /**
@@ -3720,8 +4787,9 @@
3720 4787 *
3721 4788 * @since 5.0.17
3722 4789 *
3723 4790 * @param string $text
4791 + *
3724 4792 * @return bool
3725 4793 */
3726 4794 public static function ctype_xdigit( $text ) {
3727 4795 if ( function_exists( 'ctype_xdigit' ) ) {
@@ -3736,12 +4804,14 @@
3736 4804 * @since 5.2.01
3737 4805 */
3738 4806 public static function set_current_screen_and_hook_suffix() {
3739 4807 global $hook_suffix;
4808 +
3740 4809 if ( is_null( $hook_suffix ) ) {
3741 4810 // $hook_suffix gets used in substr so make sure it's not null. PHP 8.1 deprecates null in substr.
3742 4811 $hook_suffix = ''; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
3743 4812 }
4813 +
3744 4814 set_current_screen();
3745 4815 }
3746 4816
3747 4817 /**
@@ -3748,15 +4818,15 @@
3748 4818 * Shows pill text.
3749 4819 *
3750 4820 * @since 5.2.02
3751 4821 *
3752 - * @param string $text Text in the pill. Default is NEW.
4822 + * @param string|null $text Text in the pill. Default is NEW.
3753 4823 */
3754 4824 public static function show_pill_text( $text = null ) {
3755 4825 if ( null === $text ) {
3756 4826 $text = __( 'NEW', 'formidable' );
3757 4827 }
3758 - echo '<span class="frm-new-pill">' . esc_html( $text ) . '</span>';
4828 + echo '<span class="frm-meta-tag frm-new-pill">' . esc_html( $text ) . '</span>';
3759 4829 }
3760 4830
3761 4831 /**
3762 4832 * Count the number of decimals digits.
@@ -3763,9 +4833,10 @@
3763 4833 *
3764 4834 * @since 5.2.07
3765 4835 *
3766 4836 * @param mixed $num Number.
3767 - * @return int|false Returns `false` if the passed parameter is not number.
4837 + *
4838 + * @return false|int Returns `false` if the passed parameter is not number.
3768 4839 */
3769 4840 public static function count_decimals( $num ) {
3770 4841 if ( ! is_numeric( $num ) ) {
3771 4842 return false;
@@ -3772,8 +4843,9 @@
3772 4843 }
3773 4844
3774 4845 $num = (string) $num;
3775 4846 $parts = explode( '.', $num );
4847 +
3776 4848 if ( 1 === count( $parts ) ) {
3777 4849 return 0;
3778 4850 }
3779 4851
@@ -3796,9 +4868,9 @@
3796 4868 }
3797 4869
3798 4870 add_filter(
3799 4871 'option_gmt_offset',
3800 - function( $offset ) {
4872 + function ( $offset ) {
3801 4873 if ( ! is_string( $offset ) || is_numeric( $offset ) ) {
3802 4874 // Leave a valid value alone.
3803 4875 return $offset;
3804 4876 }
@@ -3855,17 +4927,20 @@
3855 4927 * @since 5.5.5
3856 4928 *
3857 4929 * @param string $url
3858 4930 * @param string $expected_extension
4931 + *
3859 4932 * @return bool
3860 4933 */
3861 4934 public static function validate_url_is_in_s3_bucket( $url, $expected_extension ) {
3862 - $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 +
3863 4937 if ( ! $file_is_in_expected_s3_bucket ) {
3864 4938 return false;
3865 4939 }
3866 4940
3867 4941 $parsed = parse_url( $url );
4942 +
3868 4943 if ( ! is_array( $parsed ) ) {
3869 4944 // URL is malformed.
3870 4945 return false;
3871 4946 }
@@ -3871,74 +4946,13 @@
3871 4946 }
3872 4947
3873 4948 $path = $parsed['path'];
3874 4949 $ext = pathinfo( $path, PATHINFO_EXTENSION );
3875 - if ( $expected_extension !== $ext ) {
3876 - // The URL isn't to an XML file.
3877 - return false;
3878 - }
3879 -
3880 - return true;
4950 + // The URL isn't to an XML file.
4951 + return $expected_extension === $ext;
3881 4952 }
3882 4953
3883 4954 /**
3884 - * @since 4.08
3885 - * @deprecated 4.09.01
3886 - */
3887 - public static function expiring_message() {
3888 - _deprecated_function( __METHOD__, '4.09.01', 'FrmProAddonsController::expiring_message' );
3889 - if ( is_callable( 'FrmProAddonsController::expiring_message' ) ) {
3890 - FrmProAddonsController::expiring_message();
3891 - }
3892 - }
3893 -
3894 - /**
3895 - * Use the WP 4.7 wp_doing_ajax function
3896 - *
3897 - * @since 2.05.07
3898 - * @deprecated 4.04.04
3899 - */
3900 - public static function wp_doing_ajax() {
3901 - _deprecated_function( __METHOD__, '4.04.04', 'wp_doing_ajax' );
3902 - return wp_doing_ajax();
3903 - }
3904 -
3905 - /**
3906 - * @deprecated 4.0
3907 - */
3908 - public static function insert_opt_html( $args ) {
3909 - _deprecated_function( __METHOD__, '4.0', 'FrmFormsHelper::insert_opt_html' );
3910 - FrmFormsHelper::insert_opt_html( $args );
3911 - }
3912 -
3913 - /**
3914 - * @deprecated 3.01
3915 - * @codeCoverageIgnore
3916 - */
3917 - public static function sanitize_array( &$values ) {
3918 - FrmDeprecated::sanitize_array( $values );
3919 - }
3920 -
3921 - /**
3922 - * @since 2.0
3923 - * @deprecated 5.0.13
3924 - *
3925 - * @return string The base Google APIS url for the current version of jQuery UI
3926 - */
3927 - public static function jquery_ui_base_url() {
3928 - _deprecated_function( __FUNCTION__, '5.0.13', 'FrmProAppHelper::jquery_ui_base_url' );
3929 - return is_callable( 'FrmProAppHelper::jquery_ui_base_url' ) ? FrmProAppHelper::jquery_ui_base_url() : '';
3930 - }
3931 -
3932 - /**
3933 - * @since 4.07
3934 - * @deprecated 6.0
3935 - */
3936 - public static function renewal_message() {
3937 - _deprecated_function( __METHOD__, '6.0', 'FrmProAddonsController::renewal_message' );
3938 - }
3939 -
3940 - /**
3941 4955 * Display a dismissable warning message and save its dismissal state.
3942 4956 *
3943 4957 * @since 6.3
3944 4958 *
@@ -3943,8 +4957,9 @@
3943 4957 * @since 6.3
3944 4958 *
3945 4959 * @param string $message The warning message to display.
3946 4960 * @param string $option The unique identifier for the dismissal state of the message and the WP Ajax action.
4961 + *
3947 4962 * @return void
3948 4963 */
3949 4964 public static function add_dismissable_warning_message( $message = '', $option = '' ) {
3950 4965 if ( ! $message || ! $option ) {
@@ -3950,9 +4965,9 @@
3950 4965 if ( ! $message || ! $option ) {
3951 4966 return;
3952 4967 }
3953 4968
3954 - $ajax_callback = function() use ( $option ) {
4969 + $ajax_callback = function () use ( $option ) {
3955 4970 self::dismiss_warning_message( $option );
3956 4971 };
3957 4972
3958 4973 // We're handling JS codes with `doJsonPost` and it adds 'frm_' to the beginning of the action.
@@ -3960,9 +4975,9 @@
3960 4975 add_action( 'wp_ajax_frm_' . $option, $ajax_callback );
3961 4976
3962 4977 add_filter(
3963 4978 'frm_message_list',
3964 - function( $show_messages ) use ( $message, $option ) {
4979 + function ( $show_messages ) use ( $message, $option ) {
3965 4980 if ( get_option( $option, false ) ) {
3966 4981 return $show_messages;
3967 4982 }
3968 4983
@@ -3969,9 +4984,9 @@
3969 4984 $dismiss_icon = self::icon_by_class(
3970 4985 'frmfont frm_close_icon',
3971 4986 array(
3972 4987 'aria-label' => _x( 'Dismiss', 'warning message: close icon label', 'formidable' ),
3973 - 'echo' => false,
4988 + 'echo' => false,
3974 4989 )
3975 4990 );
3976 4991
3977 4992 $show_messages[] = $message;
@@ -3987,8 +5002,9 @@
3987 5002 *
3988 5003 * @since 6.3
3989 5004 *
3990 5005 * @param string $option The unique identifier for the dismissal state of the message.
5006 + *
3991 5007 * @return void
3992 5008 */
3993 5009 public static function dismiss_warning_message( $option = '' ) {
3994 5010 self::permission_check( 'frm_change_settings' );
@@ -3994,10 +5010,182 @@
3994 5010 self::permission_check( 'frm_change_settings' );
3995 5011 check_ajax_referer( 'frm_ajax', 'nonce' );
3996 5012
3997 5013 if ( $option ) {
3998 - update_option( $option, true, 'no' );
5014 + update_option( $option, true, false );
3999 5015 }
4000 5016
4001 5017 wp_send_json_success();
5018 + }
5019 +
5020 + /**
5021 + * Lite license copy.
5022 + * Used in FrmDashboardController & FrmSettingsController
5023 + *
5024 + * @since 6.8
5025 + *
5026 + * @return string
5027 + */
5028 + public static function copy_for_lite_license() {
5029 + $message = __( 'You\'re using Formidable Forms Lite - no license needed. Enjoy!', 'formidable' ) . ' 🙂';
5030 + return apply_filters( 'frm_license_type_text', $message );
5031 + }
5032 +
5033 + /**
5034 + * Removes scripts that are unnecessarily loaded across the pages!
5035 + *
5036 + * @since 6.9
5037 + *
5038 + * @return void
5039 + */
5040 + public static function dequeue_extra_global_scripts() {
5041 + wp_dequeue_script( 'frm-surveys-admin' );
5042 + wp_dequeue_script( 'frm-quizzes-form-action' );
5043 + }
5044 +
5045 + /**
5046 + * Shows tooltip icon.
5047 + *
5048 + * @since 6.12
5049 + *
5050 + * @param string $tooltip_text Tooltip text.
5051 + * @param array $atts Tooltip wrapper HTML attributes.
5052 + *
5053 + * @return void
5054 + */
5055 + public static function tooltip_icon( $tooltip_text, $atts = array() ) {
5056 + $atts['title'] = $tooltip_text;
5057 +
5058 + if ( isset( $atts['class'] ) ) {
5059 + $atts['class'] .= ' frm_help';
5060 + } else {
5061 + $atts['class'] = 'frm_help';
5062 + }
5063 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
5064 + ?>
5065 + <span <?php self::array_to_html_params( $atts, true ); ?>>
5066 + <?php self::icon_by_class( 'frmfont frm_tooltip_icon' ); ?>
5067 + </span>
5068 + <?php
5069 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
5070 + }
5071 +
5072 + /**
5073 + * Prints errors for settings in onboarding wizard or template settings.
5074 + *
5075 + * @since 6.15
5076 + *
5077 + * @param array $args Args.
5078 + *
5079 + * @return void
5080 + */
5081 + public static function print_setting_error( $args ) {
5082 + $args = wp_parse_args(
5083 + $args,
5084 + array(
5085 + 'id' => '',
5086 + 'errors' => array(),
5087 + 'class' => '',
5088 + )
5089 + );
5090 +
5091 + $args['class'] .= ' frm-validation-error frm-mt-xs frm_hidden';
5092 +
5093 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
5094 + ?>
5095 + <span id="<?php echo esc_attr( $args['id'] ); ?>" class="<?php echo esc_attr( $args['class'] ); ?>">
5096 + <?php
5097 + if ( is_array( $args['errors'] ) ) {
5098 + foreach ( $args['errors'] as $key => $msg ) {
5099 + ?>
5100 + <span frm-error="<?php echo esc_attr( $key ); ?>"><?php echo esc_html( $msg ); ?></span>
5101 + <?php
5102 + }
5103 + } else {
5104 + echo '<span>' . esc_html( $args['errors'] ) . '</span>';
5105 + }
5106 + ?>
5107 + </span>
5108 + <?php
5109 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
5110 + }
5111 +
5112 + /**
5113 + * Check if GDPR is enabled.
5114 + *
5115 + * @since 6.19
5116 + *
5117 + * @return bool
5118 + */
5119 + public static function is_gdpr_enabled() {
5120 + $frm_settings = self::get_settings();
5121 + return $frm_settings->enable_gdpr || $frm_settings->no_ips || $frm_settings->custom_header_ip || $frm_settings->no_gdpr_cookies;
5122 + }
5123 +
5124 + /**
5125 + * Check if GDPR cookies are disabled.
5126 + *
5127 + * @since 6.19
5128 + *
5129 + * @return bool
5130 + */
5131 + public static function no_gdpr_cookies() {
5132 + $frm_settings = self::get_settings();
5133 + return $frm_settings->enable_gdpr && $frm_settings->no_gdpr_cookies;
5134 + }
5135 +
5136 + /**
5137 + * Check if a string is valid UTF-8.
5138 + *
5139 + * @since 6.24
5140 + *
5141 + * @param string|null $string The string to check.
5142 + *
5143 + * @return bool
5144 + */
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 +
5151 + // wp_is_valid_utf8 is added in WP 6.9.
5152 + if ( function_exists( 'wp_is_valid_utf8' ) ) {
5153 + return wp_is_valid_utf8( $string );
5154 + }
5155 +
5156 + // As of WP 6.9, seems_utf8 is deprecated.
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;
5176 + }
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;
4002 5190 }
4003 5191 }