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