PluginProbe
SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster / trunk
SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster vtrunk
2.6.0 trunk 1.1.0 1.1.4 1.2.1 1.2.2 1.2.3 1.2.5 1.2.9 1.3.1 1.3.2 1.5.0 1.5.1 1.5.4 1.5.7 1.5.8 1.5.9 1.6.2 1.6.5 1.6.8 1.7.2 1.7.4 1.7.5 1.7.9 1.8.1 All 42 releases
sellkit / includes / elementor / modules / optin / actions / mailchimp.php

mailchimp.php in SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster trunk, at includes/elementor/modules/optin/actions/mailchimp.php

413 lines 11.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 defined( 'ABSPATH' ) || die();
4
5 use Sellkit_Elementor_Optin_Ajaxhandler as AjaxHandler;
6
7 /**
8 * Initializing the MailChimp action by extending Action base and using CRM trait.
9 *
10 * @since 1.5.0
11 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
12 */
13 class Sellkit_Elementor_Optin_Action_Mailchimp extends Sellkit_Elementor_Optin_Action_Base {
14 use Sellkit_Elementor_Optin_CRM;
15
16 private $api_key;
17
18 private $api_server;
19
20 public static $instance;
21
22 public static function get_instance() {
23 if ( empty( static::$instance ) ) {
24 static::$instance = new static();
25 }
26
27 return static::$instance;
28 }
29
30 public function get_name() {
31 return 'mailchimp';
32 }
33
34 public function get_title() {
35 return esc_html__( 'MailChimp', 'sellkit' );
36 }
37
38 protected function get_base_url() {
39 return 'https://' . $this->api_server . '.api.mailchimp.com/3.0/';
40 }
41
42 protected function get_headers() {
43 return [
44 'Authorization' => 'Basic ' . base64_encode( 'user:' . $this->api_key ),
45 'Content-Type' => 'application/json',
46 'User-Agent' => $this->user_agent,
47 ];
48 }
49
50 protected function get_get_request_args() {
51 return [
52 'timeout' => 100,
53 'sslverify' => false,
54 'headers' => $this->get_headers(),
55 ];
56 }
57
58 /**
59 * An override of the same named function in CRM trait.\
60 * Because MailChimp's api key is like XXXX-us1 and we must extract its parts.
61 */
62 private function get_api_params( $settings ) {
63 $name = $this->get_name();
64 $api_key_source = $settings[ "{$name}_api_key_source" ];
65 $api_key = '';
66
67 if ( 'custom' === $api_key_source ) {
68 $api_key = $settings[ "{$name}_custom_api_key" ];
69 } else {
70 $options = get_option( 'sellkit' );
71
72 if ( ! empty( $options[ "{$name}_api_key" ] ) ) {
73 $api_key = $options[ "{$name}_api_key" ];
74 }
75 }
76
77 $result = [
78 'token' => '',
79 'server' => '',
80 ];
81
82 $parts = explode( '-', $api_key );
83 if ( 2 === count( $parts ) ) {
84 $result['token'] = $parts[0];
85 $result['server'] = $parts[1];
86 }
87
88 return $result;
89 }
90
91 public function add_controls( $widget ) {
92 $action = $this->get_name();
93
94 $this->add_api_controls( $widget, esc_html__( 'Audience', 'sellkit' ) );
95 $this->add_field_mapping_controls( $widget );
96
97 $widget->add_control( "{$action}_groups",
98 [
99 'label' => esc_html__( 'Groups', 'sellkit' ),
100 'type' => 'select2',
101 'multiple' => 'true',
102 'label_block' => true,
103 'conditions' => [
104 'terms' => [
105 [
106 'name' => "{$action}_list",
107 'operator' => '!in',
108 'value' => [ 'none', 'fetching', 'noList' ],
109 ],
110 ],
111 ],
112 ]
113 );
114
115 $this->add_tag_control( $widget );
116
117 $widget->add_control( "{$action}_resubscribe",
118 [
119 'label' => esc_html__( 'Double Opt-In', 'sellkit' ),
120 'type' => 'switcher',
121 'description' => esc_html__( 'Activates the existing user, if unsubscribed.', 'sellkit' ),
122 'conditions' => [
123 'terms' => [
124 [
125 'name' => "{$action}_list",
126 'operator' => '!in',
127 'value' => [ 'none', 'fetching', 'noList' ],
128 ],
129 ],
130 ],
131 ]
132 );
133 }
134
135 public function run( AjaxHandler $ajax_handler ) {
136 $form_settings = $ajax_handler->form['settings'];
137
138 // Retrieve and check List.
139 $list_id = $form_settings[ $this->get_name() . '_list' ];
140 if ( empty( $list_id ) || in_array( $list_id, [ 'default', 'fetching', 'noList' ], true ) ) {
141 return $ajax_handler->add_response( 'admin_errors', $this->get_invalid_list_message( 'audience' ) );
142 }
143
144 // Retireve and check API credentials.
145 $api_data = $this->get_api_params( $form_settings );
146 $this->api_key = $api_data['token'];
147 $this->api_server = $api_data['server'];
148 if ( empty( $this->api_key ) || empty( $this->api_server ) ) {
149 return $ajax_handler->add_response( 'admin_errors', "{$this->get_title()}: " . esc_html__( 'Missing API credentials.', 'sellkit' ) );
150 }
151
152 // Try subscription.
153 $this->ajax_handler = $ajax_handler;
154 $subscriber = $this->create_subscriber_object();
155 $this->subscribe( $subscriber, $list_id );
156 }
157
158 public function get_list( AjaxHandler $ajax_handler, $params ) {
159 $api_data = $this->get_api_params( $params );
160 $this->api_key = $api_data['token'];
161 $this->api_server = $api_data['server'];
162 $results = $this->send_get( 'lists?count=999' );
163 $lists = [];
164
165 if ( ! empty( $results['body']['lists'] ) ) {
166 foreach ( $results['body']['lists'] as $list ) {
167 $lists[ $list['id'] ] = $list['name'];
168 }
169 }
170
171 $list = [ 'lists' => $lists ];
172
173 return $ajax_handler->add_response( 'success', $list );
174 }
175
176 public function get_additional_data( AjaxHandler $ajax_handler, $params ) {
177 $api_data = $this->get_api_params( $params );
178 $this->api_key = $api_data['token'];
179 $this->api_server = $api_data['server'];
180
181 $data = [
182 'custom_fields' => $this->get_remote_custom_fields( $params['list_id'] ),
183 'groups' => $this->get_remote_groups( $params['list_id'] ),
184 'tags' => $this->get_remote_tags( $params['list_id'] ),
185 ];
186
187 return $ajax_handler->add_response( 'success', $data );
188 }
189
190 private function get_remote_custom_fields( $list_id ) {
191 $results = $this->send_get( "lists/{$list_id}/merge-fields?count=999" );
192
193 $default_fields = $this->get_default_remote_fields()['optional'];
194 $custom_fields = [];
195
196 if ( empty( $results['body']['merge_fields'] ) ) {
197 return $custom_fields;
198 }
199
200 foreach ( $results['body']['merge_fields'] as $field ) {
201 if ( ! array_key_exists( $field['tag'], $default_fields ) ) {
202 $custom_fields[ $field['tag'] ] = $field['name'];
203 }
204 }
205
206 return $custom_fields;
207 }
208
209 private function get_remote_tags( $list_id ) {
210 $results = $this->send_get( "lists/{$list_id}/tag-search" );
211 $tags = [];
212
213 if ( empty( $results['body']['tags'] ) ) {
214 return $tags;
215 }
216
217 foreach ( $results['body']['tags'] as $tag ) {
218 $tags[ $tag['name'] ] = $tag['name'];
219 }
220
221 return $tags;
222 }
223
224 private function get_remote_groups( $list_id ) {
225 $results = $this->send_get( "lists/{$list_id}/interest-categories?count=999" );
226 $groups = [];
227
228 if ( ! empty( $results['body']['categories'] ) ) {
229 foreach ( $results['body']['categories'] as $category ) {
230 $_results = $this->send_get( "lists/{$list_id}/interest-categories/{$category['id']}/interests?count=999" );
231
232 if ( ! empty( $_results['body']['interests'] ) ) {
233 foreach ( $_results['body']['interests'] as $interests ) {
234 $groups[ $interests['id'] ] = "{$category['title']}: {$interests['name']}";
235 }
236 }
237 }
238 }
239
240 return $groups;
241 }
242
243 protected function get_default_remote_fields() {
244 return [
245 'required' => [
246 'email_address' => esc_html__( 'Email', 'sellkit' ),
247 ],
248 'optional' => [
249 'full_name' => esc_html__( 'Full Name', 'sellkit' ),
250 ],
251 ];
252 }
253
254 /**
255 * Create the subscriber object for MailChimp.
256 *
257 * @since 1.5.0
258 * @return array
259 * @SuppressWarnings(PHPMD.NPathComplexity)
260 */
261 protected function create_subscriber_object() {
262 $mapped_fields = $this->get_field_mappings();
263 $subscriber = [
264 'ip_opt' => static::get_client_ip(),
265 'status' => 'subscribed',
266 'status_if_new' => 'subscribed',
267 'skip_merge_validation' => true,
268 ];
269 $default_fields = $this->get_default_remote_fields();
270
271 foreach ( $mapped_fields as $key => $value ) {
272 $is_custom =
273 ! array_key_exists( $key, $default_fields['required'] ) &&
274 ! array_key_exists( $key, $default_fields['optional'] );
275
276 if ( $is_custom ) {
277 $subscriber['merge_fields'][ $key ] = $value;
278 continue;
279 }
280
281 $subscriber[ $key ] = $value;
282 }
283
284 if ( empty( $subscriber['email_address'] ) ) {
285 return [];
286 }
287
288 $settings = $this->ajax_handler->form['settings'];
289
290 // Reactivation status.
291 $reactivate = $settings['mailchimp_resubscribe'];
292 if ( 'yes' !== $reactivate ) {
293 $email_hash = md5( strtolower( $subscriber['email_address'] ) );
294 $list_id = $settings['mailchimp_list'];
295
296 $subscriber_info = $this->send_get( "lists/{$list_id}/members/{$email_hash}" );
297
298 if ( ! empty( $subscriber_info['body']['status'] ) && 'subscribed' !== $subscriber_info['body']['status'] ) {
299 $subscriber['status'] = 'pending';
300 }
301 }
302
303 // Add additional data.
304 $subscriber['tags'] = $settings['mailchimp_tags'];
305
306 $groups = $settings['mailchimp_groups'];
307
308 if ( ! empty( $groups ) ) {
309 $groups = is_array( $groups ) ? $groups : [ $groups ];
310 $subscriber['interests'] = array_fill_keys( $groups, true );
311 }
312
313 if ( isset( $subscriber['merge_fields'] ) && ! empty( $subscriber['merge_fields']['BIRTHDAY'] ) ) {
314 $formatted_birthday = gmdate( 'Y/m/d', strtotime( $subscriber['merge_fields']['BIRTHDAY'] ) );
315
316 $subscriber['merge_fields']['BIRTHDAY'] = $formatted_birthday;
317 }
318
319 if ( isset( $subscriber['merge_fields'] ) && ! empty( $subscriber['merge_fields']['ADDRESS'] ) ) {
320 $fields = $this->ajax_handler->form_data['fields'];
321 $formatted_address = $subscriber['merge_fields']['ADDRESS'];
322
323 $mapping_fields = $this->ajax_handler->form['settings'][ $this->get_name() . '_fields_mapping' ];
324
325 foreach ( $mapping_fields as $map_field ) {
326 if ( ! isset( $map_field['remote_field'] ) || empty( $map_field['local_field'] ) ) {
327 continue;
328 }
329
330 if ( 'ADDRESS' !== $map_field['remote_field'] ) {
331 continue;
332 }
333
334 if ( empty( $fields[ "hidden_{$map_field['local_field']}" ] ) ) {
335 continue;
336 }
337
338 $formatted_address = $this->handle_mailchimp_address( $fields, $map_field );
339 }
340
341 $subscriber['merge_fields']['ADDRESS'] = $formatted_address;
342 }
343
344 return $subscriber;
345 }
346
347 /**
348 * Handle the address field for MailChimp.
349 *
350 * @param array $fields
351 * @param array $map_field
352 * @since 2.3.0
353 * @return array
354 */
355 protected function handle_mailchimp_address( $fields, $map_field ) {
356 if ( empty( $fields[ "hidden_{$map_field['local_field']}" ] ) ) {
357 return [];
358 }
359
360 $address_json = stripslashes( $fields[ "hidden_{$map_field['local_field']}" ] );
361 $addresses = json_decode( $address_json, true );
362
363 $mapping = [
364 'addr1' => [ 'street_number', 'route' ],
365 'city' => 'locality',
366 'state' => 'administrative_area_level_1',
367 'zip' => 'postal_code',
368 'country' => 'country'
369 ];
370
371 $address_data = [];
372
373 foreach ( $mapping as $mailchimp_key => $source_keys ) {
374 if ( is_array( $source_keys ) ) {
375 $value = '';
376
377 foreach ( $source_keys as $key ) {
378 if ( ! empty( $addresses[ $key ] ) ) {
379 $value .= $addresses[ $key ] . ' ';
380 }
381 }
382
383 $address_data[ $mailchimp_key ] = trim( $value );
384
385 continue;
386 }
387
388 $address_data[ $mailchimp_key ] = ! empty( $addresses[ $source_keys ] ) ? $addresses[ $source_keys ] : '';
389 }
390
391 return $address_data;
392 }
393
394 protected function subscribe( $subscriber_data, $list_id ) {
395 if ( empty( $subscriber_data['email_address'] ) ) {
396 return;
397 }
398
399 $email_hash = md5( strtolower( $subscriber_data['email_address'] ) );
400
401 $endpoint = "lists/{$list_id}/members/{$email_hash}";
402
403 $args = [
404 'method' => 'PUT',
405 'timeout' => 100,
406 'headers' => $this->get_headers(),
407 'body' => wp_json_encode( $subscriber_data ),
408 ];
409
410 return $this->send_post( $endpoint, $args );
411 }
412 }
413