PluginProbe
Mailchimp List Subscribe Form / trunk
Mailchimp List Subscribe Form vtrunk
1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.5 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 All 55 releases
mailchimp / includes / admin / class-mailchimp-user-sync.php

class-mailchimp-user-sync.php in Mailchimp List Subscribe Form trunk, at includes/admin/class-mailchimp-user-sync.php

911 lines 26.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class responsible for Mailchimp User Sync Settings.
4 *
5 * @package Mailchimp
6 */
7
8 // Exit if accessed directly.
9 if ( ! defined( 'ABSPATH' ) ) {
10 exit;
11 }
12
13 /**
14 * Class Mailchimp_User_Sync
15 *
16 * @since 1.9.0
17 */
18 class Mailchimp_User_Sync {
19
20 /**
21 * The option name.
22 *
23 * @since 1.9.0
24 * @var string
25 */
26 protected $option_name = 'mailchimp_sf_user_sync_settings';
27
28 /**
29 * The errors option name.
30 *
31 * @since 1.9.0
32 * @var string
33 */
34 protected $errors_option_name = 'mailchimp_sf_user_sync_errors';
35
36 /**
37 * The background process.
38 *
39 * @since 1.9.0
40 * @var Mailchimp_User_Sync_Background_Process
41 */
42 protected $background_process;
43
44 /**
45 * Transient key for notices.
46 *
47 * @var string
48 */
49 private $notices_transient_key = 'mailchimp_sf_user_sync_notices';
50
51 /**
52 * Initialize the class
53 */
54 public function init() {
55 add_action( 'admin_init', array( $this, 'register_settings' ) );
56 add_action( 'admin_init', [ $this, 'setup_fields_sections' ] );
57 add_action( 'admin_post_mailchimp_sf_start_user_sync', [ $this, 'start_user_sync' ] );
58 add_action( 'admin_post_mailchimp_sf_cancel_user_sync', [ $this, 'cancel_user_sync' ] );
59 add_action( 'admin_post_mailchimp_sf_skip_user_sync_cta', [ $this, 'skip_user_sync_cta' ] );
60
61 $this->background_process = new Mailchimp_User_Sync_Background_Process();
62 $this->background_process->init();
63
64 // Admin notices
65 add_action( 'admin_notices', [ $this, 'render_notices' ] );
66
67 // Ajax action handler
68 add_action( 'wp_ajax_mailchimp_sf_get_user_sync_status', [ $this, 'get_user_sync_status' ] );
69 add_action( 'wp_ajax_mailchimp_sf_delete_user_sync_error', [ $this, 'delete_user_sync_error' ] );
70 // Render the user sync status and errors.
71 add_action( 'mailchimp_sf_user_sync_before_form', [ $this, 'render_user_sync_status' ] );
72
73 $settings = $this->get_user_sync_settings();
74 // If auto user sync is enabled, keep listening to user register and profile update actions.
75 if ( isset( $settings['enable_user_sync'] ) && 1 === absint( $settings['enable_user_sync'] ) ) {
76 add_action( 'user_register', [ $this, 'sync_user_to_mailchimp' ] );
77 add_action( 'profile_update', [ $this, 'sync_user_to_mailchimp' ] );
78 }
79 }
80
81 /**
82 * Register the user sync settings.
83 *
84 * @since 1.9.0
85 */
86 public function register_settings() {
87 $args = array(
88 'sanitize_callback' => array( $this, 'sanitize_user_sync_settings' ),
89 );
90
91 register_setting( $this->option_name, $this->option_name, $args );
92 }
93
94 /**
95 * Setup the fields and sections.
96 *
97 * @since 1.9.0
98 */
99 public function setup_fields_sections() {
100 $section_id = $this->option_name . '_section';
101 $user_sync_errors = $this->get_user_sync_errors();
102 add_settings_section(
103 $section_id,
104 '',
105 '__return_empty_string',
106 $this->option_name
107 );
108
109 add_settings_field(
110 'enable_user_sync',
111 __( 'Enable Sync', 'mailchimp' ),
112 array( $this, 'enable_user_sync_field' ),
113 $this->option_name,
114 $section_id,
115 [
116 'class' => 'mailchimp-user-sync-settings-field mailchimp-user-sync-enable-user-sync',
117 ]
118 );
119
120 add_settings_field(
121 'existing_contacts_only',
122 __( 'Sync Existing Contacts Only', 'mailchimp' ),
123 array( $this, 'existing_contacts_only_field' ),
124 $this->option_name,
125 $section_id,
126 [
127 'class' => 'mailchimp-user-sync-settings-field mailchimp-user-sync-existing-contacts-only',
128 ]
129 );
130
131 add_settings_field(
132 'subscriber_status',
133 __( 'Subscriber Status', 'mailchimp' ),
134 array( $this, 'subscriber_status_field' ),
135 $this->option_name,
136 $section_id,
137 [
138 'class' => 'mailchimp-user-sync-settings-field mailchimp-user-sync-subscriber-status',
139 ]
140 );
141
142 add_settings_field(
143 'user_roles',
144 __( 'Roles to Sync', 'mailchimp' ),
145 array( $this, 'user_roles_field' ),
146 $this->option_name,
147 $section_id,
148 [
149 'class' => 'mailchimp-user-sync-settings-field mailchimp-user-sync-user-roles',
150 ]
151 );
152
153 if ( ! empty( $user_sync_errors ) ) {
154 add_settings_field(
155 'user_sync_errors',
156 __( 'User Sync Errors', 'mailchimp' ),
157 array( $this, 'render_user_sync_errors' ),
158 $this->option_name,
159 $section_id,
160 [
161 'class' => 'mailchimp-user-sync-settings-field mailchimp-user-sync-user-sync-errors',
162 ]
163 );
164 }
165
166 add_settings_field(
167 'sync_all_users',
168 '',
169 array( $this, 'sync_all_users_button' ),
170 $this->option_name,
171 $section_id
172 );
173 }
174
175 /**
176 * Get the user sync settings.
177 *
178 * @since 1.9.0
179 * @param string|null $key The key to get.
180 * @return array|null The user sync settings.
181 */
182 public function get_user_sync_settings( $key = null ) {
183 $default_settings = array(
184 'enable_user_sync' => 0,
185 'user_roles' => array(
186 'subscriber' => 'subscriber',
187 ),
188 'existing_contacts_only' => 0,
189 'subscriber_status' => 'pending',
190 );
191
192 $settings = get_option( $this->option_name, array() );
193 $settings = wp_parse_args( $settings, $default_settings );
194
195 if ( $key ) {
196 return $settings[ $key ] ?? null;
197 }
198
199 return $settings;
200 }
201
202 /**
203 * Sanitize the user sync settings.
204 *
205 * @since 1.9.0
206 * @param array $new_settings The settings to sanitize.
207 * @return array The sanitized settings.
208 */
209 public function sanitize_user_sync_settings( $new_settings ) {
210 $settings = $this->get_user_sync_settings();
211 $settings['enable_user_sync'] = ( isset( $new_settings['enable_user_sync'] ) && 1 === absint( $new_settings['enable_user_sync'] ) ) ? 1 : 0;
212 $settings['user_roles'] = isset( $new_settings['user_roles'] ) ? array_map( 'sanitize_text_field', $new_settings['user_roles'] ) : array();
213 $settings['existing_contacts_only'] = ( isset( $new_settings['existing_contacts_only'] ) && 1 === absint( $new_settings['existing_contacts_only'] ) ) ? 1 : 0;
214 $settings['subscriber_status'] = isset( $new_settings['subscriber_status'] ) ? sanitize_text_field( $new_settings['subscriber_status'] ) : 'pending';
215
216 return $settings;
217 }
218
219 /**
220 * Render the user roles field.
221 *
222 * @since 1.9.0
223 */
224 public function user_roles_field() {
225 $settings = $this->get_user_sync_settings( 'user_roles' );
226 $user_roles = get_editable_roles();
227
228 ?>
229 <div class="mailchimp-sf-user-sync-user-roles">
230 <?php
231 foreach ( $user_roles as $role_name => $role_details ) {
232 $value = $settings[ $role_name ] ?? '';
233
234 // Render checkbox.
235 printf(
236 '
237 <div class="input-checkbox-wrapper">
238 <div class="input-checkbox-wrapper-inner">
239 <input type="checkbox" id="user_roles_%1$s" name="%1$s" value="%2$s" %3$s class="mailchimp-sf-checkbox" />
240 </div>
241 <label for="user_roles_%1$s">
242 %4$s
243 </label>
244 </div>
245 ',
246 esc_attr( $this->option_name . '[user_roles][' . $role_name . ']' ),
247 esc_attr( $role_name ),
248 checked( $value, $role_name, false ),
249 esc_html( $role_details['name'] )
250 );
251 }
252 ?>
253 </div>
254 <?php
255 }
256
257 /**
258 * Render the enable user sync field.
259 *
260 * @since 1.9.0
261 */
262 public function enable_user_sync_field() {
263 $value = $this->get_user_sync_settings( 'enable_user_sync' );
264 ?>
265 <div class="input-checkbox-wrapper">
266 <label class="mailchimp-sf-toggle-switch">
267 <input
268 type="checkbox"
269 name="<?php echo esc_attr( $this->option_name . '[enable_user_sync]' ); ?>"
270 id="enable_user_sync"
271 value="1"
272 <?php checked( absint( $value ), 1, true ); ?>
273 >
274 <span class="mailchimp-sf-toggle-slider"></span>
275 </label>
276 <label for="enable_user_sync">
277 <?php esc_html_e( 'Automatically sync users to Mailchimp when they are created or updated.', 'mailchimp' ); ?>
278 </label>
279 </div>
280 <?php
281 }
282
283 /**
284 * Render the subscriber status field.
285 *
286 * @since 1.9.0
287 */
288 public function subscriber_status_field() {
289 $settings = $this->get_user_sync_settings( 'subscriber_status' );
290 ?>
291 <div class="radio-wrapper">
292 <label for="subscriber_status_subscribed" class="subscribe_status_label">
293 <input type="radio" id="subscriber_status_subscribed" name="<?php echo esc_attr( $this->option_name . '[subscriber_status]' ); ?>" value="subscribed" <?php checked( $settings, 'subscribed' ); ?> class="mailchimp-sf-radio" />
294 <?php esc_html_e( 'Sync as Subscribed', 'mailchimp' ); ?>
295 </label>
296 <p class="radio-description">
297 <?php esc_html_e( 'This status indicates that you\'ve gotten permission to market to your users.', 'mailchimp' ); ?><br />
298 <a href="https://mailchimp.com/help/the-importance-of-permission/" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'Learn more about the importance of permission.', 'mailchimp' ); ?></a>
299 </p>
300 </div>
301 <div class="radio-wrapper">
302 <label for="subscriber_status_pending" class="subscribe_status_label">
303 <input type="radio" id="subscriber_status_pending" name="<?php echo esc_attr( $this->option_name . '[subscriber_status]' ); ?>" value="pending" <?php checked( $settings, 'pending' ); ?> class="mailchimp-sf-radio" />
304 <?php esc_html_e( 'Sync as Pending', 'mailchimp' ); ?>
305 </label>
306 <p class="radio-description">
307 <?php esc_html_e( 'This status indicates that a double opt-in email will be sent to users to confirm their subscription.', 'mailchimp' ); ?>
308 </p>
309 </div>
310 <div class="radio-wrapper">
311 <label for="subscriber_status_transactional" class="subscribe_status_label">
312 <input type="radio" id="subscriber_status_transactional" name="<?php echo esc_attr( $this->option_name . '[subscriber_status]' ); ?>" value="transactional" <?php checked( $settings, 'transactional' ); ?> class="mailchimp-sf-radio" />
313 <?php esc_html_e( 'Sync as Non-Subscribed', 'mailchimp' ); ?>
314 </label>
315 <p class="radio-description">
316 <?php esc_html_e( 'This status indicates you haven\'t gotten permission to market to these users. However, you can use Mailchimp to message ', 'mailchimp' ); ?><a href="https://mailchimp.com/help/about-non-subscribed-contacts/" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'non-subscribed contacts.', 'mailchimp' ); ?></a>
317 </p>
318 </div>
319 <p class="radio-description">
320 <?php
321 $users_count = $this->get_users_count();
322 echo wp_kses(
323 sprintf(
324 /* translators: %1$s: opening anchor tag, %2$s: closing anchor tag, %3$d: number of contacts. */
325 _n(
326 'You will need %1$sa Mailchimp plan%2$s that includes %3$d contact.',
327 'You will need %1$sa Mailchimp plan%2$s that includes %3$d contacts.',
328 absint( $users_count ),
329 'mailchimp'
330 ),
331 '<a href="https://mailchimp.com/help/about-mailchimp-pricing-plans/" target="_blank" rel="noopener noreferrer">',
332 '</a>',
333 absint( $users_count )
334 ),
335 array(
336 'a' => array(
337 'href' => array(),
338 'target' => array(),
339 'rel' => array(),
340 ),
341 )
342 )
343 ?>
344 <?php esc_html_e( 'If your plan does not include enough contacts, you will incur additional monthly charges.', 'mailchimp' ); ?>
345 <a href="https://mailchimp.com/help/about-additional-charges/" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'Learn about additional charges.', 'mailchimp' ); ?></a>
346 </p>
347 <?php
348 }
349
350 /**
351 * Render the existing contacts only field.
352 *
353 * @since 1.9.0
354 */
355 public function existing_contacts_only_field() {
356 $settings = $this->get_user_sync_settings();
357 $existing_contacts_only = isset( $settings['existing_contacts_only'] ) ? $settings['existing_contacts_only'] : 0;
358 ?>
359 <div class="input-checkbox-wrapper">
360 <div class="input-checkbox-wrapper-inner">
361 <input
362 type="checkbox"
363 name="<?php echo esc_attr( $this->option_name . '[existing_contacts_only]' ); ?>"
364 id="existing_contacts_only"
365 value="1"
366 class="mailchimp-sf-checkbox"
367 <?php checked( absint( $existing_contacts_only ), 1, true ); ?>
368 >
369 </div>
370 <label for="existing_contacts_only">
371 <?php esc_html_e( 'Only WordPress users who are already in your Mailchimp audience will sync.', 'mailchimp' ); ?>
372 </label>
373 </div>
374 <?php
375 }
376
377 /**
378 * Render the sync all users field.
379 *
380 * @since 1.9.0
381 */
382 public function sync_all_users_button() {
383 $start_sync_url = wp_nonce_url( add_query_arg( 'action', 'mailchimp_sf_start_user_sync', admin_url( 'admin-post.php' ) ), 'mailchimp_sf_start_user_sync', 'mailchimp_sf_start_user_sync_nonce' );
384 ?>
385 <a href="<?php echo esc_url( $start_sync_url ); ?>" class="mailchimp-sf-button mailchimp-sf-button-submit btn-primary">
386 <?php esc_html_e( 'Manual Sync', 'mailchimp' ); ?>
387 </a>
388 <?php
389 $last_sync_time = get_option( 'mailchimp_sf_last_sync_time', false );
390 if ( $last_sync_time ) {
391 ?>
392 <span class="mailchimp-sf-last-sync-time">
393 <?php
394 echo esc_html(
395 sprintf(
396 /* translators: %s: last sync time. */
397 esc_html__( 'Last sync: %s', 'mailchimp' ),
398 wp_date( get_option( 'date_format' ) . ' \a\t ' . get_option( 'time_format' ), $last_sync_time )
399 )
400 );
401 ?>
402 </span>
403 <?php
404 }
405 ?>
406 <?php
407 }
408
409 /**
410 * Start the user sync.
411 *
412 * @since 1.9.0
413 */
414 public function start_user_sync() {
415 if (
416 empty( $_GET['mailchimp_sf_start_user_sync_nonce'] ) ||
417 ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['mailchimp_sf_start_user_sync_nonce'] ) ), 'mailchimp_sf_start_user_sync' ) ||
418 ! current_user_can( 'manage_options' )
419 ) {
420 wp_die( esc_html__( 'You don\'t have permission to perform this operation.', 'mailchimp' ) );
421 }
422
423 // Mark the cta as shown.
424 update_option( 'mailchimp_sf_user_sync_start_cta_shown', true );
425
426 $return_url = add_query_arg(
427 array( 'page' => 'mailchimp_sf_options' ),
428 admin_url( 'admin.php' )
429 );
430
431 // Check if the user is connected to Mailchimp.
432 $api = mailchimp_sf_get_api();
433 if ( ! $api ) {
434 $this->add_notice( __( 'We encountered a problem starting the user sync process due to connection issues. Please try again after reconnecting your Mailchimp account.', 'mailchimp' ), 'error' );
435 wp_safe_redirect( esc_url_raw( $return_url ) );
436 exit;
437 }
438
439 // Check if the user has selected a list.
440 $list_id = get_option( 'mc_list_id' );
441 if ( ! $list_id ) {
442 $this->add_notice( __( 'Please select a list to sync users.', 'mailchimp' ), 'error' );
443 wp_safe_redirect( esc_url_raw( $return_url ) );
444 exit;
445 }
446
447 // Include the Action Scheduler library, if not already included.
448 if ( ! class_exists( 'ActionScheduler' ) ) {
449 require_once MCSF_DIR . '/vendor/woocommerce/action-scheduler/action-scheduler.php';
450 }
451
452 // Check if the user sync is already running.
453 if ( $this->background_process->in_progress() ) {
454 $this->add_notice( __( 'User sync process is already running.', 'mailchimp' ), 'warning' );
455 wp_safe_redirect( esc_url_raw( $return_url ) );
456 exit;
457 }
458
459 // Job arguments.
460 $args = array(
461 array(
462 'job_id' => str_replace( '-', '', wp_generate_uuid4() ),
463 'list_id' => $list_id,
464 'processed' => 0,
465 'failed' => 0,
466 'success' => 0,
467 'skipped' => 0,
468 'offset' => 0,
469 ),
470 );
471
472 // Schedule the user sync job.
473 $this->background_process->schedule( $args );
474
475 // Add notice that the user sync has started.
476 $this->add_notice( __( 'User sync process has started.', 'mailchimp' ) );
477
478 // Redirect to the user sync settings page.
479 wp_safe_redirect( esc_url_raw( $return_url ) );
480 exit;
481 }
482
483 /**
484 * Cancel the user sync.
485 *
486 * @since 1.9.0
487 */
488 public function cancel_user_sync() {
489 if (
490 empty( $_GET['mailchimp_sf_cancel_user_sync_nonce'] ) ||
491 ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['mailchimp_sf_cancel_user_sync_nonce'] ) ), 'mailchimp_sf_cancel_user_sync' ) ||
492 ! current_user_can( 'manage_options' )
493 ) {
494 wp_die( esc_html__( 'You don\'t have permission to perform this operation.', 'mailchimp' ) );
495 }
496
497 $unschedule = $this->background_process->unschedule();
498 if ( $unschedule ) {
499 $this->add_notice( __( 'User sync process will be cancelled soon.', 'mailchimp' ) );
500 }
501
502 // Redirect to the user sync settings page.
503 wp_safe_redirect(
504 esc_url_raw(
505 add_query_arg(
506 array( 'page' => 'mailchimp_sf_options' ),
507 admin_url( 'admin.php' )
508 )
509 )
510 );
511 exit;
512 }
513
514 /**
515 * Skip the user sync cta.
516 *
517 * @since 1.9.0
518 */
519 public function skip_user_sync_cta() {
520 if (
521 empty( $_GET['mailchimp_sf_skip_user_sync_cta_nonce'] ) ||
522 ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['mailchimp_sf_skip_user_sync_cta_nonce'] ) ), 'mailchimp_sf_skip_user_sync_cta' ) ||
523 ! current_user_can( 'manage_options' )
524 ) {
525 wp_die( esc_html__( 'You don\'t have permission to perform this operation.', 'mailchimp' ) );
526 }
527
528 update_option( 'mailchimp_sf_user_sync_start_cta_shown', 'skipped' );
529
530 // Redirect to the user sync settings page.
531 wp_safe_redirect(
532 esc_url_raw(
533 add_query_arg(
534 array( 'page' => 'mailchimp_sf_options' ),
535 admin_url( 'admin.php' )
536 )
537 )
538 );
539 exit;
540 }
541
542 /**
543 * Sync user to Mailchimp.
544 *
545 * @param int $user_id The user ID.
546 */
547 public function sync_user_to_mailchimp( $user_id ) {
548 $api = mailchimp_sf_get_api();
549 $list_id = get_option( 'mc_list_id' );
550
551 // Bail if the API or list ID is not set.
552 if ( ! $api || ! $list_id ) {
553 return;
554 }
555
556 $user = get_user_by( 'id', $user_id );
557 if ( ! $user ) {
558 return;
559 }
560
561 // Enqueue the user update action to be processed in the background.
562 if ( function_exists( 'as_enqueue_async_action' ) ) {
563 // Check if the action is already scheduled, if not, enqueue it.
564 if ( ! as_has_scheduled_action( 'mailchimp_sf_handle_user_update', array( $user_id ) ) ) {
565 as_enqueue_async_action( 'mailchimp_sf_handle_user_update', array( $user_id ) );
566 }
567 }
568 }
569
570 /**
571 * Add a notice to be displayed.
572 *
573 * @param string $message Message to display.
574 * @param string $type Type of notice.
575 */
576 public function add_notice( $message, $type = 'success' ) {
577 $notices = get_transient( $this->notices_transient_key );
578
579 if ( ! is_array( $notices ) ) {
580 $notices = [];
581 }
582
583 $notices[] = array(
584 'message' => $message,
585 'type' => $type,
586 );
587
588 set_transient( $this->notices_transient_key, $notices, 300 );
589 }
590
591 /**
592 * Render notices in the admin.
593 */
594 public function render_notices() {
595 $notices = get_transient( $this->notices_transient_key );
596
597 if ( ! empty( $notices ) ) {
598 foreach ( $notices as $notice ) {
599 ?>
600 <div class="notice notice-<?php echo esc_attr( $notice['type'] ); ?> is-dismissible">
601 <p>
602 <?php echo wp_kses_post( $notice['message'] ); ?>
603 </p>
604 </div>
605 <?php
606 }
607 delete_transient( $this->notices_transient_key );
608 }
609 }
610
611 /**
612 * Get the total users.
613 *
614 * @since 1.9.0
615 * @return int The total users.
616 */
617 public function get_users_count() {
618 $settings = $this->get_user_sync_settings();
619 $user_roles = $settings['user_roles'] ?? array();
620 $total_users = 0;
621 $total_counts = count_users();
622 if ( ! empty( $total_counts['avail_roles'] ) && is_array( $total_counts['avail_roles'] ) ) {
623 foreach ( $total_counts['avail_roles'] as $role_name => $role_count ) {
624 if ( in_array( $role_name, $user_roles, true ) ) {
625 $total_users += $role_count;
626 }
627 }
628 }
629
630 return $total_users;
631 }
632
633 /**
634 * Get the user sync status.
635 *
636 * @since 1.9.0
637 */
638 public function render_user_sync_status() {
639 $is_syncing = $this->background_process->in_progress();
640
641 if ( ! $is_syncing ) {
642 return;
643 }
644
645 ?>
646 <div class="mailchimp-sf-user-sync-status">
647 <?php
648 $this->render_user_sync_progress();
649 ?>
650 </div>
651 <?php
652 }
653
654 /**
655 * Render the user sync start cta.
656 *
657 * @since 1.9.0
658 */
659 public function render_user_sync_start_cta() {
660 // Check if the cta is already shown.
661 $cta_shown = get_option( 'mailchimp_sf_user_sync_start_cta_shown', false );
662 if ( $cta_shown ) {
663 return;
664 }
665
666 // Check if the user sync is already running.
667 $is_syncing = $this->background_process->in_progress();
668 if ( $is_syncing ) {
669 return;
670 }
671
672 // Get the start sync URL
673 $start_sync_url = wp_nonce_url(
674 add_query_arg(
675 array(
676 'action' => 'mailchimp_sf_start_user_sync',
677 ),
678 admin_url( 'admin-post.php' )
679 ),
680 'mailchimp_sf_start_user_sync',
681 'mailchimp_sf_start_user_sync_nonce'
682 );
683
684 $skip_url = wp_nonce_url(
685 add_query_arg(
686 array(
687 'action' => 'mailchimp_sf_skip_user_sync_cta',
688 ),
689 admin_url( 'admin-post.php' )
690 ),
691 'mailchimp_sf_skip_user_sync_cta',
692 'mailchimp_sf_skip_user_sync_cta_nonce'
693 );
694 ?>
695 <div class="mailchimp-sf-start-user-sync-wrapper">
696 <div class="mailchimp-sf-start-user-sync-box">
697 <div class="text-wordings">
698 <h2><?php esc_html_e( 'Sync WordPress Users to Mailchimp', 'mailchimp' ); ?></h2>
699 <p><?php esc_html_e( 'Start syncing your WordPress users to Mailchimp to build your audience and grow your business.', 'mailchimp' ); ?></p>
700 <a href="<?php echo esc_url( $start_sync_url ); ?>" class="mailchimp-sf-button btn-primary" style="float: none;">
701 <?php esc_html_e( 'Start User Sync', 'mailchimp' ); ?>
702 </a>
703 <a href="<?php echo esc_url( $skip_url ); ?>" class="skip-user-sync-cta">
704 <?php esc_html_e( 'Skip for now', 'mailchimp' ); ?>
705 </a>
706 </div>
707 </div>
708 </div>
709 <?php
710 }
711
712 /**
713 * Get the user sync progress.
714 *
715 * @since 1.9.0
716 */
717 public function render_user_sync_progress() {
718 $is_syncing = $this->background_process->in_progress();
719
720 if ( ! $is_syncing ) {
721 return;
722 }
723
724 // Get the current progress from the background process
725 $total_users = $this->get_users_count();
726 $progress = $this->background_process->get_args();
727 $progress = current( $progress ) ?? array();
728 $processed = $progress['processed'] ?? 0;
729 $success = $progress['success'] ?? 0;
730 $failed = $progress['failed'] ?? 0;
731 $skipped = $progress['skipped'] ?? 0;
732 $cancel_url = wp_nonce_url(
733 add_query_arg(
734 array(
735 'action' => 'mailchimp_sf_cancel_user_sync',
736 ),
737 admin_url( 'admin-post.php' )
738 ),
739 'mailchimp_sf_cancel_user_sync',
740 'mailchimp_sf_cancel_user_sync_nonce'
741 );
742 ?>
743 <div class="mailchimp-sf-sync-progress">
744 <span class="spinner is-active" style="float: none; margin: 0 10px 0 0;"></span>
745 <span class="sync-status-text">
746 <?php
747 printf(
748 /* translators: %1$d: number of processed users, %2$d: total number of users, %3$d: number of synced users, %4$d: number of failed users, %5$d: number of skipped users. */
749 esc_html__( 'Syncing users: %1$d out of %2$d users processed (Synced: %3$d, Failed: %4$d, Skipped: %5$d).', 'mailchimp' ),
750 absint( $processed ),
751 absint( $total_users ),
752 absint( $success ),
753 absint( $failed ),
754 absint( $skipped )
755 );
756 ?>
757 </span>
758 <a href="<?php echo esc_url( $cancel_url ); ?>" class="mailchimp-sf-button btn-secondary btn-small mailchimp-cancel-user-sync-button">
759 <?php esc_html_e( 'Cancel Sync', 'mailchimp' ); ?>
760 </a>
761 </div>
762 <?php
763 }
764
765 /**
766 * Ajax handler for refresh user sync status.
767 */
768 public function get_user_sync_status() {
769 // Check the nonce for security
770 check_ajax_referer( 'mailchimp_sf_user_sync_status', 'nonce' );
771
772 $data = array(
773 'is_running' => false,
774 'status' => '',
775 );
776
777 if ( $this->background_process->in_progress() ) {
778 $data['is_running'] = true;
779 ob_start();
780 $this->render_user_sync_progress();
781 $data['status'] = ob_get_clean();
782 }
783
784 wp_send_json_success( $data );
785 }
786
787 /**
788 * Get the user sync errors.
789 *
790 * @since 1.9.0
791 * @return array The user sync errors.
792 */
793 public function get_user_sync_errors() {
794 return get_option( $this->errors_option_name, array() );
795 }
796
797 /**
798 * Set the user sync errors.
799 *
800 * @since 1.9.0
801 * @param array $errors The user sync errors.
802 */
803 public function set_user_sync_errors( $errors ) {
804 if ( ! is_array( $errors ) || empty( $errors ) ) {
805 return;
806 }
807
808 $current_errors = $this->get_user_sync_errors();
809 $errors = array_merge( $current_errors, $errors );
810 update_option( $this->errors_option_name, $errors );
811 }
812
813 /**
814 * Delete the user sync error.
815 *
816 * @since 1.9.0
817 *
818 * @param string $id The id of the user sync error.
819 */
820 public function delete_user_sync_errors( $id ) {
821 if ( 'all' === $id ) {
822 delete_option( $this->errors_option_name );
823 return;
824 }
825
826 $errors = $this->get_user_sync_errors();
827 if ( ! isset( $errors[ $id ] ) ) {
828 return;
829 }
830
831 unset( $errors[ $id ] );
832 update_option( $this->errors_option_name, $errors );
833 }
834
835 /**
836 * Render the user sync errors.
837 * Note: This is only renders last 25 records.
838 *
839 * @since 1.9.0
840 */
841 public function render_user_sync_errors() {
842 $errors = $this->get_user_sync_errors();
843
844 if ( empty( $errors ) ) {
845 return;
846 }
847
848 // Get last 25 records
849 $errors = array_slice( $errors, -25 );
850 ?>
851 <div class="mailchimp-sf-user-sync-errors">
852 <table class="widefat striped mailchimp-sf-user-sync-errors-table">
853 <thead>
854 <tr>
855 <th><?php esc_html_e( 'ID', 'mailchimp' ); ?></th>
856 <th><?php esc_html_e( 'Email', 'mailchimp' ); ?></th>
857 <th><?php esc_html_e( 'Error', 'mailchimp' ); ?></th>
858 <th><?php esc_html_e( 'Actions', 'mailchimp' ); ?></th>
859 </tr>
860 </thead>
861 <tbody>
862 <?php
863 foreach ( $errors as $id => $error ) {
864 ?>
865 <tr id="row-<?php echo esc_attr( $id ); ?>">
866 <td class="user-id">
867 <a href="<?php echo esc_url( get_edit_user_link( $error['user_id'] ) ); ?>">
868 <?php echo esc_html( $error['user_id'] ?? '-' ); ?></td>
869 </a>
870 <td class="email"><strong><?php echo esc_html( $error['email'] ?? '-' ); ?></strong></td>
871 <td class="error"><?php echo esc_html( $error['error'] ?? '-' ); ?></td>
872 <td class="actions">
873 <div class="mailchimp-sf-user-sync-error-action">
874 <span class="spinner" style="float: none; "></span>
875 <button class="mailchimp-sf-button btn-secondary btn-small mailchimp-sf-user-sync-error-delete" data-id="<?php echo esc_attr( $id ); ?>"><?php esc_html_e( 'Delete', 'mailchimp' ); ?></button>
876 </div>
877 </td>
878 </tr>
879 <?php
880 }
881 ?>
882 </tbody>
883 </table>
884 <div class="mailchimp-sf-user-sync-errors-footer">
885 <div class="mailchimp-sf-user-sync-errors-footer-actions">
886 <span class="spinner" style="float: none;"></span>
887 <button id="mailchimp-sf-clear-user-sync-errors" class="mailchimp-sf-button btn-secondary btn-small"><?php esc_html_e( 'Clear Error logs', 'mailchimp' ); ?></button>
888 </div>
889 </div>
890 </div>
891 <?php
892 }
893
894 /**
895 * Ajax handler for deleting the user sync error.
896 */
897 public function delete_user_sync_error() {
898 // Check the nonce for security
899 check_ajax_referer( 'mailchimp_sf_delete_user_sync_error', 'nonce' );
900
901 // Get the id from the request
902 $id = isset( $_POST['id'] ) ? sanitize_text_field( wp_unslash( $_POST['id'] ) ) : '';
903
904 // Delete the user sync error
905 $this->delete_user_sync_errors( $id );
906
907 // Send the success response
908 wp_send_json_success();
909 }
910 }
911