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