PluginProbe ʕ •ᴥ•ʔ
Hustle – Email Marketing, Lead Generation, Optins, Popups / 7.8.14
Hustle – Email Marketing, Lead Generation, Optins, Popups v7.8.14
7.8.14 7.8.14.1 7.8.13 7.8.13.1 trunk 3.0 3.1 3.1.1 3.1.2 3.1.3 3.1.4 4.3.2 4.4.4 4.4.5 4.4.5.1 4.4.5.4 4.6 4.6.1.1 4.6.1.4 4.7.0.2 4.7.0.3 4.7.0.7 4.7.0.9 4.7.1.0 4.7.1.1 4.8.0.0 5.0.0 5.0.1 5.0.1.1 5.0.1.2 5.1 5.1.1 5.1.2 5.1.3 5.1.3.1 5.1.3.2 5.1.4 5.1.5 6.0 6.0.1 6.0.2 6.0.3 6.0.4.2 6.0.5 6.0.6.1 6.0.7 6.0.8.1 6.0.9 7.0.0.1 7.0.2 7.0.3 7.0.4 7.1.0 7.1.1 7.2.0 7.2.1 7.3.0 7.3.1 7.3.3 7.3.5 7.3.6 7.3.7 7.4.0 7.4.1 7.4.11 7.4.13 7.4.13.1 7.4.2 7.4.3 7.4.4 7.4.5 7.4.5.1 7.4.5.2 7.4.6 7.4.7 7.5.0 7.6.0 7.6.1 7.6.3 7.6.4 7.6.6 7.7.0 7.7.1 7.8.0 7.8.1 7.8.10 7.8.10.1 7.8.10.2 7.8.11 7.8.12 7.8.12.1 7.8.2 7.8.3 7.8.4 7.8.5 7.8.6 7.8.7 7.8.8 7.8.9 7.8.9.1 7.8.9.2 7.8.9.3
wordpress-popup / inc / providers / mailchimp / hustle-mailchimp.php
wordpress-popup / inc / providers / mailchimp Last commit date
images 7 years ago views 3 years ago hustle-mailchimp-api.php 5 months ago hustle-mailchimp-form-hooks.php 5 months ago hustle-mailchimp-form-settings.php 5 months ago hustle-mailchimp.php 5 months ago mailchimp.php 5 months ago
hustle-mailchimp.php
717 lines
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2 /**
3 * Class Hustle_Mailchimp
4 * The class that defines mailchimp provider
5 *
6 * @package Hustle
7 */
8
9 if ( ! class_exists( 'Hustle_Mailchimp' ) ) :
10
11 include_once 'hustle-mailchimp-api.php';
12
13 /**
14 * Class Hustle_Mailchimp
15 */
16 class Hustle_Mailchimp extends Hustle_Provider_Abstract {
17
18 const GROUP_TRANSIENT = 'hustle-mailchimp-group-transient';
19
20 const SLUG = 'mailchimp';
21
22 /**
23 * Api
24 *
25 * @var Mailchimp
26 */
27 protected static $api;
28
29 /**
30 * Mailchimp Provider Instance
31 *
32 * @since 3.0.5
33 *
34 * @var self|null
35 */
36 protected static $instance = null;
37
38 /**
39 * Slug
40 *
41 * @since 3.0.5
42 * @var string
43 */
44 protected $slug = 'mailchimp';
45
46 /**
47 * Version
48 *
49 * @since 3.0.5
50 * @var string
51 */
52 protected $version = '1.0';
53
54 /**
55 * Class
56 *
57 * @since 3.0.5
58 * @var string
59 */
60 protected $class = __CLASS__;
61
62 /**
63 * Title
64 *
65 * @since 3.0.5
66 * @var string
67 */
68 protected $title = 'Mailchimp';
69
70 /**
71 * Class name of form settings
72 *
73 * @since 3.0.5
74 * @var string
75 */
76 protected $form_settings = 'Hustle_Mailchimp_Form_Settings';
77
78 /**
79 * Class name of form hooks
80 *
81 * @since 4.0
82 * @var string
83 */
84 protected $form_hooks = 'Hustle_Mailchimp_Form_Hooks';
85
86 /**
87 * Hold the information of the account that's currently connected.
88 * Will be saved to @see Hustle_Mailchimp::save_settings_values()
89 * Specific for Mailchimp provider.
90 *
91 * @since 4.0
92 * @var array
93 */
94 private $connected_account = array();
95
96 /**
97 * Connection error
98 *
99 * @var string
100 */
101 private static $connection_error = '';
102
103 /**
104 * Provider constructor.
105 *
106 * @since 3.0.5
107 */
108 public function __construct() {
109 $this->icon_2x = plugin_dir_url( __FILE__ ) . 'images/icon.png';
110 $this->logo_2x = plugin_dir_url( __FILE__ ) . 'images/logo.png';
111
112 if ( wp_doing_ajax() ) {
113 add_action( 'wp_ajax_hustle_mailchimp_get_group_interests', array( $this, 'ajax_group_interests' ) );
114 }
115 }
116
117 /**
118 * Ajax group interests.
119 */
120 public function ajax_group_interests() {
121 Opt_In_Utils::validate_ajax_call( 'hustle_mailchimp_interests' );
122 $html = '';
123 $post_data = filter_input( INPUT_POST, 'data', FILTER_SANITIZE_SPECIAL_CHARS );
124 $data = array();
125 wp_parse_str( $post_data, $data );
126 $module_id = isset( $data['module_id'] ) ? (int) $data['module_id'] : '';
127 if ( $module_id ) {
128 $class_name = $this->form_settings;
129 $form_settings_instance = new $class_name( $this, $module_id );
130 $html = $form_settings_instance->get_group_interests( $data );
131 }
132
133 wp_send_json_success( $html );
134 }
135
136 /**
137 * Get Instance
138 *
139 * @since 3.0.5
140 *
141 * @return self|null
142 */
143 public static function get_instance() {
144 if ( is_null( self::$instance ) ) {
145 self::$instance = new self();
146 }
147
148 return self::$instance;
149 }
150
151 /**
152 * Hook before the save settings values method
153 * to include @see Hustle_Mailchimp::$connected_account
154 * for future reference
155 *
156 * @since 4.0
157 *
158 * @param array $values Values.
159 * @return array
160 */
161 public function before_save_settings_values( $values ) {
162 return $values;
163 }
164
165 /**
166 * Get Api
167 *
168 * @param string $api_key Api key.
169 * @return Hustle_Mailchimp_Api
170 */
171 public function get_api( $api_key ) {
172
173 if ( empty( self::$api ) ) {
174 $exploded = explode( '-', $api_key );
175 $data_center = end( $exploded );
176 self::$api = new Hustle_Mailchimp_Api( $api_key, $data_center );
177 }
178 return self::$api;
179 }
180
181 /**
182 * Get member
183 *
184 * @param string $email Email.
185 * @param string $list_id List ID.
186 * @param array $data Data.
187 * @param string $api_key Api key.
188 *
189 * @return Object Returns the member if the email address already exists otherwise false.
190 */
191 public function get_member( $email, $list_id, $data, $api_key ) {
192
193 try {
194 $api = $this->get_api( $api_key );
195
196 $member_info = $api->check_email( $list_id, $email );
197 // Mailchimp returns WP error if can't find member on a list.
198 if ( is_wp_error( $member_info ) && 404 === $member_info->get_error_code() ) {
199 return false;
200 }
201 return $member_info;
202 } catch ( Exception $e ) {
203 Hustle_Provider_Utils::maybe_log( __METHOD__, 'Failed to get member from Mailchimp list.', $e->getMessage() );
204
205 return false;
206 }
207 }
208
209 /**
210 * Delete member
211 *
212 * @param string $email Email.
213 * @param string $list_id List ID.
214 * @param array $data Data.
215 * @param string $api_key Api key.
216 *
217 * @return NULL if the member is deleted otherwise false.
218 */
219 public function delete_member( $email, $list_id, $data, $api_key ) {
220 try {
221 $api = $this->get_api( $api_key );
222
223 $delete_status = $api->delete_email( $list_id, $email );
224 // Mailchimp returns WP error if can't find member on a list.
225 if ( is_wp_error( $delete_status ) && 404 === $delete_status->get_error_code() ) {
226 return false;
227 }
228 return $delete_status;
229 } catch ( Exception $e ) {
230 Hustle_Provider_Utils::maybe_log( __METHOD__, 'Failed to delete member from Mailchimp list.', $e->getMessage() );
231
232 return false;
233 }
234 }
235
236 /**
237 * Get the wizard callbacks for the global settings.
238 *
239 * @since 4.0
240 *
241 * @return array
242 */
243 public function settings_wizards() {
244 return array(
245 array(
246 'callback' => array( $this, 'configure_api_key' ),
247 'is_completed' => array( $this, 'is_connected' ),
248 ),
249 );
250 }
251
252 /**
253 * Configure the API key settings. Global settings.
254 *
255 * @since 4.0
256 *
257 * @param array $submitted_data Submitted data.
258 * @return array
259 */
260 public function configure_api_key( $submitted_data ) {
261 $has_errors = false;
262 $default_data = array(
263 'api_key' => '',
264 'name' => '',
265 );
266 $current_data = $this->get_current_data( $default_data, $submitted_data );
267 $is_submit = isset( $submitted_data['api_key'] );
268 $global_multi_id = $this->get_global_multi_id( $submitted_data );
269
270 $api_key_validated = true;
271 if ( $is_submit ) {
272
273 $api_key_validated = $this->validate_api_key( $current_data['api_key'] );
274 if ( ! $api_key_validated ) {
275 $has_errors = true;
276 $error_message = $this->provider_connection_falied();
277 if ( ! empty( self::$connection_error ) ) {
278 $error_message = self::$connection_error;
279 }
280 }
281
282 if ( ! $has_errors ) {
283 $settings_to_save = array(
284 'api_key' => $current_data['api_key'],
285 'name' => $current_data['name'],
286 );
287
288 // If not active, activate it.
289 // TODO: Wrap this in a friendlier method.
290 if ( Hustle_Provider_Utils::is_provider_active( $this->slug )
291 || Hustle_Providers::get_instance()->activate_addon( $this->slug ) ) {
292 $this->save_multi_settings_values( $global_multi_id, $settings_to_save );
293 } else {
294 $error_message = __( "Provider couldn't be activated.", 'hustle' );
295 $has_errors = true;
296 }
297 }
298
299 if ( ! $has_errors ) {
300
301 return array(
302 'html' => Hustle_Provider_Utils::get_integration_modal_title_markup( __( 'Mailchimp Added', 'hustle' ), __( 'You can now go to your pop-ups, slide-ins and embeds and assign them to this integration', 'hustle' ) ),
303 'buttons' => array(
304 'close' => array(
305 'markup' => Hustle_Provider_Utils::get_provider_button_markup( __( 'Close', 'hustle' ), 'sui-button-ghost', 'close' ),
306 ),
307 ),
308 'redirect' => false,
309 'has_errors' => false,
310 'notification' => array(
311 'type' => 'success',
312 'text' => '<strong>' . $this->get_title() . '</strong> ' . esc_html__( 'Successfully connected', 'hustle' ),
313 ),
314 );
315
316 }
317 }
318
319 $options = array(
320 array(
321 'type' => 'wrapper',
322 'class' => $api_key_validated ? '' : 'sui-form-field-error',
323 'elements' => array(
324 'label' => array(
325 'type' => 'label',
326 'for' => 'api_key',
327 'value' => __( 'API Key', 'hustle' ),
328 ),
329 'api_key' => array(
330 'type' => 'text',
331 'name' => 'api_key',
332 'value' => $current_data['api_key'],
333 'placeholder' => __( 'Enter Key', 'hustle' ),
334 'id' => 'api_key',
335 'icon' => 'key',
336 ),
337 'error' => array(
338 'type' => 'error',
339 'class' => $api_key_validated ? 'sui-hidden' : '',
340 'value' => __( 'Please enter a valid Mailchimp API key', 'hustle' ),
341 ),
342 ),
343 ),
344 array(
345 'type' => 'wrapper',
346 'style' => 'margin-bottom: 0;',
347 'elements' => array(
348 'label' => array(
349 'type' => 'label',
350 'for' => 'mailchimp-name-input',
351 'value' => __( 'Identifier', 'hustle' ),
352 ),
353 'name' => array(
354 'type' => 'text',
355 'name' => 'name',
356 'value' => $current_data['name'],
357 'placeholder' => __( 'E.g. Business Account', 'hustle' ),
358 'id' => 'mailchimp-name-input',
359 ),
360 'message' => array(
361 'type' => 'description',
362 'value' => __( 'Helps to distinguish your integrations if you have connected to the multiple accounts of this integration.', 'hustle' ),
363 ),
364 ),
365 ),
366 array(
367 'type' => 'hidden',
368 'name' => 'global_multi_id',
369 'value' => $global_multi_id,
370 ),
371 );
372
373 if ( $has_errors ) {
374 $error_notice = array(
375 'type' => 'notice',
376 'icon' => 'info',
377 'class' => 'sui-notice-error',
378 'value' => esc_html( $error_message ),
379 );
380 array_unshift( $options, $error_notice );
381 }
382
383 $step_html = Hustle_Provider_Utils::get_integration_modal_title_markup(
384 __( 'Configure Mailchimp', 'hustle' ),
385 sprintf(
386 /* translators: 1. opening 'a' tag to Mailchimp API page, 2. closing 'a' tag */
387 __( 'Log in to your %1$sMailchimp account%2$s to get your API Key.', 'hustle' ),
388 '<a href="https://admin.mailchimp.com/account/api/" target="_blank">',
389 '</a>'
390 )
391 );
392
393 $step_html .= Hustle_Provider_Utils::get_html_for_options( $options );
394
395 $is_edit = $this->settings_are_completed( $global_multi_id );
396 if ( $is_edit ) {
397 $buttons = array(
398 'disconnect' => array(
399 'markup' => Hustle_Provider_Utils::get_provider_button_markup( __( 'Disconnect', 'hustle' ), 'sui-button-ghost', 'disconnect', true ),
400 ),
401 'save' => array(
402 'markup' => Hustle_Provider_Utils::get_provider_button_markup(
403 __( 'Save', 'hustle' ),
404 '',
405 'connect',
406 true
407 ),
408 ),
409 );
410 } else {
411 $buttons = array(
412 'connect' => array(
413 'markup' => Hustle_Provider_Utils::get_provider_button_markup(
414 __( 'Connect', 'hustle' ),
415 'sui-button-right',
416 'connect',
417 true
418 ),
419 ),
420 );
421
422 }
423
424 $response = array(
425 'html' => $step_html,
426 'buttons' => $buttons,
427 'has_errors' => $has_errors,
428 );
429
430 return $response;
431 }
432
433 /**
434 * Validate the provided API key.
435 *
436 * @since 4.0
437 *
438 * @param string $api_key Api key.
439 * @return bool
440 */
441 private function validate_api_key( $api_key ) {
442 if ( empty( trim( $api_key ) ) ) {
443 return false;
444 }
445
446 // Check API Key by validating it on get_info request.
447 try {
448 $info = $this->get_api( $api_key )->get_info();
449
450 if ( is_wp_error( $info ) ) {
451 $error_data = json_decode( $info->get_error_data(), true );
452 if ( ! empty( $error_data['title'] ) ) {
453 self::$connection_error = $error_data['title'];
454 }
455 if ( ! empty( $error_data['detail'] ) ) {
456 self::$connection_error .= ( ! empty( self::$connection_error ) ? ' - ' : '' ) . $error_data['detail'];
457 }
458 Hustle_Provider_Utils::maybe_log( __METHOD__, $info->get_error_message() );
459 return false;
460 }
461
462 $this->connected_account = array(
463 'account_id' => $info->account_id,
464 'account_name' => $info->account_name,
465 'email' => $info->email,
466 );
467
468 } catch ( Exception $e ) {
469 Hustle_Provider_Utils::maybe_log( __METHOD__, $e->getMessage() );
470 return false;
471 }
472
473 return true;
474 }
475
476 /**
477 * Get 3.0 provider mappings
478 *
479 * @return array
480 */
481 protected function get_30_provider_mappings() {
482 return array(
483 'api_key' => 'api_key',
484 );
485 }
486
487 /**
488 * Migrate 3.0
489 *
490 * @param object $module Module.
491 * @param object $old_module Old module.
492 * @return boolean
493 */
494 public function migrate_30( $module, $old_module ) {
495 $migrated = parent::migrate_30( $module, $old_module );
496 if ( ! $migrated ) {
497 return false;
498 }
499
500 /**
501 * For mailchimp version 3.x used to store a lot of unnecessary crap, let's get rid of it now.
502 */
503 $redundant = array( 'is_step', 'slug', 'step', 'current_step', 'module_type' );
504 $module_provider_settings = $module->get_provider_settings( $this->get_slug() );
505 if ( ! empty( $module_provider_settings ) ) {
506 // Remove redundants.
507 $module_provider_settings = array_diff_key( $module_provider_settings, array_combine( $redundant, $redundant ) );
508
509 // Interest options are stored differently so let's try to bridge the differences.
510 $interest_options = $this->transform_interest_options( $module_provider_settings );
511 $module_provider_settings['interest_options'] = $interest_options;
512
513 if ( isset( $module_provider_settings['group_interest'] ) ) {
514
515 // 3.x didn't store group_interest_name so let's see if we can add that.
516 $module_provider_settings['group_interest_name'] = is_array( $interest_options ) && ! empty( $interest_options[ $module_provider_settings['group_interest'] ] )
517 ? $interest_options[ $module_provider_settings['group_interest'] ]
518 : '';
519 $module_provider_settings['group_type'] = 'radio';
520
521 }
522
523 $module->set_provider_settings( $this->get_slug(), $module_provider_settings );
524 }
525
526 return $migrated;
527 }
528
529 /**
530 * Transform interest options
531 *
532 * @param array $module_provider_settings Settings.
533 * @return array
534 */
535 private function transform_interest_options( $module_provider_settings ) {
536 if (
537 empty( $module_provider_settings['list_id'] )
538 || empty( $module_provider_settings['interest_options'] )
539 || empty( $module_provider_settings['group'] )
540 ) {
541 return array();
542 }
543
544 $original = $module_provider_settings['interest_options'];
545 $list_id = $module_provider_settings['list_id'];
546 $transient = get_site_transient( self::GROUP_TRANSIENT . $list_id );
547 if ( empty( $transient ) ) {
548 return $original;
549 }
550
551 $group_id = $module_provider_settings['group'];
552 $interests = array();
553 foreach ( $transient as $group ) {
554 if (
555 isset( $group->id, $group->list_id, $group->interests )
556 && $group->id === $group_id
557 && $group->list_id === $list_id
558 ) {
559 $interests = $group->interests;
560 }
561 }
562
563 if ( empty( $interests ) ) {
564 return $original;
565 }
566
567 $interest_options = array();
568 foreach ( $interests as $interest ) {
569 if (
570 isset( $interest->name )
571 && strpos( $original . ',', $interest->name . ',' ) !== false
572 ) {
573 $interest_options[ $interest->id ] = $interest->name;
574 }
575 }
576
577 return $interest_options;
578 }
579
580 /**
581 * 3.x used to store interest options as a comma-separated string, in later versions we started storing it as an array.
582 * This method returns the interest options in a single, predictable format.
583 *
584 * @param Hustle_Module_Model $module Module model.
585 *
586 * @return array Interest options formatted as id=>name pairs
587 */
588 public function get_interest_options( Hustle_Module_Model $module ) {
589 $settings = $module->get_provider_settings( $this->get_slug() );
590 $required = array( 'selected_global_multi_id', 'interest_options', 'list_id', 'group' );
591 if ( ! $this->array_has_items( $settings, $required ) ) {
592 return array();
593 }
594
595 // If we already have the interest options in the correct format, don't bother calling remote.
596 if ( is_array( $settings['interest_options'] ) ) {
597 return $settings['interest_options'];
598 }
599
600 // No dice, call api.
601 $remote_interest_options = $this->get_remote_interest_options(
602 $settings['selected_global_multi_id'],
603 $settings['list_id'],
604 $settings['group']
605 );
606
607 // Save the new interest_options so we don't have to fetch them remotely again.
608 $settings['interest_options'] = $remote_interest_options;
609 $module->set_provider_settings( $this->get_slug(), $settings );
610
611 return $remote_interest_options;
612 }
613
614 /**
615 * Calls the API to fetch remote interest options
616 *
617 * @param string $global_multi_id Global multi ID.
618 * @param string $list_id List ID.
619 * @param string $group_id Group ID.
620 * @return type
621 */
622 public function get_remote_interest_options( $global_multi_id, $list_id, $group_id ) {
623 if ( '-1' === $group_id ) {
624 return array();
625 }
626
627 $api_key = $this->get_setting( 'api_key', '', $global_multi_id );
628 try {
629 $api = $this->get_api( $api_key );
630 $response = $api->get_interests( $list_id, $group_id, PHP_INT_MAX );
631 if ( is_wp_error( $response ) || ! is_array( $response->interests ) ) {
632 return array();
633 }
634
635 $interests = wp_list_pluck( $response->interests, 'name', 'id' );
636 } catch ( Exception $e ) {
637 return array();
638 }
639
640 return $interests;
641 }
642
643 /**
644 * Maybe add custom field
645 *
646 * @param object $api Api.
647 * @param string $list_id List ID.
648 * @param arry $merge_data Merge data.
649 * @param int $module_id Module ID.
650 * @return boolean|\Hustle_Module_Model
651 */
652 public function maybe_add_custom_fields( $api, $list_id, $merge_data, $module_id ) {
653
654 $existed_custom_fields = $api->get_custom_fields( $list_id );
655 $existed_keys = ! empty( $existed_custom_fields->merge_fields ) ? wp_list_pluck( $existed_custom_fields->merge_fields, 'tag' ) : array();
656 $new_fields = array();
657 foreach ( $merge_data as $k => $v ) {
658 if ( ! in_array( strtoupper( $k ), $existed_keys, true ) ) {
659 $new_fields[] = $k;
660 }
661 }
662 if ( ! empty( $new_fields ) ) {
663 $module = Hustle_Module_Model::new_instance( $module_id );
664 if ( is_wp_error( $module ) ) {
665 return $module;
666 }
667 $form_fields = $module->get_form_fields();
668 $possible_types = array(
669 'text',
670 'number',
671 'phone',
672 'date',
673 'url',
674 'imageurl',
675 'radio',
676 'dropdown',
677 'birthday',
678 'zip',
679 );
680
681 foreach ( $new_fields as $field ) {
682 $tag = strtoupper( $field );
683 $name = ! empty( $form_fields[ $field ]['label'] ) ? $form_fields[ $field ]['label'] : $field;
684 $type = ! empty( $form_fields[ $field ]['type'] ) ? $form_fields[ $field ]['type'] : '';
685 $api->add_custom_field(
686 $list_id,
687 array(
688 'tag' => $tag,
689 'name' => $name,
690 'type' => in_array( $type, $possible_types, true ) ? $type : 'text',
691 )
692 );
693 }
694 }
695
696 return true;
697 }
698
699 /**
700 * Array has item?
701 *
702 * @param array $source_array Array.
703 * @param type $keys Keys.
704 * @return boolean
705 */
706 private function array_has_items( $source_array, $keys ) {
707 foreach ( $keys as $key ) {
708 if ( ! isset( $source_array[ $key ] ) ) {
709 return false;
710 }
711 }
712
713 return true;
714 }
715 }
716 endif;
717