Contact Form 7 - Version 5.6

Version Description

https://contactform7.com/contact-form-7-56/

Download this release

Release Info

Developer takayukister
Plugin Icon 128x128 Contact Form 7
Version 5.6
Comparing to
See all releases

Code changes from version 5.5.6.1 to 5.6

Files changed (52) hide show
  1. admin/includes/editor.php +16 -11
  2. admin/includes/help-tabs.php +16 -10
  3. admin/includes/welcome-panel.php +2 -2
  4. includes/config-validator.php +7 -5
  5. includes/contact-form-functions.php +11 -11
  6. includes/contact-form-template.php +9 -7
  7. includes/contact-form.php +26 -7
  8. includes/file.php +92 -91
  9. includes/form-tag.php +202 -49
  10. includes/form-tags-manager.php +242 -104
  11. includes/formatting.php +60 -60
  12. includes/functions.php +68 -32
  13. includes/js/index.js +1 -1
  14. includes/rest-api.php +52 -5
  15. includes/shortcodes.php +5 -5
  16. includes/submission.php +26 -1
  17. includes/swv/rules/date.php +39 -0
  18. includes/swv/rules/email.php +39 -0
  19. includes/swv/rules/file.php +66 -0
  20. includes/swv/rules/maxdate.php +45 -0
  21. includes/swv/rules/maxfilesize.php +43 -0
  22. includes/swv/rules/maxlength.php +49 -0
  23. includes/swv/rules/maxnumber.php +45 -0
  24. includes/swv/rules/mindate.php +45 -0
  25. includes/swv/rules/minlength.php +49 -0
  26. includes/swv/rules/minnumber.php +45 -0
  27. includes/swv/rules/number.php +39 -0
  28. includes/swv/rules/required.php +39 -0
  29. includes/swv/rules/requiredfile.php +40 -0
  30. includes/swv/rules/tel.php +39 -0
  31. includes/swv/rules/url.php +39 -0
  32. includes/swv/schema-holder.php +57 -0
  33. includes/swv/swv.php +282 -0
  34. includes/validation-functions.php +86 -51
  35. includes/validation.php +59 -1
  36. load.php +7 -6
  37. modules/acceptance.php +9 -7
  38. modules/checkbox.php +21 -20
  39. modules/constant-contact/contact-form-properties.php +2 -10
  40. modules/constant-contact/contact-post-request.php +38 -45
  41. modules/date.php +52 -27
  42. modules/doi-helper.php +4 -7
  43. modules/file.php +43 -19
  44. modules/number.php +85 -34
  45. modules/quiz.php +8 -7
  46. modules/really-simple-captcha.php +5 -5
  47. modules/recaptcha/recaptcha.php +1 -1
  48. modules/select.php +21 -24
  49. modules/text.php +61 -61
  50. modules/textarea.php +41 -40
  51. readme.txt +8 -16
  52. wp-contact-form-7.php +3 -3
admin/includes/editor.php CHANGED
@@ -9,9 +9,9 @@ class WPCF7_Editor {
9
  $this->contact_form = $contact_form;
10
  }
11
 
12
- public function add_panel( $id, $title, $callback ) {
13
- if ( wpcf7_is_name( $id ) ) {
14
- $this->panels[$id] = array(
15
  'title' => $title,
16
  'callback' => $callback,
17
  );
@@ -25,19 +25,24 @@ class WPCF7_Editor {
25
 
26
  echo '<ul id="contact-form-editor-tabs">';
27
 
28
- foreach ( $this->panels as $id => $panel ) {
29
- echo sprintf( '<li id="%1$s-tab"><a href="#%1$s">%2$s</a></li>',
30
- esc_attr( $id ), esc_html( $panel['title'] ) );
 
 
 
31
  }
32
 
33
  echo '</ul>';
34
 
35
- foreach ( $this->panels as $id => $panel ) {
36
- echo sprintf( '<div class="contact-form-editor-panel" id="%1$s">',
37
- esc_attr( $id ) );
 
 
38
 
39
  if ( is_callable( $panel['callback'] ) ) {
40
- $this->notice( $id, $panel );
41
  call_user_func( $panel['callback'], $this->contact_form );
42
  }
43
 
@@ -45,7 +50,7 @@ class WPCF7_Editor {
45
  }
46
  }
47
 
48
- public function notice( $id, $panel ) {
49
  echo '<div class="config-error"></div>';
50
  }
51
  }
9
  $this->contact_form = $contact_form;
10
  }
11
 
12
+ public function add_panel( $panel_id, $title, $callback ) {
13
+ if ( wpcf7_is_name( $panel_id ) ) {
14
+ $this->panels[$panel_id] = array(
15
  'title' => $title,
16
  'callback' => $callback,
17
  );
25
 
26
  echo '<ul id="contact-form-editor-tabs">';
27
 
28
+ foreach ( $this->panels as $panel_id => $panel ) {
29
+ echo sprintf(
30
+ '<li id="%1$s-tab"><a href="#%1$s">%2$s</a></li>',
31
+ esc_attr( $panel_id ),
32
+ esc_html( $panel['title'] )
33
+ );
34
  }
35
 
36
  echo '</ul>';
37
 
38
+ foreach ( $this->panels as $panel_id => $panel ) {
39
+ echo sprintf(
40
+ '<div class="contact-form-editor-panel" id="%1$s">',
41
+ esc_attr( $panel_id )
42
+ );
43
 
44
  if ( is_callable( $panel['callback'] ) ) {
45
+ $this->notice( $panel_id, $panel );
46
  call_user_func( $panel['callback'], $this->contact_form );
47
  }
48
 
50
  }
51
  }
52
 
53
+ public function notice( $panel_id, $panel ) {
54
  echo '<div class="config-error"></div>';
55
  }
56
  }
admin/includes/help-tabs.php CHANGED
@@ -8,18 +8,20 @@ class WPCF7_Help_Tabs {
8
  $this->screen = $screen;
9
  }
10
 
11
- public function set_help_tabs( $type ) {
12
- switch ( $type ) {
13
  case 'list':
14
  $this->screen->add_help_tab( array(
15
  'id' => 'list_overview',
16
  'title' => __( 'Overview', 'contact-form-7' ),
17
- 'content' => $this->content( 'list_overview' ) ) );
 
18
 
19
  $this->screen->add_help_tab( array(
20
  'id' => 'list_available_actions',
21
  'title' => __( 'Available Actions', 'contact-form-7' ),
22
- 'content' => $this->content( 'list_available_actions' ) ) );
 
23
 
24
  $this->sidebar();
25
 
@@ -28,17 +30,20 @@ class WPCF7_Help_Tabs {
28
  $this->screen->add_help_tab( array(
29
  'id' => 'edit_overview',
30
  'title' => __( 'Overview', 'contact-form-7' ),
31
- 'content' => $this->content( 'edit_overview' ) ) );
 
32
 
33
  $this->screen->add_help_tab( array(
34
  'id' => 'edit_form_tags',
35
  'title' => __( 'Form-tags', 'contact-form-7' ),
36
- 'content' => $this->content( 'edit_form_tags' ) ) );
 
37
 
38
  $this->screen->add_help_tab( array(
39
  'id' => 'edit_mail_tags',
40
  'title' => __( 'Mail-tags', 'contact-form-7' ),
41
- 'content' => $this->content( 'edit_mail_tags' ) ) );
 
42
 
43
  $this->sidebar();
44
 
@@ -47,7 +52,8 @@ class WPCF7_Help_Tabs {
47
  $this->screen->add_help_tab( array(
48
  'id' => 'integration_overview',
49
  'title' => __( 'Overview', 'contact-form-7' ),
50
- 'content' => $this->content( 'integration_overview' ) ) );
 
51
 
52
  $this->sidebar();
53
 
@@ -73,10 +79,10 @@ class WPCF7_Help_Tabs {
73
  $content['edit_overview'] .= '<p>' . __( "<strong>Additional Settings</strong> provides a place where you can customize the behavior of this contact form by adding code snippets.", 'contact-form-7' ) . '</p>';
74
 
75
  $content['edit_form_tags'] = '<p>' . __( "A form-tag is a short code enclosed in square brackets used in a form content. A form-tag generally represents an input field, and its components can be separated into four parts: type, name, options, and values. Contact Form 7 supports several types of form-tags including text fields, number fields, date fields, checkboxes, radio buttons, menus, file-uploading fields, CAPTCHAs, and quiz fields.", 'contact-form-7' ) . '</p>';
76
- $content['edit_form_tags'] .= '<p>' . __( "While form-tags have a comparatively complex syntax, you don&#8217;t need to know the syntax to add form-tags because you can use the straightforward tag generator (<strong>Generate Tag</strong> button on this screen).", 'contact-form-7' ) . '</p>';
77
 
78
  $content['edit_mail_tags'] = '<p>' . __( "A mail-tag is also a short code enclosed in square brackets that you can use in every Mail and Mail (2) field. A mail-tag represents a user input value through an input field of a corresponding form-tag.", 'contact-form-7' ) . '</p>';
79
- $content['edit_mail_tags'] .= '<p>' . __( "There are also special mail-tags that have specific names, but don&#8217;t have corresponding form-tags. They are used to represent meta information of form submissions like the submitter&#8217;s IP address or the URL of the page.", 'contact-form-7' ) . '</p>';
80
 
81
  $content['integration_overview'] = '<p>' . __( "On this screen, you can manage services that are available through Contact Form 7. Using API will allow you to collaborate with any services that are available.", 'contact-form-7' ) . '</p>';
82
  $content['integration_overview'] .= '<p>' . __( "You may need to first sign up for an account with the service that you plan to use. When you do so, you would need to authorize Contact Form 7 to access the service with your account.", 'contact-form-7' ) . '</p>';
8
  $this->screen = $screen;
9
  }
10
 
11
+ public function set_help_tabs( $screen_type ) {
12
+ switch ( $screen_type ) {
13
  case 'list':
14
  $this->screen->add_help_tab( array(
15
  'id' => 'list_overview',
16
  'title' => __( 'Overview', 'contact-form-7' ),
17
+ 'content' => $this->content( 'list_overview' ),
18
+ ) );
19
 
20
  $this->screen->add_help_tab( array(
21
  'id' => 'list_available_actions',
22
  'title' => __( 'Available Actions', 'contact-form-7' ),
23
+ 'content' => $this->content( 'list_available_actions' ),
24
+ ) );
25
 
26
  $this->sidebar();
27
 
30
  $this->screen->add_help_tab( array(
31
  'id' => 'edit_overview',
32
  'title' => __( 'Overview', 'contact-form-7' ),
33
+ 'content' => $this->content( 'edit_overview' ),
34
+ ) );
35
 
36
  $this->screen->add_help_tab( array(
37
  'id' => 'edit_form_tags',
38
  'title' => __( 'Form-tags', 'contact-form-7' ),
39
+ 'content' => $this->content( 'edit_form_tags' ),
40
+ ) );
41
 
42
  $this->screen->add_help_tab( array(
43
  'id' => 'edit_mail_tags',
44
  'title' => __( 'Mail-tags', 'contact-form-7' ),
45
+ 'content' => $this->content( 'edit_mail_tags' ),
46
+ ) );
47
 
48
  $this->sidebar();
49
 
52
  $this->screen->add_help_tab( array(
53
  'id' => 'integration_overview',
54
  'title' => __( 'Overview', 'contact-form-7' ),
55
+ 'content' => $this->content( 'integration_overview' ),
56
+ ) );
57
 
58
  $this->sidebar();
59
 
79
  $content['edit_overview'] .= '<p>' . __( "<strong>Additional Settings</strong> provides a place where you can customize the behavior of this contact form by adding code snippets.", 'contact-form-7' ) . '</p>';
80
 
81
  $content['edit_form_tags'] = '<p>' . __( "A form-tag is a short code enclosed in square brackets used in a form content. A form-tag generally represents an input field, and its components can be separated into four parts: type, name, options, and values. Contact Form 7 supports several types of form-tags including text fields, number fields, date fields, checkboxes, radio buttons, menus, file-uploading fields, CAPTCHAs, and quiz fields.", 'contact-form-7' ) . '</p>';
82
+ $content['edit_form_tags'] .= '<p>' . __( "While form-tags have a comparatively complex syntax, you do not need to know the syntax to add form-tags because you can use the straightforward tag generator (<strong>Generate Tag</strong> button on this screen).", 'contact-form-7' ) . '</p>';
83
 
84
  $content['edit_mail_tags'] = '<p>' . __( "A mail-tag is also a short code enclosed in square brackets that you can use in every Mail and Mail (2) field. A mail-tag represents a user input value through an input field of a corresponding form-tag.", 'contact-form-7' ) . '</p>';
85
+ $content['edit_mail_tags'] .= '<p>' . __( "There are also special mail-tags that have specific names, but do not have corresponding form-tags. They are used to represent meta information of form submissions like the submitter&#8217;s IP address or the URL of the page.", 'contact-form-7' ) . '</p>';
86
 
87
  $content['integration_overview'] = '<p>' . __( "On this screen, you can manage services that are available through Contact Form 7. Using API will allow you to collaborate with any services that are available.", 'contact-form-7' ) . '</p>';
88
  $content['integration_overview'] .= '<p>' . __( "You may need to first sign up for an account with the service that you plan to use. When you do so, you would need to authorize Contact Form 7 to access the service with your account.", 'contact-form-7' ) . '</p>';
admin/includes/welcome-panel.php CHANGED
@@ -52,7 +52,7 @@ class WPCF7_WelcomePanelColumn_AntiSpam extends WPCF7_WelcomePanelColumn {
52
 
53
  protected function content() {
54
  return array(
55
- esc_html( __( "Spammers target everything; your contact forms aren&#8217;t an exception. Before you get spammed, protect your contact forms with the powerful anti-spam features Contact Form 7 provides.", 'contact-form-7' ) ),
56
  sprintf(
57
  /* translators: links labeled 1: 'Akismet', 2: 'reCAPTCHA', 3: 'disallowed list' */
58
  esc_html( __( 'Contact Form 7 supports spam-filtering with %1$s. Intelligent %2$s blocks annoying spambots. Plus, using %3$s, you can block messages containing specified keywords or those sent from specified IP addresses.', 'contact-form-7' ) ),
@@ -117,7 +117,7 @@ class WPCF7_WelcomePanelColumn_Flamingo extends WPCF7_WelcomePanelColumn {
117
 
118
  protected function content() {
119
  return array(
120
- esc_html( __( "Contact Form 7 doesn&#8217;t store submitted messages anywhere. Therefore, you may lose important messages forever if your mail server has issues or you make a mistake in mail configuration.", 'contact-form-7' ) ),
121
  sprintf(
122
  /* translators: %s: link labeled 'Flamingo' */
123
  esc_html( __( 'Install a message storage plugin before this happens to you. %s saves all messages through contact forms into the database. Flamingo is a free WordPress plugin created by the same author as Contact Form 7.', 'contact-form-7' ) ),
52
 
53
  protected function content() {
54
  return array(
55
+ esc_html( __( "Spammers target everything; your contact forms are not an exception. Before you get spammed, protect your contact forms with the powerful anti-spam features Contact Form 7 provides.", 'contact-form-7' ) ),
56
  sprintf(
57
  /* translators: links labeled 1: 'Akismet', 2: 'reCAPTCHA', 3: 'disallowed list' */
58
  esc_html( __( 'Contact Form 7 supports spam-filtering with %1$s. Intelligent %2$s blocks annoying spambots. Plus, using %3$s, you can block messages containing specified keywords or those sent from specified IP addresses.', 'contact-form-7' ) ),
117
 
118
  protected function content() {
119
  return array(
120
+ esc_html( __( "Contact Form 7 does not store submitted messages anywhere. Therefore, you may lose important messages forever if your mail server has issues or you make a mistake in mail configuration.", 'contact-form-7' ) ),
121
  sprintf(
122
  /* translators: %s: link labeled 'Flamingo' */
123
  esc_html( __( 'Install a message storage plugin before this happens to you. %s saves all messages through contact forms into the database. Flamingo is a free WordPress plugin created by the same author as Contact Form 7.', 'contact-form-7' ) ),
includes/config-validator.php CHANGED
@@ -529,15 +529,17 @@ class WPCF7_ConfigValidator {
529
  $header_name = $matches[1];
530
  $header_value = trim( $matches[2] );
531
 
532
- if ( in_array( strtolower( $header_name ), $mailbox_header_types ) ) {
 
533
  $this->detect_invalid_mailbox_syntax(
534
  sprintf( '%s.additional_headers', $template ),
535
- $header_value, array(
 
536
  'message' =>
537
  __( "Invalid mailbox syntax is used in the %name% field.", 'contact-form-7' ),
538
- 'params' => array( 'name' => $header_name ) ) );
539
- } elseif ( empty( $header_value ) ) {
540
- $invalid_mail_header_exists = true;
541
  }
542
  }
543
  }
529
  $header_name = $matches[1];
530
  $header_value = trim( $matches[2] );
531
 
532
+ if ( in_array( strtolower( $header_name ), $mailbox_header_types )
533
+ and '' !== $header_value ) {
534
  $this->detect_invalid_mailbox_syntax(
535
  sprintf( '%s.additional_headers', $template ),
536
+ $header_value,
537
+ array(
538
  'message' =>
539
  __( "Invalid mailbox syntax is used in the %name% field.", 'contact-form-7' ),
540
+ 'params' => array( 'name' => $header_name )
541
+ )
542
+ );
543
  }
544
  }
545
  }
includes/contact-form-functions.php CHANGED
@@ -39,19 +39,19 @@ function wpcf7_is_posted() {
39
  return $contact_form->is_posted();
40
  }
41
 
42
- function wpcf7_get_hangover( $name, $default = null ) {
43
  if ( ! wpcf7_is_posted() ) {
44
- return $default;
45
  }
46
 
47
  $submission = WPCF7_Submission::get_instance();
48
 
49
  if ( ! $submission
50
  or $submission->is( 'mail_sent' ) ) {
51
- return $default;
52
  }
53
 
54
- return isset( $_POST[$name] ) ? wp_unslash( $_POST[$name] ) : $default;
55
  }
56
 
57
  function wpcf7_get_validation_error( $name ) {
@@ -82,11 +82,11 @@ function wpcf7_get_message( $status ) {
82
  return $contact_form->message( $status );
83
  }
84
 
85
- function wpcf7_form_controls_class( $type, $default = '' ) {
86
  $type = trim( $type );
87
- $default = array_filter( explode( ' ', $default ) );
88
 
89
- $classes = array_merge( array( 'wpcf7-form-control' ), $default );
90
 
91
  $typebase = rtrim( $type, '*' );
92
  $required = ( '*' == substr( $type, -1 ) );
@@ -216,9 +216,9 @@ function wpcf7_save_contact_form( $args = '', $context = 'save' ) {
216
  return $contact_form;
217
  }
218
 
219
- function wpcf7_sanitize_form( $input, $default = '' ) {
220
  if ( null === $input ) {
221
- return $default;
222
  }
223
 
224
  $output = trim( $input );
@@ -291,9 +291,9 @@ function wpcf7_sanitize_messages( $input, $defaults = array() ) {
291
  return $output;
292
  }
293
 
294
- function wpcf7_sanitize_additional_settings( $input, $default = '' ) {
295
  if ( null === $input ) {
296
- return $default;
297
  }
298
 
299
  $output = trim( $input );
39
  return $contact_form->is_posted();
40
  }
41
 
42
+ function wpcf7_get_hangover( $name, $default_value = null ) {
43
  if ( ! wpcf7_is_posted() ) {
44
+ return $default_value;
45
  }
46
 
47
  $submission = WPCF7_Submission::get_instance();
48
 
49
  if ( ! $submission
50
  or $submission->is( 'mail_sent' ) ) {
51
+ return $default_value;
52
  }
53
 
54
+ return isset( $_POST[$name] ) ? wp_unslash( $_POST[$name] ) : $default_value;
55
  }
56
 
57
  function wpcf7_get_validation_error( $name ) {
82
  return $contact_form->message( $status );
83
  }
84
 
85
+ function wpcf7_form_controls_class( $type, $default_classes = '' ) {
86
  $type = trim( $type );
87
+ $default_classes = array_filter( explode( ' ', $default_classes ) );
88
 
89
+ $classes = array_merge( array( 'wpcf7-form-control' ), $default_classes );
90
 
91
  $typebase = rtrim( $type, '*' );
92
  $required = ( '*' == substr( $type, -1 ) );
216
  return $contact_form;
217
  }
218
 
219
+ function wpcf7_sanitize_form( $input, $default_template = '' ) {
220
  if ( null === $input ) {
221
+ return $default_template;
222
  }
223
 
224
  $output = trim( $input );
291
  return $output;
292
  }
293
 
294
+ function wpcf7_sanitize_additional_settings( $input, $default_template = '' ) {
295
  if ( null === $input ) {
296
+ return $default_template;
297
  }
298
 
299
  $output = trim( $input );
includes/contact-form-template.php CHANGED
@@ -127,17 +127,19 @@ class WPCF7_ContactFormTemplate {
127
 
128
  public static function from_email() {
129
  $admin_email = get_option( 'admin_email' );
130
- $sitename = strtolower( $_SERVER['SERVER_NAME'] );
131
 
132
  if ( wpcf7_is_localhost() ) {
133
  return $admin_email;
134
  }
135
 
136
- if ( substr( $sitename, 0, 4 ) == 'www.' ) {
 
 
 
137
  $sitename = substr( $sitename, 4 );
138
  }
139
 
140
- if ( strpbrk( $admin_email, '@' ) == '@' . $sitename ) {
141
  return $admin_email;
142
  }
143
 
@@ -196,22 +198,22 @@ function wpcf7_messages() {
196
  'description'
197
  => __( "There is a field that the sender must fill in", 'contact-form-7' ),
198
  'default'
199
- => __( "The field is required.", 'contact-form-7' ),
200
  ),
201
 
202
  'invalid_too_long' => array(
203
  'description'
204
  => __( "There is a field with input that is longer than the maximum allowed length", 'contact-form-7' ),
205
  'default'
206
- => __( "The field is too long.", 'contact-form-7' ),
207
  ),
208
 
209
  'invalid_too_short' => array(
210
  'description'
211
  => __( "There is a field with input that is shorter than the minimum allowed length", 'contact-form-7' ),
212
  'default'
213
- => __( "The field is too short.", 'contact-form-7' ),
214
- )
215
  );
216
 
217
  return apply_filters( 'wpcf7_messages', $messages );
127
 
128
  public static function from_email() {
129
  $admin_email = get_option( 'admin_email' );
 
130
 
131
  if ( wpcf7_is_localhost() ) {
132
  return $admin_email;
133
  }
134
 
135
+ $sitename = wp_parse_url( network_home_url(), PHP_URL_HOST );
136
+ $sitename = strtolower( $sitename );
137
+
138
+ if ( 'www.' === substr( $sitename, 0, 4 ) ) {
139
  $sitename = substr( $sitename, 4 );
140
  }
141
 
142
+ if ( strpbrk( $admin_email, '@' ) === '@' . $sitename ) {
143
  return $admin_email;
144
  }
145
 
198
  'description'
199
  => __( "There is a field that the sender must fill in", 'contact-form-7' ),
200
  'default'
201
+ => __( "Please fill out this field.", 'contact-form-7' ),
202
  ),
203
 
204
  'invalid_too_long' => array(
205
  'description'
206
  => __( "There is a field with input that is longer than the maximum allowed length", 'contact-form-7' ),
207
  'default'
208
+ => __( "This field has a too long input.", 'contact-form-7' ),
209
  ),
210
 
211
  'invalid_too_short' => array(
212
  'description'
213
  => __( "There is a field with input that is shorter than the minimum allowed length", 'contact-form-7' ),
214
  'default'
215
+ => __( "This field has a too short input.", 'contact-form-7' ),
216
+ ),
217
  );
218
 
219
  return apply_filters( 'wpcf7_messages', $messages );
includes/contact-form.php CHANGED
@@ -2,6 +2,8 @@
2
 
3
  class WPCF7_ContactForm {
4
 
 
 
5
  const post_type = 'wpcf7_contact_form';
6
 
7
  private static $found_items = 0;
@@ -319,7 +321,7 @@ class WPCF7_ContactForm {
319
  * Retrieves contact form property of the specified name from the database.
320
  *
321
  * @param string $name Property name.
322
- * @return array|string|null Property value. Null if property doesn't exist.
323
  */
324
  private function retrieve_property( $name ) {
325
  $property = null;
@@ -342,7 +344,7 @@ class WPCF7_ContactForm {
342
  * Returns the value for the given property name.
343
  *
344
  * @param string $name Property name.
345
- * @return array|string|null Property value. Null if property doesn't exist.
346
  */
347
  public function prop( $name ) {
348
  $props = $this->get_properties();
@@ -466,7 +468,7 @@ class WPCF7_ContactForm {
466
  * Returns the specified shortcode attribute value.
467
  *
468
  * @param string $name Shortcode attribute name.
469
- * @return string|null Attribute value. Null if the attribute doesn't exist.
470
  */
471
  public function shortcode_attr( $name ) {
472
  if ( isset( $this->shortcode_atts[$name] ) ) {
@@ -965,7 +967,9 @@ class WPCF7_ContactForm {
965
  $mailtags[] = $tag->name;
966
  }
967
 
968
- $mailtags = array_unique( array_filter( $mailtags ) );
 
 
969
 
970
  return apply_filters( 'wpcf7_collect_mail_tags', $mailtags, $args, $this );
971
  }
@@ -974,10 +978,10 @@ class WPCF7_ContactForm {
974
  /**
975
  * Prints a mail-tag suggestion list.
976
  *
977
- * @param string $for Optional. Mail template name. Default 'mail'.
978
  */
979
- public function suggest_mail_tags( $for = 'mail' ) {
980
- $mail = wp_parse_args( $this->prop( $for ),
981
  array(
982
  'active' => false,
983
  'recipient' => '',
@@ -1176,6 +1180,21 @@ class WPCF7_ContactForm {
1176
  }
1177
 
1178
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1179
  /**
1180
  * Upgrades this contact form properties.
1181
  */
2
 
3
  class WPCF7_ContactForm {
4
 
5
+ use WPCF7_SWV_SchemaHolder;
6
+
7
  const post_type = 'wpcf7_contact_form';
8
 
9
  private static $found_items = 0;
321
  * Retrieves contact form property of the specified name from the database.
322
  *
323
  * @param string $name Property name.
324
+ * @return array|string|null Property value. Null if property does not exist.
325
  */
326
  private function retrieve_property( $name ) {
327
  $property = null;
344
  * Returns the value for the given property name.
345
  *
346
  * @param string $name Property name.
347
+ * @return array|string|null Property value. Null if property does not exist.
348
  */
349
  public function prop( $name ) {
350
  $props = $this->get_properties();
468
  * Returns the specified shortcode attribute value.
469
  *
470
  * @param string $name Shortcode attribute name.
471
+ * @return string|null Attribute value. Null if the attribute does not exist.
472
  */
473
  public function shortcode_attr( $name ) {
474
  if ( isset( $this->shortcode_atts[$name] ) ) {
967
  $mailtags[] = $tag->name;
968
  }
969
 
970
+ $mailtags = array_unique( $mailtags );
971
+ $mailtags = array_filter( $mailtags );
972
+ $mailtags = array_values( $mailtags );
973
 
974
  return apply_filters( 'wpcf7_collect_mail_tags', $mailtags, $args, $this );
975
  }
978
  /**
979
  * Prints a mail-tag suggestion list.
980
  *
981
+ * @param string $template_name Optional. Mail template name. Default 'mail'.
982
  */
983
+ public function suggest_mail_tags( $template_name = 'mail' ) {
984
+ $mail = wp_parse_args( $this->prop( $template_name ),
985
  array(
986
  'active' => false,
987
  'recipient' => '',
1180
  }
1181
 
1182
 
1183
+ /**
1184
+ * Returns true if the specified setting has a falsey string value.
1185
+ *
1186
+ * @param string $name Name of setting.
1187
+ * @return bool True if the setting value is 'off', 'false', or '0'.
1188
+ */
1189
+ public function is_false( $name ) {
1190
+ return in_array(
1191
+ $this->pref( $name ),
1192
+ array( 'off', 'false', '0' ),
1193
+ true
1194
+ );
1195
+ }
1196
+
1197
+
1198
  /**
1199
  * Upgrades this contact form properties.
1200
  */
includes/file.php CHANGED
@@ -33,36 +33,17 @@ function wpcf7_unship_uploaded_file( $file, $args = '' ) {
33
  }
34
  }
35
 
36
- if ( $args['required'] and ! array_filter( $tmp_names ) ) {
37
- return new WP_Error( 'wpcf7_invalid_required',
38
- wpcf7_get_message( 'invalid_required' )
39
- );
40
- }
41
-
42
- // File type validation
43
- $file_type_pattern = wpcf7_acceptable_filetypes(
44
- $args['filetypes'], 'regex'
45
- );
46
-
47
- $file_type_pattern = '/\.(' . $file_type_pattern . ')$/i';
48
-
49
- foreach ( $names as $name ) {
50
- if ( ! empty( $name ) and ! preg_match( $file_type_pattern, $name ) ) {
51
- return new WP_Error( 'wpcf7_upload_file_type_invalid',
52
- wpcf7_get_message( 'upload_file_type_invalid' )
53
- );
54
  }
55
  }
56
 
57
- // File size validation
58
- $total_size = array_sum( $sizes );
59
-
60
- if ( $args['limit'] < $total_size ) {
61
- return new WP_Error( 'wpcf7_upload_file_too_large',
62
- wpcf7_get_message( 'upload_file_too_large' )
63
- );
64
- }
65
-
66
  // Move uploaded file to tmp dir
67
  $uploads_dir = wpcf7_upload_tmp_dir();
68
  $uploads_dir = wpcf7_maybe_add_random_dir( $uploads_dir );
@@ -117,23 +98,23 @@ function wpcf7_file_messages( $messages ) {
117
  return array_merge( $messages, array(
118
  'upload_failed' => array(
119
  'description' => __( "Uploading a file fails for any reason", 'contact-form-7' ),
120
- 'default' => __( "There was an unknown error uploading the file.", 'contact-form-7' )
121
  ),
122
 
123
  'upload_file_type_invalid' => array(
124
  'description' => __( "Uploaded file is not allowed for file type", 'contact-form-7' ),
125
- 'default' => __( "You are not allowed to upload files of this type.", 'contact-form-7' )
126
  ),
127
 
128
  'upload_file_too_large' => array(
129
  'description' => __( "Uploaded file is too large", 'contact-form-7' ),
130
- 'default' => __( "The file is too big.", 'contact-form-7' )
131
  ),
132
 
133
  'upload_failed_php_error' => array(
134
  'description' => __( "Uploading a file fails for PHP error", 'contact-form-7' ),
135
- 'default' => __( "There was an error uploading the file.", 'contact-form-7' )
136
- )
137
  ) );
138
  }
139
 
@@ -161,6 +142,37 @@ function wpcf7_file_form_enctype_filter( $enctype ) {
161
  }
162
 
163
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  /**
165
  * Returns a formatted list of acceptable filetypes.
166
  *
@@ -169,70 +181,67 @@ function wpcf7_file_form_enctype_filter( $enctype ) {
169
  * @return string Formatted list of acceptable filetypes.
170
  */
171
  function wpcf7_acceptable_filetypes( $types = 'default', $format = 'regex' ) {
172
- if ( 'default' === $types
173
- or empty( $types ) ) {
174
  $types = array(
175
- 'jpg',
176
- 'jpeg',
177
- 'png',
178
- 'gif',
179
- 'pdf',
180
- 'doc',
181
- 'docx',
182
- 'ppt',
183
- 'pptx',
184
- 'odt',
185
- 'avi',
186
- 'ogg',
187
- 'm4a',
188
- 'mov',
189
- 'mp3',
190
- 'mp4',
191
- 'mpg',
192
- 'wav',
193
- 'wmv',
194
  );
195
  } else {
196
- $types_tmp = (array) $types;
197
- $types = array();
198
-
199
- foreach ( $types_tmp as $val ) {
200
- if ( is_string( $val ) ) {
201
- $val = preg_split( '/[\s|,]+/', $val );
202
- }
 
203
 
204
- $types = array_merge( $types, (array) $val );
205
- }
206
  }
207
 
208
- $types = array_unique( array_filter( $types ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
 
210
- $output = '';
211
 
212
- foreach ( $types as $type ) {
213
- $type = trim( $type, ' ,.|' );
214
 
215
- $type = str_replace(
216
- array( '.', '+', '*', '?' ),
217
- array( '\.', '\+', '\*', '\?' ),
218
- $type
 
 
 
 
 
 
219
  );
220
 
221
- if ( '' === $type ) {
222
- continue;
223
- }
224
 
225
- if ( 'attr' === $format
226
- or 'attribute' === $format ) {
227
- $output .= sprintf( '.%s', $type );
228
- $output .= ',';
229
- } else {
230
- $output .= $type;
231
- $output .= '|';
232
- }
233
  }
234
 
235
- return trim( $output, ' ,|' );
236
  }
237
 
238
 
@@ -303,7 +312,7 @@ function wpcf7_upload_tmp_dir() {
303
 
304
 
305
  add_action(
306
- 'template_redirect',
307
  'wpcf7_cleanup_upload_files',
308
  20, 0
309
  );
@@ -316,14 +325,6 @@ add_action(
316
  * Default 100.
317
  */
318
  function wpcf7_cleanup_upload_files( $seconds = 60, $max = 100 ) {
319
- if ( is_admin()
320
- or 'GET' != $_SERVER['REQUEST_METHOD']
321
- or is_robots()
322
- or is_feed()
323
- or is_trackback() ) {
324
- return;
325
- }
326
-
327
  $dir = trailingslashit( wpcf7_upload_tmp_dir() );
328
 
329
  if ( ! is_dir( $dir )
33
  }
34
  }
35
 
36
+ if ( isset( $args['schema'] ) and isset( $args['name'] ) ) {
37
+ $result = $args['schema']->validate( array(
38
+ 'file' => true,
39
+ 'field' => $args['name'],
40
+ ) );
41
+
42
+ if ( is_wp_error( $result ) ) {
43
+ return $result;
 
 
 
 
 
 
 
 
 
 
44
  }
45
  }
46
 
 
 
 
 
 
 
 
 
 
47
  // Move uploaded file to tmp dir
48
  $uploads_dir = wpcf7_upload_tmp_dir();
49
  $uploads_dir = wpcf7_maybe_add_random_dir( $uploads_dir );
98
  return array_merge( $messages, array(
99
  'upload_failed' => array(
100
  'description' => __( "Uploading a file fails for any reason", 'contact-form-7' ),
101
+ 'default' => __( "There was an unknown error uploading the file.", 'contact-form-7' ),
102
  ),
103
 
104
  'upload_file_type_invalid' => array(
105
  'description' => __( "Uploaded file is not allowed for file type", 'contact-form-7' ),
106
+ 'default' => __( "You are not allowed to upload files of this type.", 'contact-form-7' ),
107
  ),
108
 
109
  'upload_file_too_large' => array(
110
  'description' => __( "Uploaded file is too large", 'contact-form-7' ),
111
+ 'default' => __( "The uploaded file is too large.", 'contact-form-7' ),
112
  ),
113
 
114
  'upload_failed_php_error' => array(
115
  'description' => __( "Uploading a file fails for PHP error", 'contact-form-7' ),
116
+ 'default' => __( "There was an error uploading the file.", 'contact-form-7' ),
117
+ ),
118
  ) );
119
  }
120
 
142
  }
143
 
144
 
145
+ /**
146
+ * Converts a MIME type string to an array of corresponding file extensions.
147
+ *
148
+ * @param string $mime MIME type.
149
+ * Wildcard (*) is available for the subtype part.
150
+ * @return array Corresponding file extensions.
151
+ */
152
+ function wpcf7_convert_mime_to_ext( $mime ) {
153
+ static $mime_types = array();
154
+
155
+ $mime_types = wp_get_mime_types();
156
+
157
+ $results = array();
158
+
159
+ if ( preg_match( '%^([a-z]+)/([*]|[a-z0-9.+-]+)$%i', $mime, $matches ) ) {
160
+ foreach ( $mime_types as $key => $val ) {
161
+ if ( '*' === $matches[2] and str_starts_with( $val, $matches[1] . '/' )
162
+ or $val === $matches[0] ) {
163
+ $results = array_merge( $results, explode( '|', $key ) );
164
+ }
165
+ }
166
+ }
167
+
168
+ $results = array_unique( $results );
169
+ $results = array_filter( $results );
170
+ $results = array_values( $results );
171
+
172
+ return $results;
173
+ }
174
+
175
+
176
  /**
177
  * Returns a formatted list of acceptable filetypes.
178
  *
181
  * @return string Formatted list of acceptable filetypes.
182
  */
183
  function wpcf7_acceptable_filetypes( $types = 'default', $format = 'regex' ) {
184
+ if ( 'default' === $types or empty( $types ) ) {
 
185
  $types = array(
186
+ 'audio/*',
187
+ 'video/*',
188
+ 'image/*',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
  );
190
  } else {
191
+ $types = array_map(
192
+ function ( $type ) {
193
+ if ( is_string( $type ) ) {
194
+ return preg_split( '/[\s|,]+/', strtolower( $type ) );
195
+ }
196
+ },
197
+ (array) $types
198
+ );
199
 
200
+ $types = wpcf7_array_flatten( $types );
201
+ $types = array_filter( array_unique( $types ) );
202
  }
203
 
204
+ if ( 'attr' === $format or 'attribute' === $format ) {
205
+ $types = array_map(
206
+ function ( $type ) {
207
+ if ( false === strpos( $type, '/' ) ) {
208
+ return sprintf( '.%s', trim( $type, '.' ) );
209
+ } elseif ( preg_match( '%^([a-z]+)/[*]$%i', $type, $matches ) ) {
210
+ if ( in_array( $matches[1], array( 'audio', 'video', 'image' ) ) ) {
211
+ return $type;
212
+ } else {
213
+ return '';
214
+ }
215
+ } elseif ( wpcf7_convert_mime_to_ext( $type ) ) {
216
+ return $type;
217
+ }
218
+ },
219
+ $types
220
+ );
221
 
222
+ $types = array_filter( $types );
223
 
224
+ return implode( ',', $types );
 
225
 
226
+ } elseif ( 'regex' === $format ) {
227
+ $types = array_map(
228
+ function ( $type ) {
229
+ if ( false === strpos( $type, '/' ) ) {
230
+ return preg_quote( trim( $type, '.' ) );
231
+ } elseif ( $type = wpcf7_convert_mime_to_ext( $type ) ) {
232
+ return $type;
233
+ }
234
+ },
235
+ $types
236
  );
237
 
238
+ $types = wpcf7_array_flatten( $types );
239
+ $types = array_filter( array_unique( $types ) );
 
240
 
241
+ return implode( '|', $types );
 
 
 
 
 
 
 
242
  }
243
 
244
+ return '';
245
  }
246
 
247
 
312
 
313
 
314
  add_action(
315
+ 'shutdown',
316
  'wpcf7_cleanup_upload_files',
317
  20, 0
318
  );
325
  * Default 100.
326
  */
327
  function wpcf7_cleanup_upload_files( $seconds = 60, $max = 100 ) {
 
 
 
 
 
 
 
 
328
  $dir = trailingslashit( wpcf7_upload_tmp_dir() );
329
 
330
  if ( ! is_dir( $dir )
includes/form-tag.php CHANGED
@@ -1,5 +1,10 @@
1
  <?php
2
 
 
 
 
 
 
3
  class WPCF7_FormTag implements ArrayAccess {
4
 
5
  public $type;
@@ -25,20 +30,42 @@ class WPCF7_FormTag implements ArrayAccess {
25
  }
26
  }
27
 
 
 
 
 
28
  public function is_required() {
29
- return ( '*' == substr( $this->type, -1 ) );
30
  }
31
 
32
- public function has_option( $opt ) {
33
- $pattern = sprintf( '/^%s(:.+)?$/i', preg_quote( $opt, '/' ) );
 
 
 
 
34
  return (bool) preg_grep( $pattern, $this->options );
35
  }
36
 
37
- public function get_option( $opt, $pattern = '', $single = false ) {
 
 
 
 
 
 
 
 
 
 
 
 
38
  $preset_patterns = array(
39
  'date' => '[0-9]{4}-[0-9]{2}-[0-9]{2}',
40
  'int' => '[0-9]+',
41
- 'signed_int' => '-?[0-9]+',
 
 
42
  'class' => '[-0-9a-zA-Z_]+',
43
  'id' => '[-0-9a-zA-Z_]+',
44
  );
@@ -51,7 +78,11 @@ class WPCF7_FormTag implements ArrayAccess {
51
  $pattern = '.+';
52
  }
53
 
54
- $pattern = sprintf( '/^%s:%s$/i', preg_quote( $opt, '/' ), $pattern );
 
 
 
 
55
 
56
  if ( $single ) {
57
  $matches = $this->get_first_match_option( $pattern );
@@ -60,7 +91,7 @@ class WPCF7_FormTag implements ArrayAccess {
60
  return false;
61
  }
62
 
63
- return substr( $matches[0], strlen( $opt ) + 1 );
64
  } else {
65
  $matches_a = $this->get_all_match_options( $pattern );
66
 
@@ -71,32 +102,52 @@ class WPCF7_FormTag implements ArrayAccess {
71
  $results = array();
72
 
73
  foreach ( $matches_a as $matches ) {
74
- $results[] = substr( $matches[0], strlen( $opt ) + 1 );
75
  }
76
 
77
  return $results;
78
  }
79
  }
80
 
 
 
 
 
81
  public function get_id_option() {
82
  return $this->get_option( 'id', 'id', true );
83
  }
84
 
85
- public function get_class_option( $default = '' ) {
86
- if ( is_string( $default ) ) {
87
- $default = explode( ' ', $default );
 
 
 
 
 
 
 
 
88
  }
89
 
90
  $options = array_merge(
91
- (array) $default,
92
- (array) $this->get_option( 'class', 'class' ) );
 
93
 
94
  $options = array_filter( array_unique( $options ) );
95
 
96
  return implode( ' ', $options );
97
  }
98
 
99
- public function get_size_option( $default = '' ) {
 
 
 
 
 
 
 
100
  $option = $this->get_option( 'size', 'int', true );
101
 
102
  if ( $option ) {
@@ -106,16 +157,22 @@ class WPCF7_FormTag implements ArrayAccess {
106
  $matches_a = $this->get_all_match_options( '%^([0-9]*)/[0-9]*$%' );
107
 
108
  foreach ( (array) $matches_a as $matches ) {
109
- if ( isset( $matches[1] )
110
- and '' !== $matches[1] ) {
111
  return $matches[1];
112
  }
113
  }
114
 
115
- return $default;
116
  }
117
 
118
- public function get_maxlength_option( $default = '' ) {
 
 
 
 
 
 
 
119
  $option = $this->get_option( 'maxlength', 'int', true );
120
 
121
  if ( $option ) {
@@ -123,28 +180,43 @@ class WPCF7_FormTag implements ArrayAccess {
123
  }
124
 
125
  $matches_a = $this->get_all_match_options(
126
- '%^(?:[0-9]*x?[0-9]*)?/([0-9]+)$%' );
 
127
 
128
  foreach ( (array) $matches_a as $matches ) {
129
- if ( isset( $matches[1] ) && '' !== $matches[1] ) {
130
  return $matches[1];
131
  }
132
  }
133
 
134
- return $default;
135
  }
136
 
137
- public function get_minlength_option( $default = '' ) {
 
 
 
 
 
 
 
138
  $option = $this->get_option( 'minlength', 'int', true );
139
 
140
  if ( $option ) {
141
  return $option;
142
  } else {
143
- return $default;
144
  }
145
  }
146
 
147
- public function get_cols_option( $default = '' ) {
 
 
 
 
 
 
 
148
  $option = $this->get_option( 'cols', 'int', true );
149
 
150
  if ( $option ) {
@@ -152,18 +224,26 @@ class WPCF7_FormTag implements ArrayAccess {
152
  }
153
 
154
  $matches_a = $this->get_all_match_options(
155
- '%^([0-9]*)x([0-9]*)(?:/[0-9]+)?$%' );
 
156
 
157
  foreach ( (array) $matches_a as $matches ) {
158
- if ( isset( $matches[1] ) && '' !== $matches[1] ) {
159
  return $matches[1];
160
  }
161
  }
162
 
163
- return $default;
164
  }
165
 
166
- public function get_rows_option( $default = '' ) {
 
 
 
 
 
 
 
167
  $option = $this->get_option( 'rows', 'int', true );
168
 
169
  if ( $option ) {
@@ -171,20 +251,28 @@ class WPCF7_FormTag implements ArrayAccess {
171
  }
172
 
173
  $matches_a = $this->get_all_match_options(
174
- '%^([0-9]*)x([0-9]*)(?:/[0-9]+)?$%' );
 
175
 
176
  foreach ( (array) $matches_a as $matches ) {
177
- if ( isset( $matches[2] )
178
- and '' !== $matches[2] ) {
179
  return $matches[2];
180
  }
181
  }
182
 
183
- return $default;
184
  }
185
 
186
- public function get_date_option( $opt ) {
187
- $option_value = $this->get_option( $opt, '', true );
 
 
 
 
 
 
 
 
188
 
189
  if ( empty( $option_value ) ) {
190
  return false;
@@ -192,7 +280,7 @@ class WPCF7_FormTag implements ArrayAccess {
192
 
193
  $date = apply_filters( 'wpcf7_form_tag_date_option',
194
  null,
195
- array( $opt => $option_value )
196
  );
197
 
198
  if ( $date ) {
@@ -216,7 +304,16 @@ class WPCF7_FormTag implements ArrayAccess {
216
  return false;
217
  }
218
 
219
- public function get_default_option( $default = '', $args = '' ) {
 
 
 
 
 
 
 
 
 
220
  $args = wp_parse_args( $args, array(
221
  'multiple' => false,
222
  'shifted' => false,
@@ -226,14 +323,13 @@ class WPCF7_FormTag implements ArrayAccess {
226
  $values = array();
227
 
228
  if ( empty( $options ) ) {
229
- return $args['multiple'] ? $values : $default;
230
  }
231
 
232
  foreach ( $options as $opt ) {
233
  $opt = sanitize_key( $opt );
234
 
235
- if ( 'user_' == substr( $opt, 0, 5 )
236
- and is_user_logged_in() ) {
237
  $primary_props = array( 'user_login', 'user_email', 'user_url' );
238
  $opt = in_array( $opt, $primary_props ) ? $opt : substr( $opt, 5 );
239
 
@@ -248,10 +344,11 @@ class WPCF7_FormTag implements ArrayAccess {
248
  }
249
  }
250
 
251
- } elseif ( 'post_meta' == $opt and in_the_loop() ) {
252
  if ( $args['multiple'] ) {
253
  $values = array_merge( $values,
254
- get_post_meta( get_the_ID(), $this->name ) );
 
255
  } else {
256
  $val = (string) get_post_meta( get_the_ID(), $this->name, true );
257
 
@@ -260,7 +357,7 @@ class WPCF7_FormTag implements ArrayAccess {
260
  }
261
  }
262
 
263
- } elseif ( 'get' == $opt and isset( $_GET[$this->name] ) ) {
264
  $vals = (array) $_GET[$this->name];
265
  $vals = array_map( 'wpcf7_sanitize_query_var', $vals );
266
 
@@ -274,7 +371,7 @@ class WPCF7_FormTag implements ArrayAccess {
274
  }
275
  }
276
 
277
- } elseif ( 'post' == $opt and isset( $_POST[$this->name] ) ) {
278
  $vals = (array) $_POST[$this->name];
279
  $vals = array_map( 'wpcf7_sanitize_query_var', $vals );
280
 
@@ -288,7 +385,7 @@ class WPCF7_FormTag implements ArrayAccess {
288
  }
289
  }
290
 
291
- } elseif ( 'shortcode_attr' == $opt ) {
292
  if ( $contact_form = WPCF7_ContactForm::get_current() ) {
293
  $val = $contact_form->shortcode_attr( $this->name );
294
 
@@ -323,17 +420,31 @@ class WPCF7_FormTag implements ArrayAccess {
323
  $values = array_unique( $values );
324
  return $values;
325
  } else {
326
- return $default;
327
  }
328
  }
329
 
 
 
 
 
 
 
 
330
  public function get_data_option( $args = '' ) {
331
  $options = (array) $this->get_option( 'data' );
332
 
333
  return apply_filters( 'wpcf7_form_tag_data_option', null, $options, $args );
334
  }
335
 
336
- public function get_limit_option( $default = MB_IN_BYTES ) {
 
 
 
 
 
 
 
337
  $pattern = '/^limit:([1-9][0-9]*)([kKmM]?[bB])?$/';
338
 
339
  $matches = $this->get_first_match_option( $pattern );
@@ -344,9 +455,9 @@ class WPCF7_FormTag implements ArrayAccess {
344
  if ( ! empty( $matches[2] ) ) {
345
  $kbmb = strtolower( $matches[2] );
346
 
347
- if ( 'kb' == $kbmb ) {
348
  $size *= KB_IN_BYTES;
349
- } elseif ( 'mb' == $kbmb ) {
350
  $size *= MB_IN_BYTES;
351
  }
352
  }
@@ -354,9 +465,18 @@ class WPCF7_FormTag implements ArrayAccess {
354
  return $size;
355
  }
356
 
357
- return (int) $default;
358
  }
359
 
 
 
 
 
 
 
 
 
 
360
  public function get_first_match_option( $pattern ) {
361
  foreach( (array) $this->options as $option ) {
362
  if ( preg_match( $pattern, $option, $matches ) ) {
@@ -367,6 +487,14 @@ class WPCF7_FormTag implements ArrayAccess {
367
  return false;
368
  }
369
 
 
 
 
 
 
 
 
 
370
  public function get_all_match_options( $pattern ) {
371
  $result = array();
372
 
@@ -379,12 +507,24 @@ class WPCF7_FormTag implements ArrayAccess {
379
  return $result;
380
  }
381
 
 
 
 
 
 
 
382
  public function offsetSet( $offset, $value ) {
383
  if ( property_exists( __CLASS__, $offset ) ) {
384
  $this->{$offset} = $value;
385
  }
386
  }
387
 
 
 
 
 
 
 
388
  public function offsetGet( $offset ) {
389
  if ( property_exists( __CLASS__, $offset ) ) {
390
  return $this->{$offset};
@@ -393,10 +533,23 @@ class WPCF7_FormTag implements ArrayAccess {
393
  return null;
394
  }
395
 
 
 
 
 
 
 
396
  public function offsetExists( $offset ) {
397
  return property_exists( __CLASS__, $offset );
398
  }
399
 
 
 
 
 
 
 
400
  public function offsetUnset( $offset ) {
401
  }
 
402
  }
1
  <?php
2
 
3
+ /**
4
+ * A form-tag.
5
+ *
6
+ * @see https://contactform7.com/tag-syntax/#form_tag
7
+ */
8
  class WPCF7_FormTag implements ArrayAccess {
9
 
10
  public $type;
30
  }
31
  }
32
 
33
+
34
+ /**
35
+ * Returns true if the type has a trailing asterisk.
36
+ */
37
  public function is_required() {
38
+ return ( '*' === substr( $this->type, -1 ) );
39
  }
40
 
41
+
42
+ /**
43
+ * Returns true if the form-tag has a specified option.
44
+ */
45
+ public function has_option( $option_name ) {
46
+ $pattern = sprintf( '/^%s(:.+)?$/i', preg_quote( $option_name, '/' ) );
47
  return (bool) preg_grep( $pattern, $this->options );
48
  }
49
 
50
+
51
+ /**
52
+ * Retrieves option values with the specified option name.
53
+ *
54
+ * @param string $option_name Option name.
55
+ * @param string $pattern Optional. A regular expression pattern or one of
56
+ * the keys of preset patterns. If specified, only options
57
+ * whose value part matches this pattern will be returned.
58
+ * @param bool $single Optional. If true, only the first matching option
59
+ * will be returned. Default false.
60
+ * @return string|array The option value or an array of option values.
61
+ */
62
+ public function get_option( $option_name, $pattern = '', $single = false ) {
63
  $preset_patterns = array(
64
  'date' => '[0-9]{4}-[0-9]{2}-[0-9]{2}',
65
  'int' => '[0-9]+',
66
+ 'signed_int' => '[-]?[0-9]+',
67
+ 'num' => '(?:[0-9]+|(?:[0-9]+)?[.][0-9]+)',
68
+ 'signed_num' => '[-]?(?:[0-9]+|(?:[0-9]+)?[.][0-9]+)',
69
  'class' => '[-0-9a-zA-Z_]+',
70
  'id' => '[-0-9a-zA-Z_]+',
71
  );
78
  $pattern = '.+';
79
  }
80
 
81
+ $pattern = sprintf(
82
+ '/^%s:%s$/i',
83
+ preg_quote( $option_name, '/' ),
84
+ $pattern
85
+ );
86
 
87
  if ( $single ) {
88
  $matches = $this->get_first_match_option( $pattern );
91
  return false;
92
  }
93
 
94
+ return substr( $matches[0], strlen( $option_name ) + 1 );
95
  } else {
96
  $matches_a = $this->get_all_match_options( $pattern );
97
 
102
  $results = array();
103
 
104
  foreach ( $matches_a as $matches ) {
105
+ $results[] = substr( $matches[0], strlen( $option_name ) + 1 );
106
  }
107
 
108
  return $results;
109
  }
110
  }
111
 
112
+
113
+ /**
114
+ * Retrieves the id option value from the form-tag.
115
+ */
116
  public function get_id_option() {
117
  return $this->get_option( 'id', 'id', true );
118
  }
119
 
120
+
121
+ /**
122
+ * Retrieves the class option value from the form-tag.
123
+ *
124
+ * @param string|array $default_classes Optional. Preset classes as an array
125
+ * or a whitespace-separated list. Default empty string.
126
+ * @return string A whitespace-separated list of classes.
127
+ */
128
+ public function get_class_option( $default_classes = '' ) {
129
+ if ( is_string( $default_classes ) ) {
130
+ $default_classes = explode( ' ', $default_classes );
131
  }
132
 
133
  $options = array_merge(
134
+ (array) $default_classes,
135
+ (array) $this->get_option( 'class', 'class' )
136
+ );
137
 
138
  $options = array_filter( array_unique( $options ) );
139
 
140
  return implode( ' ', $options );
141
  }
142
 
143
+
144
+ /**
145
+ * Retrieves the size option value from the form-tag.
146
+ *
147
+ * @param string $default_value Optional default value.
148
+ * @return string The option value.
149
+ */
150
+ public function get_size_option( $default_value = '' ) {
151
  $option = $this->get_option( 'size', 'int', true );
152
 
153
  if ( $option ) {
157
  $matches_a = $this->get_all_match_options( '%^([0-9]*)/[0-9]*$%' );
158
 
159
  foreach ( (array) $matches_a as $matches ) {
160
+ if ( isset( $matches[1] ) and '' !== $matches[1] ) {
 
161
  return $matches[1];
162
  }
163
  }
164
 
165
+ return $default_value;
166
  }
167
 
168
+
169
+ /**
170
+ * Retrieves the maxlength option value from the form-tag.
171
+ *
172
+ * @param string $default_value Optional default value.
173
+ * @return string The option value.
174
+ */
175
+ public function get_maxlength_option( $default_value = '' ) {
176
  $option = $this->get_option( 'maxlength', 'int', true );
177
 
178
  if ( $option ) {
180
  }
181
 
182
  $matches_a = $this->get_all_match_options(
183
+ '%^(?:[0-9]*x?[0-9]*)?/([0-9]+)$%'
184
+ );
185
 
186
  foreach ( (array) $matches_a as $matches ) {
187
+ if ( isset( $matches[1] ) and '' !== $matches[1] ) {
188
  return $matches[1];
189
  }
190
  }
191
 
192
+ return $default_value;
193
  }
194
 
195
+
196
+ /**
197
+ * Retrieves the minlength option value from the form-tag.
198
+ *
199
+ * @param string $default_value Optional default value.
200
+ * @return string The option value.
201
+ */
202
+ public function get_minlength_option( $default_value = '' ) {
203
  $option = $this->get_option( 'minlength', 'int', true );
204
 
205
  if ( $option ) {
206
  return $option;
207
  } else {
208
+ return $default_value;
209
  }
210
  }
211
 
212
+
213
+ /**
214
+ * Retrieves the cols option value from the form-tag.
215
+ *
216
+ * @param string $default_value Optional default value.
217
+ * @return string The option value.
218
+ */
219
+ public function get_cols_option( $default_value = '' ) {
220
  $option = $this->get_option( 'cols', 'int', true );
221
 
222
  if ( $option ) {
224
  }
225
 
226
  $matches_a = $this->get_all_match_options(
227
+ '%^([0-9]*)x([0-9]*)(?:/[0-9]+)?$%'
228
+ );
229
 
230
  foreach ( (array) $matches_a as $matches ) {
231
+ if ( isset( $matches[1] ) and '' !== $matches[1] ) {
232
  return $matches[1];
233
  }
234
  }
235
 
236
+ return $default_value;
237
  }
238
 
239
+
240
+ /**
241
+ * Retrieves the rows option value from the form-tag.
242
+ *
243
+ * @param string $default_value Optional default value.
244
+ * @return string The option value.
245
+ */
246
+ public function get_rows_option( $default_value = '' ) {
247
  $option = $this->get_option( 'rows', 'int', true );
248
 
249
  if ( $option ) {
251
  }
252
 
253
  $matches_a = $this->get_all_match_options(
254
+ '%^([0-9]*)x([0-9]*)(?:/[0-9]+)?$%'
255
+ );
256
 
257
  foreach ( (array) $matches_a as $matches ) {
258
+ if ( isset( $matches[2] ) and '' !== $matches[2] ) {
 
259
  return $matches[2];
260
  }
261
  }
262
 
263
+ return $default_value;
264
  }
265
 
266
+
267
+ /**
268
+ * Retrieves a date-type option value from the form-tag.
269
+ *
270
+ * @param string $option_name A date-type option name, such as 'min' or 'max'.
271
+ * @return string|bool The option value in YYYY-MM-DD format. False if the
272
+ * option does not exist or the date value is invalid.
273
+ */
274
+ public function get_date_option( $option_name ) {
275
+ $option_value = $this->get_option( $option_name, '', true );
276
 
277
  if ( empty( $option_value ) ) {
278
  return false;
280
 
281
  $date = apply_filters( 'wpcf7_form_tag_date_option',
282
  null,
283
+ array( $option_name => $option_value )
284
  );
285
 
286
  if ( $date ) {
304
  return false;
305
  }
306
 
307
+
308
+ /**
309
+ * Retrieves the default option value from the form-tag.
310
+ *
311
+ * @param string|array $default_value Optional default value.
312
+ * @param string|array $args Optional options for the option value retrieval.
313
+ * @return string|array The option value. If the multiple option is enabled,
314
+ * an array of option values.
315
+ */
316
+ public function get_default_option( $default_value = '', $args = '' ) {
317
  $args = wp_parse_args( $args, array(
318
  'multiple' => false,
319
  'shifted' => false,
323
  $values = array();
324
 
325
  if ( empty( $options ) ) {
326
+ return $args['multiple'] ? $values : $default_value;
327
  }
328
 
329
  foreach ( $options as $opt ) {
330
  $opt = sanitize_key( $opt );
331
 
332
+ if ( 'user_' == substr( $opt, 0, 5 ) and is_user_logged_in() ) {
 
333
  $primary_props = array( 'user_login', 'user_email', 'user_url' );
334
  $opt = in_array( $opt, $primary_props ) ? $opt : substr( $opt, 5 );
335
 
344
  }
345
  }
346
 
347
+ } elseif ( 'post_meta' === $opt and in_the_loop() ) {
348
  if ( $args['multiple'] ) {
349
  $values = array_merge( $values,
350
+ get_post_meta( get_the_ID(), $this->name )
351
+ );
352
  } else {
353
  $val = (string) get_post_meta( get_the_ID(), $this->name, true );
354
 
357
  }
358
  }
359
 
360
+ } elseif ( 'get' === $opt and isset( $_GET[$this->name] ) ) {
361
  $vals = (array) $_GET[$this->name];
362
  $vals = array_map( 'wpcf7_sanitize_query_var', $vals );
363
 
371
  }
372
  }
373
 
374
+ } elseif ( 'post' === $opt and isset( $_POST[$this->name] ) ) {
375
  $vals = (array) $_POST[$this->name];
376
  $vals = array_map( 'wpcf7_sanitize_query_var', $vals );
377
 
385
  }
386
  }
387
 
388
+ } elseif ( 'shortcode_attr' === $opt ) {
389
  if ( $contact_form = WPCF7_ContactForm::get_current() ) {
390
  $val = $contact_form->shortcode_attr( $this->name );
391
 
420
  $values = array_unique( $values );
421
  return $values;
422
  } else {
423
+ return $default_value;
424
  }
425
  }
426
 
427
+
428
+ /**
429
+ * Retrieves the data option value from the form-tag.
430
+ *
431
+ * @param string|array $args Optional options for the option value retrieval.
432
+ * @return mixed The option value.
433
+ */
434
  public function get_data_option( $args = '' ) {
435
  $options = (array) $this->get_option( 'data' );
436
 
437
  return apply_filters( 'wpcf7_form_tag_data_option', null, $options, $args );
438
  }
439
 
440
+
441
+ /**
442
+ * Retrieves the limit option value from the form-tag.
443
+ *
444
+ * @param int $default_value Optional default value. Default 1048576.
445
+ * @return int The option value.
446
+ */
447
+ public function get_limit_option( $default_value = MB_IN_BYTES ) {
448
  $pattern = '/^limit:([1-9][0-9]*)([kKmM]?[bB])?$/';
449
 
450
  $matches = $this->get_first_match_option( $pattern );
455
  if ( ! empty( $matches[2] ) ) {
456
  $kbmb = strtolower( $matches[2] );
457
 
458
+ if ( 'kb' === $kbmb ) {
459
  $size *= KB_IN_BYTES;
460
+ } elseif ( 'mb' === $kbmb ) {
461
  $size *= MB_IN_BYTES;
462
  }
463
  }
465
  return $size;
466
  }
467
 
468
+ return (int) $default_value;
469
  }
470
 
471
+
472
+ /**
473
+ * Retrieves the value of the first option matches the given
474
+ * regular expression pattern.
475
+ *
476
+ * @param string $pattern Regular expression pattern.
477
+ * @return array|bool Option value as an array of matched strings.
478
+ * False if there is no option matches the pattern.
479
+ */
480
  public function get_first_match_option( $pattern ) {
481
  foreach( (array) $this->options as $option ) {
482
  if ( preg_match( $pattern, $option, $matches ) ) {
487
  return false;
488
  }
489
 
490
+
491
+ /**
492
+ * Retrieves values of options that match the given
493
+ * regular expression pattern.
494
+ *
495
+ * @param string $pattern Regular expression pattern.
496
+ * @return array Array of arrays of strings that match the pattern.
497
+ */
498
  public function get_all_match_options( $pattern ) {
499
  $result = array();
500
 
507
  return $result;
508
  }
509
 
510
+
511
+ /**
512
+ * Assigns a value to the specified offset.
513
+ *
514
+ * @see https://www.php.net/manual/en/arrayaccess.offsetset.php
515
+ */
516
  public function offsetSet( $offset, $value ) {
517
  if ( property_exists( __CLASS__, $offset ) ) {
518
  $this->{$offset} = $value;
519
  }
520
  }
521
 
522
+
523
+ /**
524
+ * Returns the value at specified offset.
525
+ *
526
+ * @see https://www.php.net/manual/en/arrayaccess.offsetget.php
527
+ */
528
  public function offsetGet( $offset ) {
529
  if ( property_exists( __CLASS__, $offset ) ) {
530
  return $this->{$offset};
533
  return null;
534
  }
535
 
536
+
537
+ /**
538
+ * Returns true if the specified offset exists.
539
+ *
540
+ * @see https://www.php.net/manual/en/arrayaccess.offsetexists.php
541
+ */
542
  public function offsetExists( $offset ) {
543
  return property_exists( __CLASS__, $offset );
544
  }
545
 
546
+
547
+ /**
548
+ * Unsets an offset.
549
+ *
550
+ * @see https://www.php.net/manual/en/arrayaccess.offsetunset.php
551
+ */
552
  public function offsetUnset( $offset ) {
553
  }
554
+
555
  }
includes/form-tags-manager.php CHANGED
@@ -1,23 +1,38 @@
1
  <?php
2
 
3
- function wpcf7_add_form_tag( $tag, $func, $features = '' ) {
 
 
 
4
  $manager = WPCF7_FormTagsManager::get_instance();
5
 
6
- return $manager->add( $tag, $func, $features );
7
  }
8
 
9
- function wpcf7_remove_form_tag( $tag ) {
 
 
 
 
10
  $manager = WPCF7_FormTagsManager::get_instance();
11
 
12
- return $manager->remove( $tag );
13
  }
14
 
 
 
 
 
15
  function wpcf7_replace_all_form_tags( $content ) {
16
  $manager = WPCF7_FormTagsManager::get_instance();
17
 
18
  return $manager->replace_all( $content );
19
  }
20
 
 
 
 
 
21
  function wpcf7_scan_form_tags( $cond = null ) {
22
  $contact_form = WPCF7_ContactForm::get_current();
23
 
@@ -28,12 +43,20 @@ function wpcf7_scan_form_tags( $cond = null ) {
28
  return array();
29
  }
30
 
31
- function wpcf7_form_tag_supports( $tag, $feature ) {
 
 
 
 
32
  $manager = WPCF7_FormTagsManager::get_instance();
33
 
34
- return $manager->tag_type_supports( $tag, $feature );
35
  }
36
 
 
 
 
 
37
  class WPCF7_FormTagsManager {
38
 
39
  private static $instance;
@@ -43,6 +66,12 @@ class WPCF7_FormTagsManager {
43
 
44
  private function __construct() {}
45
 
 
 
 
 
 
 
46
  public static function get_instance() {
47
  if ( empty( self::$instance ) ) {
48
  self::$instance = new self;
@@ -51,12 +80,29 @@ class WPCF7_FormTagsManager {
51
  return self::$instance;
52
  }
53
 
 
 
 
 
 
 
54
  public function get_scanned_tags() {
55
  return $this->scanned_tags;
56
  }
57
 
58
- public function add( $tag, $func, $features = '' ) {
59
- if ( ! is_callable( $func ) ) {
 
 
 
 
 
 
 
 
 
 
 
60
  return;
61
  }
62
 
@@ -66,67 +112,103 @@ class WPCF7_FormTagsManager {
66
 
67
  $features = wp_parse_args( $features, array() );
68
 
69
- $tags = array_filter( array_unique( (array) $tag ) );
70
 
71
- foreach ( $tags as $tag ) {
72
- $tag = $this->sanitize_tag_type( $tag );
73
 
74
- if ( ! $this->tag_type_exists( $tag ) ) {
75
- $this->tag_types[$tag] = array(
76
- 'function' => $func,
77
  'features' => $features,
78
  );
79
  }
80
  }
81
  }
82
 
83
- public function tag_type_exists( $tag ) {
84
- return isset( $this->tag_types[$tag] );
 
 
 
 
85
  }
86
 
87
- public function tag_type_supports( $tag, $feature ) {
88
- $feature = array_filter( (array) $feature );
89
 
90
- if ( isset( $this->tag_types[$tag]['features'] ) ) {
 
 
 
 
 
 
 
 
 
 
 
91
  return (bool) array_intersect(
92
- array_keys( array_filter( $this->tag_types[$tag]['features'] ) ),
93
- $feature
94
  );
95
  }
96
 
97
  return false;
98
  }
99
 
100
- public function collect_tag_types( $feature = null, $invert = false ) {
 
 
 
 
 
 
 
 
 
 
 
101
  $tag_types = array_keys( $this->tag_types );
102
 
103
- if ( empty( $feature ) ) {
104
  return $tag_types;
105
  }
106
 
107
  $output = array();
108
 
109
- foreach ( $tag_types as $tag ) {
110
- if ( ! $invert && $this->tag_type_supports( $tag, $feature )
111
- || $invert && ! $this->tag_type_supports( $tag, $feature ) ) {
112
- $output[] = $tag;
113
  }
114
  }
115
 
116
  return $output;
117
  }
118
 
119
- private function sanitize_tag_type( $tag ) {
120
- $tag = preg_replace( '/[^a-zA-Z0-9_*]+/', '_', $tag );
121
- $tag = rtrim( $tag, '_' );
122
- $tag = strtolower( $tag );
123
- return $tag;
 
 
 
 
124
  }
125
 
126
- public function remove( $tag ) {
127
- unset( $this->tag_types[$tag] );
 
 
 
 
128
  }
129
 
 
 
 
 
130
  public function normalize( $content ) {
131
  if ( empty( $this->tag_types ) ) {
132
  return $content;
@@ -135,40 +217,62 @@ class WPCF7_FormTagsManager {
135
  $content = preg_replace_callback(
136
  '/' . $this->tag_regex() . '/s',
137
  array( $this, 'normalize_callback' ),
138
- $content );
 
139
 
140
  return $content;
141
  }
142
 
143
- private function normalize_callback( $m ) {
 
 
 
 
144
  // allow [[foo]] syntax for escaping a tag
145
- if ( $m[1] == '['
146
- and $m[6] == ']' ) {
147
- return $m[0];
148
  }
149
 
150
- $tag = $m[2];
151
 
152
- $attr = trim( preg_replace( '/[\r\n\t ]+/', ' ', $m[3] ) );
153
  $attr = strtr( $attr, array( '<' => '&lt;', '>' => '&gt;' ) );
154
 
155
- $content = trim( $m[5] );
156
  $content = str_replace( "\n", '<WPPreserveNewline />', $content );
157
 
158
- $result = $m[1] . '[' . $tag
159
  . ( $attr ? ' ' . $attr : '' )
160
- . ( $m[4] ? ' ' . $m[4] : '' )
161
  . ']'
162
  . ( $content ? $content . '[/' . $tag . ']' : '' )
163
- . $m[6];
164
 
165
  return $result;
166
  }
167
 
 
 
 
 
 
 
 
168
  public function replace_all( $content ) {
169
  return $this->scan( $content, true );
170
  }
171
 
 
 
 
 
 
 
 
 
 
 
172
  public function scan( $content, $replace = false ) {
173
  $this->scanned_tags = array();
174
 
@@ -199,6 +303,15 @@ class WPCF7_FormTagsManager {
199
  }
200
  }
201
 
 
 
 
 
 
 
 
 
 
202
  public function filter( $input, $cond ) {
203
  if ( is_array( $input ) ) {
204
  $tags = $input;
@@ -208,56 +321,62 @@ class WPCF7_FormTagsManager {
208
  $tags = $this->scanned_tags;
209
  }
210
 
211
- if ( empty( $tags ) ) {
212
- return array();
213
- }
214
-
215
  $cond = wp_parse_args( $cond, array(
216
  'type' => array(),
 
217
  'name' => array(),
218
- 'feature' => '',
219
  ) );
220
 
221
- $type = array_filter( (array) $cond['type'] );
222
- $name = array_filter( (array) $cond['name'] );
223
- $feature = is_string( $cond['feature'] ) ? trim( $cond['feature'] ) : '';
224
 
225
- if ( '!' == substr( $feature, 0, 1 ) ) {
226
- $feature_negative = true;
227
- $feature = trim( substr( $feature, 1 ) );
228
- } else {
229
- $feature_negative = false;
230
- }
231
-
232
- $output = array();
233
 
234
- foreach ( $tags as $tag ) {
235
- $tag = new WPCF7_FormTag( $tag );
 
 
236
 
237
- if ( $type and ! in_array( $tag->type, $type, true ) ) {
238
- continue;
239
- }
 
240
 
241
- if ( $name and ! in_array( $tag->name, $name, true ) ) {
242
- continue;
243
- }
 
244
 
245
- if ( $feature ) {
246
- if ( ! $this->tag_type_supports( $tag->type, $feature )
247
- and ! $feature_negative ) {
248
- continue;
249
- } elseif ( $this->tag_type_supports( $tag->type, $feature )
250
- and $feature_negative ) {
251
- continue;
 
 
 
 
 
252
  }
253
- }
254
 
255
- $output[] = $tag;
256
- }
 
257
 
258
- return $output;
259
  }
260
 
 
 
 
 
261
  private function tag_regex() {
262
  $tagnames = array_keys( $this->tag_types );
263
  $tagregexp = implode( '|', array_map( 'preg_quote', $tagnames ) );
@@ -268,23 +387,31 @@ class WPCF7_FormTagsManager {
268
  . '(\]?)';
269
  }
270
 
271
- private function replace_callback( $m ) {
272
- return $this->scan_callback( $m, true );
 
 
 
 
273
  }
274
 
275
- private function scan_callback( $m, $replace = false ) {
 
 
 
 
276
  // allow [[foo]] syntax for escaping a tag
277
- if ( $m[1] == '['
278
- and $m[6] == ']' ) {
279
- return substr( $m[0], 1, -1 );
280
  }
281
 
282
- $tag = $m[2];
283
- $attr = $this->parse_atts( $m[3] );
284
 
285
  $scanned_tag = array(
286
- 'type' => $tag,
287
- 'basetype' => trim( $tag, '*' ),
288
  'raw_name' => '',
289
  'name' => '',
290
  'options' => array(),
@@ -296,20 +423,20 @@ class WPCF7_FormTagsManager {
296
  'content' => '',
297
  );
298
 
299
- if ( $this->tag_type_supports( $tag, 'singular' )
300
- and $this->filter( $this->scanned_tags, array( 'type' => $tag ) ) ) {
301
  // Another tag in the same type already exists. Ignore this one.
302
- return $m[0];
303
  }
304
 
305
  if ( is_array( $attr ) ) {
306
  if ( is_array( $attr['options'] ) ) {
307
- if ( $this->tag_type_supports( $tag, 'name-attr' )
308
  and ! empty( $attr['options'] ) ) {
309
  $scanned_tag['raw_name'] = array_shift( $attr['options'] );
310
 
311
  if ( ! wpcf7_is_name( $scanned_tag['raw_name'] ) ) {
312
- return $m[0]; // Invalid name is used. Ignore this tag.
313
  }
314
 
315
  $scanned_tag['name'] = strtr( $scanned_tag['raw_name'], '.', '_' );
@@ -337,7 +464,7 @@ class WPCF7_FormTagsManager {
337
  $scanned_tag['values'] = array_map( 'trim', $scanned_tag['values'] );
338
  $scanned_tag['labels'] = array_map( 'trim', $scanned_tag['labels'] );
339
 
340
- $content = trim( $m[5] );
341
  $content = preg_replace( "/<br[\r\n\t ]*\/?>$/m", '', $content );
342
  $scanned_tag['content'] = $content;
343
 
@@ -348,13 +475,23 @@ class WPCF7_FormTagsManager {
348
  $this->scanned_tags[] = $scanned_tag;
349
 
350
  if ( $replace ) {
351
- $func = $this->tag_types[$tag]['function'];
352
- return $m[1] . call_user_func( $func, $scanned_tag ) . $m[6];
353
  } else {
354
- return $m[0];
355
  }
356
  }
357
 
 
 
 
 
 
 
 
 
 
 
358
  private function parse_atts( $text ) {
359
  $atts = array( 'options' => array(), 'values' => array() );
360
  $text = preg_replace( "/[\x{00a0}\x{200b}]+/u", " ", $text );
@@ -362,13 +499,13 @@ class WPCF7_FormTagsManager {
362
 
363
  $pattern = '%^([-+*=0-9a-zA-Z:.!?#$&@_/|\%\r\n\t ]*?)((?:[\r\n\t ]*"[^"]*"|[\r\n\t ]*\'[^\']*\')*)$%';
364
 
365
- if ( preg_match( $pattern, $text, $match ) ) {
366
- if ( ! empty( $match[1] ) ) {
367
- $atts['options'] = preg_split( '/[\r\n\t ]+/', trim( $match[1] ) );
368
  }
369
 
370
- if ( ! empty( $match[2] ) ) {
371
- preg_match_all( '/"[^"]*"|\'[^\']*\'/', $match[2], $matched_values );
372
  $atts['values'] = wpcf7_strip_quote_deep( $matched_values[0] );
373
  }
374
  } else {
@@ -377,4 +514,5 @@ class WPCF7_FormTagsManager {
377
 
378
  return $atts;
379
  }
 
380
  }
1
  <?php
2
 
3
+ /**
4
+ * Wrapper function of WPCF7_FormTagsManager::add().
5
+ */
6
+ function wpcf7_add_form_tag( $tag_types, $callback, $features = '' ) {
7
  $manager = WPCF7_FormTagsManager::get_instance();
8
 
9
+ return $manager->add( $tag_types, $callback, $features );
10
  }
11
 
12
+
13
+ /**
14
+ * Wrapper function of WPCF7_FormTagsManager::remove().
15
+ */
16
+ function wpcf7_remove_form_tag( $tag_type ) {
17
  $manager = WPCF7_FormTagsManager::get_instance();
18
 
19
+ return $manager->remove( $tag_type );
20
  }
21
 
22
+
23
+ /**
24
+ * Wrapper function of WPCF7_FormTagsManager::replace_all().
25
+ */
26
  function wpcf7_replace_all_form_tags( $content ) {
27
  $manager = WPCF7_FormTagsManager::get_instance();
28
 
29
  return $manager->replace_all( $content );
30
  }
31
 
32
+
33
+ /**
34
+ * Wrapper function of WPCF7_ContactForm::scan_form_tags().
35
+ */
36
  function wpcf7_scan_form_tags( $cond = null ) {
37
  $contact_form = WPCF7_ContactForm::get_current();
38
 
43
  return array();
44
  }
45
 
46
+
47
+ /**
48
+ * Wrapper function of WPCF7_FormTagsManager::tag_type_supports().
49
+ */
50
+ function wpcf7_form_tag_supports( $tag_type, $feature ) {
51
  $manager = WPCF7_FormTagsManager::get_instance();
52
 
53
+ return $manager->tag_type_supports( $tag_type, $feature );
54
  }
55
 
56
+
57
+ /**
58
+ * The singleton instance of this class manages the collection of form-tags.
59
+ */
60
  class WPCF7_FormTagsManager {
61
 
62
  private static $instance;
66
 
67
  private function __construct() {}
68
 
69
+
70
+ /**
71
+ * Returns the singleton instance.
72
+ *
73
+ * @return WPCF7_FormTagsManager The singleton manager.
74
+ */
75
  public static function get_instance() {
76
  if ( empty( self::$instance ) ) {
77
  self::$instance = new self;
80
  return self::$instance;
81
  }
82
 
83
+
84
+ /**
85
+ * Returns scanned form-tags.
86
+ *
87
+ * @return array Array of WPCF7_FormTag objects.
88
+ */
89
  public function get_scanned_tags() {
90
  return $this->scanned_tags;
91
  }
92
 
93
+
94
+ /**
95
+ * Registers form-tag types to the manager.
96
+ *
97
+ * @param string|array $tag_types The name of the form-tag type or
98
+ * an array of the names.
99
+ * @param callable $callback The callback to generates a form control HTML
100
+ * for a form-tag in this type.
101
+ * @param string|array $features Optional. Features a form-tag
102
+ * in this type supports.
103
+ */
104
+ public function add( $tag_types, $callback, $features = '' ) {
105
+ if ( ! is_callable( $callback ) ) {
106
  return;
107
  }
108
 
112
 
113
  $features = wp_parse_args( $features, array() );
114
 
115
+ $tag_types = array_filter( array_unique( (array) $tag_types ) );
116
 
117
+ foreach ( $tag_types as $tag_type ) {
118
+ $tag_type = $this->sanitize_tag_type( $tag_type );
119
 
120
+ if ( ! $this->tag_type_exists( $tag_type ) ) {
121
+ $this->tag_types[$tag_type] = array(
122
+ 'function' => $callback,
123
  'features' => $features,
124
  );
125
  }
126
  }
127
  }
128
 
129
+
130
+ /**
131
+ * Returns true if the given tag type exists.
132
+ */
133
+ public function tag_type_exists( $tag_type ) {
134
+ return isset( $this->tag_types[$tag_type] );
135
  }
136
 
 
 
137
 
138
+ /**
139
+ * Returns true if the tag type supports the features.
140
+ *
141
+ * @param string $tag_type The name of the form-tag type.
142
+ * @param array|string $features The feature to check or an array of features.
143
+ * @return bool True if the form-tag type supports at least one of
144
+ * the given features, false otherwise.
145
+ */
146
+ public function tag_type_supports( $tag_type, $features ) {
147
+ $features = array_filter( (array) $features );
148
+
149
+ if ( isset( $this->tag_types[$tag_type]['features'] ) ) {
150
  return (bool) array_intersect(
151
+ array_keys( array_filter( $this->tag_types[$tag_type]['features'] ) ),
152
+ $features
153
  );
154
  }
155
 
156
  return false;
157
  }
158
 
159
+
160
+ /**
161
+ * Returns form-tag types that support the given features.
162
+ *
163
+ * @param array|string $features Optional. The feature to check or
164
+ * an array of features. Default empty array.
165
+ * @param bool $invert Optional. If this value is true, returns form-tag
166
+ * types that do not support the given features. Default false.
167
+ * @return array An array of form-tag types. If the $features param is empty,
168
+ * returns all form-tag types that have been registered.
169
+ */
170
+ public function collect_tag_types( $features = array(), $invert = false ) {
171
  $tag_types = array_keys( $this->tag_types );
172
 
173
+ if ( empty( $features ) ) {
174
  return $tag_types;
175
  }
176
 
177
  $output = array();
178
 
179
+ foreach ( $tag_types as $tag_type ) {
180
+ if ( ! $invert && $this->tag_type_supports( $tag_type, $features )
181
+ || $invert && ! $this->tag_type_supports( $tag_type, $features ) ) {
182
+ $output[] = $tag_type;
183
  }
184
  }
185
 
186
  return $output;
187
  }
188
 
189
+
190
+ /**
191
+ * Sanitizes the form-tag type name.
192
+ */
193
+ private function sanitize_tag_type( $tag_type ) {
194
+ $tag_type = preg_replace( '/[^a-zA-Z0-9_*]+/', '_', $tag_type );
195
+ $tag_type = rtrim( $tag_type, '_' );
196
+ $tag_type = strtolower( $tag_type );
197
+ return $tag_type;
198
  }
199
 
200
+
201
+ /**
202
+ * Deregisters the form-tag type.
203
+ */
204
+ public function remove( $tag_type ) {
205
+ unset( $this->tag_types[$tag_type] );
206
  }
207
 
208
+
209
+ /**
210
+ * Normalizes the text content that includes form-tags.
211
+ */
212
  public function normalize( $content ) {
213
  if ( empty( $this->tag_types ) ) {
214
  return $content;
217
  $content = preg_replace_callback(
218
  '/' . $this->tag_regex() . '/s',
219
  array( $this, 'normalize_callback' ),
220
+ $content
221
+ );
222
 
223
  return $content;
224
  }
225
 
226
+
227
+ /**
228
+ * The callback function used within normalize().
229
+ */
230
+ private function normalize_callback( $matches ) {
231
  // allow [[foo]] syntax for escaping a tag
232
+ if ( $matches[1] == '['
233
+ and $matches[6] == ']' ) {
234
+ return $matches[0];
235
  }
236
 
237
+ $tag = $matches[2];
238
 
239
+ $attr = trim( preg_replace( '/[\r\n\t ]+/', ' ', $matches[3] ) );
240
  $attr = strtr( $attr, array( '<' => '&lt;', '>' => '&gt;' ) );
241
 
242
+ $content = trim( $matches[5] );
243
  $content = str_replace( "\n", '<WPPreserveNewline />', $content );
244
 
245
+ $result = $matches[1] . '[' . $tag
246
  . ( $attr ? ' ' . $attr : '' )
247
+ . ( $matches[4] ? ' ' . $matches[4] : '' )
248
  . ']'
249
  . ( $content ? $content . '[/' . $tag . ']' : '' )
250
+ . $matches[6];
251
 
252
  return $result;
253
  }
254
 
255
+
256
+ /**
257
+ * Replaces all form-tags in the text content.
258
+ *
259
+ * @param string $content The text content including form-tags.
260
+ * @return string The result of replacements.
261
+ */
262
  public function replace_all( $content ) {
263
  return $this->scan( $content, true );
264
  }
265
 
266
+
267
+ /**
268
+ * Scans form-tags in the text content.
269
+ *
270
+ * @param string $content The text content including form-tags.
271
+ * @param bool $replace Optional. Whether scanned form-tags will be
272
+ * replaced. Default false.
273
+ * @return array|string An array of scanned form-tags if $replace is false.
274
+ * Otherwise text that scanned form-tags are replaced.
275
+ */
276
  public function scan( $content, $replace = false ) {
277
  $this->scanned_tags = array();
278
 
303
  }
304
  }
305
 
306
+
307
+ /**
308
+ * Filters form-tags based on a condition array argument.
309
+ *
310
+ * @param array|string $input The original form-tags collection.
311
+ * If it is a string, scans form-tags from it.
312
+ * @param array $cond The conditions that filtering will be based on.
313
+ * @return array The filtered form-tags collection.
314
+ */
315
  public function filter( $input, $cond ) {
316
  if ( is_array( $input ) ) {
317
  $tags = $input;
321
  $tags = $this->scanned_tags;
322
  }
323
 
 
 
 
 
324
  $cond = wp_parse_args( $cond, array(
325
  'type' => array(),
326
+ 'basetype' => array(),
327
  'name' => array(),
328
+ 'feature' => array(),
329
  ) );
330
 
331
+ $cond = array_map( function ( $c ) {
332
+ return array_filter( array_map( 'trim', (array) $c ) );
333
+ }, $cond );
334
 
335
+ $tags = array_filter(
336
+ (array) $tags,
337
+ function ( $tag ) use ( $cond ) {
338
+ $tag = new WPCF7_FormTag( $tag );
 
 
 
 
339
 
340
+ if ( $cond['type']
341
+ and ! in_array( $tag->type, $cond['type'], true ) ) {
342
+ return false;
343
+ }
344
 
345
+ if ( $cond['basetype']
346
+ and ! in_array( $tag->basetype, $cond['basetype'], true ) ) {
347
+ return false;
348
+ }
349
 
350
+ if ( $cond['name']
351
+ and ! in_array( $tag->name, $cond['name'], true ) ) {
352
+ return false;
353
+ }
354
 
355
+ foreach ( $cond['feature'] as $feature ) {
356
+ if ( '!' === substr( $feature, 0, 1 ) ) { // Negation
357
+ $feature = trim( substr( $feature, 1 ) );
358
+
359
+ if ( $this->tag_type_supports( $tag->type, $feature ) ) {
360
+ return false;
361
+ }
362
+ } else {
363
+ if ( ! $this->tag_type_supports( $tag->type, $feature ) ) {
364
+ return false;
365
+ }
366
+ }
367
  }
 
368
 
369
+ return true;
370
+ }
371
+ );
372
 
373
+ return array_values( $tags );
374
  }
375
 
376
+
377
+ /**
378
+ * Returns the regular expression for a form-tag.
379
+ */
380
  private function tag_regex() {
381
  $tagnames = array_keys( $this->tag_types );
382
  $tagregexp = implode( '|', array_map( 'preg_quote', $tagnames ) );
387
  . '(\]?)';
388
  }
389
 
390
+
391
+ /**
392
+ * The callback function for the form-tag replacement.
393
+ */
394
+ private function replace_callback( $matches ) {
395
+ return $this->scan_callback( $matches, true );
396
  }
397
 
398
+
399
+ /**
400
+ * The callback function for the form-tag scanning.
401
+ */
402
+ private function scan_callback( $matches, $replace = false ) {
403
  // allow [[foo]] syntax for escaping a tag
404
+ if ( $matches[1] == '['
405
+ and $matches[6] == ']' ) {
406
+ return substr( $matches[0], 1, -1 );
407
  }
408
 
409
+ $tag_type = $matches[2];
410
+ $attr = $this->parse_atts( $matches[3] );
411
 
412
  $scanned_tag = array(
413
+ 'type' => $tag_type,
414
+ 'basetype' => trim( $tag_type, '*' ),
415
  'raw_name' => '',
416
  'name' => '',
417
  'options' => array(),
423
  'content' => '',
424
  );
425
 
426
+ if ( $this->tag_type_supports( $tag_type, 'singular' )
427
+ and $this->filter( $this->scanned_tags, array( 'type' => $tag_type ) ) ) {
428
  // Another tag in the same type already exists. Ignore this one.
429
+ return $matches[0];
430
  }
431
 
432
  if ( is_array( $attr ) ) {
433
  if ( is_array( $attr['options'] ) ) {
434
+ if ( $this->tag_type_supports( $tag_type, 'name-attr' )
435
  and ! empty( $attr['options'] ) ) {
436
  $scanned_tag['raw_name'] = array_shift( $attr['options'] );
437
 
438
  if ( ! wpcf7_is_name( $scanned_tag['raw_name'] ) ) {
439
+ return $matches[0]; // Invalid name is used. Ignore this tag.
440
  }
441
 
442
  $scanned_tag['name'] = strtr( $scanned_tag['raw_name'], '.', '_' );
464
  $scanned_tag['values'] = array_map( 'trim', $scanned_tag['values'] );
465
  $scanned_tag['labels'] = array_map( 'trim', $scanned_tag['labels'] );
466
 
467
+ $content = trim( $matches[5] );
468
  $content = preg_replace( "/<br[\r\n\t ]*\/?>$/m", '', $content );
469
  $scanned_tag['content'] = $content;
470
 
475
  $this->scanned_tags[] = $scanned_tag;
476
 
477
  if ( $replace ) {
478
+ $callback = $this->tag_types[$tag_type]['function'];
479
+ return $matches[1] . call_user_func( $callback, $scanned_tag ) . $matches[6];
480
  } else {
481
+ return $matches[0];
482
  }
483
  }
484
 
485
+
486
+ /**
487
+ * Parses the attributes of a form-tag to extract the name,
488
+ * options, and values.
489
+ *
490
+ * @param string $text Attributes of a form-tag.
491
+ * @return array|string An associative array of the options and values
492
+ * if the input is in the correct syntax,
493
+ * otherwise the input text itself.
494
+ */
495
  private function parse_atts( $text ) {
496
  $atts = array( 'options' => array(), 'values' => array() );
497
  $text = preg_replace( "/[\x{00a0}\x{200b}]+/u", " ", $text );
499
 
500
  $pattern = '%^([-+*=0-9a-zA-Z:.!?#$&@_/|\%\r\n\t ]*?)((?:[\r\n\t ]*"[^"]*"|[\r\n\t ]*\'[^\']*\')*)$%';
501
 
502
+ if ( preg_match( $pattern, $text, $matches ) ) {
503
+ if ( ! empty( $matches[1] ) ) {
504
+ $atts['options'] = preg_split( '/[\r\n\t ]+/', trim( $matches[1] ) );
505
  }
506
 
507
+ if ( ! empty( $matches[2] ) ) {
508
+ preg_match_all( '/"[^"]*"|\'[^\']*\'/', $matches[2], $matched_values );
509
  $atts['values'] = wpcf7_strip_quote_deep( $matched_values[0] );
510
  }
511
  } else {
514
 
515
  return $atts;
516
  }
517
+
518
  }
includes/formatting.php CHANGED
@@ -6,23 +6,23 @@
6
  * This is a variant of wpautop() that is specifically tuned for
7
  * form content uses.
8
  *
9
- * @param string $pee The text which has to be formatted.
10
  * @param bool $br Optional. If set, this will convert all remaining
11
  * line breaks after paragraphing. Default true.
12
  * @return string Text which has been converted into correct paragraph tags.
13
  */
14
- function wpcf7_autop( $pee, $br = 1 ) {
15
- if ( trim( $pee ) === '' ) {
16
  return '';
17
  }
18
 
19
- $pee = $pee . "\n"; // just to make things a little easier, pad the end
20
- $pee = preg_replace( '|<br />\s*<br />|', "\n\n", $pee );
21
  // Space things out a little
22
  /* wpcf7: remove select and input */
23
  $allblocks = '(?:table|thead|tfoot|caption|col|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|form|map|area|blockquote|address|math|style|p|h[1-6]|hr|fieldset|legend|section|article|aside|hgroup|header|footer|nav|figure|figcaption|details|menu|summary)';
24
- $pee = preg_replace( '!(<' . $allblocks . '[^>]*>)!', "\n$1", $pee );
25
- $pee = preg_replace( '!(</' . $allblocks . '>)!', "$1\n\n", $pee );
26
 
27
  /* wpcf7: take care of [response], [recaptcha], and [hidden] tags */
28
  $form_tags_manager = WPCF7_FormTagsManager::get_instance();
@@ -31,67 +31,67 @@ function wpcf7_autop( $pee, $br = 1 ) {
31
  $block_hidden_form_tags = sprintf( '(?:%s)',
32
  implode( '|', $block_hidden_form_tags ) );
33
 
34
- $pee = preg_replace( '!(\[' . $block_hidden_form_tags . '[^]]*\])!',
35
- "\n$1\n\n", $pee );
36
 
37
- $pee = str_replace( array( "\r\n", "\r" ), "\n", $pee ); // cross-platform newlines
38
 
39
- if ( strpos( $pee, '<object' ) !== false ) {
40
- $pee = preg_replace( '|\s*<param([^>]*)>\s*|', "<param$1>", $pee ); // no pee inside object/embed
41
- $pee = preg_replace( '|\s*</embed>\s*|', '</embed>', $pee );
42
  }
43
 
44
- $pee = preg_replace( "/\n\n+/", "\n\n", $pee ); // take care of duplicates
45
  // make paragraphs, including one at the end
46
- $pees = preg_split( '/\n\s*\n/', $pee, -1, PREG_SPLIT_NO_EMPTY );
47
- $pee = '';
48
 
49
- foreach ( $pees as $tinkle ) {
50
- $pee .= '<p>' . trim( $tinkle, "\n" ) . "</p>\n";
51
  }
52
 
53
- $pee = preg_replace( '!<p>([^<]+)</(div|address|form|fieldset)>!', "<p>$1</p></$2>", $pee );
54
 
55
- $pee = preg_replace( '|<p>\s*</p>|', '', $pee ); // under certain strange conditions it could create a P of entirely whitespace
56
 
57
- $pee = preg_replace( '!<p>\s*(</?' . $allblocks . '[^>]*>)\s*</p>!', "$1", $pee ); // don't pee all over a tag
58
- $pee = preg_replace( "|<p>(<li.+?)</p>|", "$1", $pee ); // problem with nested lists
59
- $pee = preg_replace( '|<p><blockquote([^>]*)>|i', "<blockquote$1><p>", $pee );
60
- $pee = str_replace( '</blockquote></p>', '</p></blockquote>', $pee );
61
- $pee = preg_replace( '!<p>\s*(</?' . $allblocks . '[^>]*>)!', "$1", $pee );
62
- $pee = preg_replace( '!(</?' . $allblocks . '[^>]*>)\s*</p>!', "$1", $pee );
63
 
64
  /* wpcf7: take care of [response], [recaptcha], and [hidden] tag */
65
- $pee = preg_replace( '!<p>\s*(\[' . $block_hidden_form_tags . '[^]]*\])!',
66
- "$1", $pee );
67
- $pee = preg_replace( '!(\[' . $block_hidden_form_tags . '[^]]*\])\s*</p>!',
68
- "$1", $pee );
69
 
70
  if ( $br ) {
71
  /* wpcf7: add textarea */
72
- $pee = preg_replace_callback(
73
  '/<(script|style|textarea).*?<\/\\1>/s',
74
- 'wpcf7_autop_preserve_newline_callback', $pee );
75
- $pee = preg_replace( '|(?<!<br />)\s*\n|', "<br />\n", $pee ); // optionally make line breaks
76
- $pee = str_replace( '<WPPreserveNewline />', "\n", $pee );
77
 
78
  /* wpcf7: remove extra <br /> just added before [response], [recaptcha], and [hidden] tags */
79
- $pee = preg_replace( '!<br />\n(\[' . $block_hidden_form_tags . '[^]]*\])!',
80
- "\n$1", $pee );
81
  }
82
 
83
- $pee = preg_replace( '!(</?' . $allblocks . '[^>]*>)\s*<br />!', "$1", $pee );
84
- $pee = preg_replace( '!<br />(\s*</?(?:p|li|div|dl|dd|dt|th|pre|td|ul|ol)[^>]*>)!', '$1', $pee );
85
 
86
- if ( strpos( $pee, '<pre' ) !== false ) {
87
- $pee = preg_replace_callback( '!(<pre[^>]*>)(.*?)</pre>!is',
88
- 'clean_pre', $pee );
89
  }
90
 
91
- $pee = preg_replace( "|<br />$|", '', $pee );
92
- $pee = preg_replace( "|\n</p>$|", '</p>', $pee );
93
 
94
- return $pee;
95
  }
96
 
97
 
@@ -152,18 +152,18 @@ function wpcf7_strip_quote( $text ) {
152
  * Navigates through an array, object, or scalar, and
153
  * strips quote characters surrounding the each value.
154
  *
155
- * @param mixed $arr The array or string to be processed.
156
  * @return mixed Processed value.
157
  */
158
- function wpcf7_strip_quote_deep( $arr ) {
159
- if ( is_string( $arr ) ) {
160
- return wpcf7_strip_quote( $arr );
161
  }
162
 
163
- if ( is_array( $arr ) ) {
164
  $result = array();
165
 
166
- foreach ( $arr as $key => $text ) {
167
  $result[$key] = wpcf7_strip_quote_deep( $text );
168
  }
169
 
@@ -198,35 +198,35 @@ function wpcf7_normalize_newline( $text, $to = "\n" ) {
198
  * Navigates through an array, object, or scalar, and
199
  * normalizes newline characters in the each value.
200
  *
201
- * @param mixed $arr The array or string to be processed.
202
  * @param string $to Optional. The newline character that is used in the output.
203
  * @return mixed Processed value.
204
  */
205
- function wpcf7_normalize_newline_deep( $arr, $to = "\n" ) {
206
- if ( is_array( $arr ) ) {
207
  $result = array();
208
 
209
- foreach ( $arr as $key => $text ) {
210
  $result[$key] = wpcf7_normalize_newline_deep( $text, $to );
211
  }
212
 
213
  return $result;
214
  }
215
 
216
- return wpcf7_normalize_newline( $arr, $to );
217
  }
218
 
219
 
220
  /**
221
  * Strips newline characters.
222
  *
223
- * @param string $str Input text.
224
  * @return string Processed one-line text.
225
  */
226
- function wpcf7_strip_newline( $str ) {
227
- $str = (string) $str;
228
- $str = str_replace( array( "\r", "\n" ), '', $str );
229
- return trim( $str );
230
  }
231
 
232
 
6
  * This is a variant of wpautop() that is specifically tuned for
7
  * form content uses.
8
  *
9
+ * @param string $text The text which has to be formatted.
10
  * @param bool $br Optional. If set, this will convert all remaining
11
  * line breaks after paragraphing. Default true.
12
  * @return string Text which has been converted into correct paragraph tags.
13
  */
14
+ function wpcf7_autop( $text, $br = 1 ) {
15
+ if ( trim( $text ) === '' ) {
16
  return '';
17
  }
18
 
19
+ $text = $text . "\n"; // just to make things a little easier, pad the end
20
+ $text = preg_replace( '|<br />\s*<br />|', "\n\n", $text );
21
  // Space things out a little
22
  /* wpcf7: remove select and input */
23
  $allblocks = '(?:table|thead|tfoot|caption|col|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|form|map|area|blockquote|address|math|style|p|h[1-6]|hr|fieldset|legend|section|article|aside|hgroup|header|footer|nav|figure|figcaption|details|menu|summary)';
24
+ $text = preg_replace( '!(<' . $allblocks . '[^>]*>)!', "\n$1", $text );
25
+ $text = preg_replace( '!(</' . $allblocks . '>)!', "$1\n\n", $text );
26
 
27
  /* wpcf7: take care of [response], [recaptcha], and [hidden] tags */
28
  $form_tags_manager = WPCF7_FormTagsManager::get_instance();
31
  $block_hidden_form_tags = sprintf( '(?:%s)',
32
  implode( '|', $block_hidden_form_tags ) );
33
 
34
+ $text = preg_replace( '!(\[' . $block_hidden_form_tags . '[^]]*\])!',
35
+ "\n$1\n\n", $text );
36
 
37
+ $text = str_replace( array( "\r\n", "\r" ), "\n", $text ); // cross-platform newlines
38
 
39
+ if ( strpos( $text, '<object' ) !== false ) {
40
+ $text = preg_replace( '|\s*<param([^>]*)>\s*|', "<param$1>", $text ); // no pee inside object/embed
41
+ $text = preg_replace( '|\s*</embed>\s*|', '</embed>', $text );
42
  }
43
 
44
+ $text = preg_replace( "/\n\n+/", "\n\n", $text ); // take care of duplicates
45
  // make paragraphs, including one at the end
46
+ $paragraphs = preg_split( '/\n\s*\n/', $text, -1, PREG_SPLIT_NO_EMPTY );
47
+ $text = '';
48
 
49
+ foreach ( $paragraphs as $paragraph ) {
50
+ $text .= '<p>' . trim( $paragraph, "\n" ) . "</p>\n";
51
  }
52
 
53
+ $text = preg_replace( '!<p>([^<]+)</(div|address|form|fieldset)>!', "<p>$1</p></$2>", $text );
54
 
55
+ $text = preg_replace( '|<p>\s*</p>|', '', $text ); // under certain strange conditions it could create a P of entirely whitespace
56
 
57
+ $text = preg_replace( '!<p>\s*(</?' . $allblocks . '[^>]*>)\s*</p>!', "$1", $text ); // don't pee all over a tag
58
+ $text = preg_replace( "|<p>(<li.+?)</p>|", "$1", $text ); // problem with nested lists
59
+ $text = preg_replace( '|<p><blockquote([^>]*)>|i', "<blockquote$1><p>", $text );
60
+ $text = str_replace( '</blockquote></p>', '</p></blockquote>', $text );
61
+ $text = preg_replace( '!<p>\s*(</?' . $allblocks . '[^>]*>)!', "$1", $text );
62
+ $text = preg_replace( '!(</?' . $allblocks . '[^>]*>)\s*</p>!', "$1", $text );
63
 
64
  /* wpcf7: take care of [response], [recaptcha], and [hidden] tag */
65
+ $text = preg_replace( '!<p>\s*(\[' . $block_hidden_form_tags . '[^]]*\])!',
66
+ "$1", $text );
67
+ $text = preg_replace( '!(\[' . $block_hidden_form_tags . '[^]]*\])\s*</p>!',
68
+ "$1", $text );
69
 
70
  if ( $br ) {
71
  /* wpcf7: add textarea */
72
+ $text = preg_replace_callback(
73
  '/<(script|style|textarea).*?<\/\\1>/s',
74
+ 'wpcf7_autop_preserve_newline_callback', $text );
75
+ $text = preg_replace( '|(?<!<br />)\s*\n|', "<br />\n", $text ); // optionally make line breaks
76
+ $text = str_replace( '<WPPreserveNewline />', "\n", $text );
77
 
78
  /* wpcf7: remove extra <br /> just added before [response], [recaptcha], and [hidden] tags */
79
+ $text = preg_replace( '!<br />\n(\[' . $block_hidden_form_tags . '[^]]*\])!',
80
+ "\n$1", $text );
81
  }
82
 
83
+ $text = preg_replace( '!(</?' . $allblocks . '[^>]*>)\s*<br />!', "$1", $text );
84
+ $text = preg_replace( '!<br />(\s*</?(?:p|li|div|dl|dd|dt|th|pre|td|ul|ol)[^>]*>)!', '$1', $text );
85
 
86
+ if ( strpos( $text, '<pre' ) !== false ) {
87
+ $text = preg_replace_callback( '!(<pre[^>]*>)(.*?)</pre>!is',
88
+ 'clean_pre', $text );
89
  }
90
 
91
+ $text = preg_replace( "|<br />$|", '', $text );
92
+ $text = preg_replace( "|\n</p>$|", '</p>', $text );
93
 
94
+ return $text;
95
  }
96
 
97
 
152
  * Navigates through an array, object, or scalar, and
153
  * strips quote characters surrounding the each value.
154
  *
155
+ * @param mixed $input The array or string to be processed.
156
  * @return mixed Processed value.
157
  */
158
+ function wpcf7_strip_quote_deep( $input ) {
159
+ if ( is_string( $input ) ) {
160
+ return wpcf7_strip_quote( $input );
161
  }
162
 
163
+ if ( is_array( $input ) ) {
164
  $result = array();
165
 
166
+ foreach ( $input as $key => $text ) {
167
  $result[$key] = wpcf7_strip_quote_deep( $text );
168
  }
169
 
198
  * Navigates through an array, object, or scalar, and
199
  * normalizes newline characters in the each value.
200
  *
201
+ * @param mixed $input The array or string to be processed.
202
  * @param string $to Optional. The newline character that is used in the output.
203
  * @return mixed Processed value.
204
  */
205
+ function wpcf7_normalize_newline_deep( $input, $to = "\n" ) {
206
+ if ( is_array( $input ) ) {
207
  $result = array();
208
 
209
+ foreach ( $input as $key => $text ) {
210
  $result[$key] = wpcf7_normalize_newline_deep( $text, $to );
211
  }
212
 
213
  return $result;
214
  }
215
 
216
+ return wpcf7_normalize_newline( $input, $to );
217
  }
218
 
219
 
220
  /**
221
  * Strips newline characters.
222
  *
223
+ * @param string $text Input text.
224
  * @return string Processed one-line text.
225
  */
226
+ function wpcf7_strip_newline( $text ) {
227
+ $text = (string) $text;
228
+ $text = str_replace( array( "\r", "\n" ), '', $text );
229
+ return trim( $text );
230
  }
231
 
232
 
includes/functions.php CHANGED
@@ -126,6 +126,23 @@ function wpcf7_array_flatten( $input ) {
126
  }
127
 
128
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  /**
130
  * Creates a comma-separated list from a multi-dimensional array.
131
  *
@@ -150,7 +167,12 @@ function wpcf7_flat_join( $input ) {
150
  * Returns true if HTML5 is supported.
151
  */
152
  function wpcf7_support_html5() {
153
- return (bool) apply_filters( 'wpcf7_support_html5', true );
 
 
 
 
 
154
  }
155
 
156
 
@@ -479,11 +501,11 @@ function wpcf7_build_query( $args, $key = '' ) {
479
  *
480
  * @see http://www.w3.org/TR/html5/infrastructure.html#code-unit-length
481
  *
482
- * @param string $string Input string.
483
  * @return int|bool The number of code units, or false if
484
  * mb_convert_encoding is not available.
485
  */
486
- function wpcf7_count_code_units( $string ) {
487
  static $use_mb = null;
488
 
489
  if ( is_null( $use_mb ) ) {
@@ -494,18 +516,18 @@ function wpcf7_count_code_units( $string ) {
494
  return false;
495
  }
496
 
497
- $string = (string) $string;
498
- $string = str_replace( "\r\n", "\n", $string );
499
 
500
- $encoding = mb_detect_encoding( $string, mb_detect_order(), true );
501
 
502
  if ( $encoding ) {
503
- $string = mb_convert_encoding( $string, 'UTF-16', $encoding );
504
  } else {
505
- $string = mb_convert_encoding( $string, 'UTF-16', 'UTF-8' );
506
  }
507
 
508
- $byte_count = mb_strlen( $string, '8bit' );
509
 
510
  return floor( $byte_count / 2 );
511
  }
@@ -515,27 +537,27 @@ function wpcf7_count_code_units( $string ) {
515
  * Returns true if WordPress is running on the localhost.
516
  */
517
  function wpcf7_is_localhost() {
518
- $server_name = strtolower( $_SERVER['SERVER_NAME'] );
519
- return in_array( $server_name, array( 'localhost', '127.0.0.1' ) );
520
  }
521
 
522
 
523
  /**
524
  * Marks a function as deprecated and informs when it has been used.
525
  *
526
- * @param string $function The function that was called.
527
  * @param string $version The version of Contact Form 7 that deprecated
528
  * the function.
529
  * @param string $replacement The function that should have been called.
530
  */
531
- function wpcf7_deprecated_function( $function, $version, $replacement ) {
532
  if ( WP_DEBUG ) {
533
  if ( function_exists( '__' ) ) {
534
  trigger_error(
535
  sprintf(
536
  /* translators: 1: PHP function name, 2: version number, 3: alternative function name */
537
  __( 'Function %1$s is <strong>deprecated</strong> since Contact Form 7 version %2$s! Use %3$s instead.', 'contact-form-7' ),
538
- $function, $version, $replacement
539
  ),
540
  E_USER_DEPRECATED
541
  );
@@ -543,7 +565,7 @@ function wpcf7_deprecated_function( $function, $version, $replacement ) {
543
  trigger_error(
544
  sprintf(
545
  'Function %1$s is <strong>deprecated</strong> since Contact Form 7 version %2$s! Use %3$s instead.',
546
- $function, $version, $replacement
547
  ),
548
  E_USER_DEPRECATED
549
  );
@@ -555,42 +577,56 @@ function wpcf7_deprecated_function( $function, $version, $replacement ) {
555
  /**
556
  * Fires functions attached to a deprecated filter hook.
557
  *
558
- * @param string $tag The name of the filter hook.
559
  * @param array $args Array of additional function arguments to be
560
  * passed to apply_filters().
561
  * @param string $version The version of Contact Form 7 that deprecated
562
  * the hook.
563
  * @param string $replacement The hook that should have been used.
564
  */
565
- function wpcf7_apply_filters_deprecated( $tag, $args, $version, $replacement ) {
566
- if ( ! has_filter( $tag ) ) {
567
  return $args[0];
568
  }
569
 
570
- if ( WP_DEBUG ) {
571
- trigger_error(
572
- sprintf(
573
- /* translators: 1: WordPress hook name, 2: version number, 3: alternative hook name */
574
- __( 'Hook %1$s is <strong>deprecated</strong> since Contact Form 7 version %2$s! Use %3$s instead.', 'contact-form-7' ),
575
- $tag, $version, $replacement
576
- ),
577
- E_USER_DEPRECATED
578
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
579
  }
580
 
581
- return apply_filters_ref_array( $tag, $args );
582
  }
583
 
584
 
585
  /**
586
  * Marks something as being incorrectly called.
587
  *
588
- * @param string $function The function that was called.
589
  * @param string $message A message explaining what has been done incorrectly.
590
  * @param string $version The version of Contact Form 7 where the message
591
  * was added.
592
  */
593
- function wpcf7_doing_it_wrong( $function, $message, $version ) {
594
  if ( WP_DEBUG ) {
595
  if ( function_exists( '__' ) ) {
596
  if ( $version ) {
@@ -605,7 +641,7 @@ function wpcf7_doing_it_wrong( $function, $message, $version ) {
605
  sprintf(
606
  /* translators: Developer debugging message. 1: PHP function name, 2: Explanatory message, 3: Contact Form 7 version number. */
607
  __( 'Function %1$s was called incorrectly. %2$s %3$s', 'contact-form-7' ),
608
- $function,
609
  $message,
610
  $version
611
  ),
@@ -622,7 +658,7 @@ function wpcf7_doing_it_wrong( $function, $message, $version ) {
622
  trigger_error(
623
  sprintf(
624
  'Function %1$s was called incorrectly. %2$s %3$s',
625
- $function,
626
  $message,
627
  $version
628
  ),
126
  }
127
 
128
 
129
+ /**
130
+ * Excludes unset or blank text values from the given array.
131
+ *
132
+ * @param array $input The array.
133
+ * @return array Array without blank text values.
134
+ */
135
+ function wpcf7_exclude_blank( $input ) {
136
+ $output = array_filter( $input,
137
+ function ( $i ) {
138
+ return isset( $i ) && '' !== $i;
139
+ }
140
+ );
141
+
142
+ return array_values( $output );
143
+ }
144
+
145
+
146
  /**
147
  * Creates a comma-separated list from a multi-dimensional array.
148
  *
167
  * Returns true if HTML5 is supported.
168
  */
169
  function wpcf7_support_html5() {
170
+ return (bool) wpcf7_apply_filters_deprecated(
171
+ 'wpcf7_support_html5',
172
+ array( true ),
173
+ '5.6',
174
+ ''
175
+ );
176
  }
177
 
178
 
501
  *
502
  * @see http://www.w3.org/TR/html5/infrastructure.html#code-unit-length
503
  *
504
+ * @param string $text Input string.
505
  * @return int|bool The number of code units, or false if
506
  * mb_convert_encoding is not available.
507
  */
508
+ function wpcf7_count_code_units( $text ) {
509
  static $use_mb = null;
510
 
511
  if ( is_null( $use_mb ) ) {
516
  return false;
517
  }
518
 
519
+ $text = (string) $text;
520
+ $text = str_replace( "\r\n", "\n", $text );
521
 
522
+ $encoding = mb_detect_encoding( $text, mb_detect_order(), true );
523
 
524
  if ( $encoding ) {
525
+ $text = mb_convert_encoding( $text, 'UTF-16', $encoding );
526
  } else {
527
+ $text = mb_convert_encoding( $text, 'UTF-16', 'UTF-8' );
528
  }
529
 
530
+ $byte_count = mb_strlen( $text, '8bit' );
531
 
532
  return floor( $byte_count / 2 );
533
  }
537
  * Returns true if WordPress is running on the localhost.
538
  */
539
  function wpcf7_is_localhost() {
540
+ $sitename = wp_parse_url( network_home_url(), PHP_URL_HOST );
541
+ return in_array( strtolower( $sitename ), array( 'localhost', '127.0.0.1' ) );
542
  }
543
 
544
 
545
  /**
546
  * Marks a function as deprecated and informs when it has been used.
547
  *
548
+ * @param string $function_name The function that was called.
549
  * @param string $version The version of Contact Form 7 that deprecated
550
  * the function.
551
  * @param string $replacement The function that should have been called.
552
  */
553
+ function wpcf7_deprecated_function( $function_name, $version, $replacement ) {
554
  if ( WP_DEBUG ) {
555
  if ( function_exists( '__' ) ) {
556
  trigger_error(
557
  sprintf(
558
  /* translators: 1: PHP function name, 2: version number, 3: alternative function name */
559
  __( 'Function %1$s is <strong>deprecated</strong> since Contact Form 7 version %2$s! Use %3$s instead.', 'contact-form-7' ),
560
+ $function_name, $version, $replacement
561
  ),
562
  E_USER_DEPRECATED
563
  );
565
  trigger_error(
566
  sprintf(
567
  'Function %1$s is <strong>deprecated</strong> since Contact Form 7 version %2$s! Use %3$s instead.',
568
+ $function_name, $version, $replacement
569
  ),
570
  E_USER_DEPRECATED
571
  );
577
  /**
578
  * Fires functions attached to a deprecated filter hook.
579
  *
580
+ * @param string $hook_name The name of the filter hook.
581
  * @param array $args Array of additional function arguments to be
582
  * passed to apply_filters().
583
  * @param string $version The version of Contact Form 7 that deprecated
584
  * the hook.
585
  * @param string $replacement The hook that should have been used.
586
  */
587
+ function wpcf7_apply_filters_deprecated( $hook_name, $args, $version, $replacement = '' ) {
588
+ if ( ! has_filter( $hook_name ) ) {
589
  return $args[0];
590
  }
591
 
592
+ if ( WP_DEBUG and apply_filters( 'deprecated_hook_trigger_error', true ) ) {
593
+ if ( $replacement ) {
594
+ trigger_error(
595
+ sprintf(
596
+ /* translators: 1: WordPress hook name, 2: version number, 3: alternative hook name */
597
+ __( 'Hook %1$s is <strong>deprecated</strong> since Contact Form 7 version %2$s! Use %3$s instead.', 'contact-form-7' ),
598
+ $hook_name,
599
+ $version,
600
+ $replacement
601
+ ),
602
+ E_USER_DEPRECATED
603
+ );
604
+ } else {
605
+ trigger_error(
606
+ sprintf(
607
+ /* translators: 1: WordPress hook name, 2: version number */
608
+ __( 'Hook %1$s is <strong>deprecated</strong> since Contact Form 7 version %2$s with no alternative available.', 'contact-form-7' ),
609
+ $hook_name,
610
+ $version
611
+ ),
612
+ E_USER_DEPRECATED
613
+ );
614
+ }
615
  }
616
 
617
+ return apply_filters_ref_array( $hook_name, $args );
618
  }
619
 
620
 
621
  /**
622
  * Marks something as being incorrectly called.
623
  *
624
+ * @param string $function_name The function that was called.
625
  * @param string $message A message explaining what has been done incorrectly.
626
  * @param string $version The version of Contact Form 7 where the message
627
  * was added.
628
  */
629
+ function wpcf7_doing_it_wrong( $function_name, $message, $version ) {
630
  if ( WP_DEBUG ) {
631
  if ( function_exists( '__' ) ) {
632
  if ( $version ) {
641
  sprintf(
642
  /* translators: Developer debugging message. 1: PHP function name, 2: Explanatory message, 3: Contact Form 7 version number. */
643
  __( 'Function %1$s was called incorrectly. %2$s %3$s', 'contact-form-7' ),
644
+ $function_name,
645
  $message,
646
  $version
647
  ),
658
  trigger_error(
659
  sprintf(
660
  'Function %1$s was called incorrectly. %2$s %3$s',
661
+ $function_name,
662
  $message,
663
  $version
664
  ),
includes/js/index.js CHANGED
@@ -1 +1 @@
1
- !function(){"use strict";const e=e=>Math.abs(parseInt(e,10)),t=(e,t)=>{const r=new Map([["init","init"],["validation_failed","invalid"],["acceptance_missing","unaccepted"],["spam","spam"],["aborted","aborted"],["mail_sent","sent"],["mail_failed","failed"],["submitting","submitting"],["resetting","resetting"],["payment_required","payment-required"]]);r.has(t)&&(t=r.get(t)),Array.from(r.values()).includes(t)||(t=`custom-${t=(t=t.replace(/[^0-9a-z]+/i," ").trim()).replace(/\s+/,"-")}`);const n=e.getAttribute("data-status");return e.wpcf7.status=t,e.setAttribute("data-status",t),e.classList.add(t),n&&n!==t&&e.classList.remove(n),t},r=(e,t,r)=>{const n=new CustomEvent(`wpcf7${t}`,{bubbles:!0,detail:r});"string"==typeof e&&(e=document.querySelector(e)),e.dispatchEvent(n)},n=e=>{const{root:t,namespace:r="contact-form-7/v1"}=wpcf7.api;return a.reduceRight(((e,t)=>r=>t(r,e)),(e=>{let n,a,{url:c,path:s,endpoint:o,headers:i,body:l,data:p,...d}=e;"string"==typeof o&&(n=r.replace(/^\/|\/$/g,""),a=o.replace(/^\//,""),s=a?n+"/"+a:n),"string"==typeof s&&(-1!==t.indexOf("?")&&(s=s.replace("?","&")),s=s.replace(/^\//,""),c=t+s),i={Accept:"application/json, */*;q=0.1",...i},delete i["X-WP-Nonce"],p&&(l=JSON.stringify(p),i["Content-Type"]="application/json");const u={code:"fetch_error",message:"You are probably offline."},f={code:"invalid_json",message:"The response is not a valid JSON response."};return window.fetch(c||s||window.location.href,{...d,headers:i,body:l}).then((e=>Promise.resolve(e).then((e=>{if(e.status>=200&&e.status<300)return e;throw e})).then((e=>{if(204===e.status)return null;if(e&&e.json)return e.json().catch((()=>{throw f}));throw f}))),(()=>{throw u}))}))(e)},a=[];function c(e){let a=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(wpcf7.blocked)return s(e),void t(e,"submitting");const c=new FormData(e);a.submitter&&a.submitter.name&&c.append(a.submitter.name,a.submitter.value);const o={contactFormId:e.wpcf7.id,pluginVersion:e.wpcf7.pluginVersion,contactFormLocale:e.wpcf7.locale,unitTag:e.wpcf7.unitTag,containerPostId:e.wpcf7.containerPost,status:e.wpcf7.status,inputs:Array.from(c,(e=>{const t=e[0],r=e[1];return!t.match(/^_/)&&{name:t,value:r}})).filter((e=>!1!==e)),formData:c},i=t=>{const r=document.createElement("li");r.setAttribute("id",t.error_id),t.idref?r.insertAdjacentHTML("beforeend",`<a href="#${t.idref}">${t.message}</a>`):r.insertAdjacentText("beforeend",t.message),e.wpcf7.parent.querySelector(".screen-reader-response ul").appendChild(r)},l=t=>{const r=e.querySelector(t.into),n=r.querySelector(".wpcf7-form-control");n.classList.add("wpcf7-not-valid"),n.setAttribute("aria-describedby",t.error_id);const a=document.createElement("span");a.setAttribute("class","wpcf7-not-valid-tip"),a.setAttribute("aria-hidden","true"),a.insertAdjacentText("beforeend",t.message),r.appendChild(a),r.querySelectorAll("[aria-invalid]").forEach((e=>{e.setAttribute("aria-invalid","true")})),n.closest(".use-floating-validation-tip")&&(n.addEventListener("focus",(e=>{a.setAttribute("style","display: none")})),a.addEventListener("mouseover",(e=>{a.setAttribute("style","display: none")})))};n({endpoint:`contact-forms/${e.wpcf7.id}/feedback`,method:"POST",body:c,wpcf7:{endpoint:"feedback",form:e,detail:o}}).then((n=>{const a=t(e,n.status);return o.status=n.status,o.apiResponse=n,["invalid","unaccepted","spam","aborted"].includes(a)?r(e,a,o):["sent","failed"].includes(a)&&r(e,`mail${a}`,o),r(e,"submit",o),n})).then((t=>{t.posted_data_hash&&(e.querySelector('input[name="_wpcf7_posted_data_hash"]').value=t.posted_data_hash),"mail_sent"===t.status&&(e.reset(),e.wpcf7.resetOnMailSent=!0),t.invalid_fields&&(t.invalid_fields.forEach(i),t.invalid_fields.forEach(l)),e.wpcf7.parent.querySelector('.screen-reader-response [role="status"]').insertAdjacentText("beforeend",t.message),e.querySelectorAll(".wpcf7-response-output").forEach((e=>{e.innerText=t.message}))})).catch((e=>console.error(e)))}n.use=e=>{a.unshift(e)},n.use(((e,n)=>{if(e.wpcf7&&"feedback"===e.wpcf7.endpoint){const{form:n,detail:a}=e.wpcf7;s(n),r(n,"beforesubmit",a),t(n,"submitting")}return n(e)}));const s=e=>{e.wpcf7.parent.querySelector('.screen-reader-response [role="status"]').innerText="",e.wpcf7.parent.querySelector(".screen-reader-response ul").innerText="",e.querySelectorAll(".wpcf7-not-valid-tip").forEach((e=>{e.remove()})),e.querySelectorAll("[aria-invalid]").forEach((e=>{e.setAttribute("aria-invalid","false")})),e.querySelectorAll(".wpcf7-form-control").forEach((e=>{e.removeAttribute("aria-describedby"),e.classList.remove("wpcf7-not-valid")})),e.querySelectorAll(".wpcf7-response-output").forEach((e=>{e.innerText=""}))};function o(e){const a=new FormData(e),c={contactFormId:e.wpcf7.id,pluginVersion:e.wpcf7.pluginVersion,contactFormLocale:e.wpcf7.locale,unitTag:e.wpcf7.unitTag,containerPostId:e.wpcf7.containerPost,status:e.wpcf7.status,inputs:Array.from(a,(e=>{const t=e[0],r=e[1];return!t.match(/^_/)&&{name:t,value:r}})).filter((e=>!1!==e)),formData:a};n({endpoint:`contact-forms/${e.wpcf7.id}/refill`,method:"GET",wpcf7:{endpoint:"refill",form:e,detail:c}}).then((n=>{e.wpcf7.resetOnMailSent?(delete e.wpcf7.resetOnMailSent,t(e,"mail_sent")):t(e,"init"),c.apiResponse=n,r(e,"reset",c)})).catch((e=>console.error(e)))}n.use(((e,r)=>{if(e.wpcf7&&"refill"===e.wpcf7.endpoint){const{form:r,detail:n}=e.wpcf7;s(r),t(r,"resetting")}return r(e)}));const i=(e,t)=>{for(const r in t){const n=t[r];e.querySelectorAll(`input[name="${r}"]`).forEach((e=>{e.value=""})),e.querySelectorAll(`img.wpcf7-captcha-${r}`).forEach((e=>{e.setAttribute("src",n)}));const a=/([0-9]+)\.(png|gif|jpeg)$/.exec(n);a&&e.querySelectorAll(`input[name="_wpcf7_captcha_challenge_${r}"]`).forEach((e=>{e.value=a[1]}))}},l=(e,t)=>{for(const r in t){const n=t[r][0],a=t[r][1];e.querySelectorAll(`.wpcf7-form-control-wrap.${r}`).forEach((e=>{e.querySelector(`input[name="${r}"]`).value="",e.querySelector(".wpcf7-quiz-label").textContent=n,e.querySelector(`input[name="_wpcf7_quiz_answer_${r}"]`).value=a}))}};function p(t){const r=new FormData(t);t.wpcf7={id:e(r.get("_wpcf7")),status:t.getAttribute("data-status"),pluginVersion:r.get("_wpcf7_version"),locale:r.get("_wpcf7_locale"),unitTag:r.get("_wpcf7_unit_tag"),containerPost:e(r.get("_wpcf7_container_post")),parent:t.closest(".wpcf7")},t.querySelectorAll(".has-spinner").forEach((e=>{e.insertAdjacentHTML("afterend",'<span class="wpcf7-spinner"></span>')})),(e=>{e.querySelectorAll(".wpcf7-exclusive-checkbox").forEach((t=>{t.addEventListener("change",(t=>{const r=t.target.getAttribute("name");e.querySelectorAll(`input[type="checkbox"][name="${r}"]`).forEach((e=>{e!==t.target&&(e.checked=!1)}))}))}))})(t),(e=>{e.querySelectorAll(".has-free-text").forEach((t=>{const r=t.querySelector("input.wpcf7-free-text"),n=t.querySelector('input[type="checkbox"], input[type="radio"]');r.disabled=!n.checked,e.addEventListener("change",(e=>{r.disabled=!n.checked,e.target===n&&n.checked&&r.focus()}))}))})(t),(e=>{e.querySelectorAll(".wpcf7-validates-as-url").forEach((e=>{e.addEventListener("change",(t=>{let r=e.value.trim();r&&!r.match(/^[a-z][a-z0-9.+-]*:/i)&&-1!==r.indexOf(".")&&(r=r.replace(/^\/+/,""),r="http://"+r),e.value=r}))}))})(t),(e=>{if(!e.querySelector(".wpcf7-acceptance")||e.classList.contains("wpcf7-acceptance-as-validation"))return;const t=()=>{let t=!0;e.querySelectorAll(".wpcf7-acceptance").forEach((e=>{if(!t||e.classList.contains("optional"))return;const r=e.querySelector('input[type="checkbox"]');(e.classList.contains("invert")&&r.checked||!e.classList.contains("invert")&&!r.checked)&&(t=!1)})),e.querySelectorAll(".wpcf7-submit").forEach((e=>{e.disabled=!t}))};t(),e.addEventListener("change",(e=>{t()})),e.addEventListener("wpcf7reset",(e=>{t()}))})(t),(t=>{const r=(t,r)=>{const n=e(t.getAttribute("data-starting-value")),a=e(t.getAttribute("data-maximum-value")),c=e(t.getAttribute("data-minimum-value")),s=t.classList.contains("down")?n-r.value.length:r.value.length;t.setAttribute("data-current-value",s),t.innerText=s,a&&a<r.value.length?t.classList.add("too-long"):t.classList.remove("too-long"),c&&r.value.length<c?t.classList.add("too-short"):t.classList.remove("too-short")},n=e=>{e={init:!1,...e},t.querySelectorAll(".wpcf7-character-count").forEach((n=>{const a=n.getAttribute("data-target-name"),c=t.querySelector(`[name="${a}"]`);c&&(c.value=c.defaultValue,r(n,c),e.init&&c.addEventListener("keyup",(e=>{r(n,c)})))}))};n({init:!0}),t.addEventListener("wpcf7reset",(e=>{n()}))})(t),window.addEventListener("load",(e=>{wpcf7.cached&&t.reset()})),t.addEventListener("reset",(e=>{wpcf7.reset(t)})),t.addEventListener("submit",(e=>{const r=e.submitter;wpcf7.submit(t,{submitter:r}),e.preventDefault()})),t.addEventListener("wpcf7submit",(e=>{e.detail.apiResponse.captcha&&i(t,e.detail.apiResponse.captcha),e.detail.apiResponse.quiz&&l(t,e.detail.apiResponse.quiz)})),t.addEventListener("wpcf7reset",(e=>{e.detail.apiResponse.captcha&&i(t,e.detail.apiResponse.captcha),e.detail.apiResponse.quiz&&l(t,e.detail.apiResponse.quiz)}))}document.addEventListener("DOMContentLoaded",(e=>{var t;if("undefined"==typeof wpcf7)return void console.error("wpcf7 is not defined.");if(void 0===wpcf7.api)return void console.error("wpcf7.api is not defined.");if("function"!=typeof window.fetch)return void console.error("Your browser doesn't support window.fetch().");if("function"!=typeof window.FormData)return void console.error("Your browser doesn't support window.FormData().");const r=document.querySelectorAll(".wpcf7 > form");"function"==typeof r.forEach?(wpcf7={init:p,submit:c,reset:o,...null!==(t=wpcf7)&&void 0!==t?t:{}},r.forEach((e=>wpcf7.init(e)))):console.error("Your browser doesn't support NodeList.forEach().")}))}();
1
+ !function(){"use strict";var e={d:function(t,n){for(var a in n)e.o(n,a)&&!e.o(t,a)&&Object.defineProperty(t,a,{enumerable:!0,get:n[a]})},o:function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r:function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};e.r(t),e.d(t,{date:function(){return m},email:function(){return p},file:function(){return h},maxdate:function(){return x},maxfilesize:function(){return A},maxlength:function(){return v},maxnumber:function(){return b},mindate:function(){return y},minlength:function(){return w},minnumber:function(){return g},number:function(){return u},required:function(){return c},requiredfile:function(){return l},tel:function(){return f},url:function(){return d}});const n=e=>Math.abs(parseInt(e,10)),a=(e,t)=>{const n=new Map([["init","init"],["validation_failed","invalid"],["acceptance_missing","unaccepted"],["spam","spam"],["aborted","aborted"],["mail_sent","sent"],["mail_failed","failed"],["submitting","submitting"],["resetting","resetting"],["validating","validating"],["payment_required","payment-required"]]);n.has(t)&&(t=n.get(t)),Array.from(n.values()).includes(t)||(t=`custom-${t=(t=t.replace(/[^0-9a-z]+/i," ").trim()).replace(/\s+/,"-")}`);const a=e.getAttribute("data-status");return e.wpcf7.status=t,e.setAttribute("data-status",t),e.classList.add(t),a&&a!==t&&e.classList.remove(a),t},o=(e,t,n)=>{const a=new CustomEvent(`wpcf7${t}`,{bubbles:!0,detail:n});"string"==typeof e&&(e=document.querySelector(e)),e.dispatchEvent(a)},i=e=>{const{root:t,namespace:n="contact-form-7/v1"}=wpcf7.api,a=s.reduceRight(((e,t)=>n=>t(n,e)),(e=>{let a,o,{url:i,path:s,endpoint:r,headers:c,body:l,data:p,...d}=e;"string"==typeof r&&(a=n.replace(/^\/|\/$/g,""),o=r.replace(/^\//,""),s=o?a+"/"+o:a),"string"==typeof s&&(-1!==t.indexOf("?")&&(s=s.replace("?","&")),s=s.replace(/^\//,""),i=t+s),c={Accept:"application/json, */*;q=0.1",...c},delete c["X-WP-Nonce"],p&&(l=JSON.stringify(p),c["Content-Type"]="application/json");const f={code:"fetch_error",message:"You are probably offline."},u={code:"invalid_json",message:"The response is not a valid JSON response."};return window.fetch(i||s||window.location.href,{...d,headers:c,body:l}).then((e=>Promise.resolve(e).then((e=>{if(e.status>=200&&e.status<300)return e;throw e})).then((e=>{if(204===e.status)return null;if(e&&e.json)return e.json().catch((()=>{throw u}));throw u}))),(()=>{throw f}))}));return a(e)},s=[];function r(e){let{rule:t,field:n,error:a,...o}=e;this.rule=t,this.field=n,this.error=a,this.properties=o}i.use=e=>{s.unshift(e)};const c=function(e){if(0===e.getAll(this.field).length)throw new r(this)},l=function(e){if(0===e.getAll(this.field).length)throw new r(this)},p=function(e){if(!e.getAll(this.field).every((e=>{if((e=e.trim()).length<6)return!1;if(-1===e.indexOf("@",1))return!1;if(e.indexOf("@")!==e.lastIndexOf("@"))return!1;const[t,n]=e.split("@",2);if(!/^[a-zA-Z0-9!#$%&\'*+\/=?^_`{|}~\.-]+$/.test(t))return!1;if(/\.{2,}/.test(n))return!1;if(/(?:^[ \t\n\r\0\x0B.]|[ \t\n\r\0\x0B.]$)/.test(n))return!1;const a=n.split(".");if(a.length<2)return!1;for(const e of a){if(/(?:^[ \t\n\r\0\x0B-]|[ \t\n\r\0\x0B-]$)/.test(e))return!1;if(!/^[a-z0-9-]+$/i.test(e))return!1}return!0})))throw new r(this)},d=function(e){const t=e.getAll(this.field);if(!t.every((e=>{if(""===(e=e.trim()))return!1;try{return(e=>-1!==["http","https","ftp","ftps","mailto","news","irc","irc6","ircs","gopher","nntp","feed","telnet","mms","rtsp","sms","svn","tel","fax","xmpp","webcal","urn"].indexOf(e))(new URL(e).protocol.replace(/:$/,""))}catch{return!1}})))throw new r(this)},f=function(e){if(!e.getAll(this.field).every((e=>(e=(e=e.trim()).replaceAll(/[()/.*#\s-]+/g,""),/^[+]?[0-9]+$/.test(e)))))throw new r(this)},u=function(e){if(!e.getAll(this.field).every((e=>(e=e.trim(),!!/^[-]?[0-9]+(?:[eE][+-]?[0-9]+)?$/.test(e)||!!/^[-]?(?:[0-9]+)?[.][0-9]+(?:[eE][+-]?[0-9]+)?$/.test(e)))))throw new r(this)},m=function(e){if(!e.getAll(this.field).every((e=>/^[0-9]{4,}-[0-9]{2}-[0-9]{2}$/.test(e.trim()))))throw new r(this)},h=function(e){if(!e.getAll(this.field).every((e=>{var t;return e instanceof File&&(null===(t=this.accept)||void 0===t?void 0:t.some((t=>/^\.[a-z0-9]+$/i.test(t)?e.name.toLowerCase().endsWith(t.toLowerCase()):(e=>{const t=[],n=e.match(/^(?<toplevel>[a-z]+)\/(?<sub>[*]|[a-z0-9.+-]+)$/i);if(n){const e=n.groups.toplevel.toLowerCase(),a=n.groups.sub.toLowerCase();for(const[o,i]of(()=>{const e=new Map;return e.set("jpg|jpeg|jpe","image/jpeg"),e.set("gif","image/gif"),e.set("png","image/png"),e.set("bmp","image/bmp"),e.set("tiff|tif","image/tiff"),e.set("webp","image/webp"),e.set("ico","image/x-icon"),e.set("heic","image/heic"),e.set("asf|asx","video/x-ms-asf"),e.set("wmv","video/x-ms-wmv"),e.set("wmx","video/x-ms-wmx"),e.set("wm","video/x-ms-wm"),e.set("avi","video/avi"),e.set("divx","video/divx"),e.set("flv","video/x-flv"),e.set("mov|qt","video/quicktime"),e.set("mpeg|mpg|mpe","video/mpeg"),e.set("mp4|m4v","video/mp4"),e.set("ogv","video/ogg"),e.set("webm","video/webm"),e.set("mkv","video/x-matroska"),e.set("3gp|3gpp","video/3gpp"),e.set("3g2|3gp2","video/3gpp2"),e.set("txt|asc|c|cc|h|srt","text/plain"),e.set("csv","text/csv"),e.set("tsv","text/tab-separated-values"),e.set("ics","text/calendar"),e.set("rtx","text/richtext"),e.set("css","text/css"),e.set("htm|html","text/html"),e.set("vtt","text/vtt"),e.set("dfxp","application/ttaf+xml"),e.set("mp3|m4a|m4b","audio/mpeg"),e.set("aac","audio/aac"),e.set("ra|ram","audio/x-realaudio"),e.set("wav","audio/wav"),e.set("ogg|oga","audio/ogg"),e.set("flac","audio/flac"),e.set("mid|midi","audio/midi"),e.set("wma","audio/x-ms-wma"),e.set("wax","audio/x-ms-wax"),e.set("mka","audio/x-matroska"),e.set("rtf","application/rtf"),e.set("js","application/javascript"),e.set("pdf","application/pdf"),e.set("swf","application/x-shockwave-flash"),e.set("class","application/java"),e.set("tar","application/x-tar"),e.set("zip","application/zip"),e.set("gz|gzip","application/x-gzip"),e.set("rar","application/rar"),e.set("7z","application/x-7z-compressed"),e.set("exe","application/x-msdownload"),e.set("psd","application/octet-stream"),e.set("xcf","application/octet-stream"),e.set("doc","application/msword"),e.set("pot|pps|ppt","application/vnd.ms-powerpoint"),e.set("wri","application/vnd.ms-write"),e.set("xla|xls|xlt|xlw","application/vnd.ms-excel"),e.set("mdb","application/vnd.ms-access"),e.set("mpp","application/vnd.ms-project"),e.set("docx","application/vnd.openxmlformats-officedocument.wordprocessingml.document"),e.set("docm","application/vnd.ms-word.document.macroEnabled.12"),e.set("dotx","application/vnd.openxmlformats-officedocument.wordprocessingml.template"),e.set("dotm","application/vnd.ms-word.template.macroEnabled.12"),e.set("xlsx","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"),e.set("xlsm","application/vnd.ms-excel.sheet.macroEnabled.12"),e.set("xlsb","application/vnd.ms-excel.sheet.binary.macroEnabled.12"),e.set("xltx","application/vnd.openxmlformats-officedocument.spreadsheetml.template"),e.set("xltm","application/vnd.ms-excel.template.macroEnabled.12"),e.set("xlam","application/vnd.ms-excel.addin.macroEnabled.12"),e.set("pptx","application/vnd.openxmlformats-officedocument.presentationml.presentation"),e.set("pptm","application/vnd.ms-powerpoint.presentation.macroEnabled.12"),e.set("ppsx","application/vnd.openxmlformats-officedocument.presentationml.slideshow"),e.set("ppsm","application/vnd.ms-powerpoint.slideshow.macroEnabled.12"),e.set("potx","application/vnd.openxmlformats-officedocument.presentationml.template"),e.set("potm","application/vnd.ms-powerpoint.template.macroEnabled.12"),e.set("ppam","application/vnd.ms-powerpoint.addin.macroEnabled.12"),e.set("sldx","application/vnd.openxmlformats-officedocument.presentationml.slide"),e.set("sldm","application/vnd.ms-powerpoint.slide.macroEnabled.12"),e.set("onetoc|onetoc2|onetmp|onepkg","application/onenote"),e.set("oxps","application/oxps"),e.set("xps","application/vnd.ms-xpsdocument"),e.set("odt","application/vnd.oasis.opendocument.text"),e.set("odp","application/vnd.oasis.opendocument.presentation"),e.set("ods","application/vnd.oasis.opendocument.spreadsheet"),e.set("odg","application/vnd.oasis.opendocument.graphics"),e.set("odc","application/vnd.oasis.opendocument.chart"),e.set("odb","application/vnd.oasis.opendocument.database"),e.set("odf","application/vnd.oasis.opendocument.formula"),e.set("wp|wpd","application/wordperfect"),e.set("key","application/vnd.apple.keynote"),e.set("numbers","application/vnd.apple.numbers"),e.set("pages","application/vnd.apple.pages"),e})())("*"===a&&i.startsWith(e+"/")||i===n[0])&&t.push(...o.split("|"))}return t})(t).some((t=>(t="."+t.trim(),e.name.toLowerCase().endsWith(t.toLowerCase())))))))})))throw new r(this)},w=function(e){const t=e.getAll(this.field);let n=0;if(t.forEach((e=>{"string"==typeof e&&(n+=e.length)})),n<parseInt(this.threshold))throw new r(this)},v=function(e){const t=e.getAll(this.field);let n=0;if(t.forEach((e=>{"string"==typeof e&&(n+=e.length)})),parseInt(this.threshold)<n)throw new r(this)},g=function(e){if(!e.getAll(this.field).every((e=>!(parseFloat(e)<parseFloat(this.threshold)))))throw new r(this)},b=function(e){if(!e.getAll(this.field).every((e=>!(parseFloat(this.threshold)<parseFloat(e)))))throw new r(this)},y=function(e){if(!e.getAll(this.field).every((e=>(e=e.trim(),!(/^[0-9]{4,}-[0-9]{2}-[0-9]{2}$/.test(e)&&/^[0-9]{4,}-[0-9]{2}-[0-9]{2}$/.test(this.threshold)&&e<this.threshold)))))throw new r(this)},x=function(e){if(!e.getAll(this.field).every((e=>(e=e.trim(),!(/^[0-9]{4,}-[0-9]{2}-[0-9]{2}$/.test(e)&&/^[0-9]{4,}-[0-9]{2}-[0-9]{2}$/.test(this.threshold)&&this.threshold<e)))))throw new r(this)},A=function(e){const t=e.getAll(this.field);let n=0;if(t.forEach((e=>{e instanceof File&&(n+=e.size)})),parseInt(this.threshold)<n)throw new r(this)};function E(e){if(this.formData={},this.tree={},!(e instanceof FormData))return this;this.formData=e;const t=()=>{const e=new Map;return e.largestIndex=0,e.set=function(t,n){""===t?t=e.largestIndex++:/^[0-9]+$/.test(t)&&(t=parseInt(t),e.largestIndex<=t&&(e.largestIndex=t+1)),Map.prototype.set.call(e,t,n)},e};this.tree=t();const n=/^(?<name>[a-z][-a-z0-9_:]*)(?<array>(?:\[(?:[a-z][-a-z0-9_:]*|[0-9]*)\])*)/i;for(const[e,a]of this.formData){const o=e.match(n);if(o)if(""===o.groups.array)this.tree.set(o.groups.name,a);else{const e=[...o.groups.array.matchAll(/\[([a-z][-a-z0-9_:]*|[0-9]*)\]/gi)].map((e=>{let[t,n]=e;return n}));e.unshift(o.groups.name);const n=e.pop();e.reduce(((e,n)=>{if(/^[0-9]+$/.test(n)&&(n=parseInt(n)),e.get(n)instanceof Map)return e.get(n);const a=t();return e.set(n,a),a}),this.tree).set(n,a)}}}function q(e){var t,n,o,i;let s=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const c=e;if(null===(t=s.target)||void 0===t||!t.closest(".wpcf7-form-control-wrap[data-name]"))return;if(null!==(n=s.target)&&void 0!==n&&n.closest(".novalidate"))return;const l=new FormData,p=[];for(const e of c.querySelectorAll(".wpcf7-form-control-wrap"))if(!e.closest(".novalidate")&&(e.querySelectorAll(":where( input, textarea, select ):enabled").forEach((e=>{if(e.name)switch(e.type){case"button":case"image":case"reset":case"submit":break;case"checkbox":case"radio":e.checked&&l.append(e.name,e.value);break;case"select-multiple":for(const t of e.selectedOptions)l.append(e.name,t.value);break;case"file":for(const t of e.files)l.append(e.name,t);break;default:l.append(e.name,e.value)}})),e.dataset.name&&(p.push(e.dataset.name),e.setAttribute("data-under-validation","1"),e.dataset.name===s.target.name.replace(/\[.*\]$/,""))))break;const d=null!==(o=q.validators)&&void 0!==o?o:{},f=(null!==(i=e.wpcf7.schema.rules)&&void 0!==i?i:[]).filter((e=>{let{rule:t,...n}=e;return"function"==typeof d[t]&&("function"==typeof d[t].matches?d[t].matches(n,s):p.includes(n.field))}));if(!f.length)return;const u=e.getAttribute("data-status");Promise.resolve(a(e,"validating")).then((t=>{const n=[],a=new E(l);for(const{rule:t,...o}of f)if(!n.includes(o.field))try{_(e,o.field),d[t].call({rule:t,...o},a)}catch(t){t instanceof r&&(S(e,o.field,t.error),n.push(o.field))}})).finally((()=>{a(e,u),e.querySelectorAll(".wpcf7-form-control-wrap[data-under-validation]").forEach((e=>{e.removeAttribute("data-under-validation")}))}))}E.prototype.entries=function(){return this.tree.entries()},E.prototype.get=function(e){return this.tree.get(e)},E.prototype.getAll=function(e){if(!this.has(e))return[];const t=e=>{const n=[];if(e instanceof Map)for(const[a,o]of e)n.push(...t(o));else""!==e&&n.push(e);return n};return t(this.get(e))},E.prototype.has=function(e){return this.tree.has(e)},E.prototype.keys=function(){return this.tree.keys()},E.prototype.values=function(){return this.tree.values()},q.validators=t;const S=(e,t,n)=>{var a;const o=`${null===(a=e.wpcf7)||void 0===a?void 0:a.unitTag}-ve-${t}`,i=e.querySelector(`.wpcf7-form-control-wrap[data-name="${t}"] .wpcf7-form-control`);(()=>{const t=document.createElement("li");t.setAttribute("id",o),i&&i.id?t.insertAdjacentHTML("beforeend",`<a href="#${i.id}">${n}</a>`):t.insertAdjacentText("beforeend",n),e.wpcf7.parent.querySelector(".screen-reader-response ul").appendChild(t)})(),e.querySelectorAll(`.wpcf7-form-control-wrap[data-name="${t}"]`).forEach((t=>{if("validating"===e.getAttribute("data-status")&&!t.dataset.underValidation)return;const a=document.createElement("span");a.classList.add("wpcf7-not-valid-tip"),a.setAttribute("aria-hidden","true"),a.insertAdjacentText("beforeend",n),t.appendChild(a),t.querySelectorAll("[aria-invalid]").forEach((e=>{e.setAttribute("aria-invalid","true")})),t.querySelectorAll(".wpcf7-form-control").forEach((e=>{e.classList.add("wpcf7-not-valid"),e.setAttribute("aria-describedby",o),"function"==typeof e.setCustomValidity&&e.setCustomValidity(n),e.closest(".use-floating-validation-tip")&&(e.addEventListener("focus",(e=>{a.setAttribute("style","display: none")})),a.addEventListener("click",(e=>{a.setAttribute("style","display: none")})))}))}))},_=(e,t)=>{var n,a;const o=`${null===(n=e.wpcf7)||void 0===n?void 0:n.unitTag}-ve-${t}`;null===(a=e.wpcf7.parent.querySelector(`.screen-reader-response ul li#${o}`))||void 0===a||a.remove(),e.querySelectorAll(`.wpcf7-form-control-wrap[data-name="${t}"]`).forEach((e=>{var t;null===(t=e.querySelector(".wpcf7-not-valid-tip"))||void 0===t||t.remove(),e.querySelectorAll("[aria-invalid]").forEach((e=>{e.setAttribute("aria-invalid","false")})),e.querySelectorAll(".wpcf7-form-control").forEach((e=>{e.removeAttribute("aria-describedby"),e.classList.remove("wpcf7-not-valid"),"function"==typeof e.setCustomValidity&&e.setCustomValidity("")}))}))};function $(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(wpcf7.blocked)return L(e),void a(e,"submitting");const n=new FormData(e);t.submitter&&t.submitter.name&&n.append(t.submitter.name,t.submitter.value);const s={contactFormId:e.wpcf7.id,pluginVersion:e.wpcf7.pluginVersion,contactFormLocale:e.wpcf7.locale,unitTag:e.wpcf7.unitTag,containerPostId:e.wpcf7.containerPost,status:e.wpcf7.status,inputs:Array.from(n,(e=>{const t=e[0],n=e[1];return!t.match(/^_/)&&{name:t,value:n}})).filter((e=>!1!==e)),formData:n};i({endpoint:`contact-forms/${e.wpcf7.id}/feedback`,method:"POST",body:n,wpcf7:{endpoint:"feedback",form:e,detail:s}}).then((t=>{const n=a(e,t.status);return s.status=t.status,s.apiResponse=t,["invalid","unaccepted","spam","aborted"].includes(n)?o(e,n,s):["sent","failed"].includes(n)&&o(e,`mail${n}`,s),o(e,"submit",s),t})).then((t=>{t.posted_data_hash&&(e.querySelector('input[name="_wpcf7_posted_data_hash"]').value=t.posted_data_hash),"mail_sent"===t.status&&(e.reset(),e.wpcf7.resetOnMailSent=!0),t.invalid_fields&&t.invalid_fields.forEach((t=>{S(e,t.field,t.message)})),e.wpcf7.parent.querySelector('.screen-reader-response [role="status"]').insertAdjacentText("beforeend",t.message),e.querySelectorAll(".wpcf7-response-output").forEach((e=>{e.innerText=t.message}))})).catch((e=>console.error(e)))}i.use(((e,t)=>{if(e.wpcf7&&"feedback"===e.wpcf7.endpoint){const{form:t,detail:n}=e.wpcf7;L(t),o(t,"beforesubmit",n),a(t,"submitting")}return t(e)}));const L=e=>{e.querySelectorAll(".wpcf7-form-control-wrap").forEach((t=>{t.dataset.name&&_(e,t.dataset.name)})),e.wpcf7.parent.querySelector('.screen-reader-response [role="status"]').innerText="",e.querySelectorAll(".wpcf7-response-output").forEach((e=>{e.innerText=""}))};function k(e){const t=new FormData(e),n={contactFormId:e.wpcf7.id,pluginVersion:e.wpcf7.pluginVersion,contactFormLocale:e.wpcf7.locale,unitTag:e.wpcf7.unitTag,containerPostId:e.wpcf7.containerPost,status:e.wpcf7.status,inputs:Array.from(t,(e=>{const t=e[0],n=e[1];return!t.match(/^_/)&&{name:t,value:n}})).filter((e=>!1!==e)),formData:t};i({endpoint:`contact-forms/${e.wpcf7.id}/refill`,method:"GET",wpcf7:{endpoint:"refill",form:e,detail:n}}).then((t=>{e.wpcf7.resetOnMailSent?(delete e.wpcf7.resetOnMailSent,a(e,"mail_sent")):a(e,"init"),n.apiResponse=t,o(e,"reset",n)})).catch((e=>console.error(e)))}i.use(((e,t)=>{if(e.wpcf7&&"refill"===e.wpcf7.endpoint){const{form:t,detail:n}=e.wpcf7;L(t),a(t,"resetting")}return t(e)}));const z=(e,t)=>{for(const n in t){const a=t[n];e.querySelectorAll(`input[name="${n}"]`).forEach((e=>{e.value=""})),e.querySelectorAll(`img.wpcf7-captcha-${n}`).forEach((e=>{e.setAttribute("src",a)}));const o=/([0-9]+)\.(png|gif|jpeg)$/.exec(a);o&&e.querySelectorAll(`input[name="_wpcf7_captcha_challenge_${n}"]`).forEach((e=>{e.value=o[1]}))}},j=(e,t)=>{for(const n in t){const a=t[n][0],o=t[n][1];e.querySelectorAll(`.wpcf7-form-control-wrap[data-name="${n}"]`).forEach((e=>{e.querySelector(`input[name="${n}"]`).value="",e.querySelector(".wpcf7-quiz-label").textContent=a,e.querySelector(`input[name="_wpcf7_quiz_answer_${n}"]`).value=o}))}};function T(e){const t=new FormData(e);e.wpcf7={id:n(t.get("_wpcf7")),status:e.getAttribute("data-status"),pluginVersion:t.get("_wpcf7_version"),locale:t.get("_wpcf7_locale"),unitTag:t.get("_wpcf7_unit_tag"),containerPost:n(t.get("_wpcf7_container_post")),parent:e.closest(".wpcf7"),schema:{}},e.querySelectorAll(".has-spinner").forEach((e=>{e.insertAdjacentHTML("afterend",'<span class="wpcf7-spinner"></span>')})),(e=>{e.querySelectorAll(".wpcf7-exclusive-checkbox").forEach((t=>{t.addEventListener("change",(t=>{const n=t.target.getAttribute("name");e.querySelectorAll(`input[type="checkbox"][name="${n}"]`).forEach((e=>{e!==t.target&&(e.checked=!1)}))}))}))})(e),(e=>{e.querySelectorAll(".has-free-text").forEach((t=>{const n=t.querySelector("input.wpcf7-free-text"),a=t.querySelector('input[type="checkbox"], input[type="radio"]');n.disabled=!a.checked,e.addEventListener("change",(e=>{n.disabled=!a.checked,e.target===a&&a.checked&&n.focus()}))}))})(e),(e=>{e.querySelectorAll(".wpcf7-validates-as-url").forEach((e=>{e.addEventListener("change",(t=>{let n=e.value.trim();n&&!n.match(/^[a-z][a-z0-9.+-]*:/i)&&-1!==n.indexOf(".")&&(n=n.replace(/^\/+/,""),n="http://"+n),e.value=n}))}))})(e),(e=>{if(!e.querySelector(".wpcf7-acceptance")||e.classList.contains("wpcf7-acceptance-as-validation"))return;const t=()=>{let t=!0;e.querySelectorAll(".wpcf7-acceptance").forEach((e=>{if(!t||e.classList.contains("optional"))return;const n=e.querySelector('input[type="checkbox"]');(e.classList.contains("invert")&&n.checked||!e.classList.contains("invert")&&!n.checked)&&(t=!1)})),e.querySelectorAll(".wpcf7-submit").forEach((e=>{e.disabled=!t}))};t(),e.addEventListener("change",(e=>{t()})),e.addEventListener("wpcf7reset",(e=>{t()}))})(e),(e=>{const t=(e,t)=>{const a=n(e.getAttribute("data-starting-value")),o=n(e.getAttribute("data-maximum-value")),i=n(e.getAttribute("data-minimum-value")),s=e.classList.contains("down")?a-t.value.length:t.value.length;e.setAttribute("data-current-value",s),e.innerText=s,o&&o<t.value.length?e.classList.add("too-long"):e.classList.remove("too-long"),i&&t.value.length<i?e.classList.add("too-short"):e.classList.remove("too-short")},a=n=>{n={init:!1,...n},e.querySelectorAll(".wpcf7-character-count").forEach((a=>{const o=a.getAttribute("data-target-name"),i=e.querySelector(`[name="${o}"]`);i&&(i.value=i.defaultValue,t(a,i),n.init&&i.addEventListener("keyup",(e=>{t(a,i)})))}))};a({init:!0}),e.addEventListener("wpcf7reset",(e=>{a()}))})(e),window.addEventListener("load",(t=>{wpcf7.cached&&e.reset()})),e.addEventListener("reset",(t=>{wpcf7.reset(e)})),e.addEventListener("submit",(t=>{wpcf7.submit(e,{submitter:t.submitter}),t.preventDefault()})),e.addEventListener("wpcf7submit",(t=>{t.detail.apiResponse.captcha&&z(e,t.detail.apiResponse.captcha),t.detail.apiResponse.quiz&&j(e,t.detail.apiResponse.quiz)})),e.addEventListener("wpcf7reset",(t=>{t.detail.apiResponse.captcha&&z(e,t.detail.apiResponse.captcha),t.detail.apiResponse.quiz&&j(e,t.detail.apiResponse.quiz)})),i({endpoint:`contact-forms/${e.wpcf7.id}/feedback/schema`,method:"GET"}).then((t=>{e.wpcf7.schema=t})),e.addEventListener("change",(t=>{t.target.closest(".wpcf7-form-control")&&wpcf7.validate(e,{target:t.target})}))}document.addEventListener("DOMContentLoaded",(e=>{var t;if("undefined"==typeof wpcf7)return void console.error("wpcf7 is not defined.");if(void 0===wpcf7.api)return void console.error("wpcf7.api is not defined.");if("function"!=typeof window.fetch)return void console.error("Your browser does not support window.fetch().");if("function"!=typeof window.FormData)return void console.error("Your browser does not support window.FormData().");const n=document.querySelectorAll(".wpcf7 > form");"function"==typeof n.forEach?(wpcf7={init:T,submit:$,reset:k,validate:q,...null!==(t=wpcf7)&&void 0!==t?t:{}},n.forEach((e=>wpcf7.init(e)))):console.error("Your browser does not support NodeList.forEach().")}))}();
includes/rest-api.php CHANGED
@@ -115,6 +115,18 @@ class WPCF7_REST_Controller {
115
  )
116
  );
117
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  register_rest_route( self::route_namespace,
119
  '/contact-forms/(?P<id>\d+)/refill',
120
  array(
@@ -314,6 +326,15 @@ class WPCF7_REST_Controller {
314
  }
315
 
316
  public function create_feedback( WP_REST_Request $request ) {
 
 
 
 
 
 
 
 
 
317
  $url_params = $request->get_url_params();
318
 
319
  $item = null;
@@ -344,13 +365,14 @@ class WPCF7_REST_Controller {
344
  $invalid_fields = array();
345
 
346
  foreach ( (array) $result['invalid_fields'] as $name => $field ) {
347
- $name = sanitize_html_class( $name );
 
 
 
 
348
 
349
  $invalid_fields[] = array(
350
- 'into' => sprintf(
351
- 'span.wpcf7-form-control-wrap.%s',
352
- $name
353
- ),
354
  'message' => $field['reason'],
355
  'idref' => $field['idref'],
356
  'error_id' => sprintf(
@@ -376,6 +398,31 @@ class WPCF7_REST_Controller {
376
  return rest_ensure_response( $response );
377
  }
378
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
379
  public function get_refill( WP_REST_Request $request ) {
380
  $id = (int) $request->get_param( 'id' );
381
  $item = wpcf7_contact_form( $id );
115
  )
116
  );
117
 
118
+ register_rest_route( self::route_namespace,
119
+ '/contact-forms/(?P<id>\d+)/feedback/schema',
120
+ array(
121
+ array(
122
+ 'methods' => WP_REST_Server::READABLE,
123
+ 'callback' => array( $this, 'get_schema' ),
124
+ 'permission_callback' => '__return_true',
125
+ ),
126
+ 'schema' => 'wpcf7_swv_get_meta_schema',
127
+ )
128
+ );
129
+
130
  register_rest_route( self::route_namespace,
131
  '/contact-forms/(?P<id>\d+)/refill',
132
  array(
326
  }
327
 
328
  public function create_feedback( WP_REST_Request $request ) {
329
+ $content_type = $request->get_header( 'Content-Type' );
330
+
331
+ if ( ! str_starts_with( $content_type, 'multipart/form-data' ) ) {
332
+ return new WP_Error( 'wpcf7_unsupported_media_type',
333
+ __( "The request payload format is not supported.", 'contact-form-7' ),
334
+ array( 'status' => 415 )
335
+ );
336
+ }
337
+
338
  $url_params = $request->get_url_params();
339
 
340
  $item = null;
365
  $invalid_fields = array();
366
 
367
  foreach ( (array) $result['invalid_fields'] as $name => $field ) {
368
+ if ( ! wpcf7_is_name( $name ) ) {
369
+ continue;
370
+ }
371
+
372
+ $name = strtr( $name, '.', '_' );
373
 
374
  $invalid_fields[] = array(
375
+ 'field' => $name,
 
 
 
376
  'message' => $field['reason'],
377
  'idref' => $field['idref'],
378
  'error_id' => sprintf(
398
  return rest_ensure_response( $response );
399
  }
400
 
401
+
402
+ public function get_schema( WP_REST_Request $request ) {
403
+ $url_params = $request->get_url_params();
404
+
405
+ $item = null;
406
+
407
+ if ( ! empty( $url_params['id'] ) ) {
408
+ $item = wpcf7_contact_form( $url_params['id'] );
409
+ }
410
+
411
+ if ( ! $item ) {
412
+ return new WP_Error( 'wpcf7_not_found',
413
+ __( "The requested contact form was not found.", 'contact-form-7' ),
414
+ array( 'status' => 404 )
415
+ );
416
+ }
417
+
418
+ $schema = $item->get_schema();
419
+
420
+ $response = isset( $schema ) ? $schema->to_array() : array();
421
+
422
+ return rest_ensure_response( $response );
423
+ }
424
+
425
+
426
  public function get_refill( WP_REST_Request $request ) {
427
  $id = (int) $request->get_param( 'id' );
428
  $item = wpcf7_contact_form( $id );
includes/shortcodes.php CHANGED
@@ -1,14 +1,14 @@
1
  <?php
2
  /**
3
  * All the functions and classes in this file are deprecated.
4
- * You shouldn't use them. The functions and classes will be
5
  * removed in a later version.
6
  */
7
 
8
- function wpcf7_add_shortcode( $tag, $func, $has_name = false ) {
9
  wpcf7_deprecated_function( __FUNCTION__, '4.6', 'wpcf7_add_form_tag' );
10
 
11
- return wpcf7_add_form_tag( $tag, $func, $has_name );
12
  }
13
 
14
  function wpcf7_remove_shortcode( $tag ) {
@@ -51,11 +51,11 @@ class WPCF7_ShortcodeManager {
51
  return self::$form_tags_manager->get_scanned_tags();
52
  }
53
 
54
- public function add_shortcode( $tag, $func, $has_name = false ) {
55
  wpcf7_deprecated_function( __METHOD__, '4.6',
56
  'WPCF7_FormTagsManager::add' );
57
 
58
- return self::$form_tags_manager->add( $tag, $func, $has_name );
59
  }
60
 
61
  public function remove_shortcode( $tag ) {
1
  <?php
2
  /**
3
  * All the functions and classes in this file are deprecated.
4
+ * You should not use them. The functions and classes will be
5
  * removed in a later version.
6
  */
7
 
8
+ function wpcf7_add_shortcode( $tag, $callback, $has_name = false ) {
9
  wpcf7_deprecated_function( __FUNCTION__, '4.6', 'wpcf7_add_form_tag' );
10
 
11
+ return wpcf7_add_form_tag( $tag, $callback, $has_name );
12
  }
13
 
14
  function wpcf7_remove_shortcode( $tag ) {
51
  return self::$form_tags_manager->get_scanned_tags();
52
  }
53
 
54
+ public function add_shortcode( $tag, $callback, $has_name = false ) {
55
  wpcf7_deprecated_function( __METHOD__, '4.6',
56
  'WPCF7_FormTagsManager::add' );
57
 
58
+ return self::$form_tags_manager->add( $tag, $callback, $has_name );
59
  }
60
 
61
  public function remove_shortcode( $tag ) {
includes/submission.php CHANGED
@@ -271,6 +271,19 @@ class WPCF7_Submission {
271
  }
272
 
273
 
 
 
 
 
 
 
 
 
 
 
 
 
 
274
  private function setup_posted_data() {
275
  $posted_data = array_filter( (array) $_POST, function( $key ) {
276
  return '_' !== substr( $key, 0, 1 );
@@ -508,6 +521,15 @@ class WPCF7_Submission {
508
 
509
  $result = new WPCF7_Validation();
510
 
 
 
 
 
 
 
 
 
 
511
  $tags = $this->contact_form->scan_form_tags( array(
512
  'feature' => '! file-uploading',
513
  ) );
@@ -726,11 +748,14 @@ class WPCF7_Submission {
726
  'required' => $tag->is_required(),
727
  'filetypes' => $tag->get_option( 'filetypes' ),
728
  'limit' => $tag->get_limit_option(),
 
729
  );
730
 
731
  $new_files = wpcf7_unship_uploaded_file( $file, $args );
732
 
733
- if ( ! is_wp_error( $new_files ) ) {
 
 
734
  $this->add_uploaded_file( $tag->name, $new_files );
735
  }
736
 
271
  }
272
 
273
 
274
+ public function get_posted_string( $name ) {
275
+ $data = $this->get_posted_data( $name );
276
+ $data = wpcf7_array_flatten( $data );
277
+
278
+ if ( empty( $data ) ) {
279
+ return '';
280
+ }
281
+
282
+ // Returns the first array item.
283
+ return trim( reset( $data ) );
284
+ }
285
+
286
+
287
  private function setup_posted_data() {
288
  $posted_data = array_filter( (array) $_POST, function( $key ) {
289
  return '_' !== substr( $key, 0, 1 );
521
 
522
  $result = new WPCF7_Validation();
523
 
524
+ $this->contact_form->validate_schema(
525
+ array(
526
+ 'text' => true,
527
+ 'file' => false,
528
+ 'field' => array(),
529
+ ),
530
+ $result
531
+ );
532
+
533
  $tags = $this->contact_form->scan_form_tags( array(
534
  'feature' => '! file-uploading',
535
  ) );
748
  'required' => $tag->is_required(),
749
  'filetypes' => $tag->get_option( 'filetypes' ),
750
  'limit' => $tag->get_limit_option(),
751
+ 'schema' => $this->contact_form->get_schema(),
752
  );
753
 
754
  $new_files = wpcf7_unship_uploaded_file( $file, $args );
755
 
756
+ if ( is_wp_error( $new_files ) ) {
757
+ $result->invalidate( $tag, $new_files );
758
+ } else {
759
  $this->add_uploaded_file( $tag->name, $new_files );
760
  }
761
 
includes/swv/rules/date.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WPCF7_SWV_DateRule extends WPCF7_SWV_Rule {
4
+
5
+ const rule_name = 'date';
6
+
7
+ public function matches( $context ) {
8
+ if ( false === parent::matches( $context ) ) {
9
+ return false;
10
+ }
11
+
12
+ if ( empty( $context['text'] ) ) {
13
+ return false;
14
+ }
15
+
16
+ return true;
17
+ }
18
+
19
+ public function validate( $context ) {
20
+ $field = $this->get_property( 'field' );
21
+ $input = isset( $_POST[$field] ) ? $_POST[$field] : '';
22
+ $input = wpcf7_array_flatten( $input );
23
+ $input = wpcf7_exclude_blank( $input );
24
+
25
+ foreach ( $input as $i ) {
26
+ if ( ! wpcf7_is_date( $i ) ) {
27
+ return new WP_Error( 'wpcf7_invalid_date',
28
+ $this->get_property( 'error' )
29
+ );
30
+ }
31
+ }
32
+
33
+ return true;
34
+ }
35
+
36
+ public function to_array() {
37
+ return array( 'rule' => self::rule_name ) + (array) $this->properties;
38
+ }
39
+ }
includes/swv/rules/email.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WPCF7_SWV_EmailRule extends WPCF7_SWV_Rule {
4
+
5
+ const rule_name = 'email';
6
+
7
+ public function matches( $context ) {
8
+ if ( false === parent::matches( $context ) ) {
9
+ return false;
10
+ }
11
+
12
+ if ( empty( $context['text'] ) ) {
13
+ return false;
14
+ }
15
+
16
+ return true;
17
+ }
18
+
19
+ public function validate( $context ) {
20
+ $field = $this->get_property( 'field' );
21
+ $input = isset( $_POST[$field] ) ? $_POST[$field] : '';
22
+ $input = wpcf7_array_flatten( $input );
23
+ $input = wpcf7_exclude_blank( $input );
24
+
25
+ foreach ( $input as $i ) {
26
+ if ( ! wpcf7_is_email( $i ) ) {
27
+ return new WP_Error( 'wpcf7_invalid_email',
28
+ $this->get_property( 'error' )
29
+ );
30
+ }
31
+ }
32
+
33
+ return true;
34
+ }
35
+
36
+ public function to_array() {
37
+ return array( 'rule' => self::rule_name ) + (array) $this->properties;
38
+ }
39
+ }
includes/swv/rules/file.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WPCF7_SWV_FileRule extends WPCF7_SWV_Rule {
4
+
5
+ const rule_name = 'file';
6
+
7
+ public function matches( $context ) {
8
+ if ( false === parent::matches( $context ) ) {
9
+ return false;
10
+ }
11
+
12
+ if ( empty( $context['file'] ) ) {
13
+ return false;
14
+ }
15
+
16
+ return true;
17
+ }
18
+
19
+ public function validate( $context ) {
20
+ $field = $this->get_property( 'field' );
21
+ $input = isset( $_FILES[$field]['name'] ) ? $_FILES[$field]['name'] : '';
22
+ $input = wpcf7_array_flatten( $input );
23
+ $input = wpcf7_exclude_blank( $input );
24
+
25
+ $acceptable_filetypes = array();
26
+
27
+ foreach ( (array) $this->get_property( 'accept' ) as $accept ) {
28
+ if ( false === strpos( $accept, '/' ) ) {
29
+ $acceptable_filetypes[] = strtolower( $accept );
30
+ } else {
31
+ foreach ( wpcf7_convert_mime_to_ext( $accept ) as $ext ) {
32
+ $acceptable_filetypes[] = sprintf(
33
+ '.%s',
34
+ strtolower( trim( $ext, ' .' ) )
35
+ );
36
+ }
37
+ }
38
+ }
39
+
40
+ $acceptable_filetypes = array_unique( $acceptable_filetypes );
41
+
42
+ foreach ( $input as $i ) {
43
+ $last_period_pos = strrpos( $i, '.' );
44
+
45
+ if ( false === $last_period_pos ) { // no period
46
+ return new WP_Error( 'wpcf7_invalid_file',
47
+ $this->get_property( 'error' )
48
+ );
49
+ }
50
+
51
+ $suffix = strtolower( substr( $i, $last_period_pos ) );
52
+
53
+ if ( ! in_array( $suffix, $acceptable_filetypes, true ) ) {
54
+ return new WP_Error( 'wpcf7_invalid_file',
55
+ $this->get_property( 'error' )
56
+ );
57
+ }
58
+ }
59
+
60
+ return true;
61
+ }
62
+
63
+ public function to_array() {
64
+ return array( 'rule' => self::rule_name ) + (array) $this->properties;
65
+ }
66
+ }
includes/swv/rules/maxdate.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WPCF7_SWV_MaxDateRule extends WPCF7_SWV_Rule {
4
+
5
+ const rule_name = 'maxdate';
6
+
7
+ public function matches( $context ) {
8
+ if ( false === parent::matches( $context ) ) {
9
+ return false;
10
+ }
11
+
12
+ if ( empty( $context['text'] ) ) {
13
+ return false;
14
+ }
15
+
16
+ return true;
17
+ }
18
+
19
+ public function validate( $context ) {
20
+ $field = $this->get_property( 'field' );
21
+ $input = isset( $_POST[$field] ) ? $_POST[$field] : '';
22
+ $input = wpcf7_array_flatten( $input );
23
+ $input = wpcf7_exclude_blank( $input );
24
+
25
+ $threshold = $this->get_property( 'threshold' );
26
+
27
+ if ( ! wpcf7_is_date( $threshold ) ) {
28
+ return true;
29
+ }
30
+
31
+ foreach ( $input as $i ) {
32
+ if ( wpcf7_is_date( $i ) and $threshold < $i ) {
33
+ return new WP_Error( 'wpcf7_invalid_maxdate',
34
+ $this->get_property( 'error' )
35
+ );
36
+ }
37
+ }
38
+
39
+ return true;
40
+ }
41
+
42
+ public function to_array() {
43
+ return array( 'rule' => self::rule_name ) + (array) $this->properties;
44
+ }
45
+ }
includes/swv/rules/maxfilesize.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WPCF7_SWV_MaxFileSizeRule extends WPCF7_SWV_Rule {
4
+
5
+ const rule_name = 'maxfilesize';
6
+
7
+ public function matches( $context ) {
8
+ if ( false === parent::matches( $context ) ) {
9
+ return false;
10
+ }
11
+
12
+ if ( empty( $context['file'] ) ) {
13
+ return false;
14
+ }
15
+
16
+ return true;
17
+ }
18
+
19
+ public function validate( $context ) {
20
+ $field = $this->get_property( 'field' );
21
+ $input = isset( $_FILES[$field]['size'] ) ? $_FILES[$field]['size'] : '';
22
+ $input = wpcf7_array_flatten( $input );
23
+ $input = wpcf7_exclude_blank( $input );
24
+
25
+ if ( empty( $input ) ) {
26
+ return true;
27
+ }
28
+
29
+ $threshold = $this->get_property( 'threshold' );
30
+
31
+ if ( $threshold < array_sum( $input ) ) {
32
+ return new WP_Error( 'wpcf7_invalid_maxfilesize',
33
+ $this->get_property( 'error' )
34
+ );
35
+ }
36
+
37
+ return true;
38
+ }
39
+
40
+ public function to_array() {
41
+ return array( 'rule' => self::rule_name ) + (array) $this->properties;
42
+ }
43
+ }
includes/swv/rules/maxlength.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WPCF7_SWV_MaxLengthRule extends WPCF7_SWV_Rule {
4
+
5
+ const rule_name = 'maxlength';
6
+
7
+ public function matches( $context ) {
8
+ if ( false === parent::matches( $context ) ) {
9
+ return false;
10
+ }
11
+
12
+ if ( empty( $context['text'] ) ) {
13
+ return false;
14
+ }
15
+
16
+ return true;
17
+ }
18
+
19
+ public function validate( $context ) {
20
+ $field = $this->get_property( 'field' );
21
+ $input = isset( $_POST[$field] ) ? $_POST[$field] : '';
22
+ $input = wpcf7_array_flatten( $input );
23
+ $input = wpcf7_exclude_blank( $input );
24
+
25
+ if ( empty( $input ) ) {
26
+ return true;
27
+ }
28
+
29
+ $total = 0;
30
+
31
+ foreach ( $input as $i ) {
32
+ $total += wpcf7_count_code_units( $i );
33
+ }
34
+
35
+ $threshold = (int) $this->get_property( 'threshold' );
36
+
37
+ if ( $total <= $threshold ) {
38
+ return true;
39
+ } else {
40
+ return new WP_Error( 'wpcf7_invalid_maxlength',
41
+ $this->get_property( 'error' )
42
+ );
43
+ }
44
+ }
45
+
46
+ public function to_array() {
47
+ return array( 'rule' => self::rule_name ) + (array) $this->properties;
48
+ }
49
+ }
includes/swv/rules/maxnumber.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WPCF7_SWV_MaxNumberRule extends WPCF7_SWV_Rule {
4
+
5
+ const rule_name = 'maxnumber';
6
+
7
+ public function matches( $context ) {
8
+ if ( false === parent::matches( $context ) ) {
9
+ return false;
10
+ }
11
+
12
+ if ( empty( $context['text'] ) ) {
13
+ return false;
14
+ }
15
+
16
+ return true;
17
+ }
18
+
19
+ public function validate( $context ) {
20
+ $field = $this->get_property( 'field' );
21
+ $input = isset( $_POST[$field] ) ? $_POST[$field] : '';
22
+ $input = wpcf7_array_flatten( $input );
23
+ $input = wpcf7_exclude_blank( $input );
24
+
25
+ $threshold = $this->get_property( 'threshold' );
26
+
27
+ if ( ! wpcf7_is_number( $threshold ) ) {
28
+ return true;
29
+ }
30
+
31
+ foreach ( $input as $i ) {
32
+ if ( wpcf7_is_number( $i ) and (float) $threshold < (float) $i ) {
33
+ return new WP_Error( 'wpcf7_invalid_maxnumber',
34
+ $this->get_property( 'error' )
35
+ );
36
+ }
37
+ }
38
+
39
+ return true;
40
+ }
41
+
42
+ public function to_array() {
43
+ return array( 'rule' => self::rule_name ) + (array) $this->properties;
44
+ }
45
+ }
includes/swv/rules/mindate.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WPCF7_SWV_MinDateRule extends WPCF7_SWV_Rule {
4
+
5
+ const rule_name = 'mindate';
6
+
7
+ public function matches( $context ) {
8
+ if ( false === parent::matches( $context ) ) {
9
+ return false;
10
+ }
11
+
12
+ if ( empty( $context['text'] ) ) {
13
+ return false;
14
+ }
15
+
16
+ return true;
17
+ }
18
+
19
+ public function validate( $context ) {
20
+ $field = $this->get_property( 'field' );
21
+ $input = isset( $_POST[$field] ) ? $_POST[$field] : '';
22
+ $input = wpcf7_array_flatten( $input );
23
+ $input = wpcf7_exclude_blank( $input );
24
+
25
+ $threshold = $this->get_property( 'threshold' );
26
+
27
+ if ( ! wpcf7_is_date( $threshold ) ) {
28
+ return true;
29
+ }
30
+
31
+ foreach ( $input as $i ) {
32
+ if ( wpcf7_is_date( $i ) and $i < $threshold ) {
33
+ return new WP_Error( 'wpcf7_invalid_mindate',
34
+ $this->get_property( 'error' )
35
+ );
36
+ }
37
+ }
38
+
39
+ return true;
40
+ }
41
+
42
+ public function to_array() {
43
+ return array( 'rule' => self::rule_name ) + (array) $this->properties;
44
+ }
45
+ }
includes/swv/rules/minlength.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WPCF7_SWV_MinLengthRule extends WPCF7_SWV_Rule {
4
+
5
+ const rule_name = 'minlength';
6
+
7
+ public function matches( $context ) {
8
+ if ( false === parent::matches( $context ) ) {
9
+ return false;
10
+ }
11
+
12
+ if ( empty( $context['text'] ) ) {
13
+ return false;
14
+ }
15
+
16
+ return true;
17
+ }
18
+
19
+ public function validate( $context ) {
20
+ $field = $this->get_property( 'field' );
21
+ $input = isset( $_POST[$field] ) ? $_POST[$field] : '';
22
+ $input = wpcf7_array_flatten( $input );
23
+ $input = wpcf7_exclude_blank( $input );
24
+
25
+ if ( empty( $input ) ) {
26
+ return true;
27
+ }
28
+
29
+ $total = 0;
30
+
31
+ foreach ( $input as $i ) {
32
+ $total += wpcf7_count_code_units( $i );
33
+ }
34
+
35
+ $threshold = (int) $this->get_property( 'threshold' );
36
+
37
+ if ( $threshold <= $total ) {
38
+ return true;
39
+ } else {
40
+ return new WP_Error( 'wpcf7_invalid_minlength',
41
+ $this->get_property( 'error' )
42
+ );
43
+ }
44
+ }
45
+
46
+ public function to_array() {
47
+ return array( 'rule' => self::rule_name ) + (array) $this->properties;
48
+ }
49
+ }
includes/swv/rules/minnumber.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WPCF7_SWV_MinNumberRule extends WPCF7_SWV_Rule {
4
+
5
+ const rule_name = 'minnumber';
6
+
7
+ public function matches( $context ) {
8
+ if ( false === parent::matches( $context ) ) {
9
+ return false;
10
+ }
11
+
12
+ if ( empty( $context['text'] ) ) {
13
+ return false;
14
+ }
15
+
16
+ return true;
17
+ }
18
+
19
+ public function validate( $context ) {
20
+ $field = $this->get_property( 'field' );
21
+ $input = isset( $_POST[$field] ) ? $_POST[$field] : '';
22
+ $input = wpcf7_array_flatten( $input );
23
+ $input = wpcf7_exclude_blank( $input );
24
+
25
+ $threshold = $this->get_property( 'threshold' );
26
+
27
+ if ( ! wpcf7_is_number( $threshold ) ) {
28
+ return true;
29
+ }
30
+
31
+ foreach ( $input as $i ) {
32
+ if ( wpcf7_is_number( $i ) and (float) $i < (float) $threshold ) {
33
+ return new WP_Error( 'wpcf7_invalid_minnumber',
34
+ $this->get_property( 'error' )
35
+ );
36
+ }
37
+ }
38
+
39
+ return true;
40
+ }
41
+
42
+ public function to_array() {
43
+ return array( 'rule' => self::rule_name ) + (array) $this->properties;
44
+ }
45
+ }
includes/swv/rules/number.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WPCF7_SWV_NumberRule extends WPCF7_SWV_Rule {
4
+
5
+ const rule_name = 'number';
6
+
7
+ public function matches( $context ) {
8
+ if ( false === parent::matches( $context ) ) {
9
+ return false;
10
+ }
11
+
12
+ if ( empty( $context['text'] ) ) {
13
+ return false;
14
+ }
15
+
16
+ return true;
17
+ }
18
+
19
+ public function validate( $context ) {
20
+ $field = $this->get_property( 'field' );
21
+ $input = isset( $_POST[$field] ) ? $_POST[$field] : '';
22
+ $input = wpcf7_array_flatten( $input );
23
+ $input = wpcf7_exclude_blank( $input );
24
+
25
+ foreach ( $input as $i ) {
26
+ if ( ! wpcf7_is_number( $i ) ) {
27
+ return new WP_Error( 'wpcf7_invalid_number',
28
+ $this->get_property( 'error' )
29
+ );
30
+ }
31
+ }
32
+
33
+ return true;
34
+ }
35
+
36
+ public function to_array() {
37
+ return array( 'rule' => self::rule_name ) + (array) $this->properties;
38
+ }
39
+ }
includes/swv/rules/required.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WPCF7_SWV_RequiredRule extends WPCF7_SWV_Rule {
4
+
5
+ const rule_name = 'required';
6
+
7
+ public function matches( $context ) {
8
+ if ( false === parent::matches( $context ) ) {
9
+ return false;
10
+ }
11
+
12
+ if ( empty( $context['text'] ) ) {
13
+ return false;
14
+ }
15
+
16
+ return true;
17
+ }
18
+
19
+ public function validate( $context ) {
20
+ $field = $this->get_property( 'field' );
21
+
22
+ $input = isset( $_POST[$field] ) ? $_POST[$field] : '';
23
+
24
+ $input = wpcf7_array_flatten( $input );
25
+ $input = wpcf7_exclude_blank( $input );
26
+
27
+ if ( empty( $input ) ) {
28
+ return new WP_Error( 'wpcf7_invalid_required',
29
+ $this->get_property( 'error' )
30
+ );
31
+ }
32
+
33
+ return true;
34
+ }
35
+
36
+ public function to_array() {
37
+ return array( 'rule' => self::rule_name ) + (array) $this->properties;
38
+ }
39
+ }
includes/swv/rules/requiredfile.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WPCF7_SWV_RequiredFileRule extends WPCF7_SWV_Rule {
4
+
5
+ const rule_name = 'requiredfile';
6
+
7
+ public function matches( $context ) {
8
+ if ( false === parent::matches( $context ) ) {
9
+ return false;
10
+ }
11
+
12
+ if ( empty( $context['file'] ) ) {
13
+ return false;
14
+ }
15
+
16
+ return true;
17
+ }
18
+
19
+ public function validate( $context ) {
20
+ $field = $this->get_property( 'field' );
21
+
22
+ $input = isset( $_FILES[$field]['tmp_name'] )
23
+ ? $_FILES[$field]['tmp_name'] : '';
24
+
25
+ $input = wpcf7_array_flatten( $input );
26
+ $input = wpcf7_exclude_blank( $input );
27
+
28
+ if ( empty( $input ) ) {
29
+ return new WP_Error( 'wpcf7_invalid_requiredfile',
30
+ $this->get_property( 'error' )
31
+ );
32
+ }
33
+
34
+ return true;
35
+ }
36
+
37
+ public function to_array() {
38
+ return array( 'rule' => self::rule_name ) + (array) $this->properties;
39
+ }
40
+ }
includes/swv/rules/tel.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WPCF7_SWV_TelRule extends WPCF7_SWV_Rule {
4
+
5
+ const rule_name = 'tel';
6
+
7
+ public function matches( $context ) {
8
+ if ( false === parent::matches( $context ) ) {
9
+ return false;
10
+ }
11
+
12
+ if ( empty( $context['text'] ) ) {
13
+ return false;
14
+ }
15
+
16
+ return true;
17
+ }
18
+
19
+ public function validate( $context ) {
20
+ $field = $this->get_property( 'field' );
21
+ $input = isset( $_POST[$field] ) ? $_POST[$field] : '';
22
+ $input = wpcf7_array_flatten( $input );
23
+ $input = wpcf7_exclude_blank( $input );
24
+
25
+ foreach ( $input as $i ) {
26
+ if ( ! wpcf7_is_tel( $i ) ) {
27
+ return new WP_Error( 'wpcf7_invalid_tel',
28
+ $this->get_property( 'error' )
29
+ );
30
+ }
31
+ }
32
+
33
+ return true;
34
+ }
35
+
36
+ public function to_array() {
37
+ return array( 'rule' => self::rule_name ) + (array) $this->properties;
38
+ }
39
+ }
includes/swv/rules/url.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WPCF7_SWV_URLRule extends WPCF7_SWV_Rule {
4
+
5
+ const rule_name = 'url';
6
+
7
+ public function matches( $context ) {
8
+ if ( false === parent::matches( $context ) ) {
9
+ return false;
10
+ }
11
+
12
+ if ( empty( $context['text'] ) ) {
13
+ return false;
14
+ }
15
+
16
+ return true;
17
+ }
18
+
19
+ public function validate( $context ) {
20
+ $field = $this->get_property( 'field' );
21
+ $input = isset( $_POST[$field] ) ? $_POST[$field] : '';
22
+ $input = wpcf7_array_flatten( $input );
23
+ $input = wpcf7_exclude_blank( $input );
24
+
25
+ foreach ( $input as $i ) {
26
+ if ( ! wpcf7_is_url( $i ) ) {
27
+ return new WP_Error( 'wpcf7_invalid_url',
28
+ $this->get_property( 'error' )
29
+ );
30
+ }
31
+ }
32
+
33
+ return true;
34
+ }
35
+
36
+ public function to_array() {
37
+ return array( 'rule' => self::rule_name ) + (array) $this->properties;
38
+ }
39
+ }
includes/swv/schema-holder.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ trait WPCF7_SWV_SchemaHolder {
4
+
5
+ protected $schema;
6
+
7
+
8
+ /**
9
+ * Retrieves SWV schema for this holder object (contact form).
10
+ *
11
+ * @return WPCF7_SWV_Schema The schema object.
12
+ */
13
+ public function get_schema() {
14
+ if ( isset( $this->schema ) ) {
15
+ return $this->schema;
16
+ }
17
+
18
+ $schema = new WPCF7_SWV_Schema( array(
19
+ 'locale' => isset( $this->locale ) ? $this->locale : '',
20
+ ) );
21
+
22
+ do_action( 'wpcf7_swv_create_schema', $schema, $this );
23
+
24
+ return $this->schema = $schema;
25
+ }
26
+
27
+
28
+ /**
29
+ * Validates form inputs based on the schema and given context.
30
+ */
31
+ public function validate_schema( $context, WPCF7_Validation $validity ) {
32
+ $callback = function ( $rule ) use ( &$callback, $context, $validity ) {
33
+ if ( ! $rule->matches( $context ) ) {
34
+ return;
35
+ }
36
+
37
+ if ( $rule instanceof WPCF7_SWV_CompositeRule ) {
38
+ foreach ( $rule->rules() as $child_rule ) {
39
+ call_user_func( $callback, $child_rule );
40
+ }
41
+ } else {
42
+ $field = $rule->get_property( 'field' );
43
+
44
+ if ( $validity->is_valid( $field ) ) {
45
+ $result = $rule->validate( $context );
46
+
47
+ if ( is_wp_error( $result ) ) {
48
+ $validity->invalidate( $field, $result );
49
+ }
50
+ }
51
+ }
52
+ };
53
+
54
+ call_user_func( $callback, $this->get_schema() );
55
+ }
56
+
57
+ }
includes/swv/swv.php ADDED
@@ -0,0 +1,282 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Schema-Woven Validation API
4
+ */
5
+
6
+ require_once WPCF7_PLUGIN_DIR . '/includes/swv/schema-holder.php';
7
+
8
+
9
+ /**
10
+ * Returns an associative array of SWV rules.
11
+ */
12
+ function wpcf7_swv_available_rules() {
13
+ $rules = array(
14
+ 'required' => 'WPCF7_SWV_RequiredRule',
15
+ 'requiredfile' => 'WPCF7_SWV_RequiredFileRule',
16
+ 'email' => 'WPCF7_SWV_EmailRule',
17
+ 'url' => 'WPCF7_SWV_URLRule',
18
+ 'tel' => 'WPCF7_SWV_TelRule',
19
+ 'number' => 'WPCF7_SWV_NumberRule',
20
+ 'date' => 'WPCF7_SWV_DateRule',
21
+ 'file' => 'WPCF7_SWV_FileRule',
22
+ 'minlength' => 'WPCF7_SWV_MinLengthRule',
23
+ 'maxlength' => 'WPCF7_SWV_MaxLengthRule',
24
+ 'minnumber' => 'WPCF7_SWV_MinNumberRule',
25
+ 'maxnumber' => 'WPCF7_SWV_MaxNumberRule',
26
+ 'mindate' => 'WPCF7_SWV_MinDateRule',
27
+ 'maxdate' => 'WPCF7_SWV_MaxDateRule',
28
+ 'maxfilesize' => 'WPCF7_SWV_MaxFileSizeRule',
29
+ );
30
+
31
+ return apply_filters( 'wpcf7_swv_available_rules', $rules );
32
+ }
33
+
34
+
35
+ add_action( 'wpcf7_init', 'wpcf7_swv_load_rules', 10, 0 );
36
+
37
+ /**
38
+ * Loads SWV fules.
39
+ */
40
+ function wpcf7_swv_load_rules() {
41
+ $rules = wpcf7_swv_available_rules();
42
+
43
+ foreach ( array_keys( $rules ) as $rule ) {
44
+ $file = sprintf( '%s.php', $rule );
45
+ $path = path_join( WPCF7_PLUGIN_DIR . '/includes/swv/rules', $file );
46
+
47
+ if ( file_exists( $path ) ) {
48
+ include_once $path;
49
+ }
50
+ }
51
+ }
52
+
53
+
54
+ /**
55
+ * Creates an SWV rule object.
56
+ *
57
+ * @param string $rule_name Rule name.
58
+ * @param string|array $properties Optional. Rule properties.
59
+ * @return WPCF7_SWV_Rule|null The rule object, or null if it failed.
60
+ */
61
+ function wpcf7_swv_create_rule( $rule_name, $properties = '' ) {
62
+ $rules = wpcf7_swv_available_rules();
63
+
64
+ if ( isset( $rules[$rule_name] ) ) {
65
+ return new $rules[$rule_name]( $properties );
66
+ }
67
+ }
68
+
69
+
70
+ /**
71
+ * Returns an associative array of JSON Schema for Contact Form 7 SWV.
72
+ */
73
+ function wpcf7_swv_get_meta_schema() {
74
+ return array(
75
+ '$schema' => 'https://json-schema.org/draft/2020-12/schema',
76
+ 'title' => 'Contact Form 7 SWV',
77
+ 'description' => 'Contact Form 7 SWV meta-schema',
78
+ 'type' => 'object',
79
+ 'properties' => array(
80
+ 'version' => array(
81
+ 'type' => 'string',
82
+ ),
83
+ 'locale' => array(
84
+ 'type' => 'string',
85
+ ),
86
+ 'rules' => array(
87
+ 'type' => 'array',
88
+ 'items' => array(
89
+ 'type' => 'object',
90
+ 'properties' => array(
91
+ 'rule' => array(
92
+ 'type' => 'string',
93
+ 'enum' => array_keys( wpcf7_swv_available_rules() ),
94
+ ),
95
+ 'field' => array(
96
+ 'type' => 'string',
97
+ 'pattern' => '^[A-Za-z][-A-Za-z0-9_:]*$',
98
+ ),
99
+ 'error' => array(
100
+ 'type' => 'string',
101
+ ),
102
+ 'accept' => array(
103
+ 'type' => 'array',
104
+ 'items' => array(
105
+ 'type' => 'string',
106
+ ),
107
+ ),
108
+ 'threshold' => array(
109
+ 'type' => 'string',
110
+ ),
111
+ ),
112
+ 'required' => array( 'rule' ),
113
+ ),
114
+ ),
115
+ ),
116
+ );
117
+ }
118
+
119
+
120
+ /**
121
+ * The base class of SWV rules.
122
+ */
123
+ abstract class WPCF7_SWV_Rule {
124
+
125
+ protected $properties = array();
126
+
127
+ public function __construct( $properties = '' ) {
128
+ $this->properties = wp_parse_args( $properties, array() );
129
+ }
130
+
131
+
132
+ /**
133
+ * Returns true if this rule matches the given context.
134
+ *
135
+ * @param array $context Context.
136
+ */
137
+ public function matches( $context ) {
138
+ $field = $this->get_property( 'field' );
139
+
140
+ if ( ! empty( $context['field'] ) ) {
141
+ if ( $field and ! in_array( $field, (array) $context['field'], true ) ) {
142
+ return false;
143
+ }
144
+ }
145
+
146
+ return true;
147
+ }
148
+
149
+
150
+ /**
151
+ * Validates with this rule's logic.
152
+ *
153
+ * @param array $context Context.
154
+ */
155
+ public function validate( $context ) {
156
+ return true;
157
+ }
158
+
159
+
160
+ /**
161
+ * Converts the properties to an array.
162
+ *
163
+ * @return array Array of properties.
164
+ */
165
+ public function to_array() {
166
+ return (array) $this->properties;
167
+ }
168
+
169
+
170
+ /**
171
+ * Returns the property value specified by the given property name.
172
+ *
173
+ * @param string $name Property name.
174
+ * @return mixed Property value.
175
+ */
176
+ public function get_property( $name ) {
177
+ if ( isset( $this->properties[$name] ) ) {
178
+ return $this->properties[$name];
179
+ }
180
+ }
181
+
182
+ }
183
+
184
+
185
+ /**
186
+ * The base class of SWV composite rules.
187
+ */
188
+ abstract class WPCF7_SWV_CompositeRule extends WPCF7_SWV_Rule {
189
+
190
+ protected $rules = array();
191
+
192
+
193
+ /**
194
+ * Adds a sub-rule to this composite rule.
195
+ *
196
+ * @param WPCF7_SWV_Rule $rule Sub-rule to be added.
197
+ */
198
+ public function add_rule( $rule ) {
199
+ if ( $rule instanceof WPCF7_SWV_Rule ) {
200
+ $this->rules[] = $rule;
201
+ }
202
+ }
203
+
204
+
205
+ /**
206
+ * Returns an iterator of sub-rules.
207
+ */
208
+ public function rules() {
209
+ foreach ( $this->rules as $rule ) {
210
+ yield $rule;
211
+ }
212
+ }
213
+
214
+
215
+ /**
216
+ * Returns true if this rule matches the given context.
217
+ *
218
+ * @param array $context Context.
219
+ */
220
+ public function matches( $context ) {
221
+ return true;
222
+ }
223
+
224
+
225
+ /**
226
+ * Validates with this rule's logic.
227
+ *
228
+ * @param array $context Context.
229
+ */
230
+ public function validate( $context ) {
231
+ foreach ( $this->rules() as $rule ) {
232
+ if ( $rule->matches( $context ) ) {
233
+ $result = $rule->validate( $context );
234
+
235
+ if ( is_wp_error( $result ) ) {
236
+ return $result;
237
+ }
238
+ }
239
+ }
240
+
241
+ return true;
242
+ }
243
+
244
+
245
+ /**
246
+ * Converts the properties to an array.
247
+ *
248
+ * @return array Array of properties.
249
+ */
250
+ public function to_array() {
251
+ $rules_arrays = array_map(
252
+ function ( $rule ) {
253
+ return $rule->to_array();
254
+ },
255
+ $this->rules
256
+ );
257
+
258
+ return array_merge(
259
+ parent::to_array(),
260
+ array(
261
+ 'rules' => $rules_arrays,
262
+ )
263
+ );
264
+ }
265
+
266
+ }
267
+
268
+
269
+ /**
270
+ * The schema class as a composite rule.
271
+ */
272
+ class WPCF7_SWV_Schema extends WPCF7_SWV_CompositeRule {
273
+
274
+ const version = 'Contact Form 7 SWV Schema 2022-03';
275
+
276
+ public function __construct( $properties = '' ) {
277
+ $this->properties = wp_parse_args( $properties, array(
278
+ 'version' => self::version,
279
+ ) );
280
+ }
281
+
282
+ }
includes/validation-functions.php CHANGED
@@ -11,46 +11,88 @@
11
  *
12
  * @return bool True if it is a valid name, false if not.
13
  */
14
- function wpcf7_is_name( $string ) {
15
- return preg_match( '/^[A-Za-z][-A-Za-z0-9_:.]*$/', $string );
16
  }
17
 
18
- function wpcf7_is_email( $email ) {
19
- $result = is_email( $email );
20
- return apply_filters( 'wpcf7_is_email', $result, $email );
 
 
 
 
21
  }
22
 
23
- function wpcf7_is_url( $url ) {
24
- $result = ( false !== filter_var( $url, FILTER_VALIDATE_URL ) );
25
- return apply_filters( 'wpcf7_is_url', $result, $url );
 
 
 
 
 
26
  }
27
 
28
- function wpcf7_is_tel( $tel ) {
29
- $pattern = '%^[+]?' // + sign
30
- . '(?:\([0-9]+\)|[0-9]+)' // (1234) or 1234
31
- . '(?:[/ -]*' // delimiter
32
- . '(?:\([0-9]+\)|[0-9]+)' // (1234) or 1234
33
- . ')*$%';
34
 
35
- $result = preg_match( $pattern, trim( $tel ) );
36
- return apply_filters( 'wpcf7_is_tel', $result, $tel );
 
 
 
 
 
37
  }
38
 
39
- function wpcf7_is_number( $number ) {
40
- $result = is_numeric( $number );
41
- return apply_filters( 'wpcf7_is_number', $result, $number );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  }
43
 
44
- function wpcf7_is_date( $date ) {
45
- $result = preg_match( '/^([0-9]{4,})-([0-9]{2})-([0-9]{2})$/', $date, $matches );
 
 
 
 
 
 
46
 
47
  if ( $result ) {
48
  $result = checkdate( $matches[2], $matches[3], $matches[1] );
49
  }
50
 
51
- return apply_filters( 'wpcf7_is_date', $result, $date );
52
  }
53
 
 
 
 
 
 
 
 
 
 
54
  function wpcf7_is_mailbox_list( $mailbox_list ) {
55
  if ( ! is_array( $mailbox_list ) ) {
56
  $mailbox_text = (string) $mailbox_list;
@@ -99,8 +141,22 @@ function wpcf7_is_mailbox_list( $mailbox_list ) {
99
  return $addresses;
100
  }
101
 
 
 
 
 
 
 
 
 
 
102
  function wpcf7_is_email_in_domain( $email, $domain ) {
103
  $email_list = wpcf7_is_mailbox_list( $email );
 
 
 
 
 
104
  $domain = strtolower( $domain );
105
 
106
  foreach ( $email_list as $email ) {
@@ -124,43 +180,22 @@ function wpcf7_is_email_in_domain( $email, $domain ) {
124
  return true;
125
  }
126
 
 
 
 
 
127
  function wpcf7_is_email_in_site_domain( $email ) {
128
  if ( wpcf7_is_localhost() ) {
129
  return true;
130
  }
131
 
132
- $site_domain = strtolower( $_SERVER['SERVER_NAME'] );
133
 
134
- if ( preg_match( '/^[0-9.]+$/', $site_domain ) ) { // 123.456.789.012
135
  return true;
136
  }
137
 
138
- if ( wpcf7_is_email_in_domain( $email, $site_domain ) ) {
139
- return true;
140
- }
141
-
142
- $home_url = home_url();
143
-
144
- // for interoperability with WordPress MU Domain Mapping plugin
145
- if ( is_multisite()
146
- and function_exists( 'domain_mapping_siteurl' ) ) {
147
- $domain_mapping_siteurl = domain_mapping_siteurl( false );
148
-
149
- if ( $domain_mapping_siteurl ) {
150
- $home_url = $domain_mapping_siteurl;
151
- }
152
- }
153
-
154
- if ( preg_match( '%^https?://([^/]+)%', $home_url, $matches ) ) {
155
- $site_domain = strtolower( $matches[1] );
156
-
157
- if ( $site_domain != strtolower( $_SERVER['SERVER_NAME'] )
158
- and wpcf7_is_email_in_domain( $email, $site_domain ) ) {
159
- return true;
160
- }
161
- }
162
-
163
- return false;
164
  }
165
 
166
 
@@ -168,7 +203,7 @@ function wpcf7_is_email_in_site_domain( $email ) {
168
  * Verifies that a given file path is under the directories that WordPress
169
  * manages for user contents.
170
  *
171
- * Returns false if the file at the given path doesn't exist yet.
172
  *
173
  * @param string $path A file path.
174
  * @return bool True if the path is under the content directories,
11
  *
12
  * @return bool True if it is a valid name, false if not.
13
  */
14
+ function wpcf7_is_name( $text ) {
15
+ return preg_match( '/^[A-Za-z][-A-Za-z0-9_:.]*$/', $text );
16
  }
17
 
18
+
19
+ /**
20
+ * Checks whether the given text is a well-formed email address.
21
+ */
22
+ function wpcf7_is_email( $text ) {
23
+ $result = is_email( $text );
24
+ return apply_filters( 'wpcf7_is_email', $result, $text );
25
  }
26
 
27
+
28
+ /**
29
+ * Checks whether the given text is a well-formed URL.
30
+ */
31
+ function wpcf7_is_url( $text ) {
32
+ $scheme = wp_parse_url( $text, PHP_URL_SCHEME );
33
+ $result = $scheme && in_array( $scheme, wp_allowed_protocols(), true );
34
+ return apply_filters( 'wpcf7_is_url', $result, $text );
35
  }
36
 
 
 
 
 
 
 
37
 
38
+ /**
39
+ * Checks whether the given text is a well-formed telephone number.
40
+ */
41
+ function wpcf7_is_tel( $text ) {
42
+ $text = preg_replace( '%[()/.*#\s-]+%', '', $text );
43
+ $result = preg_match( '/^[+]?[0-9]+$/', $text );
44
+ return apply_filters( 'wpcf7_is_tel', $result, $text );
45
  }
46
 
47
+
48
+ /**
49
+ * Checks whether the given text is a well-formed number.
50
+ *
51
+ * @see https://html.spec.whatwg.org/multipage/input.html#number-state-(type=number)
52
+ */
53
+ function wpcf7_is_number( $text ) {
54
+ $result = false;
55
+
56
+ $patterns = array(
57
+ '/^[-]?[0-9]+(?:[eE][+-]?[0-9]+)?$/',
58
+ '/^[-]?(?:[0-9]+)?[.][0-9]+(?:[eE][+-]?[0-9]+)?$/',
59
+ );
60
+
61
+ foreach ( $patterns as $pattern ) {
62
+ if ( preg_match( $pattern, $text ) ) {
63
+ $result = true;
64
+ break;
65
+ }
66
+ }
67
+
68
+ return apply_filters( 'wpcf7_is_number', $result, $text );
69
  }
70
 
71
+
72
+ /**
73
+ * Checks whether the given text is a valid date.
74
+ *
75
+ * @see https://html.spec.whatwg.org/multipage/input.html#date-state-(type=date)
76
+ */
77
+ function wpcf7_is_date( $text ) {
78
+ $result = preg_match( '/^([0-9]{4,})-([0-9]{2})-([0-9]{2})$/', $text, $matches );
79
 
80
  if ( $result ) {
81
  $result = checkdate( $matches[2], $matches[3], $matches[1] );
82
  }
83
 
84
+ return apply_filters( 'wpcf7_is_date', $result, $text );
85
  }
86
 
87
+
88
+ /**
89
+ * Checks whether the given text is a well-formed mailbox list.
90
+ *
91
+ * @param string|array $mailbox_list The subject to be checked.
92
+ * Comma-separated string or an array of mailboxes.
93
+ * @return array|bool Array of email addresses if all items are well-formed
94
+ * mailbox, false if not.
95
+ */
96
  function wpcf7_is_mailbox_list( $mailbox_list ) {
97
  if ( ! is_array( $mailbox_list ) ) {
98
  $mailbox_text = (string) $mailbox_list;
141
  return $addresses;
142
  }
143
 
144
+
145
+ /**
146
+ * Checks whether an email address belongs to a domain.
147
+ *
148
+ * @param string $email A mailbox or a comma-separated list of mailboxes.
149
+ * @param string $domain Internet domain name.
150
+ * @return bool True if all of the email addresses belong to the domain,
151
+ * false if not.
152
+ */
153
  function wpcf7_is_email_in_domain( $email, $domain ) {
154
  $email_list = wpcf7_is_mailbox_list( $email );
155
+
156
+ if ( false === $email_list ) {
157
+ return false;
158
+ }
159
+
160
  $domain = strtolower( $domain );
161
 
162
  foreach ( $email_list as $email ) {
180
  return true;
181
  }
182
 
183
+
184
+ /**
185
+ * Checks whether an email address belongs to the site domain.
186
+ */
187
  function wpcf7_is_email_in_site_domain( $email ) {
188
  if ( wpcf7_is_localhost() ) {
189
  return true;
190
  }
191
 
192
+ $sitename = wp_parse_url( network_home_url(), PHP_URL_HOST );
193
 
194
+ if ( preg_match( '/^[0-9.]+$/', $sitename ) ) { // 123.456.789.012
195
  return true;
196
  }
197
 
198
+ return wpcf7_is_email_in_domain( $email, $sitename );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
  }
200
 
201
 
203
  * Verifies that a given file path is under the directories that WordPress
204
  * manages for user contents.
205
  *
206
+ * Returns false if the file at the given path does not exist yet.
207
  *
208
  * @param string $path A file path.
209
  * @return bool True if the path is under the content directories,
includes/validation.php CHANGED
@@ -1,5 +1,8 @@
1
  <?php
2
 
 
 
 
3
  class WPCF7_Validation implements ArrayAccess {
4
  private $invalid_fields = array();
5
  private $container = array();
@@ -12,7 +15,15 @@ class WPCF7_Validation implements ArrayAccess {
12
  );
13
  }
14
 
15
- public function invalidate( $context, $message ) {
 
 
 
 
 
 
 
 
16
  if ( $context instanceof WPCF7_FormTag ) {
17
  $tag = $context;
18
  } elseif ( is_array( $context ) ) {
@@ -29,6 +40,12 @@ class WPCF7_Validation implements ArrayAccess {
29
  return;
30
  }
31
 
 
 
 
 
 
 
32
  if ( $this->is_valid( $name ) ) {
33
  $id = $tag->get_id_option();
34
 
@@ -44,6 +61,16 @@ class WPCF7_Validation implements ArrayAccess {
44
  }
45
  }
46
 
 
 
 
 
 
 
 
 
 
 
47
  public function is_valid( $name = null ) {
48
  if ( ! empty( $name ) ) {
49
  return ! isset( $this->invalid_fields[$name] );
@@ -52,10 +79,22 @@ class WPCF7_Validation implements ArrayAccess {
52
  }
53
  }
54
 
 
 
 
 
 
 
55
  public function get_invalid_fields() {
56
  return $this->invalid_fields;
57
  }
58
 
 
 
 
 
 
 
59
  public function offsetSet( $offset, $value ) {
60
  if ( isset( $this->container[$offset] ) ) {
61
  $this->container[$offset] = $value;
@@ -69,16 +108,35 @@ class WPCF7_Validation implements ArrayAccess {
69
  }
70
  }
71
 
 
 
 
 
 
 
72
  public function offsetGet( $offset ) {
73
  if ( isset( $this->container[$offset] ) ) {
74
  return $this->container[$offset];
75
  }
76
  }
77
 
 
 
 
 
 
 
78
  public function offsetExists( $offset ) {
79
  return isset( $this->container[$offset] );
80
  }
81
 
 
 
 
 
 
 
82
  public function offsetUnset( $offset ) {
83
  }
 
84
  }
1
  <?php
2
 
3
+ /**
4
+ * Server-side user input validation manager.
5
+ */
6
  class WPCF7_Validation implements ArrayAccess {
7
  private $invalid_fields = array();
8
  private $container = array();
15
  );
16
  }
17
 
18
+
19
+ /**
20
+ * Marks a form control as an invalid field.
21
+ *
22
+ * @param WPCF7_FormTag|array|string $context Context representing the
23
+ * target field.
24
+ * @param WP_Error|string $error The error of the field.
25
+ */
26
+ public function invalidate( $context, $error ) {
27
  if ( $context instanceof WPCF7_FormTag ) {
28
  $tag = $context;
29
  } elseif ( is_array( $context ) ) {
40
  return;
41
  }
42
 
43
+ if ( is_wp_error( $error ) ) {
44
+ $message = $error->get_error_message();
45
+ } else {
46
+ $message = $error;
47
+ }
48
+
49
  if ( $this->is_valid( $name ) ) {
50
  $id = $tag->get_id_option();
51
 
61
  }
62
  }
63
 
64
+
65
+ /**
66
+ * Returns true if the target field is valid.
67
+ *
68
+ * @param string|null $name Optional. If specified, this is the name of
69
+ * the target field. Default null.
70
+ * @return bool True if the target field has no error. If no target is
71
+ * specified, returns true if all fields are valid.
72
+ * Otherwise false.
73
+ */
74
  public function is_valid( $name = null ) {
75
  if ( ! empty( $name ) ) {
76
  return ! isset( $this->invalid_fields[$name] );
79
  }
80
  }
81
 
82
+
83
+ /**
84
+ * Retrieves an associative array of invalid fields.
85
+ *
86
+ * @return array The associative array of invalid fields.
87
+ */
88
  public function get_invalid_fields() {
89
  return $this->invalid_fields;
90
  }
91
 
92
+
93
+ /**
94
+ * Assigns a value to the specified offset.
95
+ *
96
+ * @see https://www.php.net/manual/en/arrayaccess.offsetset.php
97
+ */
98
  public function offsetSet( $offset, $value ) {
99
  if ( isset( $this->container[$offset] ) ) {
100
  $this->container[$offset] = $value;
108
  }
109
  }
110
 
111
+
112
+ /**
113
+ * Returns the value at specified offset.
114
+ *
115
+ * @see https://www.php.net/manual/en/arrayaccess.offsetget.php
116
+ */
117
  public function offsetGet( $offset ) {
118
  if ( isset( $this->container[$offset] ) ) {
119
  return $this->container[$offset];
120
  }
121
  }
122
 
123
+
124
+ /**
125
+ * Returns true if the specified offset exists.
126
+ *
127
+ * @see https://www.php.net/manual/en/arrayaccess.offsetexists.php
128
+ */
129
  public function offsetExists( $offset ) {
130
  return isset( $this->container[$offset] );
131
  }
132
 
133
+
134
+ /**
135
+ * Unsets an offset.
136
+ *
137
+ * @see https://www.php.net/manual/en/arrayaccess.offsetunset.php
138
+ */
139
  public function offsetUnset( $offset ) {
140
  }
141
+
142
  }
load.php CHANGED
@@ -8,6 +8,7 @@ require_once WPCF7_PLUGIN_DIR . '/includes/pipe.php';
8
  require_once WPCF7_PLUGIN_DIR . '/includes/form-tag.php';
9
  require_once WPCF7_PLUGIN_DIR . '/includes/form-tags-manager.php';
10
  require_once WPCF7_PLUGIN_DIR . '/includes/shortcodes.php';
 
11
  require_once WPCF7_PLUGIN_DIR . '/includes/contact-form-functions.php';
12
  require_once WPCF7_PLUGIN_DIR . '/includes/contact-form-template.php';
13
  require_once WPCF7_PLUGIN_DIR . '/includes/contact-form.php';
@@ -79,22 +80,22 @@ class WPCF7 {
79
  * Retrieves a named entry from the option array of Contact Form 7.
80
  *
81
  * @param string $name Array item key.
82
- * @param mixed $default Optional. Default value to return if the entry
83
- * does not exist. Default false.
84
  * @return mixed Array value tied to the $name key. If nothing found,
85
- * the $default value will be returned.
86
  */
87
- public static function get_option( $name, $default = false ) {
88
  $option = get_option( 'wpcf7' );
89
 
90
  if ( false === $option ) {
91
- return $default;
92
  }
93
 
94
  if ( isset( $option[$name] ) ) {
95
  return $option[$name];
96
  } else {
97
- return $default;
98
  }
99
  }
100
 
8
  require_once WPCF7_PLUGIN_DIR . '/includes/form-tag.php';
9
  require_once WPCF7_PLUGIN_DIR . '/includes/form-tags-manager.php';
10
  require_once WPCF7_PLUGIN_DIR . '/includes/shortcodes.php';
11
+ require_once WPCF7_PLUGIN_DIR . '/includes/swv/swv.php';
12
  require_once WPCF7_PLUGIN_DIR . '/includes/contact-form-functions.php';
13
  require_once WPCF7_PLUGIN_DIR . '/includes/contact-form-template.php';
14
  require_once WPCF7_PLUGIN_DIR . '/includes/contact-form.php';
80
  * Retrieves a named entry from the option array of Contact Form 7.
81
  *
82
  * @param string $name Array item key.
83
+ * @param mixed $default_value Optional. Default value to return if the entry
84
+ * does not exist. Default false.
85
  * @return mixed Array value tied to the $name key. If nothing found,
86
+ * the $default_value value will be returned.
87
  */
88
+ public static function get_option( $name, $default_value = false ) {
89
  $option = get_option( 'wpcf7' );
90
 
91
  if ( false === $option ) {
92
+ return $default_value;
93
  }
94
 
95
  if ( isset( $option[$name] ) ) {
96
  return $option[$name];
97
  } else {
98
+ return $default_value;
99
  }
100
  }
101
 
modules/acceptance.php CHANGED
@@ -94,11 +94,13 @@ function wpcf7_acceptance_form_tag_handler( $tag ) {
94
  $item_atts );
95
  }
96
 
97
- $atts = wpcf7_format_atts( $atts );
98
-
99
  $html = sprintf(
100
- '<span class="wpcf7-form-control-wrap %1$s"><span %2$s>%3$s</span>%4$s</span>',
101
- sanitize_html_class( $tag->name ), $atts, $html, $validation_error );
 
 
 
 
102
 
103
  return $html;
104
  }
@@ -176,12 +178,12 @@ function wpcf7_acceptance_filter( $accepted, $submission ) {
176
  add_filter( 'wpcf7_form_class_attr',
177
  'wpcf7_acceptance_form_class_attr', 10, 1 );
178
 
179
- function wpcf7_acceptance_form_class_attr( $class ) {
180
  if ( wpcf7_acceptance_as_validation() ) {
181
- return $class . ' wpcf7-acceptance-as-validation';
182
  }
183
 
184
- return $class;
185
  }
186
 
187
  function wpcf7_acceptance_as_validation() {
94
  $item_atts );
95
  }
96
 
 
 
97
  $html = sprintf(
98
+ '<span class="wpcf7-form-control-wrap" data-name="%1$s"><span %2$s>%3$s</span>%4$s</span>',
99
+ esc_attr( $tag->name ),
100
+ wpcf7_format_atts( $atts ),
101
+ $html,
102
+ $validation_error
103
+ );
104
 
105
  return $html;
106
  }
178
  add_filter( 'wpcf7_form_class_attr',
179
  'wpcf7_acceptance_form_class_attr', 10, 1 );
180
 
181
+ function wpcf7_acceptance_form_class_attr( $class_attr ) {
182
  if ( wpcf7_acceptance_as_validation() ) {
183
+ return $class_attr . ' wpcf7-acceptance-as-validation';
184
  }
185
 
186
+ return $class_attr;
187
  }
188
 
189
  function wpcf7_acceptance_as_validation() {
modules/checkbox.php CHANGED
@@ -172,36 +172,37 @@ function wpcf7_checkbox_form_tag_handler( $tag ) {
172
  $html .= $item;
173
  }
174
 
175
- $atts = wpcf7_format_atts( $atts );
176
-
177
  $html = sprintf(
178
- '<span class="wpcf7-form-control-wrap %1$s"><span %2$s>%3$s</span>%4$s</span>',
179
- sanitize_html_class( $tag->name ), $atts, $html, $validation_error
 
 
 
180
  );
181
 
182
  return $html;
183
  }
184
 
185
 
186
- /* Validation filter */
187
-
188
- add_filter( 'wpcf7_validate_checkbox',
189
- 'wpcf7_checkbox_validation_filter', 10, 2 );
190
- add_filter( 'wpcf7_validate_checkbox*',
191
- 'wpcf7_checkbox_validation_filter', 10, 2 );
192
- add_filter( 'wpcf7_validate_radio',
193
- 'wpcf7_checkbox_validation_filter', 10, 2 );
194
 
195
- function wpcf7_checkbox_validation_filter( $result, $tag ) {
196
- $name = $tag->name;
197
- $is_required = $tag->is_required() || 'radio' == $tag->type;
198
- $value = isset( $_POST[$name] ) ? (array) $_POST[$name] : array();
199
 
200
- if ( $is_required and empty( $value ) ) {
201
- $result->invalidate( $tag, wpcf7_get_message( 'invalid_required' ) );
 
 
 
 
 
202
  }
203
-
204
- return $result;
205
  }
206
 
207
 
172
  $html .= $item;
173
  }
174
 
 
 
175
  $html = sprintf(
176
+ '<span class="wpcf7-form-control-wrap" data-name="%1$s"><span %2$s>%3$s</span>%4$s</span>',
177
+ esc_attr( $tag->name ),
178
+ wpcf7_format_atts( $atts ),
179
+ $html,
180
+ $validation_error
181
  );
182
 
183
  return $html;
184
  }
185
 
186
 
187
+ add_action(
188
+ 'wpcf7_swv_create_schema',
189
+ 'wpcf7_swv_add_checkbox_rules',
190
+ 10, 2
191
+ );
 
 
 
192
 
193
+ function wpcf7_swv_add_checkbox_rules( $schema, $contact_form ) {
194
+ $tags = $contact_form->scan_form_tags( array(
195
+ 'type' => array( 'checkbox*', 'radio' ),
196
+ ) );
197
 
198
+ foreach ( $tags as $tag ) {
199
+ $schema->add_rule(
200
+ wpcf7_swv_create_rule( 'required', array(
201
+ 'field' => $tag->name,
202
+ 'error' => wpcf7_get_message( 'invalid_required' ),
203
+ ) )
204
+ );
205
  }
 
 
206
  }
207
 
208
 
modules/constant-contact/contact-form-properties.php CHANGED
@@ -56,16 +56,8 @@ function wpcf7_constant_contact_setup_property( $property, $contact_form ) {
56
 
57
  $service_option = (array) WPCF7::get_option( 'constant_contact' );
58
 
59
- $additional_settings = $contact_form->additional_setting(
60
- 'constant_contact',
61
- false
62
- );
63
-
64
- $property['enable_contact_list'] = ! array_filter(
65
- $additional_settings,
66
- function ( $setting ) {
67
- return in_array( $setting, array( 'off', 'false', '0' ), true );
68
- }
69
  );
70
 
71
  if ( isset( $service_option['contact_lists'] ) ) {
56
 
57
  $service_option = (array) WPCF7::get_option( 'constant_contact' );
58
 
59
+ $property['enable_contact_list'] = ! $contact_form->is_false(
60
+ 'constant_contact'
 
 
 
 
 
 
 
 
61
  );
62
 
63
  if ( isset( $service_option['contact_lists'] ) ) {
modules/constant-contact/contact-post-request.php CHANGED
@@ -22,68 +22,61 @@ class WPCF7_ConstantContact_ContactPostRequest {
22
  public function build( WPCF7_Submission $submission ) {
23
  $this->set_create_source( 'Contact' );
24
 
25
- $posted_data = (array) $submission->get_posted_data();
26
-
27
- if ( isset( $posted_data['your-first-name'] ) ) {
28
- $this->set_first_name( $posted_data['your-first-name'] );
29
- }
30
 
31
- if ( isset( $posted_data['your-last-name'] ) ) {
32
- $this->set_last_name( $posted_data['your-last-name'] );
33
- }
34
 
35
  if ( ! ( $this->first_name || $this->last_name )
36
- and isset( $posted_data['your-name'] ) ) {
37
- $your_name = preg_split( '/[\s]+/', $posted_data['your-name'], 2 );
38
  $this->set_first_name( array_shift( $your_name ) );
39
  $this->set_last_name( array_shift( $your_name ) );
40
  }
41
 
42
- if ( isset( $posted_data['your-email'] ) ) {
43
- $this->set_email_address( $posted_data['your-email'], 'implicit' );
44
- }
 
45
 
46
- if ( isset( $posted_data['your-job-title'] ) ) {
47
- $this->set_job_title( $posted_data['your-job-title'] );
48
- }
49
 
50
- if ( isset( $posted_data['your-company-name'] ) ) {
51
- $this->set_company_name( $posted_data['your-company-name'] );
52
- }
 
 
 
 
 
53
 
54
- if ( isset( $posted_data['your-birthday-month'] )
55
- and isset( $posted_data['your-birthday-day'] ) ) {
56
- $this->set_birthday(
57
- $posted_data['your-birthday-month'],
58
- $posted_data['your-birthday-day']
59
- );
60
- } elseif ( isset( $posted_data['your-birthday'] ) ) {
61
- $date = trim( $posted_data['your-birthday'] );
62
 
63
  if ( preg_match( '/^(\d{4})-(\d{2})-(\d{2})$/', $date, $matches ) ) {
64
  $this->set_birthday( $matches[2], $matches[3] );
65
  }
66
  }
67
 
68
- if ( isset( $posted_data['your-anniversary'] ) ) {
69
- $this->set_anniversary( $posted_data['your-anniversary'] );
70
- }
71
 
72
- if ( isset( $posted_data['your-phone-number'] ) ) {
73
- $this->add_phone_number( $posted_data['your-phone-number'] );
74
- }
75
 
76
  $this->add_street_address(
77
- isset( $posted_data['your-address-street'] )
78
- ? $posted_data['your-address-street'] : '',
79
- isset( $posted_data['your-address-city'] )
80
- ? $posted_data['your-address-city'] : '',
81
- isset( $posted_data['your-address-state'] )
82
- ? $posted_data['your-address-state'] : '',
83
- isset( $posted_data['your-address-postal-code'] )
84
- ? $posted_data['your-address-postal-code'] : '',
85
- isset( $posted_data['your-address-country'] )
86
- ? $posted_data['your-address-country'] : ''
87
  );
88
 
89
  $service_option = (array) WPCF7::get_option( 'constant_contact' );
@@ -315,8 +308,8 @@ class WPCF7_ConstantContact_ContactPostRequest {
315
  return $this->list_memberships[] = $list_id;
316
  }
317
 
318
- protected function strlen( $string ) {
319
- return wpcf7_count_code_units( $string );
320
  }
321
 
322
  }
22
  public function build( WPCF7_Submission $submission ) {
23
  $this->set_create_source( 'Contact' );
24
 
25
+ $this->set_first_name(
26
+ $submission->get_posted_string( 'your-first-name' )
27
+ );
 
 
28
 
29
+ $this->set_last_name(
30
+ $submission->get_posted_string( 'your-last-name' )
31
+ );
32
 
33
  if ( ! ( $this->first_name || $this->last_name )
34
+ and $your_name = $submission->get_posted_string( 'your-name' ) ) {
35
+ $your_name = preg_split( '/[\s]+/', $your_name, 2 );
36
  $this->set_first_name( array_shift( $your_name ) );
37
  $this->set_last_name( array_shift( $your_name ) );
38
  }
39
 
40
+ $this->set_email_address(
41
+ $submission->get_posted_string( 'your-email' ),
42
+ 'implicit'
43
+ );
44
 
45
+ $this->set_job_title(
46
+ $submission->get_posted_string( 'your-job-title' )
47
+ );
48
 
49
+ $this->set_company_name(
50
+ $submission->get_posted_string( 'your-company-name' )
51
+ );
52
+
53
+ $this->set_birthday(
54
+ $submission->get_posted_string( 'your-birthday-month' ),
55
+ $submission->get_posted_string( 'your-birthday-day' )
56
+ );
57
 
58
+ if ( ! ( $this->birthday_month && $this->birthday_day ) ) {
59
+ $date = $submission->get_posted_string( 'your-birthday' );
 
 
 
 
 
 
60
 
61
  if ( preg_match( '/^(\d{4})-(\d{2})-(\d{2})$/', $date, $matches ) ) {
62
  $this->set_birthday( $matches[2], $matches[3] );
63
  }
64
  }
65
 
66
+ $this->set_anniversary(
67
+ $submission->get_posted_string( 'your-anniversary' )
68
+ );
69
 
70
+ $this->add_phone_number(
71
+ $submission->get_posted_string( 'your-phone-number' )
72
+ );
73
 
74
  $this->add_street_address(
75
+ $submission->get_posted_string( 'your-address-street' ),
76
+ $submission->get_posted_string( 'your-address-city' ),
77
+ $submission->get_posted_string( 'your-address-state' ),
78
+ $submission->get_posted_string( 'your-address-postal-code' ),
79
+ $submission->get_posted_string( 'your-address-country' )
 
 
 
 
 
80
  );
81
 
82
  $service_option = (array) WPCF7::get_option( 'constant_contact' );
308
  return $this->list_memberships[] = $list_id;
309
  }
310
 
311
+ protected function strlen( $text ) {
312
+ return wpcf7_count_code_units( $text );
313
  }
314
 
315
  }
modules/date.php CHANGED
@@ -91,43 +91,68 @@ function wpcf7_date_form_tag_handler( $tag ) {
91
 
92
  $atts['name'] = $tag->name;
93
 
94
- $atts = wpcf7_format_atts( $atts );
95
-
96
  $html = sprintf(
97
- '<span class="wpcf7-form-control-wrap %1$s"><input %2$s />%3$s</span>',
98
- sanitize_html_class( $tag->name ), $atts, $validation_error
 
 
99
  );
100
 
101
  return $html;
102
  }
103
 
104
 
105
- /* Validation filter */
 
 
 
 
106
 
107
- add_filter( 'wpcf7_validate_date', 'wpcf7_date_validation_filter', 10, 2 );
108
- add_filter( 'wpcf7_validate_date*', 'wpcf7_date_validation_filter', 10, 2 );
 
 
109
 
110
- function wpcf7_date_validation_filter( $result, $tag ) {
111
- $name = $tag->name;
 
 
 
 
 
 
 
112
 
113
- $min = $tag->get_date_option( 'min' );
114
- $max = $tag->get_date_option( 'max' );
 
 
 
 
115
 
116
- $value = isset( $_POST[$name] )
117
- ? trim( strtr( (string) $_POST[$name], "\n", " " ) )
118
- : '';
 
 
 
 
 
 
 
 
 
119
 
120
- if ( $tag->is_required() and '' === $value ) {
121
- $result->invalidate( $tag, wpcf7_get_message( 'invalid_required' ) );
122
- } elseif ( '' !== $value and ! wpcf7_is_date( $value ) ) {
123
- $result->invalidate( $tag, wpcf7_get_message( 'invalid_date' ) );
124
- } elseif ( '' !== $value and ! empty( $min ) and $value < $min ) {
125
- $result->invalidate( $tag, wpcf7_get_message( 'date_too_early' ) );
126
- } elseif ( '' !== $value and ! empty( $max ) and $max < $value ) {
127
- $result->invalidate( $tag, wpcf7_get_message( 'date_too_late' ) );
 
128
  }
129
-
130
- return $result;
131
  }
132
 
133
 
@@ -139,17 +164,17 @@ function wpcf7_date_messages( $messages ) {
139
  return array_merge( $messages, array(
140
  'invalid_date' => array(
141
  'description' => __( "Date format that the sender entered is invalid", 'contact-form-7' ),
142
- 'default' => __( "The date format is incorrect.", 'contact-form-7' ),
143
  ),
144
 
145
  'date_too_early' => array(
146
  'description' => __( "Date is earlier than minimum limit", 'contact-form-7' ),
147
- 'default' => __( "The date is before the earliest one allowed.", 'contact-form-7' ),
148
  ),
149
 
150
  'date_too_late' => array(
151
  'description' => __( "Date is later than maximum limit", 'contact-form-7' ),
152
- 'default' => __( "The date is after the latest one allowed.", 'contact-form-7' ),
153
  ),
154
  ) );
155
  }
91
 
92
  $atts['name'] = $tag->name;
93
 
 
 
94
  $html = sprintf(
95
+ '<span class="wpcf7-form-control-wrap" data-name="%1$s"><input %2$s />%3$s</span>',
96
+ esc_attr( $tag->name ),
97
+ wpcf7_format_atts( $atts ),
98
+ $validation_error
99
  );
100
 
101
  return $html;
102
  }
103
 
104
 
105
+ add_action(
106
+ 'wpcf7_swv_create_schema',
107
+ 'wpcf7_swv_add_date_rules',
108
+ 10, 2
109
+ );
110
 
111
+ function wpcf7_swv_add_date_rules( $schema, $contact_form ) {
112
+ $tags = $contact_form->scan_form_tags( array(
113
+ 'basetype' => array( 'date' ),
114
+ ) );
115
 
116
+ foreach ( $tags as $tag ) {
117
+ if ( $tag->is_required() ) {
118
+ $schema->add_rule(
119
+ wpcf7_swv_create_rule( 'required', array(
120
+ 'field' => $tag->name,
121
+ 'error' => wpcf7_get_message( 'invalid_required' ),
122
+ ) )
123
+ );
124
+ }
125
 
126
+ $schema->add_rule(
127
+ wpcf7_swv_create_rule( 'date', array(
128
+ 'field' => $tag->name,
129
+ 'error' => wpcf7_get_message( 'invalid_date' ),
130
+ ) )
131
+ );
132
 
133
+ $min = $tag->get_date_option( 'min' );
134
+ $max = $tag->get_date_option( 'max' );
135
+
136
+ if ( false !== $min ) {
137
+ $schema->add_rule(
138
+ wpcf7_swv_create_rule( 'mindate', array(
139
+ 'field' => $tag->name,
140
+ 'threshold' => $min,
141
+ 'error' => wpcf7_get_message( 'date_too_early' ),
142
+ ) )
143
+ );
144
+ }
145
 
146
+ if ( false !== $max ) {
147
+ $schema->add_rule(
148
+ wpcf7_swv_create_rule( 'maxdate', array(
149
+ 'field' => $tag->name,
150
+ 'threshold' => $max,
151
+ 'error' => wpcf7_get_message( 'date_too_late' ),
152
+ ) )
153
+ );
154
+ }
155
  }
 
 
156
  }
157
 
158
 
164
  return array_merge( $messages, array(
165
  'invalid_date' => array(
166
  'description' => __( "Date format that the sender entered is invalid", 'contact-form-7' ),
167
+ 'default' => __( "Please enter a date in YYYY-MM-DD format.", 'contact-form-7' ),
168
  ),
169
 
170
  'date_too_early' => array(
171
  'description' => __( "Date is earlier than minimum limit", 'contact-form-7' ),
172
+ 'default' => __( "This field has a too early date.", 'contact-form-7' ),
173
  ),
174
 
175
  'date_too_late' => array(
176
  'description' => __( "Date is later than maximum limit", 'contact-form-7' ),
177
+ 'default' => __( "This field has a too late date.", 'contact-form-7' ),
178
  ),
179
  ) );
180
  }
modules/doi-helper.php CHANGED
@@ -28,15 +28,12 @@ function wpcf7_doihelper_start_session( $agent_name, $args, &$token ) {
28
 
29
  $contact_form = $submission->get_contact_form();
30
 
31
- $do_doi = ! array_filter(
32
- $contact_form->additional_setting( 'doi', false ),
33
- function ( $setting ) {
34
- return in_array( $setting, array( 'off', 'false', '0' ), true );
35
- }
36
  );
37
 
38
- $do_doi = apply_filters( 'wpcf7_do_doi', $do_doi, $agent_name, $args );
39
-
40
  if ( ! $do_doi ) {
41
  return;
42
  }
28
 
29
  $contact_form = $submission->get_contact_form();
30
 
31
+ $do_doi = apply_filters( 'wpcf7_do_doi',
32
+ ! $contact_form->is_false( 'doi' ),
33
+ $agent_name,
34
+ $args
 
35
  );
36
 
 
 
37
  if ( ! $do_doi ) {
38
  return;
39
  }
modules/file.php CHANGED
@@ -38,7 +38,8 @@ function wpcf7_file_form_tag_handler( $tag ) {
38
  $atts['tabindex'] = $tag->get_option( 'tabindex', 'signed_int', true );
39
 
40
  $atts['accept'] = wpcf7_acceptable_filetypes(
41
- $tag->get_option( 'filetypes' ), 'attr' );
 
42
 
43
  if ( $tag->is_required() ) {
44
  $atts['aria-required'] = 'true';
@@ -56,36 +57,59 @@ function wpcf7_file_form_tag_handler( $tag ) {
56
  $atts['type'] = 'file';
57
  $atts['name'] = $tag->name;
58
 
59
- $atts = wpcf7_format_atts( $atts );
60
-
61
  $html = sprintf(
62
- '<span class="wpcf7-form-control-wrap %1$s"><input %2$s />%3$s</span>',
63
- sanitize_html_class( $tag->name ), $atts, $validation_error
 
 
64
  );
65
 
66
  return $html;
67
  }
68
 
69
 
70
- /* Validation + upload handling filter */
71
-
72
- add_filter( 'wpcf7_validate_file', 'wpcf7_file_validation_filter', 10, 3 );
73
- add_filter( 'wpcf7_validate_file*', 'wpcf7_file_validation_filter', 10, 3 );
74
-
75
- function wpcf7_file_validation_filter( $result, $tag, $args ) {
76
- $args = wp_parse_args( $args, array() );
 
 
 
 
 
 
 
 
 
 
 
 
 
77
 
78
- if ( isset( $args['uploaded_files'] ) ) {
79
- $maybe_error = $args['uploaded_files'];
 
 
 
 
 
 
 
80
 
81
- if ( is_wp_error( $maybe_error ) ) {
82
- $result->invalidate( $tag, $maybe_error->get_error_message() );
83
- }
 
 
 
 
84
  }
85
-
86
- return $result;
87
  }
88
 
 
89
  add_filter( 'wpcf7_mail_tag_replaced_file', 'wpcf7_file_mail_tag', 10, 4 );
90
  add_filter( 'wpcf7_mail_tag_replaced_file*', 'wpcf7_file_mail_tag', 10, 4 );
91
 
38
  $atts['tabindex'] = $tag->get_option( 'tabindex', 'signed_int', true );
39
 
40
  $atts['accept'] = wpcf7_acceptable_filetypes(
41
+ $tag->get_option( 'filetypes' ), 'attr'
42
+ );
43
 
44
  if ( $tag->is_required() ) {
45
  $atts['aria-required'] = 'true';
57
  $atts['type'] = 'file';
58
  $atts['name'] = $tag->name;
59
 
 
 
60
  $html = sprintf(
61
+ '<span class="wpcf7-form-control-wrap" data-name="%1$s"><input %2$s />%3$s</span>',
62
+ esc_attr( $tag->name ),
63
+ wpcf7_format_atts( $atts ),
64
+ $validation_error
65
  );
66
 
67
  return $html;
68
  }
69
 
70
 
71
+ add_action(
72
+ 'wpcf7_swv_create_schema',
73
+ 'wpcf7_swv_add_file_rules',
74
+ 10, 2
75
+ );
76
+
77
+ function wpcf7_swv_add_file_rules( $schema, $contact_form ) {
78
+ $tags = $contact_form->scan_form_tags( array(
79
+ 'basetype' => array( 'file' ),
80
+ ) );
81
+
82
+ foreach ( $tags as $tag ) {
83
+ if ( $tag->is_required() ) {
84
+ $schema->add_rule(
85
+ wpcf7_swv_create_rule( 'requiredfile', array(
86
+ 'field' => $tag->name,
87
+ 'error' => wpcf7_get_message( 'invalid_required' ),
88
+ ) )
89
+ );
90
+ }
91
 
92
+ $schema->add_rule(
93
+ wpcf7_swv_create_rule( 'file', array(
94
+ 'field' => $tag->name,
95
+ 'accept' => explode( ',', wpcf7_acceptable_filetypes(
96
+ $tag->get_option( 'filetypes' ), 'attr'
97
+ ) ),
98
+ 'error' => wpcf7_get_message( 'upload_file_type_invalid' ),
99
+ ) )
100
+ );
101
 
102
+ $schema->add_rule(
103
+ wpcf7_swv_create_rule( 'maxfilesize', array(
104
+ 'field' => $tag->name,
105
+ 'threshold' => $tag->get_limit_option(),
106
+ 'error' => wpcf7_get_message( 'upload_file_too_large' ),
107
+ ) )
108
+ );
109
  }
 
 
110
  }
111
 
112
+
113
  add_filter( 'wpcf7_mail_tag_replaced_file', 'wpcf7_file_mail_tag', 10, 4 );
114
  add_filter( 'wpcf7_mail_tag_replaced_file*', 'wpcf7_file_mail_tag', 10, 4 );
115
 
modules/number.php CHANGED
@@ -38,9 +38,9 @@ function wpcf7_number_form_tag_handler( $tag ) {
38
  $atts['class'] = $tag->get_class_option( $class );
39
  $atts['id'] = $tag->get_id_option();
40
  $atts['tabindex'] = $tag->get_option( 'tabindex', 'signed_int', true );
41
- $atts['min'] = $tag->get_option( 'min', 'signed_int', true );
42
- $atts['max'] = $tag->get_option( 'max', 'signed_int', true );
43
- $atts['step'] = $tag->get_option( 'step', 'int', true );
44
 
45
  if ( $tag->has_option( 'readonly' ) ) {
46
  $atts['readonly'] = 'readonly';
@@ -73,6 +73,24 @@ function wpcf7_number_form_tag_handler( $tag ) {
73
 
74
  $atts['value'] = $value;
75
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  if ( wpcf7_support_html5() ) {
77
  $atts['type'] = $tag->basetype;
78
  } else {
@@ -81,45 +99,78 @@ function wpcf7_number_form_tag_handler( $tag ) {
81
 
82
  $atts['name'] = $tag->name;
83
 
84
- $atts = wpcf7_format_atts( $atts );
85
-
86
  $html = sprintf(
87
- '<span class="wpcf7-form-control-wrap %1$s"><input %2$s />%3$s</span>',
88
- sanitize_html_class( $tag->name ), $atts, $validation_error
 
 
89
  );
90
 
91
  return $html;
92
  }
93
 
94
 
95
- /* Validation filter */
96
-
97
- add_filter( 'wpcf7_validate_number', 'wpcf7_number_validation_filter', 10, 2 );
98
- add_filter( 'wpcf7_validate_number*', 'wpcf7_number_validation_filter', 10, 2 );
99
- add_filter( 'wpcf7_validate_range', 'wpcf7_number_validation_filter', 10, 2 );
100
- add_filter( 'wpcf7_validate_range*', 'wpcf7_number_validation_filter', 10, 2 );
101
 
102
- function wpcf7_number_validation_filter( $result, $tag ) {
103
- $name = $tag->name;
104
-
105
- $value = isset( $_POST[$name] )
106
- ? trim( strtr( (string) $_POST[$name], "\n", " " ) )
107
- : '';
108
 
109
- $min = $tag->get_option( 'min', 'signed_int', true );
110
- $max = $tag->get_option( 'max', 'signed_int', true );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
 
112
- if ( $tag->is_required() and '' === $value ) {
113
- $result->invalidate( $tag, wpcf7_get_message( 'invalid_required' ) );
114
- } elseif ( '' !== $value and ! wpcf7_is_number( $value ) ) {
115
- $result->invalidate( $tag, wpcf7_get_message( 'invalid_number' ) );
116
- } elseif ( '' !== $value and false !== $min and (float) $value < (float) $min ) {
117
- $result->invalidate( $tag, wpcf7_get_message( 'number_too_small' ) );
118
- } elseif ( '' !== $value and false !== $max and (float) $max < (float) $value ) {
119
- $result->invalidate( $tag, wpcf7_get_message( 'number_too_large' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
  }
121
-
122
- return $result;
123
  }
124
 
125
 
@@ -131,17 +182,17 @@ function wpcf7_number_messages( $messages ) {
131
  return array_merge( $messages, array(
132
  'invalid_number' => array(
133
  'description' => __( "Number format that the sender entered is invalid", 'contact-form-7' ),
134
- 'default' => __( "The number format is invalid.", 'contact-form-7' )
135
  ),
136
 
137
  'number_too_small' => array(
138
  'description' => __( "Number is smaller than minimum limit", 'contact-form-7' ),
139
- 'default' => __( "The number is smaller than the minimum allowed.", 'contact-form-7' )
140
  ),
141
 
142
  'number_too_large' => array(
143
  'description' => __( "Number is larger than maximum limit", 'contact-form-7' ),
144
- 'default' => __( "The number is larger than the maximum allowed.", 'contact-form-7' )
145
  ),
146
  ) );
147
  }
38
  $atts['class'] = $tag->get_class_option( $class );
39
  $atts['id'] = $tag->get_id_option();
40
  $atts['tabindex'] = $tag->get_option( 'tabindex', 'signed_int', true );
41
+ $atts['min'] = $tag->get_option( 'min', 'signed_num', true );
42
+ $atts['max'] = $tag->get_option( 'max', 'signed_num', true );
43
+ $atts['step'] = $tag->get_option( 'step', 'num', true );
44
 
45
  if ( $tag->has_option( 'readonly' ) ) {
46
  $atts['readonly'] = 'readonly';
73
 
74
  $atts['value'] = $value;
75
 
76
+ if ( 'range' === $tag->basetype ) {
77
+ if ( ! wpcf7_is_number( $atts['min'] ) ) {
78
+ $atts['min'] = '0';
79
+ }
80
+
81
+ if ( ! wpcf7_is_number( $atts['max'] ) ) {
82
+ $atts['max'] = '100';
83
+ }
84
+
85
+ if ( '' === $atts['value'] ) {
86
+ if ( $atts['min'] < $atts['max'] ) {
87
+ $atts['value'] = ( $atts['min'] + $atts['max'] ) / 2;
88
+ } else {
89
+ $atts['value'] = $atts['min'];
90
+ }
91
+ }
92
+ }
93
+
94
  if ( wpcf7_support_html5() ) {
95
  $atts['type'] = $tag->basetype;
96
  } else {
99
 
100
  $atts['name'] = $tag->name;
101
 
 
 
102
  $html = sprintf(
103
+ '<span class="wpcf7-form-control-wrap" data-name="%1$s"><input %2$s />%3$s</span>',
104
+ esc_attr( $tag->name ),
105
+ wpcf7_format_atts( $atts ),
106
+ $validation_error
107
  );
108
 
109
  return $html;
110
  }
111
 
112
 
113
+ add_action(
114
+ 'wpcf7_swv_create_schema',
115
+ 'wpcf7_swv_add_number_rules',
116
+ 10, 2
117
+ );
 
118
 
119
+ function wpcf7_swv_add_number_rules( $schema, $contact_form ) {
120
+ $tags = $contact_form->scan_form_tags( array(
121
+ 'basetype' => array( 'number', 'range' ),
122
+ ) );
 
 
123
 
124
+ foreach ( $tags as $tag ) {
125
+ if ( $tag->is_required() ) {
126
+ $schema->add_rule(
127
+ wpcf7_swv_create_rule( 'required', array(
128
+ 'field' => $tag->name,
129
+ 'error' => wpcf7_get_message( 'invalid_required' ),
130
+ ) )
131
+ );
132
+ }
133
+
134
+ $schema->add_rule(
135
+ wpcf7_swv_create_rule( 'number', array(
136
+ 'field' => $tag->name,
137
+ 'error' => wpcf7_get_message( 'invalid_number' ),
138
+ ) )
139
+ );
140
 
141
+ $min = $tag->get_option( 'min', 'signed_num', true );
142
+ $max = $tag->get_option( 'max', 'signed_num', true );
143
+
144
+ if ( 'range' === $tag->basetype ) {
145
+ if ( ! wpcf7_is_number( $min ) ) {
146
+ $min = '0';
147
+ }
148
+
149
+ if ( ! wpcf7_is_number( $max ) ) {
150
+ $max = '100';
151
+ }
152
+ }
153
+
154
+ if ( wpcf7_is_number( $min ) ) {
155
+ $schema->add_rule(
156
+ wpcf7_swv_create_rule( 'minnumber', array(
157
+ 'field' => $tag->name,
158
+ 'threshold' => $min,
159
+ 'error' => wpcf7_get_message( 'number_too_small' ),
160
+ ) )
161
+ );
162
+ }
163
+
164
+ if ( wpcf7_is_number( $max ) ) {
165
+ $schema->add_rule(
166
+ wpcf7_swv_create_rule( 'maxnumber', array(
167
+ 'field' => $tag->name,
168
+ 'threshold' => $max,
169
+ 'error' => wpcf7_get_message( 'number_too_large' ),
170
+ ) )
171
+ );
172
+ }
173
  }
 
 
174
  }
175
 
176
 
182
  return array_merge( $messages, array(
183
  'invalid_number' => array(
184
  'description' => __( "Number format that the sender entered is invalid", 'contact-form-7' ),
185
+ 'default' => __( "Please enter a number.", 'contact-form-7' ),
186
  ),
187
 
188
  'number_too_small' => array(
189
  'description' => __( "Number is smaller than minimum limit", 'contact-form-7' ),
190
+ 'default' => __( "This field has a too small number.", 'contact-form-7' ),
191
  ),
192
 
193
  'number_too_large' => array(
194
  'description' => __( "Number is larger than maximum limit", 'contact-form-7' ),
195
+ 'default' => __( "This field has a too large number.", 'contact-form-7' ),
196
  ),
197
  ) );
198
  }
modules/quiz.php CHANGED
@@ -77,13 +77,14 @@ function wpcf7_quiz_form_tag_handler( $tag ) {
77
  $atts['type'] = 'text';
78
  $atts['name'] = $tag->name;
79
 
80
- $atts = wpcf7_format_atts( $atts );
81
-
82
  $html = sprintf(
83
- '<span class="wpcf7-form-control-wrap %1$s"><label><span class="wpcf7-quiz-label">%2$s</span> <input %3$s /></label><input type="hidden" name="_wpcf7_quiz_answer_%4$s" value="%5$s" />%6$s</span>',
84
- sanitize_html_class( $tag->name ),
85
- esc_html( $question ), $atts, $tag->name,
86
- wp_hash( $answer, 'wpcf7_quiz' ), $validation_error
 
 
 
87
  );
88
 
89
  return $html;
@@ -195,7 +196,7 @@ function wpcf7_quiz_messages( $messages ) {
195
  $messages = array_merge( $messages, array(
196
  'quiz_answer_not_correct' => array(
197
  'description' =>
198
- __( "Sender doesn't enter the correct answer to the quiz", 'contact-form-7' ),
199
  'default' =>
200
  __( "The answer to the quiz is incorrect.", 'contact-form-7' ),
201
  ),
77
  $atts['type'] = 'text';
78
  $atts['name'] = $tag->name;
79
 
 
 
80
  $html = sprintf(
81
+ '<span class="wpcf7-form-control-wrap" data-name="%1$s"><label><span class="wpcf7-quiz-label">%2$s</span> <input %3$s /></label><input type="hidden" name="_wpcf7_quiz_answer_%4$s" value="%5$s" />%6$s</span>',
82
+ esc_attr( $tag->name ),
83
+ esc_html( $question ),
84
+ wpcf7_format_atts( $atts ),
85
+ $tag->name,
86
+ wp_hash( $answer, 'wpcf7_quiz' ),
87
+ $validation_error
88
  );
89
 
90
  return $html;
196
  $messages = array_merge( $messages, array(
197
  'quiz_answer_not_correct' => array(
198
  'description' =>
199
+ __( "Sender does not enter the correct answer to the quiz", 'contact-form-7' ),
200
  'default' =>
201
  __( "The answer to the quiz is incorrect.", 'contact-form-7' ),
202
  ),
modules/really-simple-captcha.php CHANGED
@@ -143,11 +143,11 @@ function wpcf7_captchar_form_tag_handler( $tag ) {
143
  $atts['type'] = 'text';
144
  $atts['name'] = $tag->name;
145
 
146
- $atts = wpcf7_format_atts( $atts );
147
-
148
  $html = sprintf(
149
- '<span class="wpcf7-form-control-wrap %1$s"><input %2$s />%3$s</span>',
150
- sanitize_html_class( $tag->name ), $atts, $validation_error
 
 
151
  );
152
 
153
  return $html;
@@ -547,7 +547,7 @@ function wpcf7_remove_captcha( $prefix ) {
547
  $captcha->remove( $prefix );
548
  }
549
 
550
- add_action( 'template_redirect', 'wpcf7_cleanup_captcha_files', 20, 0 );
551
 
552
  function wpcf7_cleanup_captcha_files() {
553
  if ( ! $captcha = wpcf7_init_captcha() ) {
143
  $atts['type'] = 'text';
144
  $atts['name'] = $tag->name;
145
 
 
 
146
  $html = sprintf(
147
+ '<span class="wpcf7-form-control-wrap" data-name="%1$s"><input %2$s />%3$s</span>',
148
+ esc_attr( $tag->name ),
149
+ wpcf7_format_atts( $atts ),
150
+ $validation_error
151
  );
152
 
153
  return $html;
547
  $captcha->remove( $prefix );
548
  }
549
 
550
+ add_action( 'shutdown', 'wpcf7_cleanup_captcha_files', 20, 0 );
551
 
552
  function wpcf7_cleanup_captcha_files() {
553
  if ( ! $captcha = wpcf7_init_captcha() ) {
modules/recaptcha/recaptcha.php CHANGED
@@ -253,7 +253,7 @@ function wpcf7_admin_warnings_recaptcha_v2_v3( $page, $action, $object ) {
253
 
254
  $message = sprintf(
255
  esc_html( __(
256
- "API keys for reCAPTCHA v3 are different from those for v2; keys for v2 don&#8217;t work with the v3 API. You need to register your sites again to get new keys for v3. For details, see %s.",
257
  'contact-form-7'
258
  ) ),
259
  wpcf7_link(
253
 
254
  $message = sprintf(
255
  esc_html( __(
256
+ "API keys for reCAPTCHA v3 are different from those for v2; keys for v2 do not work with the v3 API. You need to register your sites again to get new keys for v3. For details, see %s.",
257
  'contact-form-7'
258
  ) ),
259
  wpcf7_link(
modules/select.php CHANGED
@@ -117,40 +117,37 @@ function wpcf7_select_form_tag_handler( $tag ) {
117
 
118
  $atts['name'] = $tag->name . ( $multiple ? '[]' : '' );
119
 
120
- $atts = wpcf7_format_atts( $atts );
121
-
122
  $html = sprintf(
123
- '<span class="wpcf7-form-control-wrap %1$s"><select %2$s>%3$s</select>%4$s</span>',
124
- sanitize_html_class( $tag->name ), $atts, $html, $validation_error
 
 
 
125
  );
126
 
127
  return $html;
128
  }
129
 
130
 
131
- /* Validation filter */
132
-
133
- add_filter( 'wpcf7_validate_select', 'wpcf7_select_validation_filter', 10, 2 );
134
- add_filter( 'wpcf7_validate_select*', 'wpcf7_select_validation_filter', 10, 2 );
135
-
136
- function wpcf7_select_validation_filter( $result, $tag ) {
137
- $name = $tag->name;
138
-
139
- $has_value = isset( $_POST[$name] ) && '' !== $_POST[$name];
140
-
141
- if ( $has_value and $tag->has_option( 'multiple' ) ) {
142
- $vals = array_filter( (array) $_POST[$name], function( $val ) {
143
- return '' !== $val;
144
- } );
145
 
146
- $has_value = ! empty( $vals );
147
- }
 
 
148
 
149
- if ( $tag->is_required() and ! $has_value ) {
150
- $result->invalidate( $tag, wpcf7_get_message( 'invalid_required' ) );
 
 
 
 
 
151
  }
152
-
153
- return $result;
154
  }
155
 
156
 
117
 
118
  $atts['name'] = $tag->name . ( $multiple ? '[]' : '' );
119
 
 
 
120
  $html = sprintf(
121
+ '<span class="wpcf7-form-control-wrap" data-name="%1$s"><select %2$s>%3$s</select>%4$s</span>',
122
+ esc_attr( $tag->name ),
123
+ wpcf7_format_atts( $atts ),
124
+ $html,
125
+ $validation_error
126
  );
127
 
128
  return $html;
129
  }
130
 
131
 
132
+ add_action(
133
+ 'wpcf7_swv_create_schema',
134
+ 'wpcf7_swv_add_select_rules',
135
+ 10, 2
136
+ );
 
 
 
 
 
 
 
 
 
137
 
138
+ function wpcf7_swv_add_select_rules( $schema, $contact_form ) {
139
+ $tags = $contact_form->scan_form_tags( array(
140
+ 'type' => array( 'select*' ),
141
+ ) );
142
 
143
+ foreach ( $tags as $tag ) {
144
+ $schema->add_rule(
145
+ wpcf7_swv_create_rule( 'required', array(
146
+ 'field' => $tag->name,
147
+ 'error' => wpcf7_get_message( 'invalid_required' ),
148
+ ) )
149
+ );
150
  }
 
 
151
  }
152
 
153
 
modules/text.php CHANGED
@@ -95,85 +95,85 @@ function wpcf7_text_form_tag_handler( $tag ) {
95
 
96
  $atts['name'] = $tag->name;
97
 
98
- $atts = wpcf7_format_atts( $atts );
99
-
100
  $html = sprintf(
101
- '<span class="wpcf7-form-control-wrap %1$s"><input %2$s />%3$s</span>',
102
- sanitize_html_class( $tag->name ), $atts, $validation_error
 
 
103
  );
104
 
105
  return $html;
106
  }
107
 
108
 
109
- /* Validation filter */
110
-
111
- add_filter( 'wpcf7_validate_text', 'wpcf7_text_validation_filter', 10, 2 );
112
- add_filter( 'wpcf7_validate_text*', 'wpcf7_text_validation_filter', 10, 2 );
113
- add_filter( 'wpcf7_validate_email', 'wpcf7_text_validation_filter', 10, 2 );
114
- add_filter( 'wpcf7_validate_email*', 'wpcf7_text_validation_filter', 10, 2 );
115
- add_filter( 'wpcf7_validate_url', 'wpcf7_text_validation_filter', 10, 2 );
116
- add_filter( 'wpcf7_validate_url*', 'wpcf7_text_validation_filter', 10, 2 );
117
- add_filter( 'wpcf7_validate_tel', 'wpcf7_text_validation_filter', 10, 2 );
118
- add_filter( 'wpcf7_validate_tel*', 'wpcf7_text_validation_filter', 10, 2 );
119
-
120
- function wpcf7_text_validation_filter( $result, $tag ) {
121
- $name = $tag->name;
122
 
123
- $value = isset( $_POST[$name] )
124
- ? trim( wp_unslash( strtr( (string) $_POST[$name], "\n", " " ) ) )
125
- : '';
 
126
 
127
- if ( 'text' == $tag->basetype ) {
128
- if ( $tag->is_required() and '' === $value ) {
129
- $result->invalidate( $tag, wpcf7_get_message( 'invalid_required' ) );
 
 
 
 
 
130
  }
131
- }
132
 
133
- if ( 'email' == $tag->basetype ) {
134
- if ( $tag->is_required() and '' === $value ) {
135
- $result->invalidate( $tag, wpcf7_get_message( 'invalid_required' ) );
136
- } elseif ( '' !== $value and ! wpcf7_is_email( $value ) ) {
137
- $result->invalidate( $tag, wpcf7_get_message( 'invalid_email' ) );
 
 
138
  }
139
- }
140
 
141
- if ( 'url' == $tag->basetype ) {
142
- if ( $tag->is_required() and '' === $value ) {
143
- $result->invalidate( $tag, wpcf7_get_message( 'invalid_required' ) );
144
- } elseif ( '' !== $value and ! wpcf7_is_url( $value ) ) {
145
- $result->invalidate( $tag, wpcf7_get_message( 'invalid_url' ) );
 
 
146
  }
147
- }
148
 
149
- if ( 'tel' == $tag->basetype ) {
150
- if ( $tag->is_required() and '' === $value ) {
151
- $result->invalidate( $tag, wpcf7_get_message( 'invalid_required' ) );
152
- } elseif ( '' !== $value and ! wpcf7_is_tel( $value ) ) {
153
- $result->invalidate( $tag, wpcf7_get_message( 'invalid_tel' ) );
 
 
154
  }
155
- }
156
-
157
- if ( '' !== $value ) {
158
- $maxlength = $tag->get_maxlength_option();
159
- $minlength = $tag->get_minlength_option();
160
 
161
- if ( $maxlength and $minlength and $maxlength < $minlength ) {
162
- $maxlength = $minlength = null;
 
 
 
 
 
 
163
  }
164
 
165
- $code_units = wpcf7_count_code_units( $value );
166
-
167
- if ( false !== $code_units ) {
168
- if ( $maxlength and $maxlength < $code_units ) {
169
- $result->invalidate( $tag, wpcf7_get_message( 'invalid_too_long' ) );
170
- } elseif ( $minlength and $code_units < $minlength ) {
171
- $result->invalidate( $tag, wpcf7_get_message( 'invalid_too_short' ) );
172
- }
173
  }
174
  }
175
-
176
- return $result;
177
  }
178
 
179
 
@@ -187,21 +187,21 @@ function wpcf7_text_messages( $messages ) {
187
  'description' =>
188
  __( "Email address that the sender entered is invalid", 'contact-form-7' ),
189
  'default' =>
190
- __( "The e-mail address entered is invalid.", 'contact-form-7' ),
191
  ),
192
 
193
  'invalid_url' => array(
194
  'description' =>
195
  __( "URL that the sender entered is invalid", 'contact-form-7' ),
196
  'default' =>
197
- __( "The URL is invalid.", 'contact-form-7' ),
198
  ),
199
 
200
  'invalid_tel' => array(
201
  'description' =>
202
  __( "Telephone number that the sender entered is invalid", 'contact-form-7' ),
203
  'default' =>
204
- __( "The telephone number is invalid.", 'contact-form-7' ),
205
  ),
206
  ) );
207
 
95
 
96
  $atts['name'] = $tag->name;
97
 
 
 
98
  $html = sprintf(
99
+ '<span class="wpcf7-form-control-wrap" data-name="%1$s"><input %2$s />%3$s</span>',
100
+ esc_attr( $tag->name ),
101
+ wpcf7_format_atts( $atts ),
102
+ $validation_error
103
  );
104
 
105
  return $html;
106
  }
107
 
108
 
109
+ add_action(
110
+ 'wpcf7_swv_create_schema',
111
+ 'wpcf7_swv_add_text_rules',
112
+ 10, 2
113
+ );
 
 
 
 
 
 
 
 
114
 
115
+ function wpcf7_swv_add_text_rules( $schema, $contact_form ) {
116
+ $tags = $contact_form->scan_form_tags( array(
117
+ 'basetype' => array( 'text', 'email', 'url', 'tel' ),
118
+ ) );
119
 
120
+ foreach ( $tags as $tag ) {
121
+ if ( $tag->is_required() ) {
122
+ $schema->add_rule(
123
+ wpcf7_swv_create_rule( 'required', array(
124
+ 'field' => $tag->name,
125
+ 'error' => wpcf7_get_message( 'invalid_required' ),
126
+ ) )
127
+ );
128
  }
 
129
 
130
+ if ( 'email' === $tag->basetype ) {
131
+ $schema->add_rule(
132
+ wpcf7_swv_create_rule( 'email', array(
133
+ 'field' => $tag->name,
134
+ 'error' => wpcf7_get_message( 'invalid_email' ),
135
+ ) )
136
+ );
137
  }
 
138
 
139
+ if ( 'url' === $tag->basetype ) {
140
+ $schema->add_rule(
141
+ wpcf7_swv_create_rule( 'url', array(
142
+ 'field' => $tag->name,
143
+ 'error' => wpcf7_get_message( 'invalid_url' ),
144
+ ) )
145
+ );
146
  }
 
147
 
148
+ if ( 'tel' === $tag->basetype ) {
149
+ $schema->add_rule(
150
+ wpcf7_swv_create_rule( 'tel', array(
151
+ 'field' => $tag->name,
152
+ 'error' => wpcf7_get_message( 'invalid_tel' ),
153
+ ) )
154
+ );
155
  }
 
 
 
 
 
156
 
157
+ if ( $minlength = $tag->get_minlength_option() ) {
158
+ $schema->add_rule(
159
+ wpcf7_swv_create_rule( 'minlength', array(
160
+ 'field' => $tag->name,
161
+ 'threshold' => absint( $minlength ),
162
+ 'error' => wpcf7_get_message( 'invalid_too_short' ),
163
+ ) )
164
+ );
165
  }
166
 
167
+ if ( $maxlength = $tag->get_maxlength_option() ) {
168
+ $schema->add_rule(
169
+ wpcf7_swv_create_rule( 'maxlength', array(
170
+ 'field' => $tag->name,
171
+ 'threshold' => absint( $maxlength ),
172
+ 'error' => wpcf7_get_message( 'invalid_too_long' ),
173
+ ) )
174
+ );
175
  }
176
  }
 
 
177
  }
178
 
179
 
187
  'description' =>
188
  __( "Email address that the sender entered is invalid", 'contact-form-7' ),
189
  'default' =>
190
+ __( "Please enter an email address.", 'contact-form-7' ),
191
  ),
192
 
193
  'invalid_url' => array(
194
  'description' =>
195
  __( "URL that the sender entered is invalid", 'contact-form-7' ),
196
  'default' =>
197
+ __( "Please enter a URL.", 'contact-form-7' ),
198
  ),
199
 
200
  'invalid_tel' => array(
201
  'description' =>
202
  __( "Telephone number that the sender entered is invalid", 'contact-form-7' ),
203
  'default' =>
204
+ __( "Please enter a telephone number.", 'contact-form-7' ),
205
  ),
206
  ) );
207
 
modules/textarea.php CHANGED
@@ -78,58 +78,59 @@ function wpcf7_textarea_form_tag_handler( $tag ) {
78
 
79
  $atts['name'] = $tag->name;
80
 
81
- $atts = wpcf7_format_atts( $atts );
82
-
83
  $html = sprintf(
84
- '<span class="wpcf7-form-control-wrap %1$s"><textarea %2$s>%3$s</textarea>%4$s</span>',
85
- sanitize_html_class( $tag->name ), $atts,
86
- esc_textarea( $value ), $validation_error
 
 
87
  );
88
 
89
  return $html;
90
  }
91
 
92
 
93
- /* Validation filter */
94
-
95
- add_filter( 'wpcf7_validate_textarea',
96
- 'wpcf7_textarea_validation_filter', 10, 2 );
97
- add_filter( 'wpcf7_validate_textarea*',
98
- 'wpcf7_textarea_validation_filter', 10, 2 );
99
-
100
- function wpcf7_textarea_validation_filter( $result, $tag ) {
101
- $type = $tag->type;
102
- $name = $tag->name;
103
-
104
- $value = isset( $_POST[$name] )
105
- ? wp_unslash( (string) $_POST[$name] )
106
- : '';
107
-
108
- if ( $tag->is_required() and '' === $value ) {
109
- $result->invalidate( $tag, wpcf7_get_message( 'invalid_required' ) );
110
- }
111
-
112
- if ( '' !== $value ) {
113
- $maxlength = $tag->get_maxlength_option();
114
- $minlength = $tag->get_minlength_option();
115
-
116
- if ( $maxlength and $minlength
117
- and $maxlength < $minlength ) {
118
- $maxlength = $minlength = null;
119
  }
120
 
121
- $code_units = wpcf7_count_code_units( $value );
 
 
 
 
 
 
 
 
122
 
123
- if ( false !== $code_units ) {
124
- if ( $maxlength and $maxlength < $code_units ) {
125
- $result->invalidate( $tag, wpcf7_get_message( 'invalid_too_long' ) );
126
- } elseif ( $minlength and $code_units < $minlength ) {
127
- $result->invalidate( $tag, wpcf7_get_message( 'invalid_too_short' ) );
128
- }
 
 
129
  }
130
  }
131
-
132
- return $result;
133
  }
134
 
135
 
78
 
79
  $atts['name'] = $tag->name;
80
 
 
 
81
  $html = sprintf(
82
+ '<span class="wpcf7-form-control-wrap" data-name="%1$s"><textarea %2$s>%3$s</textarea>%4$s</span>',
83
+ esc_attr( $tag->name ),
84
+ wpcf7_format_atts( $atts ),
85
+ esc_textarea( $value ),
86
+ $validation_error
87
  );
88
 
89
  return $html;
90
  }
91
 
92
 
93
+ add_action(
94
+ 'wpcf7_swv_create_schema',
95
+ 'wpcf7_swv_add_textarea_rules',
96
+ 10, 2
97
+ );
98
+
99
+ function wpcf7_swv_add_textarea_rules( $schema, $contact_form ) {
100
+ $tags = $contact_form->scan_form_tags( array(
101
+ 'basetype' => array( 'textarea' ),
102
+ ) );
103
+
104
+ foreach ( $tags as $tag ) {
105
+ if ( $tag->is_required() ) {
106
+ $schema->add_rule(
107
+ wpcf7_swv_create_rule( 'required', array(
108
+ 'field' => $tag->name,
109
+ 'error' => wpcf7_get_message( 'invalid_required' ),
110
+ ) )
111
+ );
 
 
 
 
 
 
 
112
  }
113
 
114
+ if ( $minlength = $tag->get_minlength_option() ) {
115
+ $schema->add_rule(
116
+ wpcf7_swv_create_rule( 'minlength', array(
117
+ 'field' => $tag->name,
118
+ 'threshold' => absint( $minlength ),
119
+ 'error' => wpcf7_get_message( 'invalid_too_short' ),
120
+ ) )
121
+ );
122
+ }
123
 
124
+ if ( $maxlength = $tag->get_maxlength_option() ) {
125
+ $schema->add_rule(
126
+ wpcf7_swv_create_rule( 'maxlength', array(
127
+ 'field' => $tag->name,
128
+ 'threshold' => absint( $maxlength ),
129
+ 'error' => wpcf7_get_message( 'invalid_too_long' ),
130
+ ) )
131
+ );
132
  }
133
  }
 
 
134
  }
135
 
136
 
readme.txt CHANGED
@@ -2,9 +2,9 @@
2
  Contributors: takayukister
3
  Donate link: https://contactform7.com/donate/
4
  Tags: contact, form, contact form, feedback, email, ajax, captcha, akismet, multilingual
5
- Requires at least: 5.7
6
  Tested up to: 6.0
7
- Stable tag: 5.5.6.1
8
  License: GPLv2 or later
9
  License URI: https://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -16,7 +16,7 @@ Contact Form 7 can manage multiple contact forms, plus you can customize the for
16
 
17
  = Docs and support =
18
 
19
- You can find [docs](https://contactform7.com/docs/), [FAQ](https://contactform7.com/faq/) and more detailed information about Contact Form 7 on [contactform7.com](https://contactform7.com/). When you can't find the answer to your question on the FAQ or in any of the documentation, check the [support forum](https://wordpress.org/support/plugin/contact-form-7/) on WordPress.org. If you can't locate any topics that pertain to your particular issue, post a new topic for it.
20
 
21
  = Contact Form 7 needs your support =
22
 
@@ -44,7 +44,7 @@ If you activate certain features in this plugin, the contact form submitter's pe
44
  The following plugins are recommended for Contact Form 7 users:
45
 
46
  * [Flamingo](https://wordpress.org/plugins/flamingo/) by Takayuki Miyoshi - With Flamingo, you can save submitted messages via contact forms in the database.
47
- * [Bogo](https://wordpress.org/plugins/bogo/) by Takayuki Miyoshi - Bogo is a straight-forward multilingual plugin that doesn't cause headaches.
48
 
49
  = Translations =
50
 
@@ -77,6 +77,10 @@ Do you have questions or issues with Contact Form 7? Use these support channels
77
 
78
  For more information, see [Releases](https://contactform7.com/category/releases/).
79
 
 
 
 
 
80
  = 5.5.6 =
81
 
82
  [https://contactform7.com/contact-form-7-556/](https://contactform7.com/contact-form-7-556/)
@@ -107,16 +111,4 @@ For more information, see [Releases](https://contactform7.com/category/releases/
107
 
108
  [https://contactform7.com/contact-form-7-55/](https://contactform7.com/contact-form-7-55/)
109
 
110
- = 5.4.2 =
111
-
112
- [https://contactform7.com/contact-form-7-542/](https://contactform7.com/contact-form-7-542/)
113
-
114
- = 5.4.1 =
115
-
116
- [https://contactform7.com/contact-form-7-541/](https://contactform7.com/contact-form-7-541/)
117
-
118
- = 5.4 =
119
-
120
- [https://contactform7.com/contact-form-7-54/](https://contactform7.com/contact-form-7-54/)
121
-
122
  == Upgrade Notice ==
2
  Contributors: takayukister
3
  Donate link: https://contactform7.com/donate/
4
  Tags: contact, form, contact form, feedback, email, ajax, captcha, akismet, multilingual
5
+ Requires at least: 5.9
6
  Tested up to: 6.0
7
+ Stable tag: 5.6
8
  License: GPLv2 or later
9
  License URI: https://www.gnu.org/licenses/gpl-2.0.html
10
 
16
 
17
  = Docs and support =
18
 
19
+ You can find [docs](https://contactform7.com/docs/), [FAQ](https://contactform7.com/faq/) and more detailed information about Contact Form 7 on [contactform7.com](https://contactform7.com/). When you cannot find the answer to your question on the FAQ or in any of the documentation, check the [support forum](https://wordpress.org/support/plugin/contact-form-7/) on WordPress.org. If you cannot locate any topics that pertain to your particular issue, post a new topic for it.
20
 
21
  = Contact Form 7 needs your support =
22
 
44
  The following plugins are recommended for Contact Form 7 users:
45
 
46
  * [Flamingo](https://wordpress.org/plugins/flamingo/) by Takayuki Miyoshi - With Flamingo, you can save submitted messages via contact forms in the database.
47
+ * [Bogo](https://wordpress.org/plugins/bogo/) by Takayuki Miyoshi - Bogo is a straight-forward multilingual plugin that does not cause headaches.
48
 
49
  = Translations =
50
 
77
 
78
  For more information, see [Releases](https://contactform7.com/category/releases/).
79
 
80
+ = 5.6 =
81
+
82
+ [https://contactform7.com/contact-form-7-56/](https://contactform7.com/contact-form-7-56/)
83
+
84
  = 5.5.6 =
85
 
86
  [https://contactform7.com/contact-form-7-556/](https://contactform7.com/contact-form-7-556/)
111
 
112
  [https://contactform7.com/contact-form-7-55/](https://contactform7.com/contact-form-7-55/)
113
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  == Upgrade Notice ==
wp-contact-form-7.php CHANGED
@@ -7,12 +7,12 @@ Author: Takayuki Miyoshi
7
  Author URI: https://ideasilo.wordpress.com/
8
  Text Domain: contact-form-7
9
  Domain Path: /languages/
10
- Version: 5.5.6.1
11
  */
12
 
13
- define( 'WPCF7_VERSION', '5.5.6.1' );
14
 
15
- define( 'WPCF7_REQUIRED_WP_VERSION', '5.7' );
16
 
17
  define( 'WPCF7_TEXT_DOMAIN', 'contact-form-7' );
18
 
7
  Author URI: https://ideasilo.wordpress.com/
8
  Text Domain: contact-form-7
9
  Domain Path: /languages/
10
+ Version: 5.6
11
  */
12
 
13
+ define( 'WPCF7_VERSION', '5.6' );
14
 
15
+ define( 'WPCF7_REQUIRED_WP_VERSION', '5.9' );
16
 
17
  define( 'WPCF7_TEXT_DOMAIN', 'contact-form-7' );
18