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/FrmFieldsHelper.php +1131 -334 6.5 → trunk View file →
@@ -4,11 +4,23 @@
4 4 }
5 5
6 6 class FrmFieldsHelper {
7 7
8 + /**
9 + * The context is memoized for re-use as the context is checked for each field.
10 + *
11 + * @var bool|null
12 + */
13 + private static $context_is_safe_to_load_field_options_from_request_data;
14 +
15 + /**
16 + * @param string $type
17 + * @param int|string $form_id
18 + *
19 + * @return array
20 + */
8 21 public static function setup_new_vars( $type = '', $form_id = '' ) {
9 -
10 - if ( strpos( $type, '|' ) ) {
22 + if ( str_contains( $type, '|' ) ) {
11 23 list( $type, $setting ) = explode( '|', $type );
12 24 }
13 25
14 26 $values = self::get_default_field( $type );
@@ -34,31 +46,56 @@
34 46 $values['field_options'][ $setting ] = 1;
35 47 }
36 48 }
37 49
50 + // Increase the field order of submit field and fields in the same row.
51 + FrmSubmitHelper::update_last_row_fields_order_when_adding_field( $field_count );
52 +
38 53 return $values;
39 54 }
40 55
56 + /**
57 + * @param array $field
58 + * @param string $plus
59 + *
60 + * @return string
61 + */
41 62 public static function get_html_id( $field, $plus = '' ) {
42 63 return apply_filters( 'frm_field_html_id', 'field_' . $field['field_key'] . $plus, $field );
43 64 }
44 65
66 + /**
67 + * @param object $field
68 + * @param bool $doing_ajax
69 + *
70 + * @return array
71 + */
45 72 public static function setup_edit_vars( $field, $doing_ajax = false ) {
46 73 $values = self::field_object_to_array( $field );
47 -
48 74 return apply_filters( 'frm_setup_edit_field_vars', $values, array( 'doing_ajax' => $doing_ajax ) );
49 75 }
50 76
77 + /**
78 + * @param object $field
79 + *
80 + * @return array
81 + */
51 82 public static function field_object_to_array( $field ) {
52 83 $values = (array) $field;
53 84
54 85 self::fill_field_array( $field, $values );
55 86
56 - $values['custom_html'] = ( isset( $field->field_options['custom_html'] ) ) ? $field->field_options['custom_html'] : self::get_default_html( $field->type );
87 + $values['custom_html'] = $field->field_options['custom_html'] ?? self::get_default_html( $field->type );
57 88
58 89 return $values;
59 90 }
60 91
92 + /**
93 + * @param object $field
94 + * @param array $field_array
95 + *
96 + * @return void
97 + */
61 98 private static function fill_field_array( $field, array &$field_array ) {
62 99 $field_array['options'] = $field->options;
63 100 $field_array['value'] = $field->default_value;
64 101
@@ -67,11 +104,17 @@
67 104 $field_array = array_merge( (array) $field->field_options, $field_array );
68 105 }
69 106
70 107 /**
71 - * Prepare field while creating a new entry
108 + * Prepare field while creating a new entry.
72 109 *
73 110 * @since 3.0
111 + *
112 + * @param array $field_array Field values array, passed by reference.
113 + * @param stdClass $field Field object.
114 + * @param array $args Additional arguments.
115 + *
116 + * @return void
74 117 */
75 118 public static function prepare_new_front_field( &$field_array, $field, $args = array() ) {
76 119 $args['action'] = 'new';
77 120 self::prepare_front_field( $field_array, $field, $args );
@@ -77,11 +120,18 @@
77 120 self::prepare_front_field( $field_array, $field, $args );
78 121 }
79 122
80 123 /**
81 - * Prepare field while editing an entry
124 + * Prepare field while editing an entry.
82 125 *
83 126 * @since 3.0
127 + *
128 + * @param array $field_array Field values array, passed by reference.
129 + * @param array|stdClass $field Field object or array.
130 + * @param int $entry_id Entry ID.
131 + * @param array $args Additional arguments.
132 + *
133 + * @return void
84 134 */
85 135 public static function prepare_edit_front_field( &$field_array, $field, $entry_id = 0, $args = array() ) {
86 136 $args['entry_id'] = $entry_id;
87 137 $args['action'] = 'edit';
@@ -91,8 +141,14 @@
91 141 /**
92 142 * Prepare field while creating a new entry
93 143 *
94 144 * @since 3.0
145 + *
146 + * @param array $field_array
147 + * @param stdClass $field
148 + * @param array $args
149 + *
150 + * @return void
95 151 */
96 152 private static function prepare_front_field( &$field_array, $field, $args ) {
97 153 self::fill_default_field_opts( $field, $field_array );
98 154 self::fill_cleared_strings( $field, $field_array );
@@ -97,17 +153,30 @@
97 153 self::fill_default_field_opts( $field, $field_array );
98 154 self::fill_cleared_strings( $field, $field_array );
99 155
100 156 // Track the original field's type
101 - $field_array['original_type'] = isset( $field->field_options['original_type'] ) ? $field->field_options['original_type'] : $field->type;
157 + $field_array['original_type'] = $field->field_options['original_type'] ?? $field->type;
102 158
103 159 self::prepare_field_options_for_display( $field_array, $field, $args );
104 160
105 - if ( $args['action'] == 'edit' ) {
161 + if ( $args['action'] === 'edit' ) {
162 + /**
163 + * @param array $field_array
164 + * @param stdClass $field
165 + * @param int|string $entry_id
166 + * @param array $args
167 + */
106 168 $field_array = apply_filters( 'frm_setup_edit_fields_vars', $field_array, $field, $args['entry_id'], $args );
107 - } else {
108 - $field_array = apply_filters( 'frm_setup_new_fields_vars', $field_array, $field, $args );
169 +
170 + return;
109 171 }
172 +
173 + /**
174 + * @param array $field_array
175 + * @param stdClass $field
176 + * @param array $args
177 + */
178 + $field_array = apply_filters( 'frm_setup_new_fields_vars', $field_array, $field, $args );
110 179 }
111 180
112 181 /**
113 182 * @since 3.0
@@ -117,9 +186,8 @@
117 186 * @return array
118 187 */
119 188 public static function get_default_field_options( $type ) {
120 189 $field_type = FrmFieldFactory::get_field_type( $type );
121 -
122 190 return $field_type->get_default_field_options();
123 191 }
124 192
125 193 /**
@@ -125,14 +193,16 @@
125 193 /**
126 194 * @since 3.0
127 195 *
128 196 * @param object $field
129 - * @param array $values
197 + * @param array $values
198 + *
199 + * @return void
130 200 */
131 201 private static function fill_default_field_opts( $field, array &$values ) {
132 - $check_post = FrmAppHelper::is_admin_page() && $_POST && isset( $_POST['field_options'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
202 + $check_post = self::context_is_safe_to_load_field_options_from_request_data();
203 + $defaults = self::get_default_field_options_from_field( $field, $values );
133 204
134 - $defaults = self::get_default_field_options_from_field( $field, $values );
135 205 if ( ! $check_post ) {
136 206 $defaults['required_indicator'] = '';
137 207 $defaults['original_type'] = $field->type;
138 208 }
@@ -137,16 +207,65 @@
137 207 $defaults['original_type'] = $field->type;
138 208 }
139 209
140 210 foreach ( $defaults as $opt => $default ) {
141 - $values[ $opt ] = isset( $field->field_options[ $opt ] ) ? $field->field_options[ $opt ] : $default;
211 + $values[ $opt ] = $field->field_options[ $opt ] ?? $default;
142 212
143 213 if ( $check_post ) {
144 214 self::get_posted_field_setting( $opt . '_' . $field->id, $values[ $opt ] );
145 215 }
216 + }
217 + }
146 218
147 - unset( $opt, $default );
219 + /**
220 + * The fill_default_field_opts method is called when loading a field.
221 + * This is used to preserve the $_POST data after updating settings for a field.
222 + * To prevent this from happening when creating an entry, we need to check the context.
223 + *
224 + * @return bool
225 + */
226 + private static function context_is_safe_to_load_field_options_from_request_data() {
227 + if ( isset( self::$context_is_safe_to_load_field_options_from_request_data ) ) {
228 + return self::$context_is_safe_to_load_field_options_from_request_data;
148 229 }
230 +
231 + $function = function () {
232 + if ( ! FrmAppHelper::is_admin_page() ) {
233 + return false;
234 + }
235 +
236 + if ( ! $_POST || ! isset( $_POST['field_options'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
237 + return false;
238 + }
239 +
240 + if ( ! current_user_can( 'frm_edit_forms' ) ) {
241 + return false;
242 + }
243 +
244 + $action = FrmAppHelper::get_post_param( 'action', '', 'sanitize_title' );
245 +
246 + if ( 'frm_forms_preview' === $action ) {
247 + // Never trigger when previewing.
248 + return false;
249 + }
250 +
251 + // Confirm an allowed action is being used, and that the correct nonce is being used.
252 + if ( 'update' === $action ) {
253 + $nonce = FrmAppHelper::get_post_param( 'frm_save_form', '', 'sanitize_text_field' );
254 + return wp_verify_nonce( $nonce, 'frm_save_form_nonce' );
255 + }
256 +
257 + $action = FrmAppHelper::get_post_param( 'frm_action', '', 'sanitize_title' );
258 +
259 + if ( 'update_settings' === $action ) {
260 + $nonce = FrmAppHelper::get_post_param( 'process_form', '', 'sanitize_text_field' );
261 + return wp_verify_nonce( $nonce, 'process_form_nonce' );
262 + }
263 + };
264 +
265 + self::$context_is_safe_to_load_field_options_from_request_data = $function();
266 +
267 + return self::$context_is_safe_to_load_field_options_from_request_data;
149 268 }
150 269
151 270 /**
152 271 * Fill the required message, invalid message,
@@ -154,26 +273,28 @@
154 273 *
155 274 * @since 3.0
156 275 *
157 276 * @param object $field
158 - * @param array $field_array
277 + * @param array $field_array
278 + *
279 + * @return void
159 280 */
160 281 private static function fill_cleared_strings( $field, array &$field_array ) {
161 - $frm_settings = FrmAppHelper::get_settings();
162 -
282 + // phpcs:ignore Universal.Operators.StrictComparisons
163 283 if ( '' == $field_array['blank'] && '1' === $field_array['required'] ) {
164 - $field_array['blank'] = $frm_settings->blank_msg;
284 + $field_array['blank'] = self::default_blank_msg();
165 285 }
166 286
167 287 if ( '' === $field_array['invalid'] ) {
168 288 if ( 'captcha' === $field->type ) {
289 + $frm_settings = FrmAppHelper::get_settings();
169 290 $field_array['invalid'] = $frm_settings->re_msg;
170 291 } else {
171 - /* translators: %s: Field name */
172 - $field_array['invalid'] = sprintf( __( '%s is invalid', 'formidable' ), $field_array['name'] );
292 + $field_array['invalid'] = self::default_invalid_msg();
173 293 }
174 294 }
175 295
296 + // phpcs:ignore Universal.Operators.StrictComparisons
176 297 if ( '' == $field_array['custom_html'] ) {
177 298 $field_array['custom_html'] = self::get_default_html( $field->type );
178 299 }
179 300 }
@@ -178,12 +299,48 @@
178 299 }
179 300 }
180 301
181 302 /**
303 + * @since 6.8.3
304 + *
305 + * @return string
306 + */
307 + public static function default_invalid_msg() {
308 + /* translators: %s: [field_name] shortcode (Which gets replaced by a Field Name) */
309 + return sprintf( __( '%s is invalid', 'formidable' ), '[field_name]' );
310 + }
311 +
312 + /**
313 + * @since 6.8.3
314 + *
315 + * @return string
316 + */
317 + public static function default_unique_msg() {
318 + $frm_settings = FrmAppHelper::get_settings();
319 + $unique_message = $frm_settings->unique_msg;
320 + return str_replace( 'This value', '[field_name]', $unique_message );
321 + }
322 +
323 + /**
324 + * @since 6.8.3
325 + *
326 + * @return string
327 + */
328 + public static function default_blank_msg() {
329 + $frm_settings = FrmAppHelper::get_settings();
330 + $blank_message = $frm_settings->blank_msg;
331 + return str_replace( 'This field', '[field_name]', $blank_message );
332 + }
333 +
334 + /**
335 + * When loading settings for a field, check the $_POST data and possibly use that instead of the DB value.
336 + *
182 337 * @since 3.0
183 338 *
184 339 * @param string $setting
185 - * @param mixed $value
340 + * @param mixed $value
341 + *
342 + * @return void
186 343 */
187 344 private static function get_posted_field_setting( $setting, &$value ) {
188 345 if ( ! isset( $_POST['field_options'][ $setting ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
189 346 return;
@@ -188,16 +345,20 @@
188 345 if ( ! isset( $_POST['field_options'][ $setting ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
189 346 return;
190 347 }
191 348
192 - if ( strpos( $setting, 'html' ) !== false ) {
193 - // Strip slashes from HTML but not regex or script tags.
194 - $value = wp_unslash( $_POST['field_options'][ $setting ] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing
195 - } elseif ( strpos( $setting, 'format_' ) === 0 ) {
349 + if ( str_contains( $setting, 'html' ) ) {
350 + $value = wp_unslash( $_POST['field_options'][ $setting ] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing, SlevomatCodingStandard.Files.LineLength.LineTooLong
351 +
352 + // Conditionally strip script tags if the user sending $_POST data is not allowed to use unfiltered HTML.
353 + if ( ! FrmAppHelper::allow_unfiltered_html() ) {
354 + $value = FrmAppHelper::kses( $value, 'all' );
355 + }
356 + } elseif ( str_starts_with( $setting, 'format_' ) ) {
196 357 // TODO: Remove stripslashes on output, and use on input only.
197 - $value = sanitize_text_field( $_POST['field_options'][ $setting ] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.NonceVerification.Missing
358 + $value = sanitize_text_field( $_POST['field_options'][ $setting ] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.NonceVerification.Missing, SlevomatCodingStandard.Files.LineLength.LineTooLong
198 359 } else {
199 - $value = wp_unslash( $_POST['field_options'][ $setting ] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing
360 + $value = wp_unslash( $_POST['field_options'][ $setting ] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing, SlevomatCodingStandard.Files.LineLength.LineTooLong
200 361 FrmAppHelper::sanitize_value( 'wp_kses_post', $value );
201 362 }
202 363 }
203 364
@@ -204,9 +365,9 @@
204 365 /**
205 366 * @since 3.0
206 367 *
207 368 * @param object $field
208 - * @param array $values The field array is needed for hooks
369 + * @param array $values The field array is needed for hooks.
209 370 *
210 371 * @return array
211 372 */
212 373 public static function get_default_field_options_from_field( $field, $values = array() ) {
@@ -211,13 +372,11 @@
211 372 */
212 373 public static function get_default_field_options_from_field( $field, $values = array() ) {
213 374 $field_type = self::get_original_field( $field );
214 375 $opts = $field_type->get_default_field_options();
376 + $opts = apply_filters( 'frm_default_field_opts', $opts, $values, $field );
215 377
216 - $opts = apply_filters( 'frm_default_field_opts', $opts, $values, $field );
217 - $opts = apply_filters( 'frm_default_' . $field->type . '_field_opts', $opts, $values, $field );
218 -
219 - return $opts;
378 + return apply_filters( 'frm_default_' . $field->type . '_field_opts', $opts, $values, $field );
220 379 }
221 380
222 381 /**
223 382 * @since 3.0
@@ -223,13 +382,14 @@
223 382 * @since 3.0
224 383 *
225 384 * @param object $field
226 385 *
227 - * @return array
386 + * @return FrmFieldType
228 387 */
229 388 private static function get_original_field( $field ) {
230 389 $original_type = FrmField::get_option( $field, 'original_type' );
231 - if ( ! empty( $original_type ) && $field->type != $original_type ) {
390 +
391 + if ( $original_type && $field->type !== $original_type ) {
232 392 $field->type = $original_type;
233 393 }
234 394
235 395 return FrmFieldFactory::get_field_object( $field );
@@ -237,11 +397,13 @@
237 397
238 398 /**
239 399 * @since 3.0
240 400 *
241 - * @param array $field_array
401 + * @param array $field_array
242 402 * @param object $field
243 - * @param array $atts
403 + * @param array $atts
404 + *
405 + * @return void
244 406 */
245 407 private static function prepare_field_options_for_display( &$field_array, $field, $atts ) {
246 408 $field_obj = FrmFieldFactory::get_field_object( $field );
247 409 $field_array = $field_obj->prepare_front_field( $field_array, $atts );
@@ -255,12 +417,19 @@
255 417 * @return array
256 418 */
257 419 public static function get_default_field( $type ) {
258 420 $field_type = FrmFieldFactory::get_field_type( $type );
259 -
260 421 return $field_type->get_new_field_defaults();
261 422 }
262 423
424 + /**
425 + * @param array $values
426 + * @param object $field
427 + * @param int|string $form_id
428 + * @param string $new_key
429 + *
430 + * @return void
431 + */
263 432 public static function fill_field( &$values, $field, $form_id, $new_key = '' ) {
264 433 global $wpdb;
265 434
266 435 $values['field_key'] = FrmAppHelper::get_unique_key( $new_key, $wpdb->prefix . 'frm_fields', 'field_key' );
@@ -275,29 +444,26 @@
275 444
276 445 /**
277 446 * @since 2.0
278 447 *
279 - * @param $field
280 - * @param $error
448 + * @param array|object $field
449 + * @param string $error
281 450 *
282 451 * @return string
283 452 */
284 453 public static function get_error_msg( $field, $error ) {
285 - $frm_settings = FrmAppHelper::get_settings();
286 - $default_settings = $frm_settings->default_options();
287 - $field_name = is_array( $field ) ? $field['name'] : $field->name;
288 -
289 - $conf_msg = __( 'The entered values do not match', 'formidable' );
290 - $defaults = array(
454 + $frm_settings = FrmAppHelper::get_settings();
455 + $conf_msg = __( 'The entered values do not match', 'formidable' );
456 + $defaults = array(
291 457 'unique_msg' => array(
292 - 'full' => $default_settings['unique_msg'],
458 + 'full' => self::default_unique_msg(),
293 459 /* translators: %s: Field name */
294 - 'part' => sprintf( __( '%s must be unique', 'formidable' ), $field_name ),
460 + 'part' => sprintf( __( '%s must be unique', 'formidable' ), '[field_name]' ),
295 461 ),
296 462 'invalid' => array(
297 463 'full' => __( 'This field is invalid', 'formidable' ),
298 464 /* translators: %s: Field name */
299 - 'part' => sprintf( __( '%s is invalid', 'formidable' ), $field_name ),
465 + 'part' => sprintf( __( '%s is invalid', 'formidable' ), '[field_name]' ),
300 466 ),
301 467 'blank' => array(
302 468 'full' => $frm_settings->blank_msg,
303 469 'part' => $frm_settings->blank_msg,
@@ -307,26 +473,107 @@
307 473 'part' => $conf_msg,
308 474 ),
309 475 );
310 476
477 + /**
478 + * @since 6.28
479 + *
480 + * @param array $defaults
481 + * @param array|object $field
482 + */
483 + $defaults = apply_filters( 'frm_default_field_validation_messages', $defaults, $field );
484 +
311 485 $msg = FrmField::get_option( $field, $error );
312 - $msg = empty( $msg ) ? $defaults[ $error ]['part'] : $msg;
486 + $msg = $msg ? $msg : $defaults[ $error ]['part'];
313 487 $msg = do_shortcode( $msg );
314 488
315 - return $msg;
489 + return self::maybe_replace_substrings_with_field_name( $msg, $error, $field );
316 490 }
317 491
492 + /**
493 + * @since 6.8.3
494 + *
495 + * @param string $msg
496 + * @param string $error
497 + * @param array|object $field
498 + *
499 + * @return string
500 + */
501 + private static function maybe_replace_substrings_with_field_name( $msg, $error, $field ) {
502 + $field_name = is_array( $field ) ? $field['name'] : $field->name;
503 + $field_name = FrmAppHelper::maybe_kses( $field_name );
504 + $substrings = self::get_substrings_to_replace_with_field_name( $field_name, compact( 'msg', 'error', 'field' ) );
505 +
506 + // Use the "This value"/"This field" placeholder strings if field name is empty.
507 + if ( ! $field_name ) {
508 + $field_name = 'unique_msg' === $error ? __( 'This value', 'formidable' ) : __( 'This field', 'formidable' );
509 + }
510 +
511 + return str_replace( $substrings, $field_name, $msg );
512 + }
513 +
514 + /**
515 + * @since 6.8.3
516 + *
517 + * @param string $field_name
518 + * @param array $filter_args {
519 + * Filter arguments.
520 + *
521 + * @type string $msg The current error message before the substrings are replaced.
522 + * @type string $error A key including 'unique_msg', 'invalid', 'blank', or 'conf_msg'.
523 + * @type array|object $field The field with the error.
524 + * }
525 + *
526 + * @return array
527 + */
528 + private static function get_substrings_to_replace_with_field_name( $field_name, $filter_args ) {
529 + $substrings = array( '[field_name]' );
530 +
531 + if ( $field_name ) {
532 + array_push( $substrings, 'This value', 'This field' );
533 + }
534 +
535 + /**
536 + * @since 6.8.3
537 + *
538 + * @param array<string> $substrings
539 + * @param array $filter_args
540 + */
541 + $filtered_substrings = apply_filters( 'frm_error_substrings_to_replace_with_field_name', $substrings, $filter_args );
542 +
543 + if ( is_array( $filtered_substrings ) ) {
544 + $substrings = $filtered_substrings;
545 + } else {
546 + _doing_it_wrong( __METHOD__, 'Only arrays should be returned when using the frm_error_substrings_to_replace_with_field_name filter.', '6.8.3' );
547 + }
548 +
549 + return $substrings;
550 + }
551 +
552 + /**
553 + * @param int|string $form_id
554 + * @param array $error
555 + *
556 + * @return array
557 + */
318 558 public static function get_form_fields( $form_id, $error = array() ) {
319 559 $fields = FrmField::get_all_for_form( $form_id );
320 560
561 + FrmFieldName::track_first_name_field( $fields );
562 +
321 563 return apply_filters( 'frm_get_paged_fields', $fields, $form_id, $error );
322 564 }
323 565
566 + /**
567 + * @param string $type
568 + *
569 + * @return string
570 + */
324 571 public static function get_default_html( $type = 'text' ) {
325 572 $field = FrmFieldFactory::get_field_type( $type );
326 573 $default_html = $field->default_html();
327 574
328 - // these hooks are here for reverse compatibility since 3.0
575 + // These hooks are here for reverse compatibility since 3.0
329 576 if ( ! apply_filters( 'frm_normal_field_type_html', true, $type ) ) {
330 577 $default_html = apply_filters( 'frm_other_custom_html', '', $type );
331 578 }
332 579
@@ -333,15 +580,21 @@
333 580 return apply_filters( 'frm_custom_html', $default_html, $type );
334 581 }
335 582
336 583 /**
337 - * @param array $fields
338 - * @param array $errors
584 + * @param array $fields
585 + * @param array $errors
339 586 * @param object $form
340 - * @param $form_action
587 + * @param object $form_action
588 + *
589 + * @return void
341 590 */
342 591 public static function show_fields( $fields, $errors, $form, $form_action ) {
343 592 foreach ( $fields as $field ) {
593 + if ( ! is_array( $field ) ) {
594 + continue;
595 + }
596 +
344 597 $field_obj = FrmFieldFactory::get_field_type( $field['type'], $field );
345 598 $field_obj->show_field( compact( 'errors', 'form', 'form_action' ) );
346 599 }
347 600 }
@@ -348,19 +601,25 @@
348 601
349 602 /**
350 603 * @since 3.0
351 604 *
352 - * @param array $atts
353 - * @param string|array $value
605 + * @param array $atts
606 + * @param array|string $value
607 + *
608 + * @return void
354 609 */
355 610 public static function run_wpautop( $atts, &$value ) {
356 - $autop = isset( $atts['wpautop'] ) ? $atts['wpautop'] : true;
357 - if ( apply_filters( 'frm_use_wpautop', $autop ) ) {
358 - if ( is_array( $value ) ) {
359 - $value = implode( "\n", $value );
360 - }
361 - $value = wpautop( $value );
611 + $autop = $atts['wpautop'] ?? true;
612 +
613 + if ( ! apply_filters( 'frm_use_wpautop', $autop ) ) {
614 + return;
362 615 }
616 +
617 + if ( is_array( $value ) ) {
618 + $value = implode( "\n", $value );
619 + }
620 +
621 + $value = wpautop( $value );
363 622 }
364 623
365 624 /**
366 625 * Get the class to use for the label position
@@ -365,13 +624,19 @@
365 624 /**
366 625 * Get the class to use for the label position
367 626 *
368 627 * @since 2.05
628 + *
629 + * @param string $position
630 + * @param array $field
631 + * @param bool|object|string $form
632 + *
633 + * @return string
369 634 */
370 - public static function &label_position( $position, $field, $form ) {
371 - if ( $position && $position != '' ) {
372 - if ( $position == 'inside' && ! self::is_placeholder_field_type( $field['type'] ) ) {
373 - $position = 'top';
635 + public static function label_position( $position, $field, $form ) {
636 + if ( $position ) {
637 + if ( $position === 'inside' && ! self::is_placeholder_field_type( $field['type'] ) ) {
638 + return 'top';
374 639 }
375 640
376 641 return $position;
377 642 }
@@ -376,20 +641,19 @@
376 641 return $position;
377 642 }
378 643
379 644 $position = FrmStylesController::get_style_val( 'position', $form );
380 - if ( $position == 'none' ) {
645 +
646 + if ( $position === 'none' ) {
381 647 $position = 'top';
382 - } elseif ( $position == 'no_label' ) {
648 + } elseif ( $position === 'no_label' ) {
383 649 $position = 'none';
384 - } elseif ( $position == 'inside' && ! self::is_placeholder_field_type( $field['type'] ) ) {
650 + } elseif ( $position === 'inside' && ! self::is_placeholder_field_type( $field['type'] ) ) {
385 651 $position = 'top';
386 652 }
387 653
388 654 $position = apply_filters( 'frm_html_label_position', $position, $field, $form );
389 - $position = ( ! empty( $position ) ) ? $position : 'top';
390 -
391 - return $position;
655 + return $position ? $position : 'top';
392 656 }
393 657
394 658 /**
395 659 * Check if this field type allows placeholders
@@ -394,9 +658,11 @@
394 658 /**
395 659 * Check if this field type allows placeholders
396 660 *
397 661 * @since 2.05
662 + *
398 663 * @param string $type
664 + *
399 665 * @return bool
400 666 */
401 667 public static function is_placeholder_field_type( $type ) {
402 668 return ! in_array( $type, array( 'radio', 'checkbox', 'hidden', 'file' ), true );
@@ -401,11 +667,19 @@
401 667 public static function is_placeholder_field_type( $type ) {
402 668 return ! in_array( $type, array( 'radio', 'checkbox', 'hidden', 'file' ), true );
403 669 }
404 670
671 + /**
672 + * @param array $field
673 + * @param int|string $opt_key
674 + * @param string $type
675 + *
676 + * @return string
677 + */
405 678 public static function get_checkbox_id( $field, $opt_key, $type = 'checkbox' ) {
406 679 $id = $field['id'];
407 - if ( isset( $field['in_section'] ) && $field['in_section'] && ! FrmAppHelper::is_admin_page( 'formidable' ) ) {
680 +
681 + if ( ! empty( $field['in_section'] ) && ! FrmAppHelper::is_admin_page( 'formidable' ) ) {
408 682 $id .= '-' . $field['in_section'];
409 683 }
410 684
411 685 return 'frm_' . $type . '_' . $id . '-' . $opt_key;
@@ -410,8 +684,13 @@
410 684
411 685 return 'frm_' . $type . '_' . $id . '-' . $opt_key;
412 686 }
413 687
688 + /**
689 + * @param array $field
690 + *
691 + * @return void
692 + */
414 693 public static function show_single_option( $field ) {
415 694 self::hidden_field_option( $field );
416 695
417 696 if ( ! is_array( $field['options'] ) ) {
@@ -417,28 +696,28 @@
417 696 if ( ! is_array( $field['options'] ) ) {
418 697 return;
419 698 }
420 699
421 - $base_name = 'default_value_' . $field['id'];
422 - $html_id = isset( $field['html_id'] ) ? $field['html_id'] : self::get_html_id( $field );
700 + $base_name = 'default_value_' . $field['id'];
701 + $html_id = $field['html_id'] ?? self::get_html_id( $field );
702 + $default_type = self::get_default_value_type( $field );
703 + $options_count = count( $field['options'] );
423 704
424 - $default_type = self::get_default_value_type( $field );
425 -
426 705 foreach ( $field['options'] as $opt_key => $opt ) {
427 - $field_val = self::get_value_from_array( $opt, $opt_key, $field );
428 - $opt = self::get_label_from_array( $opt, $opt_key, $field );
429 -
706 + $field_val = self::get_value_from_array( $opt, $opt_key, $field );
707 + $opt = self::get_label_from_array( $opt, $opt_key, $field );
430 708 $field_name = $base_name . ( $default_type === 'checkbox' ? '[' . $opt_key . ']' : '' );
431 709
432 - $checked = ( isset( $field['default_value'] ) && ( ( ! is_array( $field['default_value'] ) && $field['default_value'] == $field_val ) || ( is_array( $field['default_value'] ) && in_array( $field_val, $field['default_value'] ) ) ) );
710 + // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict, Universal.Operators.StrictComparisons, SlevomatCodingStandard.Files.LineLength.LineTooLong
711 + $checked = isset( $field['default_value'] ) && ( is_array( $field['default_value'] ) ? in_array( $field_val, $field['default_value'] ) : $field['default_value'] == $field_val );
433 712
434 713 // If this is an "Other" option, get the HTML for it.
435 714 if ( self::is_other_opt( $opt_key ) ) {
436 715 if ( FrmAppHelper::pro_is_installed() ) {
437 - require( FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/other-option.php' );
716 + require FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/other-option.php';
438 717 }
439 718 } else {
440 - require( FrmAppHelper::plugin_path() . '/classes/views/frm-fields/single-option.php' );
719 + require FrmAppHelper::plugin_path() . '/classes/views/frm-fields/single-option.php';
441 720 }
442 721
443 722 unset( $checked );
444 723 }
@@ -447,28 +726,31 @@
447 726 /**
448 727 * Include hidden row for javascript to duplicate.
449 728 *
450 729 * @since 4.0
730 + *
451 731 * @param array $field
732 + *
733 + * @return void
452 734 */
453 735 private static function hidden_field_option( $field ) {
454 736 // Don't duplicate during an ajax add option.
455 737 $ajax_action = FrmAppHelper::get_param( 'action', '', 'post', 'sanitize_text_field' );
738 +
456 739 if ( $ajax_action === 'frm_add_field_option' ) {
457 740 return;
458 741 }
459 742
460 - $opt_key = '000';
461 - $field_val = __( 'New Option', 'formidable' );
462 - $opt = __( 'New Option', 'formidable' );
463 - $checked = false;
464 - $field_name = 'default_value_' . $field['id'];
465 - $html_id = isset( $field['html_id'] ) ? $field['html_id'] : self::get_html_id( $field );
466 -
743 + $opt_key = '000';
744 + $field_val = __( 'New Option', 'formidable' );
745 + $opt = __( 'New Option', 'formidable' );
746 + $checked = false;
747 + $field_name = 'default_value_' . $field['id'];
748 + $html_id = $field['html_id'] ?? self::get_html_id( $field );
467 749 $default_type = self::get_default_value_type( $field );
468 - $field_name .= ( $default_type === 'checkbox' ? '[' . $opt_key . ']' : '' );
750 + $field_name .= $default_type === 'checkbox' ? '[' . $opt_key . ']' : '';
469 751
470 - require( FrmAppHelper::plugin_path() . '/classes/views/frm-fields/single-option.php' );
752 + require FrmAppHelper::plugin_path() . '/classes/views/frm-fields/single-option.php';
471 753 }
472 754
473 755 /**
474 756 * @since 4.0
@@ -473,27 +755,39 @@
473 755 /**
474 756 * @since 4.0
475 757 *
476 758 * @param array $field
759 + *
477 760 * @return string radio or checkbox
478 761 */
479 762 private static function get_default_value_type( $field ) {
480 - $default_type = $field['type'];
481 763 if ( $field['type'] === 'select' ) {
482 - $default_type = FrmField::is_multiple_select( $field ) ? 'checkbox' : 'radio';
764 + return FrmField::is_multiple_select( $field ) ? 'checkbox' : 'radio';
483 765 }
484 - return $default_type;
766 + return $field['type'];
485 767 }
486 768
769 + /**
770 + * @param array|string $opt
771 + * @param int|string $opt_key
772 + * @param array $field
773 + *
774 + * @return mixed
775 + */
487 776 public static function get_value_from_array( $opt, $opt_key, $field ) {
488 777 $opt = apply_filters( 'frm_field_value_saved', $opt, $opt_key, $field );
489 -
490 778 return FrmFieldsController::check_value( $opt, $opt_key, $field );
491 779 }
492 780
781 + /**
782 + * @param array|string $opt
783 + * @param int|string $opt_key
784 + * @param array $field
785 + *
786 + * @return string
787 + */
493 788 public static function get_label_from_array( $opt, $opt_key, $field ) {
494 789 $opt = apply_filters( 'frm_field_label_seen', $opt, $opt_key, $field );
495 -
496 790 return FrmFieldsController::check_label( $opt );
497 791 }
498 792
499 793 /**
@@ -502,50 +796,62 @@
502 796 * @since 4.0
503 797 * @since 6.4.1 Added `inside_class` in the arguments.
504 798 *
505 799 * @param array $args The arguments.
800 + *
801 + * @return void
506 802 */
507 803 public static function inline_modal( $args ) {
508 804 $defaults = array(
509 - 'id' => '',
510 - 'class' => '',
511 - 'show' => 0,
512 - 'callback' => array(),
513 - 'args' => array(),
514 - 'title' => '',
805 + 'id' => '',
806 + 'class' => '',
807 + 'show' => 0,
808 + 'callback' => array(),
809 + 'args' => array(),
810 + 'title' => '',
515 811 'inside_class' => 'inside',
812 + 'dismiss-icon' => true,
516 813 );
517 - $args = array_merge( $defaults, $args );
814 + $args = array_merge( $defaults, $args );
518 815
519 - include( FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/inline-modal.php' );
816 + include FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/inline-modal.php';
520 817 }
521 818
522 819 /**
523 820 * @since 4.0
821 + *
822 + * @return void
524 823 */
525 824 public static function smart_values() {
526 825 $continue = apply_filters( 'frm_smart_values_box', true );
527 - if ( $continue === true ) {
528 - $upgrade_link = array(
529 - 'medium' => 'builder',
530 - 'content' => 'smart-tags',
531 - );
532 - include( FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/smart-values.php' );
826 +
827 + if ( $continue !== true ) {
828 + return;
533 829 }
830 +
831 + $upgrade_link = array(
832 + 'medium' => 'builder',
833 + 'content' => 'smart-tags',
834 + );
835 + include FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/smart-values.php';
534 836 }
535 837
536 838 /**
537 839 * @since 4.0
840 + *
841 + * @return void
538 842 */
539 843 public static function input_mask() {
540 - include( FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/input-mask-info.php' );
844 + include FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/input-mask-info.php';
541 845 }
542 846
543 847 /**
544 848 * @since 4.0
849 + *
850 + * @return void
545 851 */
546 852 public static function layout_classes() {
547 - include( FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/layout-classes.php' );
853 + include FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/layout-classes.php';
548 854 }
549 855
550 856 /**
551 857 * @param int $tax_id
@@ -553,28 +859,33 @@
553 859 * @return string
554 860 */
555 861 public static function get_term_link( $tax_id ) {
556 862 $tax = get_taxonomy( $tax_id );
863 +
557 864 if ( ! $tax ) {
558 865 return '';
559 866 }
560 867
561 - $link = sprintf(
868 + return sprintf(
562 869 /* translators: %1$s: Start HTML link, %2$s: Content type label, %3$s: Content type, %4$s: End HTML link */
563 870 esc_html__( 'Options are dynamically created from your %1$s%2$s: %3$s%4$s', 'formidable' ),
564 871 '<a href="' . esc_url( admin_url( 'edit-tags.php?taxonomy=' . $tax->name ) ) . '" target="_blank">',
565 872 esc_html__( 'taxonomy', 'formidable' ),
566 - empty( $tax->labels->name ) ? esc_html__( 'Categories', 'formidable' ) : $tax->labels->name,
873 + ! empty( $tax->labels->name ) ? $tax->labels->name : esc_html__( 'Categories', 'formidable' ),
567 874 '</a>'
568 875 );
569 - unset( $tax );
570 -
571 - return $link;
572 876 }
573 877
878 + /**
879 + * @param array|string $observed_value
880 + * @param string $cond
881 + * @param array|string $hide_opt
882 + *
883 + * @return bool
884 + */
574 885 public static function value_meets_condition( $observed_value, $cond, $hide_opt ) {
575 - $hide_opt = self::get_value_for_comparision( $hide_opt );
576 - $observed_value = self::get_value_for_comparision( $observed_value );
886 + $hide_opt = self::get_value_for_comparison( $hide_opt );
887 + $observed_value = self::get_value_for_comparison( $observed_value );
577 888
578 889 if ( is_array( $observed_value ) ) {
579 890 return self::array_value_condition( $observed_value, $cond, $hide_opt );
580 891 }
@@ -579,12 +890,13 @@
579 890 return self::array_value_condition( $observed_value, $cond, $hide_opt );
580 891 }
581 892
582 893 $m = false;
894 +
583 895 if ( $cond === '==' ) {
584 - $m = $observed_value == $hide_opt;
896 + $m = $observed_value == $hide_opt; // phpcs:ignore Universal.Operators.StrictComparisons
585 897 } elseif ( $cond === '!=' ) {
586 - $m = $observed_value != $hide_opt;
898 + $m = $observed_value != $hide_opt; // phpcs:ignore Universal.Operators.StrictComparisons
587 899 } elseif ( $cond === '>' ) {
588 900 $m = $observed_value > $hide_opt;
589 901 } elseif ( $cond === '>=' ) {
590 902 $m = $observed_value >= $hide_opt;
@@ -593,24 +905,20 @@
593 905 } elseif ( $cond === '<=' ) {
594 906 $m = $observed_value <= $hide_opt;
595 907 } elseif ( $cond === 'LIKE' || $cond === 'not LIKE' ) {
596 908 $m = stripos( $observed_value, $hide_opt );
597 - if ( $cond === 'not LIKE' ) {
598 - $m = ( $m === false ) ? true : false;
599 - } else {
600 - $m = ( $m === false ) ? false : true;
601 - }
909 + $m = $cond === 'not LIKE' ? $m === false : $m !== false;
602 910 } elseif ( $cond === '%LIKE' ) {
603 - // ends with
911 + // Ends with
604 912 $length = strlen( $hide_opt );
605 913 $substr = substr( $observed_value, strlen( $observed_value ) - $length );
606 914 $m = 0 === strcasecmp( $substr, $hide_opt );
607 915 } elseif ( 'LIKE%' === $cond ) {
608 - // starts with
916 + // Starts with
609 917 $length = strlen( $hide_opt );
610 918 $substr = substr( $observed_value, 0, $length );
611 919 $m = 0 === strcasecmp( $substr, $hide_opt );
612 - }
920 + }//end if
613 921
614 922 return $m;
615 923 }
616 924
@@ -617,10 +925,14 @@
617 925 /**
618 926 * Trim and sanitize the values
619 927 *
620 928 * @since 2.05
929 + *
930 + * @param array|string $value
931 + *
932 + * @return array|string
621 933 */
622 - private static function get_value_for_comparision( $value ) {
934 + private static function get_value_for_comparison( $value ) {
623 935 // Remove white space from hide_opt
624 936 if ( ! is_array( $value ) ) {
625 937 $value = trim( $value );
626 938 }
@@ -629,28 +941,37 @@
629 941
630 942 return $value;
631 943 }
632 944
945 + /**
946 + * @param array $observed_value
947 + * @param string $cond
948 + * @param array|string $hide_opt
949 + *
950 + * @return bool
951 + */
633 952 public static function array_value_condition( $observed_value, $cond, $hide_opt ) {
634 953 $m = false;
954 +
635 955 if ( $cond === '==' ) {
636 956 if ( is_array( $hide_opt ) ) {
637 957 $m = array_intersect( $hide_opt, $observed_value );
638 - $m = empty( $m ) ? false : true;
958 + $m = $m !== array();
639 959 } else {
960 + // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
640 961 $m = in_array( $hide_opt, $observed_value );
641 962 }
642 963 } elseif ( $cond === '!=' ) {
964 + // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
643 965 $m = ! in_array( $hide_opt, $observed_value );
644 966 } elseif ( $cond === '>' ) {
645 - $min = min( $observed_value );
646 - $m = $min > $hide_opt;
967 + $m = min( $observed_value ) > $hide_opt;
647 968 } elseif ( $cond === '<' ) {
648 - $max = max( $observed_value );
649 - $m = $max < $hide_opt;
969 + $m = max( $observed_value ) < $hide_opt;
650 970 } elseif ( $cond === 'LIKE' || $cond === 'not LIKE' ) {
651 971 foreach ( $observed_value as $ob ) {
652 972 $m = strpos( $ob, $hide_opt );
973 +
653 974 if ( $m !== false ) {
654 975 $m = true;
655 976 break;
656 977 }
@@ -656,12 +977,12 @@
656 977 }
657 978 }
658 979
659 980 if ( $cond === 'not LIKE' ) {
660 - $m = ( $m === false ) ? true : false;
981 + $m = $m === false;
661 982 }
662 983 } elseif ( $cond === '%LIKE' ) {
663 - // ends with
984 + // Ends with
664 985 foreach ( $observed_value as $ob ) {
665 986 if ( $hide_opt === substr( $ob, strlen( $ob ) - strlen( $hide_opt ) ) ) {
666 987 $m = true;
667 988 break;
@@ -667,16 +988,16 @@
667 988 break;
668 989 }
669 990 }
670 991 } elseif ( $cond === 'LIKE%' ) {
671 - // starts with
992 + // Starts with
672 993 foreach ( $observed_value as $ob ) {
673 - if ( $hide_opt === substr( $ob, 0, strlen( $hide_opt ) ) ) {
994 + if ( str_starts_with( $ob, $hide_opt ) ) {
674 995 $m = true;
675 996 break;
676 997 }
677 998 }
678 - }
999 + }//end if
679 1000
680 1001 return $m;
681 1002 }
682 1003
@@ -683,22 +1004,32 @@
683 1004 /**
684 1005 * Replace a few basic shortcodes and field ids
685 1006 *
686 1007 * @since 2.0
1008 + *
1009 + * @param string $value
1010 + * @param object $form
1011 + * @param object $entry
1012 + *
687 1013 * @return string
688 1014 */
689 1015 public static function basic_replace_shortcodes( $value, $form, $entry ) {
690 - if ( strpos( $value, '[sitename]' ) !== false ) {
1016 + if ( str_contains( $value, '[sitename]' ) ) {
691 1017 $new_value = wp_specialchars_decode( FrmAppHelper::site_name(), ENT_QUOTES );
692 1018 $value = str_replace( '[sitename]', $new_value, $value );
693 1019 }
694 1020
695 1021 $value = apply_filters( 'frm_content', $value, $form, $entry );
696 - $value = do_shortcode( $value );
697 1022
698 - return $value;
1023 + return do_shortcode( $value );
699 1024 }
700 1025
1026 + /**
1027 + * @param string $content
1028 + * @param int|string $form_id
1029 + *
1030 + * @return array
1031 + */
701 1032 public static function get_shortcodes( $content, $form_id ) {
702 1033 if ( FrmAppHelper::pro_is_installed() ) {
703 1034 return FrmProDisplaysHelper::get_shortcodes( $content, $form_id );
704 1035 }
@@ -716,8 +1047,13 @@
716 1047
717 1048 return $matches;
718 1049 }
719 1050
1051 + /**
1052 + * @param array $fields
1053 + *
1054 + * @return string
1055 + */
720 1056 public static function allowed_shortcodes( $fields = array() ) {
721 1057 $tagregexp = array(
722 1058 'editlink',
723 1059 'id',
@@ -725,13 +1061,16 @@
725 1061 'ip',
726 1062 'siteurl',
727 1063 'sitename',
728 1064 'admin_email',
1065 + 'default-email',
1066 + 'default-from-email',
729 1067 'post[-|_]id',
730 1068 'created[-|_]at',
731 1069 'updated[-|_]at',
732 1070 'updated[-|_]by',
733 1071 'parent[-|_]id',
1072 + 'form_name',
734 1073 );
735 1074
736 1075 foreach ( $fields as $field ) {
737 1076 $tagregexp[] = $field->id;
@@ -737,16 +1076,21 @@
737 1076 $tagregexp[] = $field->id;
738 1077 $tagregexp[] = $field->field_key;
739 1078 }
740 1079
741 - $tagregexp = implode( '|', $tagregexp );
742 -
743 - return $tagregexp;
1080 + return implode( '|', $tagregexp );
744 1081 }
745 1082
1083 + /**
1084 + * @param string $content
1085 + * @param false|object $entry
1086 + * @param array $shortcodes
1087 + *
1088 + * @return string
1089 + */
746 1090 public static function replace_content_shortcodes( $content, $entry, $shortcodes ) {
747 1091 foreach ( $shortcodes[0] as $short_key => $tag ) {
748 - if ( empty( $tag ) ) {
1092 + if ( ! $tag ) {
749 1093 continue;
750 1094 }
751 1095
752 1096 $atts = FrmShortcodeHelper::get_shortcode_attribute_array( $shortcodes[3][ $short_key ] );
@@ -770,21 +1114,25 @@
770 1114
771 1115 /**
772 1116 * @param string $replace_with
773 1117 * @param array $atts
1118 + *
774 1119 * @return string
775 1120 */
776 1121 private static function trigger_shortcode_atts( $replace_with, $atts ) {
777 1122 $supported_atts = array( 'remove_accents', 'sanitize', 'sanitize_url' );
778 1123 $included_atts = array_intersect( $supported_atts, array_keys( $atts ) );
1124 +
779 1125 foreach ( $included_atts as $included_att ) {
780 1126 if ( '0' === $atts[ $included_att ] ) {
781 1127 // Skip any option that uses 0 so sanitize_url=0 does not encode.
782 1128 continue;
783 1129 }
1130 +
784 1131 $function = 'atts_' . $included_att;
785 - $replace_with = self::$function( $replace_with, $atts );
1132 + $replace_with = self::$function( $replace_with );
786 1133 }
1134 +
787 1135 return $replace_with;
788 1136 }
789 1137
790 1138 /**
@@ -801,8 +1149,9 @@
801 1149 }
802 1150
803 1151 /**
804 1152 * @param string $replace_with
1153 + *
805 1154 * @return string
806 1155 */
807 1156 private static function atts_sanitize( $replace_with ) {
808 1157 return sanitize_title_with_dashes( $replace_with );
@@ -809,8 +1158,9 @@
809 1158 }
810 1159
811 1160 /**
812 1161 * @param string $replace_with
1162 + *
813 1163 * @return string
814 1164 */
815 1165 private static function atts_sanitize_url( $replace_with ) {
816 1166 return urlencode( $replace_with );
@@ -820,14 +1170,21 @@
820 1170 * Prevent shortcodes in fields from being processed
821 1171 *
822 1172 * @since 3.01.02
823 1173 *
824 - * @param array $atts - includes entry object
825 - * @param string $value
1174 + * @param array $atts Includes entry object.
1175 + * @param string|null $value
1176 + *
1177 + * @return void
826 1178 */
827 1179 public static function sanitize_embedded_shortcodes( $atts, &$value ) {
1180 + if ( is_null( $value ) ) {
1181 + return;
1182 + }
1183 +
828 1184 $atts['value'] = $value;
829 1185 $should_sanitize = apply_filters( 'frm_sanitize_shortcodes', true, $atts );
1186 +
830 1187 if ( $should_sanitize ) {
831 1188 $value = str_replace( '[', '&#91;', $value );
832 1189 }
833 1190 }
@@ -834,9 +1191,9 @@
834 1191
835 1192 /**
836 1193 * @since 3.0
837 1194 *
838 - * @param $atts
1195 + * @param array $atts
839 1196 *
840 1197 * @return string
841 1198 */
842 1199 private static function get_value_for_shortcode( $atts ) {
@@ -847,13 +1204,13 @@
847 1204 'key' => $atts['entry']->item_key,
848 1205 'ip' => $atts['entry']->ip,
849 1206 );
850 1207
851 - $dynamic_default = array( 'admin_email', 'siteurl', 'frmurl', 'sitename', 'get' );
1208 + $dynamic_default = array( 'admin_email', 'siteurl', 'frmurl', 'sitename', 'get', 'default-email', 'default-from-email' );
852 1209
853 1210 if ( isset( $shortcode_values[ $atts['tag'] ] ) ) {
854 1211 $replace_with = $shortcode_values[ $atts['tag'] ];
855 - } elseif ( in_array( $atts['tag'], $dynamic_default ) ) {
1212 + } elseif ( in_array( $atts['tag'], $dynamic_default, true ) ) {
856 1213 $replace_with = self::dynamic_default_values( $atts['tag'], $atts );
857 1214 } elseif ( $clean_tag === 'user_agent' ) {
858 1215 $description = $atts['entry']->description;
859 1216 $replace_with = FrmEntriesHelper::get_browser( $description['browser'] );
@@ -871,9 +1228,9 @@
871 1228
872 1229 /**
873 1230 * @since 3.0
874 1231 *
875 - * @param $atts
1232 + * @param array $atts
876 1233 *
877 1234 * @return string
878 1235 */
879 1236 private static function get_entry_timestamp( $atts ) {
@@ -889,15 +1246,16 @@
889 1246
890 1247 /**
891 1248 * @since 3.0
892 1249 *
893 - * @param $atts
1250 + * @param array $atts
894 1251 *
895 - * @return null|string
1252 + * @return string|null
896 1253 */
897 1254 private static function get_field_shortcode_value( $atts ) {
898 1255 $field = FrmField::getOne( $atts['tag'] );
899 - if ( empty( $field ) ) {
1256 +
1257 + if ( ! $field ) {
900 1258 return null;
901 1259 }
902 1260
903 1261 if ( isset( $atts['show'] ) && $atts['show'] === 'field_label' ) {
@@ -906,14 +1264,16 @@
906 1264 $replace_with = $field->description;
907 1265 } else {
908 1266 $replace_with = FrmEntryMeta::get_meta_value( $atts['entry'], $field->id );
909 1267 $string_value = $replace_with;
1268 +
910 1269 if ( is_array( $replace_with ) ) {
911 - $sep = isset( $atts['sep'] ) ? $atts['sep'] : ', ';
912 - $string_value = implode( $sep, $replace_with );
1270 + $sep = $atts['sep'] ?? ', ';
1271 + $string_value = FrmAppHelper::safe_implode( $sep, $replace_with );
913 1272 }
914 1273
915 - if ( empty( $string_value ) && $string_value != '0' ) {
1274 + // phpcs:ignore Universal.Operators.StrictComparisons
1275 + if ( ! $string_value && $string_value != '0' ) {
916 1276 $replace_with = '';
917 1277 } else {
918 1278 $atts['entry_id'] = $atts['entry']->id;
919 1279 $atts['entry_key'] = $atts['entry']->item_key;
@@ -918,9 +1278,9 @@
918 1278 $atts['entry_id'] = $atts['entry']->id;
919 1279 $atts['entry_key'] = $atts['entry']->item_key;
920 1280 $replace_with = self::get_display_value( $replace_with, $field, $atts );
921 1281 }
922 - }
1282 + }//end if
923 1283
924 1284 return $replace_with;
925 1285 }
926 1286
@@ -927,8 +1287,13 @@
927 1287 /**
928 1288 * Get the value to replace a few standard shortcodes
929 1289 *
930 1290 * @since 2.0
1291 + *
1292 + * @param string $tag
1293 + * @param array $atts
1294 + * @param bool $return_array
1295 + *
931 1296 * @return string
932 1297 */
933 1298 public static function dynamic_default_values( $tag, $atts = array(), $return_array = false ) {
934 1299 $new_value = '';
@@ -935,8 +1300,16 @@
935 1300 switch ( $tag ) {
936 1301 case 'admin_email':
937 1302 $new_value = get_option( 'admin_email' );
938 1303 break;
1304 + case 'default-email':
1305 + $frm_settings = FrmAppHelper::get_settings();
1306 + $new_value = ! empty( $frm_settings->default_email ) && is_email( $frm_settings->default_email ) ? $frm_settings->default_email : get_option( 'admin_email' );
1307 + break;
1308 + case 'default-from-email':
1309 + $frm_settings = FrmAppHelper::get_settings();
1310 + $new_value = ! empty( $frm_settings->from_email ) && is_email( $frm_settings->from_email ) ? $frm_settings->from_email : get_option( 'admin_email' );
1311 + break;
939 1312 case 'siteurl':
940 1313 $new_value = FrmAppHelper::site_url();
941 1314 break;
942 1315 case 'frmurl':
@@ -946,9 +1319,9 @@
946 1319 $new_value = FrmAppHelper::site_name();
947 1320 break;
948 1321 case 'get':
949 1322 $new_value = self::process_get_shortcode( $atts, $return_array );
950 - }
1323 + }//end switch
951 1324
952 1325 return $new_value;
953 1326 }
954 1327
@@ -955,9 +1328,13 @@
955 1328 /**
956 1329 * Process the [get] shortcode
957 1330 *
958 1331 * @since 2.0
959 - * @return string|array
1332 + *
1333 + * @param array $atts
1334 + * @param bool $return_array
1335 + *
1336 + * @return array|string
960 1337 */
961 1338 public static function process_get_shortcode( $atts, $return_array = false ) {
962 1339 if ( ! isset( $atts['param'] ) ) {
963 1340 return '';
@@ -962,9 +1339,9 @@
962 1339 if ( ! isset( $atts['param'] ) ) {
963 1340 return '';
964 1341 }
965 1342
966 - if ( strpos( $atts['param'], '&#91;' ) ) {
1343 + if ( str_contains( $atts['param'], '&#91;' ) ) {
967 1344 $atts['param'] = str_replace( '&#91;', '[', $atts['param'] );
968 1345 $atts['param'] = str_replace( '&#93;', ']', $atts['param'] );
969 1346 }
970 1347
@@ -970,28 +1347,34 @@
970 1347
971 1348 $new_value = FrmAppHelper::get_param( $atts['param'], '', 'get', 'sanitize_text_field' );
972 1349 $new_value = FrmAppHelper::get_query_var( $new_value, $atts['param'] );
973 1350
1351 + // phpcs:ignore Universal.Operators.StrictComparisons
974 1352 if ( $new_value == '' ) {
975 1353 if ( ! isset( $atts['prev_val'] ) ) {
976 1354 $atts['prev_val'] = '';
977 1355 }
978 1356
979 - $new_value = isset( $atts['default'] ) ? $atts['default'] : $atts['prev_val'];
1357 + $new_value = $atts['default'] ?? $atts['prev_val'];
980 1358 }
981 1359
982 1360 if ( is_array( $new_value ) && ! $return_array ) {
983 - $new_value = implode( ', ', $new_value );
1361 + return implode( ', ', $new_value );
984 1362 }
985 1363
986 1364 return $new_value;
987 1365 }
988 1366
1367 + /**
1368 + * @param mixed $value
1369 + * @param object $field
1370 + * @param array $atts
1371 + *
1372 + * @return mixed
1373 + */
989 1374 public static function get_display_value( $value, $field, $atts = array() ) {
990 -
991 1375 $value = apply_filters( 'frm_get_' . $field->type . '_display_value', $value, $field, $atts );
992 1376 $value = apply_filters( 'frm_get_display_value', $value, $field, $atts );
993 -
994 1377 $value = self::get_unfiltered_display_value( compact( 'value', 'field', 'atts' ) );
995 1378
996 1379 return apply_filters( 'frm_display_value', $value, $field, $atts );
997 1380 }
@@ -996,14 +1379,16 @@
996 1379 return apply_filters( 'frm_display_value', $value, $field, $atts );
997 1380 }
998 1381
999 1382 /**
1000 - * @param $atts array Includes value, field, and atts
1383 + * @param array $atts Includes value, field, and atts.
1384 + *
1385 + * @return mixed
1001 1386 */
1002 1387 public static function get_unfiltered_display_value( $atts ) {
1003 1388 $value = $atts['value'];
1004 1389 $field = $atts['field'];
1005 - $atts = isset( $atts['atts'] ) ? $atts['atts'] : $atts;
1390 + $atts = $atts['atts'] ?? $atts;
1006 1391
1007 1392 if ( is_array( $field ) ) {
1008 1393 $field = $field['id'];
1009 1394 }
@@ -1016,8 +1401,14 @@
1016 1401 /**
1017 1402 * Get a value from the user profile from the user ID
1018 1403 *
1019 1404 * @since 3.0
1405 + *
1406 + * @param int|string $user_id
1407 + * @param string $user_info
1408 + * @param array $args
1409 + *
1410 + * @return string
1020 1411 */
1021 1412 public static function get_user_display_name( $user_id, $user_info = 'display_name', $args = array() ) {
1022 1413 $defaults = array(
1023 1414 'blank' => false,
@@ -1025,51 +1416,52 @@
1025 1416 'size' => 96,
1026 1417 );
1027 1418
1028 1419 $args = wp_parse_args( $args, $defaults );
1029 -
1030 1420 $user = get_userdata( $user_id );
1031 1421 $info = '';
1032 1422
1033 1423 if ( $user ) {
1034 - if ( $user_info == 'avatar' ) {
1424 + if ( $user_info === 'avatar' ) {
1035 1425 $info = get_avatar( $user_id, $args['size'] );
1036 - } elseif ( $user_info == 'author_link' ) {
1426 + } elseif ( $user_info === 'author_link' ) {
1037 1427 $info = get_author_posts_url( $user_id );
1038 1428 } else {
1039 - $info = isset( $user->$user_info ) ? $user->$user_info : '';
1429 + $info = $user->$user_info ?? '';
1040 1430 }
1041 1431
1042 - if ( 'display_name' === $user_info && empty( $info ) && ! $args['blank'] ) {
1432 + if ( 'display_name' === $user_info && ! $info && ! $args['blank'] ) {
1043 1433 $info = $user->user_login;
1044 1434 }
1045 1435 }
1046 1436
1047 1437 if ( $args['link'] ) {
1048 - $info = '<a href="' . esc_url( admin_url( 'user-edit.php?user_id=' . $user_id ) ) . '">' . $info . '</a>';
1438 + return '<a href="' . esc_url( admin_url( 'user-edit.php?user_id=' . $user_id ) ) . '">' . $info . '</a>';
1049 1439 }
1050 1440
1051 1441 return $info;
1052 1442 }
1053 1443
1444 + /**
1445 + * @param string $type
1446 + *
1447 + * @return array
1448 + */
1054 1449 public static function get_field_types( $type ) {
1055 - $single_input = self::single_input_fields();
1056 - $multiple_input = array( 'radio', 'checkbox', 'select', 'scale', 'star', 'lookup' );
1057 -
1450 + $single_input = self::single_input_fields();
1451 + $multiple_input = array( 'radio', 'checkbox', 'select', 'scale', 'star', 'lookup' );
1058 1452 $field_selection = FrmField::all_field_selection();
1453 + $field_types = array();
1059 1454
1060 - $field_types = array();
1061 - if ( in_array( $type, $single_input ) ) {
1455 + if ( in_array( $type, $single_input, true ) ) {
1062 1456 self::field_types_for_input( $single_input, $field_selection, $field_types );
1063 - } elseif ( in_array( $type, $multiple_input ) ) {
1457 + } elseif ( in_array( $type, $multiple_input, true ) ) {
1064 1458 self::field_types_for_input( $multiple_input, $field_selection, $field_types );
1065 1459 } elseif ( isset( $field_selection[ $type ] ) ) {
1066 1460 $field_types[ $type ] = $field_selection[ $type ];
1067 1461 }
1068 1462
1069 - $field_types = apply_filters( 'frm_switch_field_types', $field_types, compact( 'type', 'field_selection' ) );
1070 -
1071 - return $field_types;
1463 + return apply_filters( 'frm_switch_field_types', $field_types, compact( 'type', 'field_selection' ) );
1072 1464 }
1073 1465
1074 1466 /**
1075 1467 * Get a list of all fields that use a single value input.
@@ -1074,8 +1466,10 @@
1074 1466 /**
1075 1467 * Get a list of all fields that use a single value input.
1076 1468 *
1077 1469 * @since 4.0
1470 + *
1471 + * @return array
1078 1472 */
1079 1473 public static function single_input_fields() {
1080 1474 $fields = array(
1081 1475 'text',
@@ -1089,15 +1483,26 @@
1089 1483 'hidden',
1090 1484 'time',
1091 1485 'tag',
1092 1486 'password',
1487 + 'gdpr',
1093 1488 );
1094 1489 return apply_filters( 'frm_single_input_fields', $fields );
1095 1490 }
1096 1491
1492 + /**
1493 + * @param string[] $inputs
1494 + * @param array $fields
1495 + * @param array $field_types
1496 + *
1497 + * @return void
1498 + */
1097 1499 private static function field_types_for_input( $inputs, $fields, &$field_types ) {
1098 1500 foreach ( $inputs as $input ) {
1099 - $field_types[ $input ] = $fields[ $input ];
1501 + // This may not be set if a field type was removed using the frm_available_fields or frm_pro_available_fields filters.
1502 + if ( array_key_exists( $input, $fields ) ) {
1503 + $field_types[ $input ] = $fields[ $input ];
1504 + }
1100 1505 unset( $input );
1101 1506 }
1102 1507 }
1103 1508
@@ -1107,12 +1512,12 @@
1107 1512 * @since 2.0.6
1108 1513 *
1109 1514 * @param string $opt_key
1110 1515 *
1111 - * @return boolean Returns true if current field option is an "Other" option
1516 + * @return bool Returns true if current field option is an "Other" option
1112 1517 */
1113 1518 public static function is_other_opt( $opt_key ) {
1114 - return $opt_key && strpos( $opt_key, 'other_' ) === 0;
1519 + return $opt_key && str_starts_with( $opt_key, 'other_' );
1115 1520 }
1116 1521
1117 1522 /**
1118 1523 * Get value that belongs in "Other" text box
@@ -1119,18 +1524,19 @@
1119 1524 *
1120 1525 * @since 2.0.6
1121 1526 *
1122 1527 * @param array $args
1528 + *
1529 + * @return string
1123 1530 */
1124 - public static function get_other_val( $args ) {
1125 - $defaults = array(
1531 + public static function get_other_val( $args ) { // phpcs:ignore SlevomatCodingStandard.Complexity.Cognitive.ComplexityTooHigh
1532 + $defaults = array(
1126 1533 'opt_key' => 0,
1127 1534 'field' => array(),
1128 1535 'parent' => false,
1129 1536 'pointer' => false,
1130 1537 );
1131 - $args = wp_parse_args( $args, $defaults );
1132 -
1538 + $args = wp_parse_args( $args, $defaults );
1133 1539 $opt_key = $args['opt_key'];
1134 1540 $field = $args['field'];
1135 1541 $parent = $args['parent'];
1136 1542 $pointer = $args['pointer'];
@@ -1136,9 +1542,9 @@
1136 1542 $pointer = $args['pointer'];
1137 1543 $other_val = '';
1138 1544
1139 1545 // If option is an "other" option and there is a value set for this field,
1140 - // check if the value belongs in the current "Other" option text field
1546 + // Check if the value belongs in the current "Other" option text field
1141 1547 if ( ! self::is_other_opt( $opt_key ) || ! FrmField::is_option_true( $field, 'value' ) ) {
1142 1548 return $other_val;
1143 1549 }
1144 1550
@@ -1145,21 +1551,22 @@
1145 1551 // Check posted vals before checking saved values
1146 1552 // For fields inside repeating sections - note, don't check if $pointer is true because it will often be zero
1147 1553 if ( $parent && isset( $_POST['item_meta'][ $parent ][ $pointer ]['other'][ $field['id'] ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
1148 1554 if ( FrmField::is_field_with_multiple_values( $field ) ) {
1555 + $other_val = isset( $_POST['item_meta'][ $parent ][ $pointer ]['other'][ $field['id'] ][ $opt_key ] ) ? sanitize_text_field( wp_unslash( $_POST['item_meta'][ $parent ][ $pointer ]['other'][ $field['id'] ][ $opt_key ] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing, SlevomatCodingStandard.Files.LineLength.LineTooLong
1556 + } else {
1149 1557 // phpcs:ignore WordPress.Security.NonceVerification.Missing
1150 - $other_val = isset( $_POST['item_meta'][ $parent ][ $pointer ]['other'][ $field['id'] ][ $opt_key ] ) ? sanitize_text_field( wp_unslash( $_POST['item_meta'][ $parent ][ $pointer ]['other'][ $field['id'] ][ $opt_key ] ) ) : '';
1151 - } else {
1152 - $other_val = sanitize_text_field( wp_unslash( $_POST['item_meta'][ $parent ][ $pointer ]['other'][ $field['id'] ] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
1558 + $other_val = sanitize_text_field( wp_unslash( $_POST['item_meta'][ $parent ][ $pointer ]['other'][ $field['id'] ] ) );
1153 1559 }
1154 1560
1155 1561 return $other_val;
1562 + }
1156 1563
1157 - } elseif ( isset( $field['id'] ) && isset( $_POST['item_meta']['other'][ $field['id'] ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
1564 + if ( isset( $field['id'] ) && isset( $_POST['item_meta']['other'][ $field['id'] ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
1158 1565 // For normal fields
1159 1566
1160 1567 if ( FrmField::is_field_with_multiple_values( $field ) ) {
1161 - // phpcs:ignore WordPress.Security.NonceVerification.Missing
1568 + // phpcs:ignore WordPress.Security.NonceVerification.Missing, SlevomatCodingStandard.Files.LineLength.LineTooLong
1162 1569 $other_val = isset( $_POST['item_meta']['other'][ $field['id'] ][ $opt_key ] ) ? sanitize_text_field( wp_unslash( $_POST['item_meta']['other'][ $field['id'] ][ $opt_key ] ) ) : '';
1163 1570 } else {
1164 1571 $other_val = sanitize_text_field( wp_unslash( $_POST['item_meta']['other'][ $field['id'] ] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
1165 1572 }
@@ -1164,14 +1571,15 @@
1164 1571 $other_val = sanitize_text_field( wp_unslash( $_POST['item_meta']['other'][ $field['id'] ] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
1165 1572 }
1166 1573
1167 1574 return $other_val;
1168 - }
1575 + }//end if
1169 1576
1170 1577 // For checkboxes
1171 1578 if ( $field['type'] === 'checkbox' && is_array( $field['value'] ) ) {
1172 1579 // Check if there is an "other" val in saved value and make sure the
1173 1580 // "other" val is not equal to the Other checkbox option
1581 + // phpcs:ignore Universal.Operators.StrictComparisons
1174 1582 if ( isset( $field['value'][ $opt_key ] ) && $field['options'][ $opt_key ] != $field['value'][ $opt_key ] ) {
1175 1583 $other_val = $field['value'][ $opt_key ];
1176 1584 }
1177 1585 } else {
@@ -1178,29 +1586,33 @@
1178 1586 /**
1179 1587 * For radio buttons and dropdowns
1180 1588 * Check if saved value equals any of the options. If not, set it as the other value.
1181 1589 */
1182 - foreach ( $field['options'] as $opt_key => $opt_val ) {
1590 + foreach ( $field['options'] as $opt_val ) {
1183 1591 $temp_val = is_array( $opt_val ) ? $opt_val['value'] : $opt_val;
1592 +
1184 1593 // Multi-select dropdowns - key is not preserved
1185 1594 if ( is_array( $field['value'] ) ) {
1595 + // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
1186 1596 $o_key = array_search( $temp_val, $field['value'] );
1597 +
1187 1598 if ( isset( $field['value'][ $o_key ] ) ) {
1188 1599 unset( $field['value'][ $o_key ], $o_key );
1189 1600 }
1190 - } elseif ( $temp_val == $field['value'] ) {
1601 + } elseif ( $temp_val == $field['value'] ) { // phpcs:ignore Universal.Operators.StrictComparisons
1191 1602 // For radio and regular dropdowns
1192 1603 return '';
1193 1604 } else {
1194 1605 $other_val = $field['value'];
1195 1606 }
1196 - unset( $opt_key, $opt_val, $temp_val );
1607 + unset( $opt_val, $temp_val );
1197 1608 }
1609 +
1198 1610 // For multi-select dropdowns only
1199 - if ( is_array( $field['value'] ) && ! empty( $field['value'] ) ) {
1611 + if ( is_array( $field['value'] ) && $field['value'] ) {
1200 1612 $other_val = reset( $field['value'] );
1201 1613 }
1202 - }
1614 + }//end if
1203 1615
1204 1616 return $other_val;
1205 1617 }
1206 1618
@@ -1209,13 +1621,13 @@
1209 1621 * Intended for front-end use
1210 1622 *
1211 1623 * @since 2.0.6
1212 1624 *
1213 - * @param array $args should include field, opt_key and field name
1214 - * @param boolean $other_opt
1625 + * @param array $args Should include field, opt_key and field name.
1626 + * @param bool $other_opt
1215 1627 * @param string $checked
1216 1628 *
1217 - * @return array $other_args
1629 + * @return array Other args.
1218 1630 */
1219 1631 public static function prepare_other_input( $args, &$other_opt, &$checked ) {
1220 1632 $other_args = array(
1221 1633 'name' => '',
@@ -1221,14 +1633,14 @@
1221 1633 'name' => '',
1222 1634 'value' => '',
1223 1635 );
1224 1636
1225 - //Check if this is an "Other" option
1637 + // Check if this is an "Other" option.
1226 1638 if ( ! self::is_other_opt( $args['opt_key'] ) ) {
1227 1639 return $other_args;
1228 1640 }
1229 1641
1230 - $other_opt = true;
1642 + $other_opt = true;
1231 1643
1232 1644 self::set_other_name( $args, $other_args );
1233 1645 self::set_other_value( $args, $other_args );
1234 1646
@@ -1235,25 +1647,37 @@
1235 1647 if ( '' !== $other_args['value'] ) {
1236 1648 $checked = 'checked="checked" ';
1237 1649 }
1238 1650
1651 + // If 'other' is selected as one of the default values for a checkbox field, 'checked' attribute should be on.
1652 + if ( ! $checked &&
1653 + $args['field']['type'] === 'checkbox' &&
1654 + is_array( $args['field']['value'] ) &&
1655 + isset( $args['field_val'] ) &&
1656 + in_array( $args['field_val'], $args['field']['value'], true )
1657 + ) {
1658 + $checked = 'checked="checked" ';
1659 + }
1660 +
1239 1661 return $other_args;
1240 1662 }
1241 1663
1242 1664 /**
1665 + * @since 2.0.6
1666 + *
1243 1667 * @param array $args
1244 1668 * @param array $other_args
1245 1669 *
1246 - * @since 2.0.6
1670 + * @return void
1247 1671 */
1248 1672 private static function set_other_name( $args, &$other_args ) {
1249 - //Set up name for other field
1673 + // Set up name for other field
1250 1674 $other_args['name'] = str_replace( '[]', '', $args['field_name'] );
1251 1675 $other_args['name'] = preg_replace( '/\[' . $args['field']['id'] . '\]$/', '', $other_args['name'] );
1252 1676 $other_args['name'] = $other_args['name'] . '[other][' . $args['field']['id'] . ']';
1253 1677
1254 - //Converts item_meta[field_id] => item_meta[other][field_id] and
1255 - //item_meta[parent][0][field_id] => item_meta[parent][0][other][field_id]
1678 + // Converts item_meta[field_id] => item_meta[other][field_id] and
1679 + // item_meta[parent][0][field_id] => item_meta[parent][0][other][field_id]
1256 1680 if ( FrmField::is_field_with_multiple_values( $args['field'] ) ) {
1257 1681 $other_args['name'] .= '[' . $args['opt_key'] . ']';
1258 1682 }
1259 1683 }
@@ -1260,12 +1684,14 @@
1260 1684
1261 1685 /**
1262 1686 * Find the parent and pointer, and get text for "other" text field
1263 1687 *
1688 + * @since 2.0.6
1689 + *
1264 1690 * @param array $args
1265 1691 * @param array $other_args
1266 1692 *
1267 - * @since 2.0.6
1693 + * @return void
1268 1694 */
1269 1695 private static function set_other_value( $args, &$other_args ) {
1270 1696 $parent = '';
1271 1697 $pointer = '';
@@ -1292,11 +1718,13 @@
1292 1718
1293 1719 /**
1294 1720 * If this field includes an other option, show it
1295 1721 *
1296 - * @param $args array
1722 + * @since 2.0.6
1297 1723 *
1298 - * @since 2.0.6
1724 + * @param array $args
1725 + *
1726 + * @return void
1299 1727 */
1300 1728 public static function include_other_input( $args ) {
1301 1729 if ( ! $args['other_opt'] ) {
1302 1730 return;
@@ -1302,13 +1730,15 @@
1302 1730 return;
1303 1731 }
1304 1732
1305 1733 $classes = array( 'frm_other_input' );
1306 - if ( ! $args['checked'] || trim( $args['checked'] ) == '' ) {
1307 - // hide the field if the other option is not selected
1734 +
1735 + if ( ! $args['checked'] || trim( $args['checked'] ) === '' ) {
1736 + // Hide the field if the other option is not selected
1308 1737 $classes[] = 'frm_pos_none';
1309 1738 }
1310 - if ( $args['field']['type'] == 'select' && $args['field']['multiple'] ) {
1739 +
1740 + if ( $args['field']['type'] === 'select' && ! empty( $args['field']['multiple'] ) ) {
1311 1741 $classes[] = 'frm_other_full';
1312 1742 }
1313 1743
1314 1744 // Set up HTML ID for Other field
@@ -1313,10 +1743,11 @@
1313 1743
1314 1744 // Set up HTML ID for Other field
1315 1745 $other_id = self::get_other_field_html_id( $args['field']['type'], $args['html_id'], $args['opt_key'] );
1316 1746
1317 - $label = isset( $args['opt_label'] ) ? $args['opt_label'] : $args['field']['name'];
1747 + $label = $args['opt_label'] ?? $args['field']['name'];
1318 1748
1749 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
1319 1750 echo '<label for="' . esc_attr( $other_id ) . '" class="frm_screen_reader frm_hidden">' .
1320 1751 esc_html( $label ) .
1321 1752 '</label>' .
1322 1753 '<input type="text" id="' . esc_attr( $other_id ) . '" class="' . esc_attr( implode( ' ', $classes ) ) . '" ' .
@@ -1321,8 +1752,9 @@
1321 1752 '</label>' .
1322 1753 '<input type="text" id="' . esc_attr( $other_id ) . '" class="' . esc_attr( implode( ' ', $classes ) ) . '" ' .
1323 1754 ( $args['read_only'] ? ' readonly="readonly" disabled="disabled"' : '' ) .
1324 1755 ' name="' . esc_attr( $args['name'] ) . '" value="' . esc_attr( $args['value'] ) . '" />';
1756 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
1325 1757 }
1326 1758
1327 1759 /**
1328 1760 * Get the HTML id for an "Other" text field
@@ -1329,19 +1761,19 @@
1329 1761 * Note: This does not affect fields in repeating sections
1330 1762 *
1331 1763 * @since 2.0.08
1332 1764 *
1333 - * @param string $type - field type
1334 - * @param string $html_id
1335 - * @param string|boolean $opt_key
1765 + * @param string $type Field type.
1766 + * @param string $html_id
1767 + * @param bool|string $opt_key
1336 1768 *
1337 - * @return string $other_id
1769 + * @return string Other ID.
1338 1770 */
1339 1771 public static function get_other_field_html_id( $type, $html_id, $opt_key = false ) {
1340 1772 $other_id = $html_id;
1341 1773
1342 1774 // If hidden radio field, add an opt key of 0
1343 - if ( $type == 'radio' && $opt_key === false ) {
1775 + if ( $type === 'radio' && $opt_key === false ) {
1344 1776 $opt_key = 0;
1345 1777 }
1346 1778
1347 1779 if ( $opt_key !== false ) {
@@ -1347,17 +1779,21 @@
1347 1779 if ( $opt_key !== false ) {
1348 1780 $other_id .= '-' . $opt_key;
1349 1781 }
1350 1782
1351 - $other_id .= '-otext';
1352 -
1353 - return $other_id;
1783 + return $other_id . '-otext';
1354 1784 }
1355 1785
1786 + /**
1787 + * @param array|string $val
1788 + *
1789 + * @return array|string
1790 + */
1356 1791 public static function switch_field_ids( $val ) {
1357 1792 global $frm_duplicate_ids;
1358 1793 $replace = array();
1359 1794 $replace_with = array();
1795 +
1360 1796 foreach ( (array) $frm_duplicate_ids as $old => $new ) {
1361 1797 $replace[] = '[if ' . $old . ']';
1362 1798 $replace_with[] = '[if ' . $new . ']';
1363 1799 $replace[] = '[if ' . $old . ' ';
@@ -1379,19 +1815,31 @@
1379 1815 $replace[] = 'field_id="' . $old . '"';
1380 1816 $replace_with[] = 'field_id="' . $new . '"';
1381 1817 $replace[] = 'field_id=\"' . $old . '\"';
1382 1818 $replace_with[] = 'field_id=\"' . $new . '\"';
1819 + // This covers conditional logic.
1820 + $replace[] = '_field":"' . $old . '","';
1821 + $replace_with[] = '_field":"' . $new . '","';
1383 1822 unset( $old, $new );
1823 + }//end foreach
1824 +
1825 + if ( ! is_array( $val ) ) {
1826 + return str_replace( $replace, $replace_with, $val );
1384 1827 }
1385 - if ( is_array( $val ) ) {
1386 - foreach ( $val as $k => $v ) {
1387 - if ( is_string( $v ) ) {
1388 - $val[ $k ] = str_replace( $replace, $replace_with, $v );
1389 - unset( $k, $v );
1390 - }
1828 +
1829 + foreach ( $val as $k => $v ) {
1830 + if ( ! is_string( $v ) ) {
1831 + continue;
1391 1832 }
1392 - } else {
1393 - $val = str_replace( $replace, $replace_with, $val );
1833 +
1834 + if ( 'custom_html' === $k ) {
1835 + $val[ $k ] = self::switch_ids_except_strings( $replace, $replace_with, array( '[if description]', '[description]', '[/if description]' ), $v );
1836 + unset( $k, $v );
1837 + continue;
1838 + }
1839 +
1840 + $val[ $k ] = str_replace( $replace, $replace_with, $v );
1841 + unset( $k, $v );
1394 1842 }
1395 1843
1396 1844 return $val;
1397 1845 }
@@ -1396,17 +1844,48 @@
1396 1844 return $val;
1397 1845 }
1398 1846
1399 1847 /**
1848 + * Removes exception strings from replacement arrays and replaces the rest in the provided value string.
1849 + *
1850 + * @since 6.14
1851 + *
1852 + * @param array $replace Values to be replaced.
1853 + * @param array $replace_with Replacement values.
1854 + * @param array $exceptions Array of strings to skip.
1855 + * @param string $value Value to be updated.
1856 + *
1857 + * @return string
1858 + */
1859 + private static function switch_ids_except_strings( $replace, $replace_with, $exceptions, $value ) {
1860 + foreach ( $exceptions as $exception ) {
1861 + $index = array_search( $exception, $replace, true );
1862 +
1863 + if ( false === $index ) {
1864 + continue;
1865 + }
1866 + unset( $replace[ $index ] );
1867 + unset( $replace_with[ $index ] );
1868 + }
1869 +
1870 + return str_replace( $replace, $replace_with, $value );
1871 + }
1872 +
1873 + /**
1400 1874 * @since 4.0
1875 + *
1876 + * @return void
1401 1877 */
1402 1878 public static function bulk_options_overlay() {
1403 1879 $prepop = array();
1404 1880 self::get_bulk_prefilled_opts( $prepop, true );
1405 1881
1406 - include( FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/bulk-options-overlay.php' );
1882 + include FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/bulk-options-overlay.php';
1407 1883 }
1408 1884
1885 + /**
1886 + * @return array
1887 + */
1409 1888 public static function get_us_states() {
1410 1889 $states = array(
1411 1890 'AL' => 'Alabama',
1412 1891 'AK' => 'Alaska',
@@ -1463,8 +1942,11 @@
1463 1942
1464 1943 return apply_filters( 'frm_us_states', $states );
1465 1944 }
1466 1945
1946 + /**
1947 + * @return array
1948 + */
1467 1949 public static function get_countries() {
1468 1950 $countries = array(
1469 1951 __( 'Afghanistan', 'formidable' ),
1470 1952 __( 'Aland Islands', 'formidable' ),
@@ -1729,12 +2211,15 @@
1729 2211 * @since 5.0.04 Add `$include_class` param.
1730 2212 *
1731 2213 * @param array $prepop Bulk options.
1732 2214 * @param array|false $include_class Include the class in the bulk options.
2215 + *
2216 + * @return void
1733 2217 */
1734 2218 public static function get_bulk_prefilled_opts( array &$prepop, $include_class = false ) {
1735 2219 // Countries.
1736 2220 $countries = self::get_countries();
2221 +
1737 2222 if ( $include_class ) {
1738 2223 $countries['class'] = 'frm-countries-opts';
1739 2224 }
1740 2225
@@ -1743,8 +2228,9 @@
1743 2228 // State abv.
1744 2229 $states = self::get_us_states();
1745 2230 $state_abv = array_keys( $states );
1746 2231 sort( $state_abv );
2232 +
1747 2233 if ( $include_class ) {
1748 2234 $state_abv['class'] = 'frm-state-abv-opts';
1749 2235 }
1750 2236
@@ -1752,8 +2238,9 @@
1752 2238
1753 2239 // States.
1754 2240 $states = array_values( $states );
1755 2241 sort( $states );
2242 +
1756 2243 if ( $include_class ) {
1757 2244 $states['class'] = 'frm-states-opts';
1758 2245 }
1759 2246
@@ -1770,8 +2257,9 @@
1770 2257 __( '55-64', 'formidable' ),
1771 2258 __( '65 or Above', 'formidable' ),
1772 2259 __( 'Prefer Not to Answer', 'formidable' ),
1773 2260 );
2261 +
1774 2262 if ( $include_class ) {
1775 2263 $ages['class'] = 'frm-age-opts';
1776 2264 }
1777 2265
@@ -1785,8 +2273,9 @@
1785 2273 __( 'Satisfied', 'formidable' ),
1786 2274 __( 'Very Satisfied', 'formidable' ),
1787 2275 __( 'N/A', 'formidable' ),
1788 2276 );
2277 +
1789 2278 if ( $include_class ) {
1790 2279 $satisfaction['class'] = 'frm-satisfaction-opts';
1791 2280 }
1792 2281
@@ -1800,8 +2289,9 @@
1800 2289 __( 'Important', 'formidable' ),
1801 2290 __( 'Very Important', 'formidable' ),
1802 2291 __( 'N/A', 'formidable' ),
1803 2292 );
2293 +
1804 2294 if ( $include_class ) {
1805 2295 $importance['class'] = 'frm-importance-opts';
1806 2296 }
1807 2297
@@ -1815,8 +2305,9 @@
1815 2305 __( 'Agree', 'formidable' ),
1816 2306 __( 'Strongly Agree', 'formidable' ),
1817 2307 __( 'N/A', 'formidable' ),
1818 2308 );
2309 +
1819 2310 if ( $include_class ) {
1820 2311 $agreement['class'] = 'frm-agreement-opts';
1821 2312 }
1822 2313
@@ -1830,8 +2321,9 @@
1830 2321 __( 'Likely', 'formidable' ),
1831 2322 __( 'Extremely Likely', 'formidable' ),
1832 2323 __( 'N/A', 'formidable' ),
1833 2324 );
2325 +
1834 2326 if ( $include_class ) {
1835 2327 $likely['class'] = 'frm-likely-opts';
1836 2328 }
1837 2329
@@ -1844,10 +2336,12 @@
1844 2336 * Display a field value selector
1845 2337 *
1846 2338 * @since 2.03.05
1847 2339 *
1848 - * @param int $selector_field_id
2340 + * @param int $selector_field_id
1849 2341 * @param array $selector_args
2342 + *
2343 + * @return void
1850 2344 */
1851 2345 public static function display_field_value_selector( $selector_field_id, $selector_args ) {
1852 2346 $field_value_selector = FrmFieldFactory::create_field_value_selector( $selector_field_id, $selector_args );
1853 2347 $field_value_selector->display();
@@ -1862,9 +2356,9 @@
1862 2356 *
1863 2357 * @return array
1864 2358 */
1865 2359 public static function convert_field_object_to_flat_array( $field ) {
1866 - $field_options = $field->field_options;
2360 + $field_options = is_array( $field->field_options ) ? $field->field_options : array();
1867 2361 $field_array = get_object_vars( $field );
1868 2362 unset( $field_array['field_options'] );
1869 2363
1870 2364 return $field_array + $field_options;
@@ -1870,11 +2364,43 @@
1870 2364 return $field_array + $field_options;
1871 2365 }
1872 2366
1873 2367 /**
2368 + * Shows add field link.
2369 + *
2370 + * @since 6.30
2371 + *
2372 + * @param array $field_type See file `classes/views/frm-forms/add_field_links.php`.
2373 + *
2374 + * @return void
2375 + */
2376 + public static function show_add_field_link( $field_type ) {
2377 + $field_label = FrmFormsHelper::get_field_link_name( $field_type );
2378 + $classes = 'frmbutton frm6 frm_t' . $field_type['key'];
2379 +
2380 + if ( ! empty( $field_type['hide'] ) ) {
2381 + $classes .= ' frm_hidden';
2382 + }
2383 + ?>
2384 +<li class="<?php echo esc_attr( $classes ); ?>" id="<?php echo esc_attr( $field_type['key'] ); ?>">
2385 + <a href="#" class="frm_add_field" title="<?php echo esc_attr( $field_label ); ?>" role="button" aria-label="<?php echo esc_attr( $field_label ); ?>">
2386 + <?php FrmAppHelper::icon_by_class( FrmFormsHelper::get_field_link_icon( $field_type ) ); ?>
2387 + <span><?php echo esc_html( $field_label ); ?></span>
2388 + <?php
2389 + if ( 'credit_card' === $field_type['key'] && ! FrmTransLiteAppHelper::payments_table_exists() ) {
2390 + FrmAppHelper::show_pill_text();
2391 + }
2392 + ?>
2393 + </a>
2394 +</li>
2395 + <?php
2396 + }
2397 +
2398 + /**
1874 2399 * @since 4.04
1875 2400 *
1876 2401 * @param array $args
2402 + *
1877 2403 * @return void
1878 2404 */
1879 2405 public static function show_add_field_buttons( $args ) {
1880 2406 $field_key = $args['field_key'];
@@ -1882,34 +2408,32 @@
1882 2408 $field_label = FrmAppHelper::icon_by_class( FrmFormsHelper::get_field_link_icon( $field_type ), array( 'echo' => false ) );
1883 2409 $field_name = FrmFormsHelper::get_field_link_name( $field_type );
1884 2410 $field_label .= ' <span>' . $field_name . '</span>';
1885 2411
2412 + if ( ! empty( $field_type['is_new'] ) ) {
2413 + ob_start();
2414 + FrmAppHelper::show_pill_text();
2415 + $field_label .= ob_get_clean();
2416 + }
2417 +
1886 2418 // If the individual field isn't allowed, disable it.
1887 - $run_filter = true;
1888 - $single_no_allow = ' ';
1889 - $install_data = '';
1890 - $requires = '';
1891 - $link = isset( $field_type['link'] ) ? esc_url_raw( $field_type['link'] ) : '';
1892 - $has_show_upgrade_class = strpos( $field_type['icon'], ' frm_show_upgrade' );
1893 - $show_upgrade = $has_show_upgrade_class || false !== strpos( $args['no_allow_class'], 'frm_show_upgrade' );
2419 + $link = isset( $field_type['link'] ) ? esc_url_raw( $field_type['link'] ) : '';
1894 2420
1895 - if ( $has_show_upgrade_class ) {
1896 - $single_no_allow .= 'frm_show_upgrade';
1897 - $field_type['icon'] = str_replace( ' frm_show_upgrade', '', $field_type['icon'] );
1898 - $run_filter = false;
1899 - if ( isset( $field_type['addon'] ) ) {
1900 - $upgrading = FrmAddonsController::install_link( $field_type['addon'] );
1901 - if ( isset( $upgrading['url'] ) ) {
1902 - $install_data = json_encode( $upgrading );
1903 - }
1904 - $requires = FrmFormsHelper::get_plan_required( $upgrading );
1905 - } elseif ( isset( $field_type['require'] ) ) {
1906 - $requires = $field_type['require'];
1907 - }
1908 - }
2421 + list(
2422 + $run_filter,
2423 + $single_no_allow,
2424 + $install_data,
2425 + $requires,
2426 + $has_show_upgrade_class,
2427 + $has_show_update_class,
2428 + $upgrading,
2429 + $update_addon_name
2430 + ) = self::get_field_upgrade_state( $field_type );
1909 2431
2432 + $show_upgrade = $has_show_upgrade_class || $has_show_update_class || str_contains( $args['no_allow_class'], 'frm_show_upgrade' );
1910 2433 $upgrade_label = '';
1911 2434 $upgrade_message = '';
2435 +
1912 2436 if ( $show_upgrade ) {
1913 2437 /* translators: %s: Field name */
1914 2438 $upgrade_label = sprintf( esc_html__( '%s fields', 'formidable' ), $field_name );
1915 2439
@@ -1928,11 +2452,45 @@
1928 2452 'data-content' => $field_key,
1929 2453 'data-requires' => $requires,
1930 2454 );
1931 2455
1932 - if ( $upgrade_message ) {
2456 + if ( ! empty( $field_type['hide'] ) ) {
2457 + $li_params['class'] .= ' frm_hidden';
2458 + }
2459 +
2460 + if ( ! $show_upgrade && ! empty( $field_type['limit'] ) ) {
2461 + $li_params = self::update_params_with_limit_data( $li_params, $args['id'], $field_key, $field_type['limit'] );
2462 + }
2463 +
2464 + if ( ! empty( $field_type['upsell_image'] ) ) {
2465 + $li_params['data-upsell-image'] = $field_type['upsell_image'];
2466 + }
2467 +
2468 + if ( ! empty( $field_type['learn-more'] ) ) {
2469 + $li_params['data-learn-more'] = FrmAppHelper::get_doc_url(
2470 + $field_type['learn-more'],
2471 + 'form-builder-add-fields',
2472 + str_contains( $field_type['learn-more'], '/' )
2473 + );
2474 + }
2475 +
2476 + if ( $has_show_update_class ) {
2477 + $li_params['data-message'] = sprintf(
2478 + // translators: %1$s: Add-on name, %2$s: Field type name.
2479 + esc_html__( 'You need a newer version of %1$s for %2$s fields.', 'formidable' ),
2480 + $update_addon_name,
2481 + $field_name
2482 + );
2483 + } elseif ( isset( $upgrading['url'] ) ) {
2484 + $li_params['data-message'] = sprintf(
2485 + // translators: %s: Field name
2486 + esc_html__( 'You already have access to %s fields, you\'ll just need to activate to start using them.', 'formidable' ),
2487 + $field_name
2488 + );
2489 + } elseif ( $upgrade_message ) {
1933 2490 $li_params['data-message'] = $upgrade_message;
1934 2491 }
2492 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
1935 2493 ?>
1936 2494 <li <?php FrmAppHelper::array_to_html_params( $li_params, true ); ?>>
1937 2495 <?php
1938 2496 if ( $run_filter ) {
@@ -1937,26 +2495,130 @@
1937 2495 <?php
1938 2496 if ( $run_filter ) {
1939 2497 $field_label = apply_filters( 'frmpro_field_links', $field_label, $args['id'], $field_key );
1940 2498 }
1941 - echo FrmAppHelper::kses( $field_label, array( 'a', 'i', 'span', 'use', 'svg' ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
2499 +
2500 + FrmAppHelper::kses_echo( $field_label, array( 'a', 'i', 'span', 'use', 'svg' ) );
1942 2501 ?>
1943 2502 </li>
1944 2503 <?php
2504 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
1945 2505 }
1946 2506
1947 2507 /**
2508 + * Parse the upgrade and update flags from a field type's icon class.
2509 + *
2510 + * @since 6.29
2511 + *
2512 + * @param array $field_type Field type configuration, modified in place to strip icon flag classes.
2513 + *
2514 + * @return array Upgrade and update state values for the field button, in positional order.
2515 + */
2516 + private static function get_field_upgrade_state( &$field_type ) {
2517 + $single_no_allow = ' ';
2518 + $run_filter = true;
2519 + $has_show_upgrade_class = false;
2520 + $has_show_update_class = false;
2521 + $install_data = '';
2522 + $requires = '';
2523 + $upgrading = array();
2524 + $update_addon_name = '';
2525 +
2526 + if ( isset( $field_type['icon'] ) ) {
2527 + $has_show_upgrade_class = str_contains( $field_type['icon'], ' frm_show_upgrade' );
2528 + $has_show_update_class = str_contains( $field_type['icon'], ' frm_show_update' );
2529 + }
2530 +
2531 + if ( $has_show_upgrade_class ) {
2532 + $single_no_allow .= 'frm_show_upgrade';
2533 + $field_type['icon'] = str_replace( ' frm_show_upgrade', '', $field_type['icon'] );
2534 + $run_filter = false;
2535 +
2536 + if ( isset( $field_type['addon'] ) ) {
2537 + $upgrading = FrmAddonsController::install_link( $field_type['addon'] );
2538 +
2539 + if ( isset( $upgrading['url'] ) ) {
2540 + $install_data = json_encode( $upgrading );
2541 + }
2542 +
2543 + $requires = FrmFormsHelper::get_plan_required( $upgrading );
2544 + } elseif ( isset( $field_type['require'] ) ) {
2545 + $requires = $field_type['require'];
2546 + }
2547 + }
2548 +
2549 + if ( $has_show_update_class ) {
2550 + $single_no_allow .= ' frm_show_update';
2551 + $field_type['icon'] = str_replace( ' frm_show_update', '', $field_type['icon'] );
2552 + $run_filter = false;
2553 + $addon_slug = $field_type['addon'] ?? 'pro';
2554 +
2555 + if ( 'pro' === $addon_slug ) {
2556 + $update_addon_name = __( 'Formidable Pro', 'formidable' );
2557 + } else {
2558 + $addon_data = FrmAddonsController::get_addon( $addon_slug );
2559 + $update_addon_name = $addon_data && ! empty( $addon_data['title'] ) ? $addon_data['title'] : ucfirst( $addon_slug );
2560 + }
2561 +
2562 + $install_data = FrmAddonsController::get_update_install_data( $addon_slug );
2563 + }
2564 +
2565 + return array(
2566 + $run_filter,
2567 + $single_no_allow,
2568 + $install_data,
2569 + $requires,
2570 + $has_show_upgrade_class,
2571 + $has_show_update_class,
2572 + $upgrading,
2573 + $update_addon_name,
2574 + );
2575 + }
2576 +
2577 + /**
2578 + * Updates the params with limit data (the data-limit attribute, and possibly the frm_at_limit class).
2579 + * Some field types are limited to a certain number per form, including coupon fields.
2580 + *
2581 + * @since 6.27
2582 + *
2583 + * @param array $li_params The params.
2584 + * @param int $form_id The form ID.
2585 + * @param string $field_type The field type.
2586 + * @param int $limit The limit.
2587 + *
2588 + * @return array The updated params.
2589 + */
2590 + private static function update_params_with_limit_data( $li_params, $form_id, $field_type, $limit ) {
2591 + $fields_in_form = FrmDb::get_count(
2592 + 'frm_fields',
2593 + array(
2594 + 'form_id' => $form_id,
2595 + 'type' => $field_type,
2596 + )
2597 + );
2598 +
2599 + if ( $fields_in_form >= $limit ) {
2600 + $li_params['class'] .= ' frm_at_limit';
2601 + }
2602 +
2603 + $li_params['data-limit'] = $limit;
2604 +
2605 + return $li_params;
2606 + }
2607 +
2608 + /**
1948 2609 * Shows Display format option.
1949 2610 *
1950 2611 * @since 5.0.04
1951 2612 *
1952 2613 * @param array $field Field data.
2614 + *
2615 + * @return void
1953 2616 */
1954 2617 public static function show_radio_display_format( $field ) {
1955 2618 $options = self::get_display_format_options( $field );
2619 + $args = self::get_display_format_args( $field, $options );
1956 2620
1957 - $args = self::get_display_format_args( $field, $options );
1958 -
1959 2621 include FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/radio-display-format.php';
1960 2622 }
1961 2623
1962 2624 /**
@@ -1970,10 +2632,10 @@
1970 2632 */
1971 2633 public static function get_display_format_options( $field ) {
1972 2634 $options = array(
1973 2635 '0' => array(
1974 - 'text' => __( 'Simple', 'formidable' ),
1975 - 'svg' => 'frm_simple_radio',
2636 + 'text' => __( 'Simple', 'formidable' ),
2637 + 'svg' => 'frm_simple_radio',
1976 2638 ),
1977 2639 '1' => array(
1978 2640 'text' => __( 'Images', 'formidable' ),
1979 2641 'svg' => 'frm_image_as_option',
@@ -1978,9 +2640,9 @@
1978 2640 'text' => __( 'Images', 'formidable' ),
1979 2641 'svg' => 'frm_image_as_option',
1980 2642 'addon' => 'pro',
1981 2643 'upgrade' => __( 'Image Options', 'formidable' ),
1982 - 'message' => __( 'Show images instead of radio buttons or check boxes. This is ideal for polls, surveys, segmenting questionnaires and more.', 'formidable' ) . '<img src="' . esc_url( FrmAppHelper::plugin_url() ) . '/images/image-options.png" />',
2644 + 'message' => __( 'Show images instead of radio buttons or check boxes. This is ideal for polls, surveys, segmenting questionnaires and more.', 'formidable' ) . '<img src="' . esc_url( FrmAppHelper::plugin_url() ) . '/images/image-options.png" />', // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
1983 2645 'content' => 'image-options',
1984 2646 ),
1985 2647 'buttons' => array(
1986 2648 'text' => __( 'Buttons', 'formidable' ),
@@ -1991,8 +2653,12 @@
1991 2653 'content' => 'button-options',
1992 2654 ),
1993 2655 );
1994 2656
2657 + if ( 'product' === FrmField::get_field_type( $field ) ) {
2658 + unset( $options['buttons'] );
2659 + }
2660 +
1995 2661 /**
1996 2662 * Allows modifying the options of Display format setting of Radio field.
1997 2663 *
1998 2664 * @since 5.0.04
@@ -1999,11 +2665,9 @@
1999 2665 *
2000 2666 * @param array $options Options.
2001 2667 * @param array $field
2002 2668 */
2003 - $options = apply_filters( 'frm_' . $field['type'] . '_display_format_options', $options, $field );
2004 -
2005 - return $options;
2669 + return apply_filters( 'frm_' . $field['type'] . '_display_format_options', $options, $field );
2006 2670 }
2007 2671
2008 2672 /**
2009 2673 * Gets display format arguments to pass to the images_dropdown() method.
@@ -2011,8 +2675,9 @@
2011 2675 * @since 5.0.04
2012 2676 *
2013 2677 * @param array $field Field data.
2014 2678 * @param array $options Options array.
2679 + *
2015 2680 * @return array
2016 2681 */
2017 2682 public static function get_display_format_args( $field, $options ) {
2018 2683 $args = array(
@@ -2038,8 +2703,13 @@
2038 2703 }
2039 2704
2040 2705 /**
2041 2706 * @since 5.0.04
2707 + *
2708 + * @param array $options Options.
2709 + * @param array $args Arguments.
2710 + *
2711 + * @return void
2042 2712 */
2043 2713 private static function fill_image_setting_options( $options, &$args ) {
2044 2714 foreach ( $options as $key => $option ) {
2045 2715 $args['options'][ $key ] = $option;
@@ -2049,8 +2719,9 @@
2049 2719 }
2050 2720
2051 2721 unset( $args['options'][ $key ]['addon'] );
2052 2722 $fill = array( 'upgrade', 'message', 'content' );
2723 +
2053 2724 foreach ( $fill as $f ) {
2054 2725 unset( $args['options'][ $key ][ $f ], $f );
2055 2726 }
2056 2727 }
@@ -2058,8 +2729,10 @@
2058 2729
2059 2730 /**
2060 2731 * @since 5.0.04
2061 2732 *
2733 + * @param array $option Option.
2734 + *
2062 2735 * @return array
2063 2736 */
2064 2737 private static function fill_image_setting_addon_link( $option ) {
2065 2738 $custom_attrs = array(
@@ -2070,8 +2743,9 @@
2070 2743 // translators: Add-on name.
2071 2744 $custom_attrs['data-upgrade'] = sprintf( __( 'Formidable %s', 'formidable' ), ucwords( $option['addon'] ) );
2072 2745
2073 2746 $fill = array( 'upgrade', 'message', 'content' );
2747 +
2074 2748 foreach ( $fill as $f ) {
2075 2749 if ( isset( $option[ $f ] ) ) {
2076 2750 $custom_attrs[ 'data-' . $f ] = $option[ $f ];
2077 2751 }
@@ -2081,15 +2755,10 @@
2081 2755 return $custom_attrs;
2082 2756 }
2083 2757
2084 2758 $upgrading = FrmAddonsController::install_link( $option['addon'] );
2085 - if ( isset( $upgrading['url'] ) ) {
2086 - $install_data = wp_json_encode( $upgrading );
2087 - } else {
2088 - $install_data = '';
2089 - }
2090 2759
2091 - $custom_attrs['data-oneclick'] = $install_data;
2760 + $custom_attrs['data-oneclick'] = isset( $upgrading['url'] ) ? wp_json_encode( $upgrading ) : '';
2092 2761 $custom_attrs['data-requires'] = FrmFormsHelper::get_plan_required( $upgrading );
2093 2762
2094 2763 return $custom_attrs;
2095 2764 }
@@ -2095,14 +2764,16 @@
2095 2764 }
2096 2765
2097 2766 /**
2098 2767 * Maybe adjust a field value based on type.
2099 - * Some types require unserializing an array (@see self::field_type_requires_unserialize).
2768 + * Some types require unserializing an array.
2769 + * These types are defined by a array_allowed property on their field model class.
2100 2770 *
2101 2771 * @since 6.2
2102 2772 *
2103 2773 * @param mixed $value
2104 2774 * @param string $field_type
2775 + *
2105 2776 * @return void
2106 2777 */
2107 2778 public static function prepare_field_value( &$value, $field_type ) {
2108 2779 $field_object = FrmFieldFactory::get_field_type( $field_type );
@@ -2109,95 +2780,221 @@
2109 2780 $value = $field_object->maybe_decode_value( $value );
2110 2781 }
2111 2782
2112 2783 /**
2113 - * @deprecated 4.0
2784 + * @since 6.8
2785 + *
2786 + * @param int|string $form_id
2787 + * @param array $field_ids If this is not empty, the results will be filtered by field id.
2788 + *
2789 + * @return array
2114 2790 */
2115 - public static function show_icon_link_js( $atts ) {
2116 - _deprecated_function( __METHOD__, '4.0' );
2117 - $atts['icon'] .= $atts['is_selected'] ? ' ' : ' frm_inactive_icon ';
2118 - if ( isset( $atts['has_default'] ) && ! $atts['has_default'] ) {
2119 - $atts['icon'] .= 'frm_hidden ';
2791 + public static function get_draft_field_results( $form_id, $field_ids = array() ) {
2792 + if ( FrmAppHelper::pro_is_installed() ) {
2793 + $child_form_ids = FrmDb::get_col( 'frm_forms', array( 'parent_form_id' => $form_id ) );
2794 + $form_ids = array_merge( array( $form_id ), $child_form_ids );
2795 + } else {
2796 + $form_ids = array( $form_id );
2120 2797 }
2121 - echo '<a href="javascript:void(0)" class="frm_bstooltip ' . esc_attr( $atts['icon'] ) . 'frm_default_val_icons frm_action_icon frm_icon_font" title="' . esc_attr( $atts['message'] ) . '"></a>';
2798 +
2799 + $where = array(
2800 + 'form_id' => $form_ids,
2801 + // Do a soft check for fields that look like drafts only.
2802 + 'field_options LIKE' => 's:5:"draft";i:1;',
2803 + );
2804 +
2805 + if ( $field_ids ) {
2806 + $where['id'] = $field_ids;
2807 + }
2808 +
2809 + $rows = FrmDb::get_results( 'frm_fields', $where, 'id, field_options' );
2810 +
2811 + return array_filter(
2812 + $rows,
2813 + function ( $row ) {
2814 + FrmAppHelper::unserialize_or_decode( $row->field_options );
2815 + return is_array( $row->field_options ) && ! empty( $row->field_options['draft'] );
2816 + }
2817 + );
2122 2818 }
2123 2819
2124 2820 /**
2125 - * @deprecated 4.0
2821 + * This is called when loading the form builder.
2822 + * Any unsaved draft fields get added to a hidden draft_fields input on load.
2823 + *
2824 + * @since 6.8
2825 + *
2826 + * @param int|string $form_id
2827 + *
2828 + * @return array
2126 2829 */
2127 - public static function show_default_blank_js( $is_selected, $has_default_value = true ) {
2128 - _deprecated_function( __METHOD__, '4.0' );
2830 + public static function get_all_draft_field_ids( $form_id ) {
2831 + $draft_field_rows = self::get_draft_field_results( $form_id );
2832 + return wp_list_pluck( $draft_field_rows, 'id' );
2129 2833 }
2130 2834
2131 2835 /**
2132 - * @deprecated 4.0
2836 + * Render AI generate options button.
2837 + *
2838 + * @since 6.24
2839 + *
2840 + * @param array $args Field arguments.
2841 + * @param bool $should_hide_bulk_edit Whether to hide bulk edit.
2842 + *
2843 + * @return void
2133 2844 */
2134 - public static function clear_on_focus_html( $field, $display, $id = '' ) {
2135 - _deprecated_function( __METHOD__, '4.0' );
2136 - }
2845 + public static function render_ai_generate_options_button( $args, $should_hide_bulk_edit = false ) {
2846 + $attributes = array(
2847 + 'type' => 'button',
2848 + );
2137 2849
2138 - /**
2139 - * @deprecated 4.0
2140 - */
2141 - public static function show_onfocus_js( $is_selected, $has_default_value = true ) {
2142 - _deprecated_function( __METHOD__, '4.0' );
2143 - }
2850 + $attributes['class'] = ! empty( $args['class'] ) ? $args['class'] : self::get_ai_generate_options_button_class();
2144 2851
2145 - /**
2146 - * @deprecated 3.0
2147 - * @codeCoverageIgnore
2148 - */
2149 - public static function display_recaptcha() {
2150 - _deprecated_function( __FUNCTION__, '3.0', 'FrmFieldCaptcha::field_input' );
2852 + if ( $should_hide_bulk_edit ) {
2853 + $attributes['class'] .= ' frm-force-hidden';
2854 + }
2855 +
2856 + $data = FrmAppHelper::get_upgrade_data_params(
2857 + 'ai',
2858 + array(
2859 + 'requires' => 'Business',
2860 + 'upgrade' => $args['upgrade_text'] ?? __( 'Generate options with AI', 'formidable' ),
2861 + 'medium' => 'builder',
2862 + 'content' => 'generate-options-with-ai',
2863 + ),
2864 + true
2865 + );
2866 +
2867 + if ( in_array( FrmAddonsController::license_type(), array( 'elite', 'business' ), true ) && 'active' === $data['plugin-status'] ) {
2868 + // Backwards compatibility "@since 6.24".
2869 + if ( method_exists( 'FrmAIAppController', 'get_ai_generated_options_summary' ) ) {
2870 + $attributes['class'] .= ' frm-ai-generate-options-modal-trigger';
2871 + $attributes['data-fid'] = $args['likert_id'] ?? $args['field']['id'];
2872 + } else {
2873 + $data = array(
2874 + 'modal-title' => __( 'Generate options with AI', 'formidable' ),
2875 + 'modal-content' => __( 'Update the Formidable AI add-on to the last version to use this feature.', 'formidable' ),
2876 + );
2877 + }
2878 + }
2879 +
2880 + if ( empty( $attributes['data-fid'] ) ) {
2881 + unset( $data['plugin-status'] );
2882 +
2883 + foreach ( $data as $key => $value ) {
2884 + $attributes[ 'data-' . $key ] = $value;
2885 + }
2886 + }
2887 +
2888 + include FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/generate-options-with-ai.php';
2151 2889 }
2152 2890
2153 2891 /**
2154 - * @deprecated 3.0
2155 - * @codeCoverageIgnore
2892 + * Get AI generate options button class.
2893 + *
2894 + * @since 6.24
2895 + *
2896 + * @return string Button class.
2156 2897 */
2157 - public static function remove_inline_conditions( $no_vars, $code, $replace_with, &$html ) {
2158 - FrmDeprecated::remove_inline_conditions( $no_vars, $code, $replace_with, $html );
2898 + private static function get_ai_generate_options_button_class() {
2899 + return implode(
2900 + ' ',
2901 + array(
2902 + 'frm_form_field',
2903 + 'frm6',
2904 + 'frm6_followed',
2905 + 'frm-h-stack',
2906 + 'button',
2907 + 'frm-button-secondary',
2908 + 'frm-button-gradient',
2909 + 'frm-rounded-6',
2910 + 'frm-max-w-fit',
2911 + 'frm-font-normal',
2912 + 'frm-py-2xs',
2913 + 'frm-px-xs',
2914 + 'frm-mt-xs',
2915 + 'frm-mb-12',
2916 + )
2917 + );
2159 2918 }
2160 2919
2161 2920 /**
2162 - * @deprecated 3.0
2163 - * @codeCoverageIgnore
2921 + * Checks if the field choice should be hidden due to choice limit being reached.
2922 + *
2923 + * @since 6.28
2924 + *
2925 + * @param string $choice_key
2926 + * @param array $field
2927 + *
2928 + * @return bool
2164 2929 */
2165 - public static function get_shortcode_tag( $shortcodes, $short_key, $args ) {
2166 - return FrmDeprecated::get_shortcode_tag( $shortcodes, $short_key, $args );
2930 + public static function should_hide_field_choice( $choice_key, $field ) {
2931 + /**
2932 + * @since 6.28
2933 + *
2934 + * @param bool $hide_field_choice
2935 + * @param string $choice_key
2936 + * @param array $field
2937 + */
2938 + return (bool) apply_filters( 'frm_hide_field_choice', false, $choice_key, $field );
2167 2939 }
2168 2940
2169 2941 /**
2170 - * @deprecated 3.0
2171 - * @codeCoverageIgnore
2942 + * @since 6.28
2172 2943 *
2173 - * @param string $html
2174 - * @param array $field
2175 - * @param array $errors
2176 - * @param object|false $form
2177 - * @param array $args
2944 + * @param array $field
2178 2945 *
2179 - * @return string
2946 + * @return bool
2180 2947 */
2181 - public static function replace_shortcodes( $html, $field, $errors = array(), $form = false, $args = array() ) {
2182 - return FrmDeprecated::replace_shortcodes( $html, $field, $errors, $form, $args );
2948 + public static function should_skip_rendering_choices_for_field( $field ) {
2949 + /**
2950 + * @since 6.28
2951 + *
2952 + * @param bool $skip_rendering_options_for_field
2953 + * @param array $field
2954 + */
2955 + return (bool) apply_filters( 'frm_should_skip_rendering_choices_for_field', false, $field );
2183 2956 }
2184 2957
2185 2958 /**
2186 - * @deprecated 3.0
2187 - * @codeCoverageIgnore
2959 + * Determine if 'disabled' attribute should be echoed in a field choice's HTML.
2960 + *
2961 + * @since 6.28
2962 + *
2963 + * @param string $choice_key
2964 + * @param bool $is_selected_choice
2965 + * @param array $field
2966 + *
2967 + * @return bool
2188 2968 */
2189 - public static function get_default_field_opts( $type, $field = null, $limit = false ) {
2190 - return FrmDeprecated::get_default_field_opts( $type, $field, $limit );
2969 + public static function should_disable_choice( $choice_key, $is_selected_choice, $field ) {
2970 + /**
2971 + * @since 6.28
2972 + *
2973 + * @param bool $echo_disabled_attribute
2974 + * @param string $choice_key
2975 + * @param bool $is_selected_choice
2976 + * @param array $field
2977 + */
2978 + return (bool) apply_filters( 'frm_disable_choice', false, $choice_key, $is_selected_choice, $field );
2191 2979 }
2192 2980
2193 2981 /**
2194 - * @deprecated 2.02.07 This is still referenced in the Highrise add on as of v1.06.
2195 - * @codeCoverageIgnore
2982 + * @since 6.28
2196 2983 *
2197 - * @param array $args
2198 - * @return string
2984 + * @param array $field
2985 + * @param string $choice_key
2986 + *
2987 + * @return void
2199 2988 */
2200 - public static function dropdown_categories( $args ) {
2201 - return FrmDeprecated::dropdown_categories( $args );
2989 + public static function after_choice_input( $field, $choice_key ) {
2990 + /**
2991 + * Allows adding content after checkbox, radio button, or dropdown fields.
2992 + *
2993 + * @since 6.28
2994 + *
2995 + * @param array $field The field data.
2996 + * @param string $choice_key The option key.
2997 + */
2998 + do_action( 'frm_after_choice_input', $field, $choice_key );
2202 2999 }
2203 3000 }