From 60668a300541f9b264234909ccd44267915fb33a Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Wed, 7 May 2025 11:21:17 +0530 Subject: [PATCH 01/22] Add User sync settings. --- assets/css/admin.css | 62 ++- assets/js/admin.js | 22 + .../class-mailchimp-user-sync-settings.php | 294 ++++++++++++++ includes/admin/templates/settings.php | 133 ++++++- includes/admin/templates/setup-page.php | 272 +++++++++++++ includes/admin/templates/user-sync.php | 17 + includes/class-mailchimp-admin.php | 30 +- mailchimp.php | 29 +- views/setup_page.php | 375 ------------------ 9 files changed, 824 insertions(+), 410 deletions(-) create mode 100644 includes/admin/class-mailchimp-user-sync-settings.php create mode 100644 includes/admin/templates/setup-page.php create mode 100644 includes/admin/templates/user-sync.php delete mode 100644 views/setup_page.php diff --git a/assets/css/admin.css b/assets/css/admin.css index 5e05be8b..c59027d5 100644 --- a/assets/css/admin.css +++ b/assets/css/admin.css @@ -19,7 +19,8 @@ table.mc-user, .mc-list-row, .mc-list-note, -.mc-section { +.mc-section, +.mailchimp-sf-nav-tab-wrapper { max-width: 900px; width: 100%; } @@ -38,17 +39,23 @@ table.mc-user, } /* Sections */ -table.mc-widefat { +table.mc-widefat, .mailchimp-sf-user-sync-page table.form-table { background: var(--mailchimp-color-white); border: 2px solid var(--mailchimp-color-header-bg) !important; border-radius: 6px; margin: 2.75rem 0 2.25rem; } +.mailchimp-sf-user-sync-page table.form-table { + display: block; +} + +.mailchimp-sf-user-sync-page table.form-table tr:first-child, table.mc-widefat tr:first-child { background: var(--mailchimp-color-header-bg); } +.mailchimp-sf-user-sync-page table.form-table tr:first-child th, table.mc-widefat tr:first-child th { color: var(--mailchimp-color-text); font-weight: 500; @@ -181,6 +188,8 @@ table.mc-list-select { } /* Table */ +.mailchimp-sf-user-sync-page table.form-table td, +.mailchimp-sf-user-sync-page table.form-table th, table.mc-widefat td, table.mc-widefat th { padding: 18px; @@ -188,6 +197,8 @@ table.mc-widefat th { text-shadow: none; } +.mailchimp-sf-user-sync-page table.form-table .last-row td, +.mailchimp-sf-user-sync-page table.form-table .last-row th, table.mc-widefat .last-row td, table.mc-widefat .last-row th { border-bottom: none !important; @@ -199,6 +210,11 @@ table.mc-widefat th { width: 130px; } +.mailchimp-sf-user-sync-page table.form-table th { + color: var(--mailchimp-color-text-light); + font-weight: 500; +} + table.mc-widefat td label { display: block; font-size: 0.75rem; @@ -213,6 +229,7 @@ table.mc-widefat td { line-height: 1.125 !important; } +.mailchimp-sf-user-sync-page table.form-table td input, table.mc-widefat td input { display: inline-block; font-style: normal; @@ -261,6 +278,31 @@ th.mailchimp-connect { margin-top: 26px; } +/** + * Navigation + */ +.mailchimp-sf-nav-tab-wrapper { + margin-top: 1em; +} + +.mailchimp-sf-nav-tab-wrapper a.nav-tab { + border: 0px; + background: transparent; + color: #000; + margin-left: 0; + font-weight: 500; + padding: 5px 12px; +} + +.mailchimp-sf-nav-tab-wrapper a.nav-tab:hover { + color: var(--mailchimp-color-link); +} + +.mailchimp-sf-nav-tab-wrapper a.nav-tab.nav-tab-active { + border-bottom: 2px solid var(--mailchimp-color-link); + color: var(--mailchimp-color-link); +} + /** * Mailchimp OAuth CSS */ @@ -516,7 +558,8 @@ body.toplevel_page_mailchimp_sf_options #footer-upgrade { cursor: not-allowed; } -.button.mailchimp-sf-button.small { +.button.mailchimp-sf-button.small, +.button.mailchimp-sf-button.user-sync-settings-submit { padding: 8px 16px; line-height: 14px; float: right; @@ -749,3 +792,16 @@ body.toplevel_page_mailchimp_sf_options #footer-upgrade { column-gap: 16px; } } + +.mailchimp-sf-user-sync-page { + max-width: 900px; +} + +.mailchimp-sf-user-sync-page .subscribe_status_label { + font-weight: 500; +} + +.mailchimp-sf-user-sync-page p.description_small { + font-size: 0.9em; + margin-bottom: 10px; +} diff --git a/assets/js/admin.js b/assets/js/admin.js index 663c6ad7..84c45848 100644 --- a/assets/js/admin.js +++ b/assets/js/admin.js @@ -414,3 +414,25 @@ }); }); })(jQuery); // eslint-disable-line no-undef + +// User Sync Settings. +(function ($) { + const userSyncSettingsPage = $('.mailchimp-sf-user-sync-page'); + if (userSyncSettingsPage.length > 0) { + const syncExistingContactsOnly = $( + 'tr.mailchimp-user-sync-existing-contacts-only input[type="checkbox"]', + ); + if (syncExistingContactsOnly) { + syncExistingContactsOnly.change(function () { + if (this.checked) { + $('tr.mailchimp-user-sync-subscriber-status').hide(); + } else { + $('tr.mailchimp-user-sync-subscriber-status').show(); + } + }); + + // Trigger change event to hide/show subscriber status. + syncExistingContactsOnly.trigger('change'); + } + } +})(jQuery); // eslint-disable-line no-undef diff --git a/includes/admin/class-mailchimp-user-sync-settings.php b/includes/admin/class-mailchimp-user-sync-settings.php new file mode 100644 index 00000000..27ff051e --- /dev/null +++ b/includes/admin/class-mailchimp-user-sync-settings.php @@ -0,0 +1,294 @@ + array( $this, 'sanitize_user_sync_settings' ), + ); + + register_setting( $this->option_name, $this->option_name, $args ); + } + + /** + * Setup the fields and sections. + * + * @since x.x.x + */ + public function setup_fields_sections() { + $section_id = $this->option_name . '_section'; + add_settings_section( + $section_id, + '', + '__return_empty_string', + $this->option_name + ); + + add_settings_field( + 'user_sync_title', + __( 'User Sync settings', 'mailchimp' ), + '__return_empty_string', + $this->option_name, + $section_id + ); + + add_settings_field( + 'enable_user_sync', + __( 'Enable Auto User Sync', 'mailchimp' ), + array( $this, 'enable_user_sync_field' ), + $this->option_name, + $section_id, + [ + 'class' => 'mailchimp-user-sync-enable-user-sync', + ] + ); + + add_settings_field( + 'existing_contacts_only', + __( 'Sync existing contacts only', 'mailchimp' ), + array( $this, 'existing_contacts_only_field' ), + $this->option_name, + $section_id, + [ + 'class' => 'mailchimp-user-sync-existing-contacts-only', + ] + ); + + add_settings_field( + 'subscriber_status', + __( 'Subscriber Status', 'mailchimp' ), + array( $this, 'subscriber_status_field' ), + $this->option_name, + $section_id, + [ + 'class' => 'mailchimp-user-sync-subscriber-status', + ] + ); + + add_settings_field( + 'user_roles', + __( 'Roles to sync', 'mailchimp' ), + array( $this, 'user_roles_field' ), + $this->option_name, + $section_id, + [ + 'class' => 'mailchimp-user-sync-user-roles', + ] + ); + + add_settings_field( + 'sync_all_users', + __( 'Sync users', 'mailchimp' ), + array( $this, 'sync_all_users_button' ), + $this->option_name, + $section_id + ); + } + + /** + * Get the user sync settings. + * + * @since x.x.x + * @return array The user sync settings. + */ + public function get_user_sync_settings( $key = null ) { + $default_settings = array( + 'enable_user_sync' => 0, + 'user_roles' => array( + 'subscriber' => 'subscriber' + ), + 'existing_contacts_only' => 0, + 'subscriber_status' => 'pending' + ); + + $settings = get_option( $this->option_name, array() ); + $settings = wp_parse_args( $settings, $default_settings ); + + if ( $key ) { + return $settings[ $key ] ?? null; + } + + return $settings; + } + + /** + * Sanitize the user sync settings. + * + * @since x.x.x + * @param array $settings The settings to sanitize. + * @return array The sanitized settings. + */ + public function sanitize_user_sync_settings( $new_settings ) { + $settings = $this->get_user_sync_settings(); + $settings['enable_user_sync'] = isset( $new_settings['enable_user_sync'] ) ? 1 : 0; + $settings['user_roles'] = isset( $new_settings['user_roles'] ) ? array_map( 'sanitize_text_field', $new_settings['user_roles'] ) : array(); + $settings['existing_contacts_only'] = isset( $new_settings['existing_contacts_only'] ) ? 1 : 0; + $settings['subscriber_status'] = isset( $new_settings['subscriber_status'] ) ? sanitize_text_field( $new_settings['subscriber_status'] ) : 'pending'; + + return $settings; + } + + /** + * Render the user roles field. + * + * @since x.x.x + */ + public function user_roles_field() { + $settings = $this->get_user_sync_settings( 'user_roles' ); + $user_roles = get_editable_roles(); + + foreach ( $user_roles as $role_name => $role_details ) { + $value = $settings[ $role_name ] ?? ''; + + // Render checkbox. + printf( + '

+ +

', + esc_attr( $this->option_name . '[user_roles][' . $role_name . ']' ), + esc_attr( $role_name ), + checked( $value, $role_name, false ), + esc_html( $role_details['name'] ) + ); + } + ?> +

+ +

+ get_user_sync_settings( 'enable_user_sync' ); + ?> + + > +

+ +

+ get_user_sync_settings( 'subscriber_status' ); + ?> +
+ +

+ + +

+
+
+ +

+ +

+
+
+ +

+ +

+
+

+ +

+ get_user_sync_settings(); + $existing_contacts_only = isset( $settings['existing_contacts_only'] ) ? $settings['existing_contacts_only'] : 0; + ?> + /> +

+ +

+ + +

+ +

+
+
+
+ + + + + +

:

+
+
+ + + +
+
+ + +
+

+
+

+

+ +
+ get( 'lists', 100, array( 'fields' => 'lists.id,lists.name,lists.email_type_option' ) ); + if ( is_wp_error( $lists ) ) { + $msg = sprintf( + /* translators: %s: error message */ + esc_html__( 'Uh-oh, we couldn\'t get your lists from Mailchimp! Error: %s', 'mailchimp' ), + esc_html( $lists->get_error_message() ) + ); + admin_notice_error( $msg ); + } elseif ( isset( $lists['lists'] ) && count( $lists['lists'] ) === 0 ) { + $msg = sprintf( + /* translators: %s: link to Mailchimp */ + esc_html__( 'Uh-oh, you don\'t have any lists defined! Please visit %s, login, and setup a list before using this tool!', 'mailchimp' ), + "Mailchimp" + ); + admin_notice_error( $msg ); + } else { + $lists = $lists['lists']; + $option = get_option( 'mc_list_id' ); + $list_ids = array_map( + function ( $ele ) { + return $ele['id']; + }, + $lists + ); + $is_list_selected = in_array( $option, $list_ids, true ); + ?> + + + + + +
+ + + + + +
+ +
+
+ +
+ + __( 'Settings', 'mailchimp' ), + 'user_sync' => __( 'User Sync', 'mailchimp' ), + ) + ?> + + +
+
+
diff --git a/includes/admin/templates/setup-page.php b/includes/admin/templates/setup-page.php new file mode 100644 index 00000000..0a5c98dd --- /dev/null +++ b/includes/admin/templates/setup-page.php @@ -0,0 +1,272 @@ + +
+
+
+ + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+ + +
+
+ +
+ + + +
+
+ + + + + +
onclick="showMe('mc-custom-styling')"/>
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + /> + +
+ + + + px +
+ + + # + + # +
+ + + # + + # +
+ + + # + + # +
+
+ + + + + + + + + + + + + + + + + + +
id="mc_double_optin" class="code" /> + +
id="mc_update_existing" class="code" /> + +
id="mc_use_unsub_link" class="code" /> + +
+
+
+ + +
+ + + +
+
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + /> + +  —  + +
+
+
+ + +
+

+
+ + + + + + + + + + + + + + + + + + + +
+ + + /> +
+
    + +
  • + +
+
+ +
+
diff --git a/includes/admin/templates/user-sync.php b/includes/admin/templates/user-sync.php new file mode 100644 index 00000000..4a37a32a --- /dev/null +++ b/includes/admin/templates/user-sync.php @@ -0,0 +1,17 @@ + +
+
+ +
+
diff --git a/includes/class-mailchimp-admin.php b/includes/class-mailchimp-admin.php index 53f675d9..bf8c79a1 100644 --- a/includes/class-mailchimp-admin.php +++ b/includes/class-mailchimp-admin.php @@ -44,8 +44,11 @@ public function init() { add_action( 'wp_ajax_mailchimp_sf_check_login_session', array( $this, 'check_login_session' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_page_scripts' ) ); - add_action( 'admin_menu', array( $this, 'add_create_account_page' ) ); + add_action( 'admin_menu', array( $this, 'add_admin_menu_pages' ) ); add_filter( 'admin_footer_text', array( $this, 'admin_footer_text' ) ); + + $user_sync_settings = new Mailchimp_User_Sync_Settings(); + $user_sync_settings->init(); } /** @@ -507,11 +510,21 @@ public function enqueue_admin_page_scripts( $hook_suffix ) { } /** - * Add the create account page. + * Add the create account page and the settings page to the admin menu. * * @since 1.6.0 */ - public function add_create_account_page() { + public function add_admin_menu_pages() { + // Add settings page. + add_menu_page( + esc_html__( 'Mailchimp Setup', 'mailchimp' ), + esc_html__( 'Mailchimp', 'mailchimp' ), + MCSF_CAP_THRESHOLD, + 'mailchimp_sf_options', + array( $this, 'settings_page' ), + 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1Mi4wMyA1NSI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNmZmY7fTwvc3R5bGU+PC9kZWZzPjx0aXRsZT5Bc3NldCAxPC90aXRsZT48ZyBpZD0iTGF5ZXJfMiIgZGF0YS1uYW1lPSJMYXllciAyIj48ZyBpZD0iTGF5ZXJfMS0yIiBkYXRhLW5hbWU9IkxheWVyIDEiPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTExLjY0LDI4LjU0YTQuNzUsNC43NSwwLDAsMC0xLjE3LjA4Yy0yLjc5LjU2LTQuMzYsMi45NC00LjA1LDZhNi4yNCw2LjI0LDAsMCwwLDUuNzIsNS4yMSw0LjE3LDQuMTcsMCwwLDAsLjgtLjA2YzIuODMtLjQ4LDMuNTctMy41NSwzLjEtNi41N0MxNS41MSwyOS44MywxMy4yMSwyOC42MywxMS42NCwyOC41NFptMi43Nyw4LjA3YTEuMTcsMS4xNywwLDAsMS0xLjEuNTUsMS41MywxLjUzLDAsMCwxLTEuMzctMS41OEE0LDQsMCwwLDEsMTIuMjMsMzRhMS40NCwxLjQ0LDAsMCwwLS41NS0xLjc0LDEuNDgsMS40OCwwLDAsMC0xLjEyLS4yMSwxLjQ0LDEuNDQsMCwwLDAtLjkyLjY0LDMuMzksMy4zOSwwLDAsMC0uMzQuNzlsMCwuMTFjLS4xMy4zNC0uMzMuNDUtLjQ3LjQzcy0uMTYtLjA1LS4yMS0uMjFhMywzLDAsMCwxLC43OC0yLjU1LDIuNDYsMi40NiwwLDAsMSwyLjExLS43NiwyLjUsMi41LDAsMCwxLDEuOTEsMS4zOSwzLjE5LDMuMTksMCwwLDEtLjIzLDIuODJsLS4wOS4yQTEuMTYsMS4xNiwwLDAsMCwxMywzNmEuNzQuNzQsMCwwLDAsLjYzLjMyLDEuMzgsMS4zOCwwLDAsMCwuMzQsMGMuMTUsMCwuMy0uMDcuMzksMEEuMjQuMjQsMCwwLDEsMTQuNDEsMzYuNjFaIi8+PHBhdGggY2xhc3M9ImNscy0xIiBkPSJNNTEsMzMuODhhMy44NCwzLjg0LDAsMCwwLTEuMTUtMWwtLjExLS4zNy0uMTQtLjQyYTUuNTcsNS41NywwLDAsMCwuNS0zLjMyLDUuNDMsNS40MywwLDAsMC0xLjU0LTMsMTAuMDksMTAuMDksMCwwLDAtNC4yNC0yLjI2YzAtLjY3LDAtMS40My0uMDYtMS45YTEyLjgzLDEyLjgzLDAsMCwwLS40OS0zLjI1LDEwLjQ2LDEwLjQ2LDAsMCwwLTEuMy0yLjkyYzIuMTQtMi41NiwzLjI5LTUuMjEsMy4yOS03LjU3LDAtMy44My0zLTYuMy03LjU5LTYuM2ExOS4zLDE5LjMsMCwwLDAtNy4yMiwxLjZsLS4zNC4xNEwyOC43LDEuNTJBNi4zMSw2LjMxLDAsMCwwLDI0LjQzLDAsMTQuMDcsMTQuMDcsMCwwLDAsMTcuNiwyLjJhMzYuOTMsMzYuOTMsMCwwLDAtNi43OCw1LjIxYy00LjYsNC4zOC04LjMsOS42My05LjkxLDE0QTEyLjUxLDEyLjUxLDAsMCwwLDAsMjYuNTRhNi4xNiw2LjE2LDAsMCwwLDIuMTMsNC40bC43OC42NkExMC40NCwxMC40NCwwLDAsMCwyLjc0LDM1YTkuMzYsOS4zNiwwLDAsMCwzLjIxLDYsMTAsMTAsMCwwLDAsNS4xMywyLjQzLDIwLjE5LDIwLjE5LDAsMCwwLDcuMzEsOEEyMy4zMywyMy4zMywwLDAsMCwzMC4xNyw1NUgzMWEyMy4yNywyMy4yNywwLDAsMCwxMi0zLjE2LDE5LjEsMTkuMSwwLDAsMCw3LjgyLTkuMDZsMCwwQTE2Ljg5LDE2Ljg5LDAsMCwwLDUyLDM3LjIzLDUuMTcsNS4xNywwLDAsMCw1MSwzMy44OFptLTEuNzgsOC4yMWMtMyw3LjI5LTEwLjMsMTEuMzUtMTksMTEuMDktOC4wNi0uMjQtMTQuOTQtNC41LTE4LTExLjQzYTcuOTQsNy45NCwwLDAsMS01LjEyLTIuMDYsNy41Niw3LjU2LDAsMCwxLTIuNjEtNC44NUE4LjMxLDguMzEsMCwwLDEsNSwzMUwzLjMyLDI5LjU2Qy00LjQyLDIzLDE5Ljc3LTMuODYsMjcuNTEsMi44OWwyLjY0LDIuNTgsMS40NC0uNjFjNi43OS0yLjgxLDEyLjMtMS40NSwxMi4zLDMsMCwyLjMzLTEuNDgsNS4wNS0zLjg2LDcuNTJhNy41NCw3LjU0LDAsMCwxLDIsMy40OCwxMSwxMSwwLDAsMSwuNDIsMi44MmMwLDEsLjA5LDMuMTYuMDksMy4ybDEsLjI3QTguNjQsOC42NCwwLDAsMSw0Ny4yLDI3YTMuNjYsMy42NiwwLDAsMSwxLjA2LDIuMDZBNCw0LDAsMCwxLDQ3LjU1LDMyLDEwLjE1LDEwLjE1LDAsMCwxLDQ4LDMzLjA4Yy4yLjY0LjM1LDEuMTguMzcsMS4yNS43NCwwLDEuODkuODUsMS44OSwyLjg5QTE1LjI5LDE1LjI5LDAsMCwxLDQ5LjE4LDQyLjA5WiIvPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTQ4LDM2YTEuMzYsMS4zNiwwLDAsMC0uODYtLjE2LDExLjc2LDExLjc2LDAsMCwwLS44Mi0yLjc4QTE3Ljg5LDE3Ljg5LDAsMCwxLDQwLjQ1LDM2YTIzLjY0LDIzLjY0LDAsMCwxLTcuODEuODRjLTEuNjktLjE0LTIuODEtLjYzLTMuMjMuNzRhMTguMywxOC4zLDAsMCwwLDgsLjgxLjE0LjE0LDAsMCwxLC4xNi4xMy4xNS4xNSwwLDAsMS0uMDkuMTVzLTMuMTQsMS40Ni04LjE0LS4wOGEyLjU4LDIuNTgsMCwwLDAsMS44MywxLjkxLDguMjQsOC4yNCwwLDAsMCwxLjQ0LjM5YzYuMTksMS4wNiwxMi0yLjQ3LDEzLjI3LTMuMzYuMS0uMDcuMTYsMCwuMDguMTJsLS4xMy4xOGMtMS41OSwyLjA2LTUuODgsNC40NC0xMS40NSw0LjQ0LTIuNDMsMC00Ljg2LS44Ni01Ljc1LTIuMTctMS4zOC0yLS4wNy01LDIuMjQtNC43MWwxLC4xMWEyMS4xMywyMS4xMywwLDAsMCwxMC41LTEuNjhjMy4xNS0xLjQ2LDQuMzQtMy4wNyw0LjE2LTQuMzdBMS44NywxLjg3LDAsMCwwLDQ2LDI4LjM0YTYuOCw2LjgsMCwwLDAtMy0xLjQxYy0uNS0uMTQtLjg0LS4yMy0xLjItLjM1LS42NS0uMjEtMS0uMzktMS0xLjYxLDAtLjUzLS4xMi0yLjQtLjE2LTMuMTYtLjA2LTEuMzUtLjIyLTMuMTktMS4zNi00YTEuOTIsMS45MiwwLDAsMC0xLS4zMSwxLjg2LDEuODYsMCwwLDAtLjU4LjA2LDMuMDcsMy4wNywwLDAsMC0xLjUyLjg2LDUuMjQsNS4yNCwwLDAsMS00LDEuMzJjLS44LDAtMS42NS0uMTYtMi42Mi0uMjJsLS41NywwYTUuMjIsNS4yMiwwLDAsMC01LDQuNTdjLS41NiwzLjgzLDIuMjIsNS44MSwzLDdhMSwxLDAsMCwxLC4yMi41Mi44My44MywwLDAsMS0uMjguNTVoMGE5LjgsOS44LDAsMCwwLTIuMTYsOS4yLDcuNTksNy41OSwwLDAsMCwuNDEsMS4xMmMyLDQuNzMsOC4zLDYuOTMsMTQuNDMsNC45M2ExNS4wNiwxNS4wNiwwLDAsMCwyLjMzLTEsMTIuMjMsMTIuMjMsMCwwLDAsMy41Ny0yLjY3LDEwLjYxLDEwLjYxLDAsMCwwLDMtNS44MkM0OC42LDM2LjcsNDguMzMsMzYuMjMsNDgsMzZabS04LjI1LTcuODJjMCwuNS0uMzEuOTEtLjY4LjlzLS42Ni0uNDItLjY1LS45Mi4zMS0uOTEuNjgtLjlTMzkuNzIsMjcuNjgsMzkuNzEsMjguMThabS0xLjY4LTZjLjcxLS4xMiwxLjA2LjYyLDEuMzIsMS44NWEzLjY0LDMuNjQsMCwwLDEtLjA1LDIsNC4xNCw0LjE0LDAsMCwwLTEuMDYsMCw0LjEzLDQuMTMsMCwwLDEtLjY4LTEuNjRDMzcuMjksMjMuMjMsMzcuMzEsMjIuMzQsMzgsMjIuMjNabS0yLjQsNi41N2EuODIuODIsMCwwLDEsMS4xMS0uMTljLjQ1LjIyLjY5LjY3LjUzLDFhLjgyLjgyLDAsMCwxLTEuMTEuMTlDMzUuNywyOS41OCwzNS40NywyOS4xMywzNS42MywyOC44Wm0tMi44LS4zN2MtLjA3LjExLS4yMy4wOS0uNTcuMDZhNC4yNCw0LjI0LDAsMCwwLTIuMTQuMjIsMiwyLDAsMCwxLS40OS4xNC4xNi4xNiwwLDAsMS0uMTEsMCwuMTUuMTUsMCwwLDEtLjA1LS4xMi44MS44MSwwLDAsMSwuMzItLjUxLDIuNDEsMi40MSwwLDAsMSwxLjI3LS41MywxLjk0LDEuOTQsMCwwLDEsMS43NS41N0EuMTkuMTksMCwwLDEsMzIuODMsMjguNDNabS01LjExLTEuMjZjLS4xMiwwLS4xNy0uMDctLjE5LS4xNHMuMjgtLjU2LjYyLS44MWEzLjYsMy42LDAsMCwxLDMuNTEtLjQyQTMsMywwLDAsMSwzMywyNi44N2MuMTIuMi4xNS4zNS4wNy40NHMtLjQ0LDAtLjk1LS4yNGE0LjE4LDQuMTgsMCwwLDAtMi0uNDNBMjEuODUsMjEuODUsMCwwLDAsMjcuNzEsMjcuMTdaIi8+PHBhdGggY2xhc3M9ImNscy0xIiBkPSJNMzUuNSwxMy4yOWMuMSwwLC4xNi0uMTUuMDctLjJhMTEsMTEsMCwwLDAtNC42OS0xLjIzLjA5LjA5LDAsMCwxLS4wNy0uMTQsNC43OCw0Ljc4LDAsMCwxLC44OC0uODkuMDkuMDksMCwwLDAtLjA2LS4xNiwxMi40NiwxMi40NiwwLDAsMC01LjYxLDIsLjA5LjA5LDAsMCwxLS4xMy0uMDksNi4xNiw2LjE2LDAsMCwxLC41OS0xLjQ1LjA4LjA4LDAsMCwwLS4xMS0uMTFBMjIuNzksMjIuNzksMCwwLDAsMjAsMTYuMjRhLjA5LjA5LDAsMCwwLC4xMi4xM0ExOS41MywxOS41MywwLDAsMSwyNywxMy4zMiwxOS4xLDE5LjEsMCwwLDEsMzUuNSwxMy4yOVoiLz48cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik0yOC4zNCw2LjQyUzI2LjIzLDQsMjUuNiwzLjhDMjEuNjksMi43NCwxMy4yNCw4LjU3LDcuODQsMTYuMjcsNS42NiwxOS4zOSwyLjUzLDI0LjksNCwyNy43NGExMS40MywxMS40MywwLDAsMCwxLjc5LDEuNzJBNi42NSw2LjY1LDAsMCwxLDEwLDI2Ljc4LDM0LjIxLDM0LjIxLDAsMCwxLDIwLjgsMTEuNjIsNTUuMDksNTUuMDksMCwwLDEsMjguMzQsNi40MloiLz48L2c+PC9nPjwvc3ZnPg==' + ); + add_submenu_page( 'admin.php', esc_html__( 'Create Mailchimp Account', 'mailchimp' ), @@ -541,6 +554,17 @@ public function create_account_page() { init(); @@ -213,25 +214,6 @@ function mailchimp_sf_main_css() { require_once MCSF_DIR . '/views/css/frontend.php'; } - -/** - * Add our settings page to the admin menu - * - * @return void - */ -function mailchimp_sf_add_pages() { - // Add settings page for users who can edit plugins - add_menu_page( - esc_html__( 'Mailchimp Setup', 'mailchimp' ), - esc_html__( 'Mailchimp', 'mailchimp' ), - MCSF_CAP_THRESHOLD, - 'mailchimp_sf_options', - 'mailchimp_sf_setup_page', - 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1Mi4wMyA1NSI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNmZmY7fTwvc3R5bGU+PC9kZWZzPjx0aXRsZT5Bc3NldCAxPC90aXRsZT48ZyBpZD0iTGF5ZXJfMiIgZGF0YS1uYW1lPSJMYXllciAyIj48ZyBpZD0iTGF5ZXJfMS0yIiBkYXRhLW5hbWU9IkxheWVyIDEiPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTExLjY0LDI4LjU0YTQuNzUsNC43NSwwLDAsMC0xLjE3LjA4Yy0yLjc5LjU2LTQuMzYsMi45NC00LjA1LDZhNi4yNCw2LjI0LDAsMCwwLDUuNzIsNS4yMSw0LjE3LDQuMTcsMCwwLDAsLjgtLjA2YzIuODMtLjQ4LDMuNTctMy41NSwzLjEtNi41N0MxNS41MSwyOS44MywxMy4yMSwyOC42MywxMS42NCwyOC41NFptMi43Nyw4LjA3YTEuMTcsMS4xNywwLDAsMS0xLjEuNTUsMS41MywxLjUzLDAsMCwxLTEuMzctMS41OEE0LDQsMCwwLDEsMTIuMjMsMzRhMS40NCwxLjQ0LDAsMCwwLS41NS0xLjc0LDEuNDgsMS40OCwwLDAsMC0xLjEyLS4yMSwxLjQ0LDEuNDQsMCwwLDAtLjkyLjY0LDMuMzksMy4zOSwwLDAsMC0uMzQuNzlsMCwuMTFjLS4xMy4zNC0uMzMuNDUtLjQ3LjQzcy0uMTYtLjA1LS4yMS0uMjFhMywzLDAsMCwxLC43OC0yLjU1LDIuNDYsMi40NiwwLDAsMSwyLjExLS43NiwyLjUsMi41LDAsMCwxLDEuOTEsMS4zOSwzLjE5LDMuMTksMCwwLDEtLjIzLDIuODJsLS4wOS4yQTEuMTYsMS4xNiwwLDAsMCwxMywzNmEuNzQuNzQsMCwwLDAsLjYzLjMyLDEuMzgsMS4zOCwwLDAsMCwuMzQsMGMuMTUsMCwuMy0uMDcuMzksMEEuMjQuMjQsMCwwLDEsMTQuNDEsMzYuNjFaIi8+PHBhdGggY2xhc3M9ImNscy0xIiBkPSJNNTEsMzMuODhhMy44NCwzLjg0LDAsMCwwLTEuMTUtMWwtLjExLS4zNy0uMTQtLjQyYTUuNTcsNS41NywwLDAsMCwuNS0zLjMyLDUuNDMsNS40MywwLDAsMC0xLjU0LTMsMTAuMDksMTAuMDksMCwwLDAtNC4yNC0yLjI2YzAtLjY3LDAtMS40My0uMDYtMS45YTEyLjgzLDEyLjgzLDAsMCwwLS40OS0zLjI1LDEwLjQ2LDEwLjQ2LDAsMCwwLTEuMy0yLjkyYzIuMTQtMi41NiwzLjI5LTUuMjEsMy4yOS03LjU3LDAtMy44My0zLTYuMy03LjU5LTYuM2ExOS4zLDE5LjMsMCwwLDAtNy4yMiwxLjZsLS4zNC4xNEwyOC43LDEuNTJBNi4zMSw2LjMxLDAsMCwwLDI0LjQzLDAsMTQuMDcsMTQuMDcsMCwwLDAsMTcuNiwyLjJhMzYuOTMsMzYuOTMsMCwwLDAtNi43OCw1LjIxYy00LjYsNC4zOC04LjMsOS42My05LjkxLDE0QTEyLjUxLDEyLjUxLDAsMCwwLDAsMjYuNTRhNi4xNiw2LjE2LDAsMCwwLDIuMTMsNC40bC43OC42NkExMC40NCwxMC40NCwwLDAsMCwyLjc0LDM1YTkuMzYsOS4zNiwwLDAsMCwzLjIxLDYsMTAsMTAsMCwwLDAsNS4xMywyLjQzLDIwLjE5LDIwLjE5LDAsMCwwLDcuMzEsOEEyMy4zMywyMy4zMywwLDAsMCwzMC4xNyw1NUgzMWEyMy4yNywyMy4yNywwLDAsMCwxMi0zLjE2LDE5LjEsMTkuMSwwLDAsMCw3LjgyLTkuMDZsMCwwQTE2Ljg5LDE2Ljg5LDAsMCwwLDUyLDM3LjIzLDUuMTcsNS4xNywwLDAsMCw1MSwzMy44OFptLTEuNzgsOC4yMWMtMyw3LjI5LTEwLjMsMTEuMzUtMTksMTEuMDktOC4wNi0uMjQtMTQuOTQtNC41LTE4LTExLjQzYTcuOTQsNy45NCwwLDAsMS01LjEyLTIuMDYsNy41Niw3LjU2LDAsMCwxLTIuNjEtNC44NUE4LjMxLDguMzEsMCwwLDEsNSwzMUwzLjMyLDI5LjU2Qy00LjQyLDIzLDE5Ljc3LTMuODYsMjcuNTEsMi44OWwyLjY0LDIuNTgsMS40NC0uNjFjNi43OS0yLjgxLDEyLjMtMS40NSwxMi4zLDMsMCwyLjMzLTEuNDgsNS4wNS0zLjg2LDcuNTJhNy41NCw3LjU0LDAsMCwxLDIsMy40OCwxMSwxMSwwLDAsMSwuNDIsMi44MmMwLDEsLjA5LDMuMTYuMDksMy4ybDEsLjI3QTguNjQsOC42NCwwLDAsMSw0Ny4yLDI3YTMuNjYsMy42NiwwLDAsMSwxLjA2LDIuMDZBNCw0LDAsMCwxLDQ3LjU1LDMyLDEwLjE1LDEwLjE1LDAsMCwxLDQ4LDMzLjA4Yy4yLjY0LjM1LDEuMTguMzcsMS4yNS43NCwwLDEuODkuODUsMS44OSwyLjg5QTE1LjI5LDE1LjI5LDAsMCwxLDQ5LjE4LDQyLjA5WiIvPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTQ4LDM2YTEuMzYsMS4zNiwwLDAsMC0uODYtLjE2LDExLjc2LDExLjc2LDAsMCwwLS44Mi0yLjc4QTE3Ljg5LDE3Ljg5LDAsMCwxLDQwLjQ1LDM2YTIzLjY0LDIzLjY0LDAsMCwxLTcuODEuODRjLTEuNjktLjE0LTIuODEtLjYzLTMuMjMuNzRhMTguMywxOC4zLDAsMCwwLDgsLjgxLjE0LjE0LDAsMCwxLC4xNi4xMy4xNS4xNSwwLDAsMS0uMDkuMTVzLTMuMTQsMS40Ni04LjE0LS4wOGEyLjU4LDIuNTgsMCwwLDAsMS44MywxLjkxLDguMjQsOC4yNCwwLDAsMCwxLjQ0LjM5YzYuMTksMS4wNiwxMi0yLjQ3LDEzLjI3LTMuMzYuMS0uMDcuMTYsMCwuMDguMTJsLS4xMy4xOGMtMS41OSwyLjA2LTUuODgsNC40NC0xMS40NSw0LjQ0LTIuNDMsMC00Ljg2LS44Ni01Ljc1LTIuMTctMS4zOC0yLS4wNy01LDIuMjQtNC43MWwxLC4xMWEyMS4xMywyMS4xMywwLDAsMCwxMC41LTEuNjhjMy4xNS0xLjQ2LDQuMzQtMy4wNyw0LjE2LTQuMzdBMS44NywxLjg3LDAsMCwwLDQ2LDI4LjM0YTYuOCw2LjgsMCwwLDAtMy0xLjQxYy0uNS0uMTQtLjg0LS4yMy0xLjItLjM1LS42NS0uMjEtMS0uMzktMS0xLjYxLDAtLjUzLS4xMi0yLjQtLjE2LTMuMTYtLjA2LTEuMzUtLjIyLTMuMTktMS4zNi00YTEuOTIsMS45MiwwLDAsMC0xLS4zMSwxLjg2LDEuODYsMCwwLDAtLjU4LjA2LDMuMDcsMy4wNywwLDAsMC0xLjUyLjg2LDUuMjQsNS4yNCwwLDAsMS00LDEuMzJjLS44LDAtMS42NS0uMTYtMi42Mi0uMjJsLS41NywwYTUuMjIsNS4yMiwwLDAsMC01LDQuNTdjLS41NiwzLjgzLDIuMjIsNS44MSwzLDdhMSwxLDAsMCwxLC4yMi41Mi44My44MywwLDAsMS0uMjguNTVoMGE5LjgsOS44LDAsMCwwLTIuMTYsOS4yLDcuNTksNy41OSwwLDAsMCwuNDEsMS4xMmMyLDQuNzMsOC4zLDYuOTMsMTQuNDMsNC45M2ExNS4wNiwxNS4wNiwwLDAsMCwyLjMzLTEsMTIuMjMsMTIuMjMsMCwwLDAsMy41Ny0yLjY3LDEwLjYxLDEwLjYxLDAsMCwwLDMtNS44MkM0OC42LDM2LjcsNDguMzMsMzYuMjMsNDgsMzZabS04LjI1LTcuODJjMCwuNS0uMzEuOTEtLjY4LjlzLS42Ni0uNDItLjY1LS45Mi4zMS0uOTEuNjgtLjlTMzkuNzIsMjcuNjgsMzkuNzEsMjguMThabS0xLjY4LTZjLjcxLS4xMiwxLjA2LjYyLDEuMzIsMS44NWEzLjY0LDMuNjQsMCwwLDEtLjA1LDIsNC4xNCw0LjE0LDAsMCwwLTEuMDYsMCw0LjEzLDQuMTMsMCwwLDEtLjY4LTEuNjRDMzcuMjksMjMuMjMsMzcuMzEsMjIuMzQsMzgsMjIuMjNabS0yLjQsNi41N2EuODIuODIsMCwwLDEsMS4xMS0uMTljLjQ1LjIyLjY5LjY3LjUzLDFhLjgyLjgyLDAsMCwxLTEuMTEuMTlDMzUuNywyOS41OCwzNS40NywyOS4xMywzNS42MywyOC44Wm0tMi44LS4zN2MtLjA3LjExLS4yMy4wOS0uNTcuMDZhNC4yNCw0LjI0LDAsMCwwLTIuMTQuMjIsMiwyLDAsMCwxLS40OS4xNC4xNi4xNiwwLDAsMS0uMTEsMCwuMTUuMTUsMCwwLDEtLjA1LS4xMi44MS44MSwwLDAsMSwuMzItLjUxLDIuNDEsMi40MSwwLDAsMSwxLjI3LS41MywxLjk0LDEuOTQsMCwwLDEsMS43NS41N0EuMTkuMTksMCwwLDEsMzIuODMsMjguNDNabS01LjExLTEuMjZjLS4xMiwwLS4xNy0uMDctLjE5LS4xNHMuMjgtLjU2LjYyLS44MWEzLjYsMy42LDAsMCwxLDMuNTEtLjQyQTMsMywwLDAsMSwzMywyNi44N2MuMTIuMi4xNS4zNS4wNy40NHMtLjQ0LDAtLjk1LS4yNGE0LjE4LDQuMTgsMCwwLDAtMi0uNDNBMjEuODUsMjEuODUsMCwwLDAsMjcuNzEsMjcuMTdaIi8+PHBhdGggY2xhc3M9ImNscy0xIiBkPSJNMzUuNSwxMy4yOWMuMSwwLC4xNi0uMTUuMDctLjJhMTEsMTEsMCwwLDAtNC42OS0xLjIzLjA5LjA5LDAsMCwxLS4wNy0uMTQsNC43OCw0Ljc4LDAsMCwxLC44OC0uODkuMDkuMDksMCwwLDAtLjA2LS4xNiwxMi40NiwxMi40NiwwLDAsMC01LjYxLDIsLjA5LjA5LDAsMCwxLS4xMy0uMDksNi4xNiw2LjE2LDAsMCwxLC41OS0xLjQ1LjA4LjA4LDAsMCwwLS4xMS0uMTFBMjIuNzksMjIuNzksMCwwLDAsMjAsMTYuMjRhLjA5LjA5LDAsMCwwLC4xMi4xM0ExOS41MywxOS41MywwLDAsMSwyNywxMy4zMiwxOS4xLDE5LjEsMCwwLDEsMzUuNSwxMy4yOVoiLz48cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik0yOC4zNCw2LjQyUzI2LjIzLDQsMjUuNiwzLjhDMjEuNjksMi43NCwxMy4yNCw4LjU3LDcuODQsMTYuMjcsNS42NiwxOS4zOSwyLjUzLDI0LjksNCwyNy43NGExMS40MywxMS40MywwLDAsMCwxLjc5LDEuNzJBNi42NSw2LjY1LDAsMCwxLDEwLDI2Ljc4LDM0LjIxLDM0LjIxLDAsMCwxLDIwLjgsMTEuNjIsNTUuMDksNTUuMDksMCwwLDEsMjguMzQsNi40MloiLz48L2c+PC9nPjwvc3ZnPg==' - ); -} -add_action( 'admin_menu', 'mailchimp_sf_add_pages' ); - /** * Request handler * @@ -771,15 +753,6 @@ function mailchimp_sf_get_interest_categories( $list_id, $new_list, $update_opti return $igs['categories']; } - -/** - * Outputs the Settings/Options page - */ -function mailchimp_sf_setup_page() { - $path = plugin_dir_path( __FILE__ ); - require_once $path . '/includes/admin/templates/settings.php'; -} - /** * Register the widget. * diff --git a/views/setup_page.php b/views/setup_page.php deleted file mode 100644 index 4e06ef4f..00000000 --- a/views/setup_page.php +++ /dev/null @@ -1,375 +0,0 @@ - -
-
- - - - - -

:

-
-
- - - -
-
- -

- -
-

-

- -
- get( 'lists', 100, array( 'fields' => 'lists.id,lists.name,lists.email_type_option' ) ); - if ( is_wp_error( $lists ) ) { - $msg = sprintf( - /* translators: %s: error message */ - esc_html__( 'Uh-oh, we couldn\'t get your lists from Mailchimp! Error: %s', 'mailchimp' ), - esc_html( $lists->get_error_message() ) - ); - admin_notice_error( $msg ); - } elseif ( isset( $lists['lists'] ) && count( $lists['lists'] ) === 0 ) { - $msg = sprintf( - /* translators: %s: link to Mailchimp */ - esc_html__( 'Uh-oh, you don\'t have any lists defined! Please visit %s, login, and setup a list before using this tool!', 'mailchimp' ), - "Mailchimp" - ); - admin_notice_error( $msg ); - } else { - $lists = $lists['lists']; - $option = get_option( 'mc_list_id' ); - $list_ids = array_map( - function ( $ele ) { - return $ele['id']; - }, - $lists - ); - $is_list_selected = in_array( $option, $list_ids, true ); - ?> - - - - - -
- - - - - -
- -
-
- -
- - -
-
-
- - - - - - - - - - - - - - - - - - - -
- - -
- -
- - -
-
- -
- - - -
-
- - - - - -
onclick="showMe('mc-custom-styling')"/>
- - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - /> - -
- - - - px -
- - - # - - # -
- - - # - - # -
- - - # - - # -
-
- - - - - - - - - - - - - - - - - - -
id="mc_double_optin" class="code" /> - -
id="mc_update_existing" class="code" /> - -
id="mc_use_unsub_link" class="code" /> - -
-
-
- - -
- - - -
-
- -
- - - - - - - - - - - - - - - - - - -
- -
- - - /> - -  —  - -
-
-
- - -
-

-
- - - - - - - - - - - - - - - - - - - -
- - - /> -
-
    - -
  • - -
-
- -
-
-
From a6fe5d579049b096bb2fb56a6203c8867afa835c Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Tue, 13 May 2025 16:12:58 +0530 Subject: [PATCH 02/22] Install action scheduler dependency. --- composer.json | 5 +++-- composer.lock | 52 +++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index 0da82f6f..ef422f81 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,8 @@ ], "prefer-stable": true, "require": { - "php": ">=7.0" + "php": ">=7.0", + "woocommerce/action-scheduler": "3.8.2" }, "require-dev": { "10up/phpcs-composer": "^3.0", @@ -37,4 +38,4 @@ "scripts": { "lint": "phpcs --standard=./phpcs.xml -p -s ." } -} \ No newline at end of file +} diff --git a/composer.lock b/composer.lock index a2b433e6..1db8650d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,52 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b4631e7ae4a2f6a3795a92a813440087", - "packages": [], + "content-hash": "5b8fa284bf852263974f1227edb89665", + "packages": [ + { + "name": "woocommerce/action-scheduler", + "version": "3.8.2", + "source": { + "type": "git", + "url": "https://github.com/woocommerce/action-scheduler.git", + "reference": "2bc91d88fdbc2c07ab899cbb56b983e11e62cf69" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/woocommerce/action-scheduler/zipball/2bc91d88fdbc2c07ab899cbb56b983e11e62cf69", + "reference": "2bc91d88fdbc2c07ab899cbb56b983e11e62cf69", + "shasum": "" + }, + "require": { + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.5", + "woocommerce/woocommerce-sniffs": "0.1.0", + "wp-cli/wp-cli": "~2.5.0", + "yoast/phpunit-polyfills": "^2.0" + }, + "type": "wordpress-plugin", + "extra": { + "scripts-description": { + "test": "Run unit tests", + "phpcs": "Analyze code against the WordPress coding standards with PHP_CodeSniffer", + "phpcbf": "Fix coding standards warnings/errors automatically with PHP Code Beautifier" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-3.0-or-later" + ], + "description": "Action Scheduler for WordPress and WooCommerce", + "homepage": "https://actionscheduler.org/", + "support": { + "issues": "https://github.com/woocommerce/action-scheduler/issues", + "source": "https://github.com/woocommerce/action-scheduler/tree/3.8.2" + }, + "time": "2024-09-12T23:12:58+00:00" + } + ], "packages-dev": [ { "name": "10up/phpcs-composer", @@ -810,12 +854,12 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": true, "prefer-lowest": false, "platform": { "php": ">=7.0" }, - "platform-dev": [], + "platform-dev": {}, "plugin-api-version": "2.6.0" } From abbb7fb1f0348ea40fc6f158224fb87e39ca841d Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Tue, 13 May 2025 16:16:09 +0530 Subject: [PATCH 03/22] Some updates to User sync settings and added initial background processing class. --- ...ings.php => class-mailchimp-user-sync.php} | 104 +++++- includes/admin/templates/settings.php | 1 + includes/class-mailchimp-admin.php | 4 +- ...-mailchimp-user-sync-backgroud-process.php | 345 ++++++++++++++++++ mailchimp.php | 3 +- 5 files changed, 447 insertions(+), 10 deletions(-) rename includes/admin/{class-mailchimp-user-sync-settings.php => class-mailchimp-user-sync.php} (73%) create mode 100644 includes/class-mailchimp-user-sync-backgroud-process.php diff --git a/includes/admin/class-mailchimp-user-sync-settings.php b/includes/admin/class-mailchimp-user-sync.php similarity index 73% rename from includes/admin/class-mailchimp-user-sync-settings.php rename to includes/admin/class-mailchimp-user-sync.php index 27ff051e..25c80724 100644 --- a/includes/admin/class-mailchimp-user-sync-settings.php +++ b/includes/admin/class-mailchimp-user-sync.php @@ -11,11 +11,11 @@ } /** - * Class Mailchimp_User_Sync_Settings + * Class Mailchimp_User_Sync * * @since x.x.x */ -class Mailchimp_User_Sync_Settings { +class Mailchimp_User_Sync { /** * The option name. @@ -25,12 +25,60 @@ class Mailchimp_User_Sync_Settings { */ protected $option_name = 'mailchimp_sf_user_sync_settings'; + /** + * The background process. + * + * @since x.x.x + * @var Mailchimp_User_Sync_Background_Process + */ + protected $background_process; + + /** + * Transient key for notices. + * + * @var string + */ + private $notices_transient_key = 'mailchimp_sf_user_sync_notices'; + /** * Initialize the class */ public function init() { add_action( 'admin_init', array( $this, 'register_settings' ) ); add_action( 'admin_init', [ $this, 'setup_fields_sections' ] ); + add_action( 'admin_post_mailchimp_sf_start_user_sync', [ $this, 'start_user_sync' ] ); + + $this->background_process = new Mailchimp_User_Sync_Background_Process(); + $this->background_process->init(); + + // Admin notices + add_action( 'admin_notices', [ $this, 'render_notices' ] ); + } + + public function start_user_sync() { + if ( + empty( $_GET['mailchimp_sf_start_user_sync_nonce'] ) || + ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['mailchimp_sf_start_user_sync_nonce'] ) ), 'mailchimp_sf_start_user_sync' ) || + ! current_user_can( 'manage_options' ) + ) { + wp_die( esc_html__( 'You don\'t have permission to perform this operation.', 'mailchimp' ) ); + } + + $args = array( + array( + 'job_id' => str_replace( '-', '', wp_generate_uuid4() ), + 'processed' => 0, + 'offset' => 0, + ) + ); + $this->background_process->schedule( $args ); + + // Add notice that the user sync has started. + $this->add_notice( __( 'User sync process has started.', 'mailchimp' ) ); + + // Redirect to the user sync settings page. + wp_safe_redirect( esc_url_raw( add_query_arg( array( 'page' => 'mailchimp_sf_options', 'tab' => 'user_sync' ), admin_url( 'admin.php' ) ) ) ); + exit; } /** @@ -246,8 +294,8 @@ public function subscriber_status_field() {

-
diff --git a/includes/class-mailchimp-admin.php b/includes/class-mailchimp-admin.php index 59515df6..fbb1d25f 100644 --- a/includes/class-mailchimp-admin.php +++ b/includes/class-mailchimp-admin.php @@ -476,20 +476,22 @@ public function enqueue_admin_page_scripts( $hook_suffix ) { wp_enqueue_style( 'mailchimp_sf_admin_css', MCSF_URL . 'assets/css/admin.css', array( 'wp-jquery-ui-dialog' ), true ); wp_enqueue_script( 'showMe', MCSF_URL . 'assets/js/hidecss.js', array( 'jquery' ), MCSF_VER, true ); - wp_enqueue_script( 'mailchimp_sf_admin', MCSF_URL . 'assets/js/admin.js', array( 'jquery', 'jquery-ui-dialog' ), MCSF_VER, true ); + wp_enqueue_script( 'mailchimp_sf_admin', MCSF_URL . 'assets/js/admin.js', array( 'jquery', 'jquery-ui-dialog', 'jquery-blockui' ), MCSF_VER, true ); $data = array( - 'ajax_url' => esc_url( admin_url( 'admin-ajax.php' ) ), - 'oauth_url' => esc_url( $this->oauth_url ), - 'oauth_start_nonce' => wp_create_nonce( 'mailchimp_sf_oauth_start_nonce' ), - 'oauth_finish_nonce' => wp_create_nonce( 'mailchimp_sf_oauth_finish_nonce' ), - 'oauth_window_name' => esc_html__( 'Mailchimp For WordPress OAuth', 'mailchimp' ), - 'generic_error' => esc_html__( 'An error occurred. Please try again.', 'mailchimp' ), - 'modal_title' => esc_html__( 'Login Popup is blocked!', 'mailchimp' ), - 'modal_button_try_again' => esc_html__( 'Try again', 'mailchimp' ), - 'modal_button_cancel' => esc_html__( 'No, cancel!', 'mailchimp' ), - 'admin_settings_url' => esc_url( admin_url( 'admin.php?page=mailchimp_sf_options' ) ), - 'user_sync_status_nonce' => wp_create_nonce( 'mailchimp_sf_user_sync_status' ), + 'ajax_url' => esc_url( admin_url( 'admin-ajax.php' ) ), + 'oauth_url' => esc_url( $this->oauth_url ), + 'oauth_start_nonce' => wp_create_nonce( 'mailchimp_sf_oauth_start_nonce' ), + 'oauth_finish_nonce' => wp_create_nonce( 'mailchimp_sf_oauth_finish_nonce' ), + 'oauth_window_name' => esc_html__( 'Mailchimp For WordPress OAuth', 'mailchimp' ), + 'generic_error' => esc_html__( 'An error occurred. Please try again.', 'mailchimp' ), + 'modal_title' => esc_html__( 'Login Popup is blocked!', 'mailchimp' ), + 'modal_button_try_again' => esc_html__( 'Try again', 'mailchimp' ), + 'modal_button_cancel' => esc_html__( 'No, cancel!', 'mailchimp' ), + 'admin_settings_url' => esc_url( admin_url( 'admin.php?page=mailchimp_sf_options' ) ), + 'user_sync_status_nonce' => wp_create_nonce( 'mailchimp_sf_user_sync_status' ), + 'delete_user_sync_error_nonce' => wp_create_nonce( 'mailchimp_sf_delete_user_sync_error' ), + 'no_errors_found' => esc_html__( 'No errors found', 'mailchimp' ), ); // Create account page specific data. diff --git a/includes/class-mailchimp-user-sync-backgroud-process.php b/includes/class-mailchimp-user-sync-backgroud-process.php index 83da3fea..0edfd94f 100644 --- a/includes/class-mailchimp-user-sync-backgroud-process.php +++ b/includes/class-mailchimp-user-sync-backgroud-process.php @@ -87,6 +87,7 @@ public function run( $item = array() ) { $offset = $item['offset'] ? absint( $item['offset'] ) : 0; $user_sync_settings = $this->get_user_sync_settings(); $user_roles = $user_sync_settings['user_roles'] ?? array(); + $errors = array(); // If no user roles to sync, add a notice and return. if ( empty( $user_roles ) ) { @@ -121,23 +122,55 @@ public function run( $item = array() ) { } // Sync users. - foreach ( $users as $user ) { + foreach ( $users as $user_id ) { try{ - $this->sync_user( $user ); + $user = get_user_by( 'id', $user_id ); + if ( ! $user ) { + $this->log( 'User not found' ); + $item['skipped'] += 1; + continue; + } + + $synced = $this->sync_user( $user ); + if ( is_wp_error( $synced ) ) { + $item['failed'] += 1; + $errors[ uniqid('mailchimp_sf_error_') ] = array( + 'time' => time(), + 'email' => $user->user_email, + 'error' => $synced->get_error_message(), + ); + } elseif ( $synced ) { + $item['success'] += 1; + } else { + $item['skipped'] += 1; + } } catch ( Exception $e ) { $this->log( 'Error getting user: ' . $e->getMessage() ); + $item['failed'] += 1; + $errors[ uniqid('mailchimp_sf_error_') ] = array( + 'time' => time(), + 'email' => $user->user_email, + 'error' => $e->getMessage(), + ); continue; } } + // Save errors. + $this->user_sync->set_user_sync_errors( $errors ); + // If no more users to sync, add a notice and return. $found_users = count( $users ); if ( $found_users < $limit ) { - $this->log( 'No more users to sync, User sync process completed.' ); + $processed += $found_users; + $this->log( 'No more users to sync, User sync process completed. ' . absint( $processed ) . ' users processed.' ); $this->user_sync->add_notice( sprintf( - _n( 'User sync process completed. %d user processed.', 'User sync process completed. %d users processed.', $processed, 'mailchimp' ), - $processed + _n( 'User sync process completed. %d user processed (Synced: %d, Failed: %d, Skipped: %d).', 'User sync process completed. %d users processed (Synced: %d, Failed: %d, Skipped: %d).', absint( $processed ), 'mailchimp' ), + absint( $processed ), + absint( $item['success'] ), + absint( $item['failed'] ), + absint( $item['skipped'] ) ), 'success' ); @@ -154,65 +187,112 @@ public function run( $item = array() ) { /** * Sync the user. * - * @param WP_User $user The user to sync. + * @param WP_User $user The user. + * @return bool|WP_Error True if the user is synced, WP_Error if there is an error and false if the user is not found or not synced. */ - public function sync_user( $user_id ) { - $list_id = $this->get_list_id(); - $api = $this->get_api(); - $settings = $this->get_user_sync_settings(); + public function sync_user( $user ) { + $list_id = $this->get_list_id(); + $api = $this->get_api(); + $settings = $this->get_user_sync_settings(); $existing_contacts_only = (bool) ($settings['existing_contacts_only'] ?? false); - $subscribe_status = $settings['subscriber_status'] ?? 'subscribed'; - - $user = get_user_by( 'id', $user_id ); + $subscribe_status = $settings['subscriber_status'] ?? 'pending'; - if ( ! $user ) { - $this->log( 'User not found' ); - return; - } - - $this->log( 'Syncing user: ' . $user->ID ); + $this->log( 'Syncing user: ' . $user->user_email . ' (ID: ' . $user->ID . ')' ); $user_email = strtolower( trim( $user->user_email ) ); - $user_first_name = $user->first_name; - $user_last_name = $user->last_name; - - $merge_fields = array( - 'FNAME' => $user_first_name, - 'LNAME' => $user_last_name, - ); - - $merge_fields = apply_filters( 'mailchimp_sf_user_sync_merge_fields', $merge_fields, $user ); + // Check if user exists on Mailchimp. $current_status = $this->get_mailchimp_user_status( $user_email ); if ( $existing_contacts_only && ! $current_status ) { $this->log( 'User not exists on Mailchimp, skipping' ); - return; + return false; } $request_body = array( 'email_address' => $user_email, - 'merge_fields' => $merge_fields + 'merge_fields' => $this->get_user_merge_fields( $user ), + 'status' => $this->get_subscribe_status( $subscribe_status, $current_status, $user ) ); + $endpoint = 'lists/' . $list_id . '/members/' . md5( $user_email ) . '?skip_merge_validation=true'; + $response = $api->post( $endpoint, $request_body, 'PUT', $list_id, true ); + + if ( is_wp_error( $response ) ) { + $this->log( 'Error syncing user: ' . $response->get_error_message() ); + return $response; + } + + $this->log( 'User synced: ' . $user_email . ' Response: ' . wp_json_encode( $response ) ); + return true; + } + + /** + * Get the subscribe status. + * + * @param string $subscribe_status The subscribe status. + * @param string $current_status The current status. + * @param WP_User $user The user. + * @return string + */ + public function get_subscribe_status( $subscribe_status, $current_status, $user ) { if ( $current_status ) { - if ( $current_status === 'archived' ) { - $request_body['status'] = $subscribe_status; - } elseif ( $current_status === 'cleaned' ) { - $request_body['status'] = 'pending'; + switch ( $current_status ) { + // If user is already subscribed, unsubscribed or transactional, don't change the status. + case 'subscribed': + case 'unsubscribed': + case 'transactional': + $subscribe_status = $current_status; + break; + + // If user is cleaned, set the status as pending. + case 'cleaned': + $subscribe_status = 'pending'; + break; + + // If user is archived, pending or anything else, set the status as per the subscribe status in settings. + case 'archived': + case 'pending': + default: + break; } - } else { - $request_body['status_if_new'] = $subscribe_status; } - $endpoint = 'lists/' . $list_id . '/members/' . md5( $user_email ) . '?skip_merge_validation=true'; - $response = $api->post( $endpoint, $request_body, 'PUT', $list_id ); + /** + * Filter the subscribe status. + * + * @param string $subscribe_status The subscribe status set in settings. + * @param string $current_status The current subscribe status of the user on Mailchimp. + * @param WP_User $user The user. + * @return string + */ + return apply_filters( 'mailchimp_sf_user_sync_subscribe_status', $subscribe_status, $current_status, $user ); + } - if ( is_wp_error( $response ) ) { - $this->log( 'Error syncing user: ' . $response->get_error_message() ); - return; + /** + * Get the user merge fields. + * + * @param WP_User $user The user to get the merge fields for. + * @return array + */ + public function get_user_merge_fields( $user ) { + $merge_fields = array(); + + if ( !empty( $user->first_name ) ) { + $merge_fields['FNAME'] = $user->first_name; } - $this->log( 'User synced: ' . $user_email ); + if ( !empty( $user->last_name ) ) { + $merge_fields['LNAME'] = $user->last_name; + } + + /** + * Filter the user merge fields. + * + * @param array $merge_fields The merge fields. + * @param WP_User $user The user. + * @return array + */ + return apply_filters( 'mailchimp_sf_user_sync_merge_fields', $merge_fields, $user ); } /** diff --git a/lib/mailchimp/mailchimp.php b/lib/mailchimp/mailchimp.php index 43214163..77930a9a 100644 --- a/lib/mailchimp/mailchimp.php +++ b/lib/mailchimp/mailchimp.php @@ -146,9 +146,10 @@ public function get( $endpoint, $count = 10, $fields = array() ) { * @param string $body The body of the request * @param string $method The request method. * @param string $list_id The list id. + * @param boolean $is_sync Whether the request is for user sync. * @return mixed */ - public function post( $endpoint, $body, $method = 'POST', $list_id = '' ) { + public function post( $endpoint, $body, $method = 'POST', $list_id = '', $is_sync = false ) { $url = $this->api_url . $endpoint; $headers = array(); @@ -183,7 +184,17 @@ public function post( $endpoint, $body, $method = 'POST', $list_id = '' ) { update_option( 'mailchimp_sf_auth_error', true ); } - $body = json_decode( $request['body'], true ); + $body = json_decode( $request['body'], true ); + + // If the request is for user sync, return the error message from the API. + if ( $is_sync ) { + if ( isset( $body['detail'] ) && ! empty( $body['detail'] ) ) { + return new WP_Error( 'mc-subscribe-error-api', $body['detail'] ); + } else { + return new WP_Error( 'mc-subscribe-error-api', $request['body'] ); + } + } + $merges = get_option( 'mc_merge_vars' ); // Get merge fields for the list if we have a list id. if ( ! empty( $list_id ) ) { From 80aa4d181dbbfb18459713baf0887befa66d8e03 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Wed, 14 May 2025 17:34:06 +0530 Subject: [PATCH 07/22] Some code improvements. --- includes/admin/class-mailchimp-user-sync.php | 80 ++++++++++++------- includes/admin/templates/settings.php | 6 +- includes/admin/templates/user-sync.php | 2 +- ...-mailchimp-user-sync-backgroud-process.php | 52 ++++++------ 4 files changed, 84 insertions(+), 56 deletions(-) diff --git a/includes/admin/class-mailchimp-user-sync.php b/includes/admin/class-mailchimp-user-sync.php index dd01fa1c..36c7feb1 100644 --- a/includes/admin/class-mailchimp-user-sync.php +++ b/includes/admin/class-mailchimp-user-sync.php @@ -85,7 +85,13 @@ public function start_user_sync() { wp_die( esc_html__( 'You don\'t have permission to perform this operation.', 'mailchimp' ) ); } - $return_url = add_query_arg( array( 'page' => 'mailchimp_sf_options', 'tab' => 'user_sync' ), admin_url( 'admin.php' ) ); + $return_url = add_query_arg( + array( + 'page' => 'mailchimp_sf_options', + 'tab' => 'user_sync', + ), + admin_url( 'admin.php' ) + ); // Check if the user is connected to Mailchimp. $api = mailchimp_sf_get_api(); @@ -119,7 +125,7 @@ public function start_user_sync() { 'success' => 0, 'skipped' => 0, 'offset' => 0, - ) + ), ); // Schedule the user sync job. @@ -153,7 +159,17 @@ public function cancel_user_sync() { } // Redirect to the user sync settings page. - wp_safe_redirect( esc_url_raw( add_query_arg( array( 'page' => 'mailchimp_sf_options', 'tab' => 'user_sync' ), admin_url( 'admin.php' ) ) ) ); + wp_safe_redirect( + esc_url_raw( + add_query_arg( + array( + 'page' => 'mailchimp_sf_options', + 'tab' => 'user_sync', + ), + admin_url( 'admin.php' ) + ) + ) + ); exit; } @@ -249,16 +265,17 @@ public function setup_fields_sections() { * Get the user sync settings. * * @since x.x.x - * @return array The user sync settings. + * @param string|null $key The key to get. + * @return array|null The user sync settings. */ public function get_user_sync_settings( $key = null ) { $default_settings = array( - 'enable_user_sync' => 0, - 'user_roles' => array( - 'subscriber' => 'subscriber' + 'enable_user_sync' => 0, + 'user_roles' => array( + 'subscriber' => 'subscriber', ), 'existing_contacts_only' => 0, - 'subscriber_status' => 'pending' + 'subscriber_status' => 'pending', ); $settings = get_option( $this->option_name, array() ); @@ -275,7 +292,7 @@ public function get_user_sync_settings( $key = null ) { * Sanitize the user sync settings. * * @since x.x.x - * @param array $settings The settings to sanitize. + * @param array $new_settings The settings to sanitize. * @return array The sanitized settings. */ public function sanitize_user_sync_settings( $new_settings ) { @@ -326,7 +343,7 @@ public function user_roles_field() { * * @since x.x.x */ - public function enable_user_sync_field( $args ) { + public function enable_user_sync_field() { $value = $this->get_user_sync_settings( 'enable_user_sync' ); ?> get_users_count(); echo wp_kses( sprintf( + /* translators: %1$s: opening anchor tag, %2$s: closing anchor tag, %3$d: number of contacts. */ _n( 'You will need %1$sa Mailchimp plan%2$s that includes %3$d contact.', 'You will need %1$sa Mailchimp plan%2$s that includes %3$d contacts.', @@ -394,9 +412,9 @@ public function subscriber_status_field() { ), array( 'a' => array( - 'href' => array(), + 'href' => array(), 'target' => array(), - 'rel' => array(), + 'rel' => array(), ), ) ) @@ -413,7 +431,7 @@ public function subscriber_status_field() { * @since x.x.x */ public function existing_contacts_only_field() { - $settings = $this->get_user_sync_settings(); + $settings = $this->get_user_sync_settings(); $existing_contacts_only = isset( $settings['existing_contacts_only'] ) ? $settings['existing_contacts_only'] : 0; ?> /> @@ -488,9 +506,9 @@ public function render_notices() { * @return int The total users. */ public function get_users_count() { - $settings = $this->get_user_sync_settings(); - $user_roles = $settings['user_roles'] ?? array(); - $total_users = 0; + $settings = $this->get_user_sync_settings(); + $user_roles = $settings['user_roles'] ?? array(); + $total_users = 0; $total_counts = count_users(); if ( ! empty( $total_counts['avail_roles'] ) && is_array( $total_counts['avail_roles'] ) ) { foreach ( $total_counts['avail_roles'] as $role_name => $role_count ) { @@ -509,7 +527,7 @@ public function get_users_count() { * @since x.x.x */ public function render_user_sync_status() { - $is_syncing = $this->background_process->in_progress(); + $is_syncing = $this->background_process->in_progress(); if ( ! $is_syncing ) { return; @@ -530,7 +548,7 @@ public function render_user_sync_status() { * @since x.x.x */ public function render_user_sync_progress() { - $is_syncing = $this->background_process->in_progress(); + $is_syncing = $this->background_process->in_progress(); if ( ! $is_syncing ) { return; @@ -549,7 +567,7 @@ public function render_user_sync_progress() { array( 'action' => 'mailchimp_sf_cancel_user_sync', ), - admin_url('admin-post.php') + admin_url( 'admin-post.php' ) ), 'mailchimp_sf_cancel_user_sync', 'mailchimp_sf_cancel_user_sync_nonce' @@ -560,17 +578,18 @@ public function render_user_sync_progress() { - + false, 'status' => '', ); @@ -620,7 +639,7 @@ public function set_user_sync_errors( $errors ) { } $current_errors = $this->get_user_sync_errors(); - $errors = array_merge( $current_errors, $errors ); + $errors = array_merge( $current_errors, $errors ); update_option( $this->errors_option_name, $errors ); } @@ -628,6 +647,8 @@ public function set_user_sync_errors( $errors ) { * Delete the user sync error. * * @since x.x.x + * + * @param string $id The id of the user sync error. */ public function delete_user_sync_errors( $id ) { if ( 'all' === $id ) { @@ -674,7 +695,8 @@ public function render_user_sync_errors() { - $error ) { + $error ) { ?> diff --git a/includes/admin/templates/settings.php b/includes/admin/templates/settings.php index cbc7ea01..e2b06ca3 100644 --- a/includes/admin/templates/settings.php +++ b/includes/admin/templates/settings.php @@ -121,10 +121,10 @@ function ( $ele ) { return; } - $current_tab = empty( $_GET['tab'] ) ? 'settings' : sanitize_title( wp_unslash( $_GET['tab'] ) ); + $current_tab = empty( $_GET['tab'] ) ? 'settings' : sanitize_title( wp_unslash( $_GET['tab'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended $tabs = array( - 'settings' => __( 'Settings', 'mailchimp' ), - 'user_sync' => __( 'User Sync', 'mailchimp' ), + 'settings' => __( 'Settings', 'mailchimp' ), + 'user_sync' => __( 'User Sync', 'mailchimp' ), ) ?>