PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.2.1
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.2.1
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
formidable / classes / models / FrmEntryValidate.php

FrmEntryValidate.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 6.2.1, at classes/models/FrmEntryValidate.php

826 lines 24.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 die( 'You are not allowed to call this page directly.' );
4 }
5
6 class FrmEntryValidate {
7 public static function validate( $values, $exclude = false ) {
8 FrmEntry::sanitize_entry_post( $values );
9 $errors = array();
10
11 if ( ! isset( $values['form_id'] ) || ! isset( $values['item_meta'] ) ) {
12 $errors['form'] = __( 'There was a problem with your submission. Please try again.', 'formidable' );
13
14 return $errors;
15 }
16
17 if ( FrmAppHelper::is_admin() && is_user_logged_in() && ( ! isset( $values[ 'frm_submit_entry_' . $values['form_id'] ] ) || ! wp_verify_nonce( $values[ 'frm_submit_entry_' . $values['form_id'] ], 'frm_submit_entry_nonce' ) ) ) {
18 $frm_settings = FrmAppHelper::get_settings();
19 $errors['form'] = $frm_settings->admin_permission;
20 }
21
22 self::set_item_key( $values );
23
24 $posted_fields = self::get_fields_to_validate( $values, $exclude );
25
26 // Pass exclude value to validate_field function so it can be used for repeating sections
27 $args = array( 'exclude' => $exclude );
28
29 foreach ( $posted_fields as $posted_field ) {
30 self::validate_field( $posted_field, $errors, $values, $args );
31 unset( $posted_field );
32 }
33
34 if ( empty( $errors ) ) {
35 self::spam_check( $exclude, $values, $errors );
36 }
37
38 /**
39 * Allows modifying the validation errors after validating all fields.
40 *
41 * @since 5.0.04 Added `posted_fields` to the third param.
42 *
43 * @param array $errors Errors data.
44 * @param array $values Value data of the form.
45 * @param array $args Custom arguments. Contains `exclude` and `posted_fields`.
46 */
47 $errors = apply_filters( 'frm_validate_entry', $errors, $values, compact( 'exclude', 'posted_fields' ) );
48
49 return $errors;
50 }
51
52 private static function set_item_key( &$values ) {
53 if ( ! isset( $values['item_key'] ) || $values['item_key'] == '' ) {
54 global $wpdb;
55 $values['item_key'] = FrmAppHelper::get_unique_key( '', $wpdb->prefix . 'frm_items', 'item_key' );
56 $_POST['item_key'] = $values['item_key'];
57 }
58 }
59
60 private static function get_fields_to_validate( $values, $exclude ) {
61 $where = apply_filters( 'frm_posted_field_ids', array( 'fi.form_id' => $values['form_id'] ) );
62
63 // Don't get subfields
64 $where['fr.parent_form_id'] = array( null, 0 );
65
66 // Don't get excluded fields (like file upload fields in the ajax validation)
67 if ( ! empty( $exclude ) ) {
68 $where['fi.type not'] = $exclude;
69 }
70
71 $fields = FrmField::getAll( $where, 'field_order' );
72
73 /**
74 * Allows modifying fields to validate.
75 *
76 * @since 5.0.06
77 *
78 * @param array $fields List of fields.
79 * @param array $args Includes `values`, `exclude`, `where`.
80 */
81 return apply_filters( 'frm_fields_to_validate', $fields, compact( 'values', 'exclude', 'where' ) );
82 }
83
84 public static function validate_field( $posted_field, &$errors, $values, $args = array() ) {
85 $defaults = array(
86 'id' => $posted_field->id,
87 'parent_field_id' => '', // the id of the repeat or embed form
88 'key_pointer' => '', // the pointer in the posted array
89 'exclude' => array(), // exclude these field types from validation
90 );
91 $args = wp_parse_args( $args, $defaults );
92
93 if ( empty( $args['parent_field_id'] ) ) {
94 $value = isset( $values['item_meta'][ $args['id'] ] ) ? $values['item_meta'][ $args['id'] ] : '';
95 } else {
96 // value is from a nested form
97 $value = $values;
98 }
99
100 // Check for values in "Other" fields
101 FrmEntriesHelper::maybe_set_other_validation( $posted_field, $value, $args );
102
103 self::maybe_clear_value_for_default_blank_setting( $posted_field, $value );
104
105 $should_trim = is_array( $value ) && count( $value ) == 1 && isset( $value[0] ) && $posted_field->type !== 'checkbox';
106 if ( $should_trim ) {
107 $value = reset( $value );
108 }
109
110 if ( ! is_array( $value ) ) {
111 $value = trim( $value );
112 }
113
114 if ( $posted_field->required == '1' && FrmAppHelper::is_empty_value( $value ) ) {
115 $errors[ 'field' . $args['id'] ] = FrmFieldsHelper::get_error_msg( $posted_field, 'blank' );
116 } elseif ( ! isset( $_POST['item_name'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
117 self::maybe_add_item_name( $value, $posted_field );
118 }
119
120 FrmEntriesHelper::set_posted_value( $posted_field, $value, $args );
121
122 self::validate_field_types( $errors, $posted_field, $value, $args );
123
124 // Field might want to modify value before other parts of the system
125 // e.g. trim off excess values like in the case of fields with limit.
126 $value = apply_filters( 'frm_modify_posted_field_value', $value, $errors, $posted_field, $args );
127
128 if ( $value != '' ) {
129 self::validate_phone_field( $errors, $posted_field, $value, $args );
130 }
131
132 $errors = apply_filters( 'frm_validate_' . $posted_field->type . '_field_entry', $errors, $posted_field, $value, $args );
133 $errors = apply_filters( 'frm_validate_field_entry', $errors, $posted_field, $value, $args );
134 }
135
136 /**
137 * Maybe add item_name to $_POST to save it in items table.
138 *
139 * @since 5.2.02
140 *
141 * @param object $field Field object.
142 */
143 private static function maybe_add_item_name( $value, $field ) {
144 $item_name = false;
145 if ( 'name' === $field->type ) {
146 $field_obj = FrmFieldFactory::get_field_object( $field );
147 $item_name = $field_obj->get_display_value( $value );
148 } elseif ( 'text' === $field->type ) {
149 $item_name = $value;
150 }
151
152 if ( false !== $item_name ) {
153 // Item name has a max length of 255 characters so truncate it so it doesn't fail to save in the database.
154 $_POST['item_name'] = substr( $item_name, 0, 255 );
155 }
156 }
157
158 /**
159 * Set $value to an empty string if it matches its label
160 *
161 * @param object $field
162 * @param string $value
163 */
164 private static function maybe_clear_value_for_default_blank_setting( $field, &$value ) {
165 $position = FrmField::get_option( $field, 'label' );
166 if ( ! $position ) {
167 $position = FrmStylesController::get_style_val( 'position', $field->form_id );
168 }
169
170 if ( $position === 'inside' && FrmFieldsHelper::is_placeholder_field_type( $field->type ) && $value === $field->name ) {
171 $value = '';
172 }
173 }
174
175 public static function validate_field_types( &$errors, $posted_field, $value, $args ) {
176 $field_obj = FrmFieldFactory::get_field_object( $posted_field );
177 $args['value'] = $value;
178 $args['errors'] = $errors;
179
180 $new_errors = $field_obj->validate( $args );
181 if ( ! empty( $new_errors ) ) {
182 $errors = array_merge( $errors, $new_errors );
183 }
184 }
185
186 public static function validate_phone_field( &$errors, $field, $value, $args ) {
187 if ( $field->type == 'phone' || ( $field->type == 'text' && FrmField::is_option_true_in_object( $field, 'format' ) ) ) {
188
189 $pattern = self::phone_format( $field );
190
191 if ( ! preg_match( $pattern, $value ) ) {
192 $errors[ 'field' . $args['id'] ] = FrmFieldsHelper::get_error_msg( $field, 'invalid' );
193 }
194 }
195 }
196
197 public static function phone_format( $field ) {
198 if ( FrmField::is_option_empty( $field, 'format' ) ) {
199 $pattern = self::default_phone_format();
200 } else {
201 $pattern = FrmField::get_option( $field, 'format' );
202 }
203
204 $pattern = apply_filters( 'frm_phone_pattern', $pattern, $field );
205
206 // Create a regexp if format is not already a regexp
207 if ( strpos( $pattern, '^' ) !== 0 ) {
208 $pattern = self::create_regular_expression_from_format( $pattern );
209 }
210
211 $pattern = '/' . $pattern . '/';
212
213 return $pattern;
214 }
215
216 /**
217 * @since 3.01
218 */
219 private static function default_phone_format() {
220 return '^((\+\d{1,3}(-|.| )?\(?\d\)?(-| |.)?\d{1,5})|(\(?\d{2,6}\)?))(-|.| )?(\d{3,4})(-|.| )?(\d{4})(( x| ext)\d{1,5}){0,1}$';
221 }
222
223 /**
224 * Create a regular expression from a phone number format
225 *
226 * @since 2.02.02
227 *
228 * @param string $pattern
229 *
230 * @return string
231 */
232 private static function create_regular_expression_from_format( $pattern ) {
233 $pattern = preg_quote( $pattern );
234
235 // Firefox doesn't like escaped dashes or colons
236 $pattern = str_replace( array( '\-', '\:' ), array( '-', ':' ), $pattern );
237
238 // Switch generic values out for their regular expression
239 $pattern = preg_replace( '/\d/', '\d', $pattern );
240 $pattern = str_replace( 'A', '[A-Z]', $pattern );
241 $pattern = str_replace( 'a', '[a-zA-Z]', $pattern );
242 $pattern = str_replace( '*', 'w', $pattern );
243 $pattern = str_replace( '/', '\/', $pattern );
244
245 if ( strpos( $pattern, '\?' ) !== false ) {
246 $parts = explode( '\?', $pattern );
247 $pattern = '';
248 foreach ( $parts as $part ) {
249 if ( empty( $pattern ) ) {
250 $pattern .= $part;
251 } else {
252 $pattern .= '(' . $part . ')?';
253 }
254 }
255 }
256 $pattern = '^' . $pattern . '$';
257
258 return $pattern;
259 }
260
261 /**
262 * Check for spam
263 *
264 * @param boolean $exclude
265 * @param array $values
266 * @param array $errors by reference
267 */
268 public static function spam_check( $exclude, $values, &$errors ) {
269 if ( ! empty( $exclude ) || ! isset( $values['item_meta'] ) || empty( $values['item_meta'] ) || ! empty( $errors ) ) {
270 // only check spam if there are no other errors
271 return;
272 }
273
274 $antispam_check = self::is_antispam_check( $values['form_id'] );
275 if ( is_string( $antispam_check ) ) {
276 $errors['spam'] = $antispam_check;
277 } elseif ( self::is_honeypot_spam( $values ) || self::is_spam_bot() ) {
278 $errors['spam'] = __( 'Your entry appears to be spam!', 'formidable' );
279 } elseif ( self::blacklist_check( $values ) ) {
280 $errors['spam'] = __( 'Your entry appears to be blocked spam!', 'formidable' );
281 }
282
283 if ( isset( $errors['spam'] ) || self::form_is_in_progress( $values ) ) {
284 return;
285 }
286
287 if ( self::is_akismet_enabled_for_user( $values['form_id'] ) && self::is_akismet_spam( $values ) ) {
288 $errors['spam'] = __( 'Your entry appears to be spam!', 'formidable' );
289 }
290 }
291
292 /**
293 * Checks if form is in progress.
294 *
295 * @since 5.0.13
296 *
297 * @param array $values The values.
298 * @return bool
299 */
300 private static function form_is_in_progress( $values ) {
301 return FrmAppHelper::pro_is_installed() &&
302 ( isset( $values[ 'frm_page_order_' . $values['form_id'] ] ) || FrmAppHelper::get_post_param( 'frm_next_page' ) ) &&
303 FrmField::get_all_types_in_form( $values['form_id'], 'break' );
304 }
305
306 /**
307 * @param int $form_id
308 *
309 * @return bool|string
310 */
311 private static function is_antispam_check( $form_id ) {
312 $aspm = new FrmAntiSpam( $form_id );
313 return $aspm->validate();
314 }
315
316 /**
317 * @param array $values
318 * @return boolean
319 */
320 private static function is_honeypot_spam( $values ) {
321 $honeypot = new FrmHoneypot( $values['form_id'] );
322 return ! $honeypot->validate();
323 }
324
325 /**
326 * @return boolean
327 */
328 private static function is_spam_bot() {
329 $ip = FrmAppHelper::get_ip_address();
330
331 return empty( $ip );
332 }
333
334 /**
335 * @param array $values
336 * @return boolean
337 */
338 private static function is_akismet_spam( $values ) {
339 global $wpcom_api_key;
340
341 return ( is_callable( 'Akismet::http_post' ) && ( get_option( 'wordpress_api_key' ) || $wpcom_api_key ) && self::akismet( $values ) );
342 }
343
344 /**
345 * @param int $form_id
346 * @return bool
347 */
348 private static function is_akismet_enabled_for_user( $form_id ) {
349 $form = FrmForm::getOne( $form_id );
350
351 return ( ! empty( $form->options['akismet'] ) && ( $form->options['akismet'] !== 'logged' || ! is_user_logged_in() ) );
352 }
353
354 public static function blacklist_check( $values ) {
355 if ( ! apply_filters( 'frm_check_blacklist', true, $values ) ) {
356 return false;
357 }
358
359 $mod_keys = trim( self::get_disallowed_words() );
360 if ( empty( $mod_keys ) ) {
361 return false;
362 }
363
364 $content = FrmEntriesHelper::entry_array_to_string( $values );
365
366 self::prepare_values_for_spam_check( $values );
367 $ip = FrmAppHelper::get_ip_address();
368 $user_agent = FrmAppHelper::get_server_value( 'HTTP_USER_AGENT' );
369 $user_info = self::get_spam_check_user_info( $values );
370
371 return self::check_disallowed_words( $user_info['comment_author'], $user_info['comment_author_email'], $user_info['comment_author_url'], $content, $ip, $user_agent );
372 }
373
374 /**
375 * For WP 5.5 compatibility.
376 *
377 * @since 4.06.02
378 */
379 private static function check_disallowed_words( $author, $email, $url, $content, $ip, $user_agent ) {
380 if ( function_exists( 'wp_check_comment_disallowed_list' ) ) {
381 return wp_check_comment_disallowed_list( $author, $email, $url, $content, $ip, $user_agent );
382 } else {
383 return wp_blacklist_check( $author, $email, $url, $content, $ip, $user_agent );
384 }
385 }
386
387 /**
388 * For WP 5.5 compatibility.
389 *
390 * @since 4.06.02
391 */
392 private static function get_disallowed_words() {
393 $keys = get_option( 'disallowed_keys' );
394 if ( false === $keys ) {
395 // Fallback for WP < 5.5.
396 $keys = get_option( 'blacklist_keys' );
397 }
398 return $keys;
399 }
400
401 /**
402 * Check entries for Akismet spam
403 *
404 * @return boolean true if is spam
405 */
406 public static function akismet( $values ) {
407 if ( empty( $values['item_meta'] ) ) {
408 return false;
409 }
410
411 $datas = array(
412 'comment_type' => 'formidable',
413 );
414 self::parse_akismet_array( $datas, $values );
415
416 /**
417 * Allows modifying the values sent to Akismet.
418 *
419 * @since 5.0.07
420 *
421 * @param array $datas The array of values being sent to Akismet.
422 */
423 $datas = apply_filters( 'frm_akismet_values', $datas );
424
425 $query_string = _http_build_query( $datas, '', '&' );
426 $response = Akismet::http_post( $query_string, 'comment-check' );
427
428 return ( is_array( $response ) && $response[1] == 'true' );
429 }
430
431 /**
432 * @since 2.0
433 */
434 private static function parse_akismet_array( &$datas, $values ) {
435 self::add_site_info_to_akismet( $datas );
436 self::add_server_values_to_akismet( $datas );
437
438 self::prepare_values_for_spam_check( $values );
439
440 self::add_user_info_to_akismet( $datas, $values );
441 self::add_comment_content_to_akismet( $datas, $values );
442 }
443
444 private static function add_site_info_to_akismet( &$datas ) {
445 $datas['blog'] = FrmAppHelper::site_url();
446 $datas['user_ip'] = preg_replace( '/[^0-9., ]/', '', FrmAppHelper::get_ip_address() );
447 $datas['user_agent'] = FrmAppHelper::get_server_value( 'HTTP_USER_AGENT' );
448 $datas['referrer'] = isset( $_SERVER['HTTP_REFERER'] ) ? FrmAppHelper::get_server_value( 'HTTP_REFERER' ) : false;
449 $datas['blog_lang'] = get_locale();
450 $datas['blog_charset'] = get_option( 'blog_charset' );
451
452 if ( akismet_test_mode() ) {
453 $datas['is_test'] = 'true';
454 }
455 }
456
457 private static function add_user_info_to_akismet( &$datas, $values ) {
458 $user_info = self::get_spam_check_user_info( $values );
459 $datas = $datas + $user_info;
460
461 if ( isset( $user_info['user_ID'] ) ) {
462 $datas['user_role'] = Akismet::get_user_roles( $user_info['user_ID'] );
463 }
464 }
465
466 /**
467 * Gets user info for Akismet spam check.
468 *
469 * @since 5.0.13 Separate code for guest. Handle value of embedded|repeater.
470 *
471 * @param array $values Entry values after running through {@see FrmEntryValidate::prepare_values_for_spam_check()}.
472 * @return array
473 */
474 private static function get_spam_check_user_info( $values ) {
475 if ( ! is_user_logged_in() ) {
476 return self::get_spam_check_user_info_for_guest( $values );
477 }
478
479 $user = wp_get_current_user();
480
481 return array(
482 'user_ID' => $user->ID,
483 'user_id' => $user->ID,
484 'comment_author' => $user->display_name,
485 'comment_author_email' => $user->user_email,
486 'comment_author_url' => $user->user_url,
487 );
488 }
489
490 /**
491 * Gets user info for Akismet spam check for guest.
492 *
493 * @since 5.0.13
494 *
495 * @param array $values Entry values after flattened.
496 * @return array
497 */
498 private static function get_spam_check_user_info_for_guest( $values ) {
499 $datas = array(
500 'comment_author' => '',
501 'comment_author_email' => '',
502 'comment_author_url' => '',
503 'name_field_ids' => $values['name_field_ids'],
504 'missing_keys' => array( 'comment_author_email', 'comment_author_url', 'comment_author' ),
505 'frm_duplicated' => array(),
506 );
507
508 if ( isset( $values['item_meta'] ) ) {
509 $values = $values['item_meta'];
510 }
511
512 $values = array_filter( $values );
513
514 self::recursive_add_akismet_guest_info( $datas, $values );
515 unset( $datas['name_field_ids'] );
516 unset( $datas['missing_keys'] );
517
518 return $datas;
519 }
520
521 /**
522 * Recursive adds akismet guest info.
523 *
524 * @since 5.0.13
525 *
526 * @param array $datas Guest data.
527 * @param array $values The values.
528 * @param int|null $custom_index Custom index (or field ID).
529 */
530 private static function recursive_add_akismet_guest_info( &$datas, $values, $custom_index = null ) {
531 foreach ( $values as $index => $value ) {
532 if ( ! $datas['missing_keys'] ) {
533 return; // Found all info.
534 }
535
536 if ( is_array( $value ) ) {
537 self::recursive_add_akismet_guest_info( $datas, $value, $index );
538 continue;
539 }
540
541 $field_id = ! is_null( $custom_index ) ? $custom_index : $index;
542 foreach ( $datas['missing_keys'] as $key_index => $key ) {
543 $found = self::is_akismet_guest_info_value( $key, $value, $field_id, $datas['name_field_ids'] );
544 if ( $found ) {
545 $datas[ $key ] = $value;
546 $datas['frm_duplicated'][] = $field_id;
547 unset( $datas['missing_keys'][ $key_index ] );
548 }
549 }
550 }
551 }
552
553 /**
554 * Checks if given value is an akismet guest info.
555 *
556 * @since 5.0.13
557 *
558 * @param string $key Guest info key.
559 * @param string $value Value to check.
560 * @param int $field_id Field ID.
561 * @param array $name_field_ids Name field IDs.
562 * @return bool
563 */
564 private static function is_akismet_guest_info_value( $key, $value, $field_id, $name_field_ids ) {
565 if ( ! $value || is_numeric( $value ) ) {
566 return false;
567 }
568
569 switch ( $key ) {
570 case 'comment_author_email':
571 return strpos( $value, '@' ) && is_email( $value );
572
573 case 'comment_author_url':
574 return 0 === strpos( $value, 'http' );
575
576 case 'comment_author':
577 if ( $name_field_ids ) {
578 // If there is name field in the form, we should always use it as author name.
579 return in_array( $field_id, $name_field_ids, true );
580 }
581 return strlen( $value ) < 200;
582 }
583
584 return false;
585 }
586
587 private static function add_server_values_to_akismet( &$datas ) {
588 foreach ( $_SERVER as $key => $value ) {
589 $include_value = is_string( $value ) && ! preg_match( '/^HTTP_COOKIE/', $key ) && preg_match( '/^(HTTP_|REMOTE_ADDR|REQUEST_URI|DOCUMENT_URI)/', $key );
590
591 // Send any potentially useful $_SERVER vars, but avoid sending junk we don't need.
592 if ( $include_value ) {
593 $datas[ $key ] = $value;
594 }
595 unset( $key, $value );
596 }
597 }
598
599 /**
600 * Adds comment content to Akismet data.
601 *
602 * @since 5.0.09
603 *
604 * @param array $datas The array of values being sent to Akismet.
605 * @param array $values Entry values.
606 */
607 private static function add_comment_content_to_akismet( &$datas, $values ) {
608 if ( isset( $datas['frm_duplicated'] ) ) {
609 foreach ( $datas['frm_duplicated'] as $index ) {
610 if ( isset( $values['item_meta'][ $index ] ) ) {
611 unset( $values['item_meta'][ $index ] );
612 } else {
613 unset( $values[ $index ] );
614 }
615 }
616 unset( $datas['frm_duplicated'] );
617 }
618
619 self::skip_adding_values_to_akismet( $values );
620
621 $datas['comment_content'] = FrmEntriesHelper::entry_array_to_string( $values );
622 }
623
624 /**
625 * Skips adding field values to Akismet.
626 *
627 * @since 5.0.09
628 *
629 * @param array $values Entry values.
630 */
631 private static function skip_adding_values_to_akismet( &$values ) {
632 $skipped_fields = self::get_akismet_skipped_field_ids( $values );
633 foreach ( $skipped_fields as $skipped_field ) {
634 if ( ! isset( $values['item_meta'][ $skipped_field->id ] ) ) {
635 continue;
636 }
637
638 if ( self::should_really_skip_field( $skipped_field, $values ) ) {
639 unset( $values['item_meta'][ $skipped_field->id ] );
640 if ( isset( $values['item_meta']['other'][ $skipped_field->id ] ) ) {
641 unset( $values['item_meta']['other'][ $skipped_field->id ] );
642 }
643 }
644 }
645 }
646
647 /**
648 * Checks if a skip field should be really skipped.
649 *
650 * @since 5.02.04
651 *
652 * @param object $field_data Object contains `id` and `options`.
653 * @param array $values Entry values.
654 * @return bool
655 */
656 private static function should_really_skip_field( $field_data, $values ) {
657 if ( empty( $field_data->options ) ) { // This is skipped field types.
658 return true;
659 }
660
661 FrmAppHelper::unserialize_or_decode( $field_data->options );
662 if ( ! $field_data->options ) { // Check if an error happens when unserializing, or empty options.
663 return true;
664 }
665
666 end( $field_data->options );
667 $last_key = key( $field_data->options );
668
669 // If a choice field has no Other option.
670 if ( is_numeric( $last_key ) || 0 !== strpos( $last_key, 'other_' ) ) {
671 return true;
672 }
673
674 // If a choice field has Other option, but Other is not selected.
675 if ( empty( $values['item_meta']['other'][ $field_data->id ] ) ) {
676 return true;
677 }
678
679 // Check if submitted value is same as one of field option.
680 foreach ( $field_data->options as $option ) {
681 $option_value = ! is_array( $option ) ? $option : ( isset( $option['value'] ) ? $option['value'] : '' );
682 if ( $values['item_meta']['other'][ $field_data->id ] === $option_value ) {
683 return true;
684 }
685 }
686
687 return false;
688 }
689
690 /**
691 * Gets field IDs that are skipped from sending to Akismet spam check.
692 *
693 * @since 5.0.09
694 * @since 5.0.13 Move out get_all_form_ids_and_flatten_meta() call and get `form_ids` from `$values`.
695 * @since 5.2.04 This method returns array of object contains `id` and `options` instead of array of `id` only.
696 *
697 * @param array $values Entry values after running through {@see FrmEntryValidate::prepare_values_for_spam_check()}.
698 * @return array
699 */
700 private static function get_akismet_skipped_field_ids( $values ) {
701 if ( empty( $values['form_ids'] ) ) {
702 return array();
703 }
704
705 $skipped_types = array( 'divider', 'form', 'hidden', 'user_id', 'file', 'date', 'time', 'scale', 'star', 'range', 'toggle', 'data', 'lookup', 'likert', 'nps' );
706 $has_other_types = array( 'radio', 'checkbox', 'select' );
707
708 $where = array(
709 array(
710 'form_id' => $values['form_ids'],
711 'type' => array_merge( $skipped_types, $has_other_types ),
712 ),
713 );
714
715 return FrmDb::get_results( 'frm_fields', $where, 'id,options' );
716 }
717
718 /**
719 * Prepares values array for spam check.
720 *
721 * @since 5.0.13
722 *
723 * @param array $values Entry values.
724 */
725 private static function prepare_values_for_spam_check( &$values ) {
726 $form_ids = self::get_all_form_ids_and_flatten_meta( $values );
727 $values['form_ids'] = $form_ids;
728 }
729
730 /**
731 * Gets all form IDs (include child form IDs) and flatten item_meta array. Used for skipping values sent to Akismet.
732 * This also removes some unused data from the item_meta.
733 *
734 * @since 5.0.09
735 * @since 5.0.13 Convert name field value to string.
736 *
737 * @param array $values Entry values.
738 * @return array Form IDs.
739 */
740 private static function get_all_form_ids_and_flatten_meta( &$values ) {
741 $values['name_field_ids'] = array();
742
743 // Blacklist check for File field in the old version doesn't contain `form_id`.
744 $form_ids = isset( $values['form_id'] ) ? array( absint( $values['form_id'] ) ) : array();
745 foreach ( $values['item_meta'] as $field_id => $value ) {
746 if ( ! is_numeric( $field_id ) ) { // Maybe `other`.
747 continue;
748 }
749
750 // Convert name array to string.
751 if ( isset( $value['first'] ) && isset( $value['last'] ) ) {
752 $values['item_meta'][ $field_id ] = trim( implode( ' ', $value ) );
753 $values['name_field_ids'][] = $field_id;
754 continue;
755 }
756
757 if ( ! is_array( $value ) || empty( $value['form'] ) ) {
758 continue;
759 }
760
761 $form_ids[] = absint( $value['form'] );
762
763 foreach ( $value as $subindex => $subvalue ) {
764 if ( ! is_numeric( $subindex ) || ! is_array( $subvalue ) ) {
765 continue;
766 }
767
768 foreach ( $subvalue as $subsubindex => $subsubvalue ) {
769 if ( ! $subsubvalue ) {
770 continue;
771 }
772
773 if ( ! isset( $values['item_meta'][ $subsubindex ] ) ) {
774 $values['item_meta'][ $subsubindex ] = array();
775 }
776
777 // Convert name array to string.
778 if ( isset( $subsubvalue['first'] ) && isset( $subsubvalue['last'] ) ) {
779 $subsubvalue = trim( implode( ' ', $subsubvalue ) );
780
781 $values['name_field_ids'][] = $subsubindex;
782 }
783
784 $values['item_meta'][ $subsubindex ][] = $subsubvalue;
785 }
786 }
787
788 unset( $values['item_meta'][ $field_id ] );
789 }
790
791 return $form_ids;
792 }
793
794 /**
795 * @deprecated 3.0
796 * @codeCoverageIgnore
797 */
798 public static function validate_url_field( &$errors, $field, $value, $args ) {
799 FrmDeprecated::validate_url_field( $errors, $field, $value, $args );
800 }
801
802 /**
803 * @deprecated 3.0
804 * @codeCoverageIgnore
805 */
806 public static function validate_email_field( &$errors, $field, $value, $args ) {
807 FrmDeprecated::validate_email_field( $errors, $field, $value, $args );
808 }
809
810 /**
811 * @deprecated 3.0
812 * @codeCoverageIgnore
813 */
814 public static function validate_number_field( &$errors, $field, $value, $args ) {
815 FrmDeprecated::validate_number_field( $errors, $field, $value, $args );
816 }
817
818 /**
819 * @deprecated 3.0
820 * @codeCoverageIgnore
821 */
822 public static function validate_recaptcha( &$errors, $field, $args ) {
823 FrmDeprecated::validate_recaptcha( $errors, $field, $args );
824 }
825 }
826