iubenda Cookie Solution for GDPR - Version 2.3.19-beta

Version Description

Download this release

Release Info

Developer iubenda
Plugin Icon 128x128 iubenda Cookie Solution for GDPR
Version 2.3.19-beta
Comparing to
See all releases

Code changes from version 2.3.18 to 2.3.19-beta

Files changed (4) hide show
  1. includes/forms.php +204 -77
  2. iubenda_cookie_solution.php +4 -3
  3. js/frontend.js +1 -1
  4. readme.txt +3 -0
includes/forms.php CHANGED
@@ -22,6 +22,57 @@ class iubenda_Forms {
22
  add_action( 'init', array( $this, 'register_post_type' ) );
23
  add_action( 'init', array( $this, 'register_post_status' ) );
24
  add_action( 'wp_enqueue_scripts', array( $this, 'wp_enqueue_scripts' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  }
26
 
27
  /**
@@ -37,12 +88,12 @@ class iubenda_Forms {
37
  if ( class_exists( 'WPCF7' ) ) {
38
  $this->sources['wpcf7'] = 'Contact Form 7';
39
  }
40
-
41
  // check if WP Forms is active
42
  if ( function_exists( 'wpforms' ) ) {
43
  $this->sources['wpforms'] = 'WP Forms';
44
  }
45
-
46
  // check if EooCommerce is active
47
  if ( function_exists( 'WC' ) ) {
48
  $this->sources['woocommerce'] = 'WooCommerce Checkout';
@@ -72,60 +123,11 @@ class iubenda_Forms {
72
  );
73
 
74
  $forms = $this->get_forms( $form_args );
75
-
76
  // echo '<pre>'; print_r( $forms ); echo '</pre>';
77
 
78
  if ( ! empty( $forms ) ) {
79
- // required form parameters
80
- $form_parameters = array(
81
- 'subject',
82
- 'preferences',
83
- 'exclude',
84
- 'legal_notices'
85
- );
86
- // loop through forms
87
- foreach ( $forms as $form ) {
88
- // bail if user is logged in and source is WP comment form
89
- if ( $form->form_source == 'wp_comment_form' && is_user_logged_in() )
90
- continue;
91
-
92
- // we need unique identifier for the html form
93
- // by default it's object id, used in form html id
94
- $args[$form->form_source][$form->object_id] = array();
95
-
96
- foreach ( $form_parameters as $parameter ) {
97
- $parameter_name = 'form_' . $parameter;
98
- $parameter_value = ! empty( $form->$parameter_name ) ? $form->$parameter_name : '';
99
-
100
- // echo '<pre>'; print_r( $parameter_value ); echo '</pre>';
101
-
102
- switch ( $parameter ) {
103
- case 'legal_notices' :
104
- if ( $parameter_value && is_array( $parameter_value ) ) {
105
- foreach( $parameter_value as $value ) {
106
- $args[$form->form_source][$form->object_id]['consent']['legal_notices'][] = array( 'identifier' => $value );
107
- }
108
- }
109
- break;
110
- default :
111
- if ( $parameter_value ) {
112
- switch ( $form->form_source ) {
113
- case 'wpforms' :
114
- // replace integers with field names
115
- foreach ( $parameter_value as $index => $parameter_item ) {
116
- $parameter_value[$index] = $form->form_fields[$parameter_item]['name'];
117
- }
118
- $args[$form->form_source][$form->object_id]['form']['map'][$parameter] = $parameter_value;
119
- break;
120
- default :
121
- $args[$form->form_source][$form->object_id]['form']['map'][$parameter] = $parameter_value;
122
- break;
123
- }
124
- }
125
- break;
126
- }
127
- }
128
- }
129
  }
130
 
131
  // echo '<pre>'; print_r( $args ); echo '</pre>'; exit;
@@ -351,9 +353,9 @@ class iubenda_Forms {
351
  $args['form_preferences'] = ! empty( $args['form_preferences'] ) && is_array( $args['form_preferences'] ) ? array_map( 'esc_attr', $args['form_preferences'] ) : array();
352
  $args['form_exclude'] = ! empty( $args['form_exclude'] ) && is_array( $args['form_exclude'] ) ? array_map( 'esc_attr', $args['form_exclude'] ) : array();
353
  $args['form_legal_notices'] = ! empty( $args['form_legal_notices'] ) && is_array( $args['form_legal_notices'] ) ? array_map( 'esc_attr', $args['form_legal_notices'] ) : array();
354
-
355
  $form_fields = array();
356
-
357
  // sanitize form fields
358
  if ( ! empty( $args['form_fields'] ) && is_array( $args['form_fields'] ) ) {
359
  foreach ( $args['form_fields'] as $form_field ) {
@@ -364,7 +366,7 @@ class iubenda_Forms {
364
  }
365
  }
366
  }
367
-
368
  // echo '<pre>'; print_r( $args ); echo '</pre>'; exit;
369
 
370
  // bail if any issues
@@ -473,16 +475,16 @@ class iubenda_Forms {
473
 
474
  if ( $new_fields ) {
475
  $new_forms['updated'] = $exists->ID;
476
-
477
  // update form
478
  $formdata['ID'] = $exists->ID;
479
-
480
  // update to need status if form is already mapped
481
  if ( $exists->post_status == 'mapped' )
482
  $formdata['status'] = 'needs_update';
483
-
484
  // echo '<pre>'; print_r( $formdata ); echo '</pre>'; exit;
485
-
486
  $result = $this->save_form( $formdata );
487
  }
488
  }
@@ -523,7 +525,7 @@ class iubenda_Forms {
523
  'nopaging' => true,
524
  );
525
  $posts = get_posts( $args );
526
-
527
  // echo '<pre>'; print_r( $posts ); echo '</pre>'; exit;
528
 
529
  if ( ! empty( $posts ) ) {
@@ -628,7 +630,7 @@ class iubenda_Forms {
628
  'label' => $field['label']
629
  );
630
  break;
631
- default :
632
  $formdata['form_fields'][] = array(
633
  'id' => $field['id'],
634
  'name' => 'wpforms[fields][' . $index . ']',
@@ -636,7 +638,7 @@ class iubenda_Forms {
636
  'label' => $field['label']
637
  );
638
  }
639
-
640
  }
641
  }
642
  }
@@ -648,7 +650,7 @@ class iubenda_Forms {
648
  }
649
 
650
  break;
651
-
652
  case 'wpcf7' :
653
  $args = array(
654
  'post_type' => 'wpcf7_contact_form',
@@ -692,16 +694,16 @@ class iubenda_Forms {
692
  }
693
 
694
  break;
695
-
696
  case 'woocommerce' :
697
  $checkout_form = '';
698
 
699
  ob_start();
700
-
701
  // Ensure gateways and shipping methods are loaded early.
702
  WC()->payment_gateways();
703
  WC()->shipping();
704
-
705
  /*
706
  * First lets start the session. You cant use here WC_Session directly
707
  * because it's an abstract class. But you can use WC_Session_Handler which
@@ -719,7 +721,7 @@ class iubenda_Forms {
719
 
720
  // Create a cart contents
721
  WC()->cart = new WC_Cart;
722
-
723
  // Create an abstract order
724
  WC()->order = new WC_Order;
725
 
@@ -736,7 +738,7 @@ class iubenda_Forms {
736
  'checkout' => WC()->checkout()
737
  )
738
  );
739
-
740
  wc_get_template(
741
  'checkout/form-pay.php', array(
742
  'order' => WC()->order
@@ -745,7 +747,7 @@ class iubenda_Forms {
745
 
746
  $checkout_form = ob_get_contents();
747
  ob_end_clean();
748
-
749
  if ( ! empty( $checkout_form ) ) {
750
  $formdata = array(
751
  'object_type' => 'custom', // object type where the form data is stored
@@ -761,7 +763,7 @@ class iubenda_Forms {
761
  'textarea',
762
  'select'
763
  );
764
-
765
  // DOMDoc parser
766
  if ( iubenda()->options['cs']['parser_engine'] == 'new' ) {
767
  libxml_use_internal_errors( true );
@@ -826,7 +828,7 @@ class iubenda_Forms {
826
 
827
  }
828
  }
829
-
830
  }
831
 
832
  /*
@@ -839,10 +841,10 @@ class iubenda_Forms {
839
 
840
  case 'wp_comment_form' :
841
  $comment_form = '';
842
-
843
  // get comment form for logged out user
844
  $current_user_id = get_current_user_id();
845
-
846
  // get first post
847
  $post_args = array(
848
  'numberposts' => 1,
@@ -850,9 +852,9 @@ class iubenda_Forms {
850
  'order' => 'ASC',
851
  'fields' => 'ids'
852
  );
853
-
854
  $posts = get_posts( $post_args );
855
-
856
  // get comment form
857
  if ( ! empty( $posts ) ) {
858
  wp_set_current_user( 0 );
@@ -988,7 +990,7 @@ class iubenda_Forms {
988
  // check result
989
  if ( ! $posts || is_wp_error( $posts ) )
990
  return false;
991
-
992
  $form = $this->get_form( $posts[0] );
993
 
994
  // kick back results
@@ -1017,4 +1019,129 @@ class iubenda_Forms {
1017
  return $results;
1018
  }
1019
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1020
  }
22
  add_action( 'init', array( $this, 'register_post_type' ) );
23
  add_action( 'init', array( $this, 'register_post_status' ) );
24
  add_action( 'wp_enqueue_scripts', array( $this, 'wp_enqueue_scripts' ) );
25
+ // Save cons for non ajax forms
26
+ add_action( 'wpforms_process_complete', array( $this, 'process_entry_for_wp_forms' ), 10, 4 );
27
+ }
28
+
29
+ /**
30
+ * Process entry for WP forms
31
+ *
32
+ * @param $fields
33
+ * @param $entry
34
+ * @param $form_data
35
+ * @param $entry_id
36
+ */
37
+ public function process_entry_for_wp_forms($fields, $entry, $form_data, $entry_id ) {
38
+ global $wp_version;
39
+ $public_api_key = iubenda()->options['cons']['public_api_key'];
40
+
41
+ // Escape on ajax request because it will be handle by injected JS "frontend.js"
42
+ // Or escape if the public api key is not defined
43
+ // Check current WP version is newer than 4.7 to use the wp_doing_ajax function
44
+ if ( ( version_compare( $wp_version, '4.7', '>=' ) && wp_doing_ajax() ) || ! $public_api_key ) {
45
+ return;
46
+ }
47
+
48
+ $form_id = $this->array_get( $_POST, 'wpforms.id' );
49
+ $form_args = array(
50
+ 'post_status' => array( 'mapped', 'needs_update' ),
51
+ 'source' => 'wpforms',
52
+ 'id' => $form_id,
53
+ );
54
+
55
+ $form = $this->get_form_by_object_id($form_args);
56
+
57
+ if ( ! $form ) {
58
+ return;
59
+ }
60
+
61
+ $data = array();
62
+ // Prepare form subjects
63
+ foreach ( array( 'subject', 'preferences', 'legal_notices' ) as $key ) {
64
+ $data = $this->prepare_data_for_wp_forms( $form, $key, $entry, $data );
65
+ }
66
+
67
+ $data['proofs'][0]['content'] = json_encode( $_POST );
68
+
69
+ wp_remote_post( iubenda()->options['cons']['cons_endpoint'], array(
70
+ 'body' => json_encode( $data ),
71
+ 'headers' => array(
72
+ 'apikey' => $public_api_key,
73
+ 'Content-Type' => 'application/json',
74
+ ),
75
+ ) );
76
  }
77
 
78
  /**
88
  if ( class_exists( 'WPCF7' ) ) {
89
  $this->sources['wpcf7'] = 'Contact Form 7';
90
  }
91
+
92
  // check if WP Forms is active
93
  if ( function_exists( 'wpforms' ) ) {
94
  $this->sources['wpforms'] = 'WP Forms';
95
  }
96
+
97
  // check if EooCommerce is active
98
  if ( function_exists( 'WC' ) ) {
99
  $this->sources['woocommerce'] = 'WooCommerce Checkout';
123
  );
124
 
125
  $forms = $this->get_forms( $form_args );
126
+
127
  // echo '<pre>'; print_r( $forms ); echo '</pre>';
128
 
129
  if ( ! empty( $forms ) ) {
130
+ $args = $this->prepare_mapped_forms( $forms, $args );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  }
132
 
133
  // echo '<pre>'; print_r( $args ); echo '</pre>'; exit;
353
  $args['form_preferences'] = ! empty( $args['form_preferences'] ) && is_array( $args['form_preferences'] ) ? array_map( 'esc_attr', $args['form_preferences'] ) : array();
354
  $args['form_exclude'] = ! empty( $args['form_exclude'] ) && is_array( $args['form_exclude'] ) ? array_map( 'esc_attr', $args['form_exclude'] ) : array();
355
  $args['form_legal_notices'] = ! empty( $args['form_legal_notices'] ) && is_array( $args['form_legal_notices'] ) ? array_map( 'esc_attr', $args['form_legal_notices'] ) : array();
356
+
357
  $form_fields = array();
358
+
359
  // sanitize form fields
360
  if ( ! empty( $args['form_fields'] ) && is_array( $args['form_fields'] ) ) {
361
  foreach ( $args['form_fields'] as $form_field ) {
366
  }
367
  }
368
  }
369
+
370
  // echo '<pre>'; print_r( $args ); echo '</pre>'; exit;
371
 
372
  // bail if any issues
475
 
476
  if ( $new_fields ) {
477
  $new_forms['updated'] = $exists->ID;
478
+
479
  // update form
480
  $formdata['ID'] = $exists->ID;
481
+
482
  // update to need status if form is already mapped
483
  if ( $exists->post_status == 'mapped' )
484
  $formdata['status'] = 'needs_update';
485
+
486
  // echo '<pre>'; print_r( $formdata ); echo '</pre>'; exit;
487
+
488
  $result = $this->save_form( $formdata );
489
  }
490
  }
525
  'nopaging' => true,
526
  );
527
  $posts = get_posts( $args );
528
+
529
  // echo '<pre>'; print_r( $posts ); echo '</pre>'; exit;
530
 
531
  if ( ! empty( $posts ) ) {
630
  'label' => $field['label']
631
  );
632
  break;
633
+ default :
634
  $formdata['form_fields'][] = array(
635
  'id' => $field['id'],
636
  'name' => 'wpforms[fields][' . $index . ']',
638
  'label' => $field['label']
639
  );
640
  }
641
+
642
  }
643
  }
644
  }
650
  }
651
 
652
  break;
653
+
654
  case 'wpcf7' :
655
  $args = array(
656
  'post_type' => 'wpcf7_contact_form',
694
  }
695
 
696
  break;
697
+
698
  case 'woocommerce' :
699
  $checkout_form = '';
700
 
701
  ob_start();
702
+
703
  // Ensure gateways and shipping methods are loaded early.
704
  WC()->payment_gateways();
705
  WC()->shipping();
706
+
707
  /*
708
  * First lets start the session. You cant use here WC_Session directly
709
  * because it's an abstract class. But you can use WC_Session_Handler which
721
 
722
  // Create a cart contents
723
  WC()->cart = new WC_Cart;
724
+
725
  // Create an abstract order
726
  WC()->order = new WC_Order;
727
 
738
  'checkout' => WC()->checkout()
739
  )
740
  );
741
+
742
  wc_get_template(
743
  'checkout/form-pay.php', array(
744
  'order' => WC()->order
747
 
748
  $checkout_form = ob_get_contents();
749
  ob_end_clean();
750
+
751
  if ( ! empty( $checkout_form ) ) {
752
  $formdata = array(
753
  'object_type' => 'custom', // object type where the form data is stored
763
  'textarea',
764
  'select'
765
  );
766
+
767
  // DOMDoc parser
768
  if ( iubenda()->options['cs']['parser_engine'] == 'new' ) {
769
  libxml_use_internal_errors( true );
828
 
829
  }
830
  }
831
+
832
  }
833
 
834
  /*
841
 
842
  case 'wp_comment_form' :
843
  $comment_form = '';
844
+
845
  // get comment form for logged out user
846
  $current_user_id = get_current_user_id();
847
+
848
  // get first post
849
  $post_args = array(
850
  'numberposts' => 1,
852
  'order' => 'ASC',
853
  'fields' => 'ids'
854
  );
855
+
856
  $posts = get_posts( $post_args );
857
+
858
  // get comment form
859
  if ( ! empty( $posts ) ) {
860
  wp_set_current_user( 0 );
990
  // check result
991
  if ( ! $posts || is_wp_error( $posts ) )
992
  return false;
993
+
994
  $form = $this->get_form( $posts[0] );
995
 
996
  // kick back results
1019
  return $results;
1020
  }
1021
 
1022
+ /**
1023
+ * @param array $forms
1024
+ * @param array $args
1025
+ *
1026
+ * @return array
1027
+ */
1028
+ private function prepare_mapped_forms( array $forms, array $args ) {
1029
+ // required form parameters
1030
+ $form_parameters = array(
1031
+ 'subject',
1032
+ 'preferences',
1033
+ 'exclude',
1034
+ 'legal_notices'
1035
+ );
1036
+ // loop through forms
1037
+ foreach ( $forms as $form ) {
1038
+ // bail if user is logged in and source is WP comment form
1039
+ if ( $form->form_source == 'wp_comment_form' && is_user_logged_in() )
1040
+ continue;
1041
+
1042
+ // we need unique identifier for the html form
1043
+ // by default it's object id, used in form html id
1044
+ $args[ $form->form_source ][ $form->object_id ] = array();
1045
+
1046
+ foreach ( $form_parameters as $parameter ) {
1047
+ $parameter_name = 'form_' . $parameter;
1048
+ $parameter_value = ! empty( $form->$parameter_name ) ? $form->$parameter_name : '';
1049
+
1050
+ // echo '<pre>'; print_r( $parameter_value ); echo '</pre>';
1051
+
1052
+ switch ( $parameter ) {
1053
+ case 'legal_notices' :
1054
+ if ( $parameter_value && is_array( $parameter_value ) ) {
1055
+ foreach ( $parameter_value as $value ) {
1056
+ $args[ $form->form_source ][ $form->object_id ]['consent']['legal_notices'][] = array( 'identifier' => $value );
1057
+ }
1058
+ }
1059
+ break;
1060
+ default :
1061
+ if ( $parameter_value ) {
1062
+ switch ( $form->form_source ) {
1063
+ case 'wpforms' :
1064
+ // replace integers with field names
1065
+ foreach ( $parameter_value as $index => $parameter_item ) {
1066
+ $parameter_value[ $index ] = $form->form_fields[ $parameter_item ]['name'];
1067
+ }
1068
+ $args[ $form->form_source ][ $form->object_id ]['form']['map'][ $parameter ] = $parameter_value;
1069
+ break;
1070
+ default :
1071
+ $args[ $form->form_source ][ $form->object_id ]['form']['map'][ $parameter ] = $parameter_value;
1072
+ break;
1073
+ }
1074
+ }
1075
+ break;
1076
+ }
1077
+ }
1078
+ }
1079
+
1080
+ return $args;
1081
+ }
1082
+
1083
+ private function array_get( $array, $key, $default = null ) {
1084
+ $value = $default;
1085
+ if ( ! is_array( $array ) ) {
1086
+ return $value( $array );
1087
+ }
1088
+
1089
+ if ( is_null( $key ) ) {
1090
+ return $array;
1091
+ }
1092
+
1093
+ if ( array_key_exists( $key, $array ) ) {
1094
+ return $array[ $key ];
1095
+ }
1096
+
1097
+ if ( strpos( $key, '.' ) === false ) {
1098
+ return $array[ $key ] ?: $default;
1099
+ }
1100
+
1101
+ foreach ( explode( '.', $key ) as $segment ) {
1102
+ if ( ( is_array( $array ) || $array instanceof ArrayAccess ) && array_key_exists( $segment, $array ) ) {
1103
+ $array = $array[ $segment ];
1104
+ } else {
1105
+ return $default;
1106
+ }
1107
+ }
1108
+
1109
+ return $array;
1110
+ }
1111
+
1112
+ /**
1113
+ * Prepare data for subject and preferences
1114
+ *
1115
+ * @param WP_Post $form
1116
+ * @param string $key
1117
+ * @param $entry
1118
+ * @param array $data
1119
+ *
1120
+ * @return array
1121
+ */
1122
+ private function prepare_data_for_wp_forms( $form, $key, $entry, $data ) {
1123
+ // Check is key exist in form before looping
1124
+ if ( ! isset( $form->{"form_{$key}"} ) || ! is_array( $form->{"form_{$key}"} ) ) {
1125
+ return $data;
1126
+ }
1127
+
1128
+ // Prepare form preferences and subject
1129
+ foreach ( $form->{"form_{$key}"} as $map_key => $index ) {
1130
+
1131
+ if ( ! is_numeric( $index ) ) {
1132
+ continue;
1133
+ }
1134
+
1135
+ if ( 'legal_notices' === $key ) {
1136
+ $data['legal_notices'][] = array( 'identifier' => $index );
1137
+ continue;
1138
+ }
1139
+
1140
+ $array_key = trim( $form->form_fields[ $index ]['name'], 'wpforms' );
1141
+ $array_key = substr( str_replace( '][', '.', $array_key ), 1, - 1 );
1142
+ $data[ $key ][ $map_key ] = $this->array_get( $entry, $array_key );
1143
+ }
1144
+
1145
+ return $data;
1146
+ }
1147
  }
iubenda_cookie_solution.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: Cookie and Consent Solution for the GDPR & ePrivacy
4
  Plugin URI: https://www.iubenda.com
5
  Description: An All-in-One approach developed by iubenda, which includes functionalities of two powerful solutions that help to make your website GDPR and ePrivacy compliant.
6
- Version: 2.3.18
7
  Author: iubenda
8
  Author URI: https://www.iubenda.com
9
  License: MIT License
@@ -32,7 +32,7 @@ define( 'IUB_DEBUG', false );
32
  * iubenda final class.
33
  *
34
  * @class iubenda
35
- * @version 2.3.18
36
  */
37
  class iubenda {
38
 
@@ -58,10 +58,11 @@ class iubenda {
58
  ),
59
  'cons' => array(
60
  'public_api_key' => '',
 
61
  )
62
  );
63
  public $base_url;
64
- public $version = '2.3.18';
65
  public $activation = array(
66
  'update_version' => 0,
67
  'update_notice' => true,
3
  Plugin Name: Cookie and Consent Solution for the GDPR & ePrivacy
4
  Plugin URI: https://www.iubenda.com
5
  Description: An All-in-One approach developed by iubenda, which includes functionalities of two powerful solutions that help to make your website GDPR and ePrivacy compliant.
6
+ Version: 2.3.19-beta
7
  Author: iubenda
8
  Author URI: https://www.iubenda.com
9
  License: MIT License
32
  * iubenda final class.
33
  *
34
  * @class iubenda
35
+ * @version 2.3.19-beta
36
  */
37
  class iubenda {
38
 
58
  ),
59
  'cons' => array(
60
  'public_api_key' => '',
61
+ 'cons_endpoint' => 'https://consent.iubenda.com/public/consent',
62
  )
63
  );
64
  public $base_url;
65
+ public $version = '2.3.19';
66
  public $activation = array(
67
  'update_version' => 0,
68
  'update_notice' => true,
js/frontend.js CHANGED
@@ -111,7 +111,7 @@
111
  };
112
 
113
  // send only if specific form has been submitted
114
- if ( selector.parentElement.id == e.target.id ) {
115
  _this.submitForm( form, formArgs );
116
 
117
  _iub.cons.sendData();
111
  };
112
 
113
  // send only if specific form has been submitted
114
+ if ( selector.parentElement.id == e.target.parentElement.id ) {
115
  _this.submitForm( form, formArgs );
116
 
117
  _iub.cons.sendData();
readme.txt CHANGED
@@ -150,6 +150,9 @@ We will be very happy to receive feedback here: [Uservoice forum](https://suppor
150
 
151
  == Changelog ==
152
 
 
 
 
153
  = 2.3.18 =
154
  * Fix: Avoid overriding the purposes attr if it was set
155
 
150
 
151
  == Changelog ==
152
 
153
+ = 2.3.19 =
154
+ * Save Cons for non Ajax forms in WPForms
155
+
156
  = 2.3.18 =
157
  * Fix: Avoid overriding the purposes attr if it was set
158