From 7a176b5ffcdb3e12ac7599c1d89395c16f9379d9 Mon Sep 17 00:00:00 2001 From: Darin Kotter Date: Mon, 1 Jul 2024 14:05:18 -0600 Subject: [PATCH 01/48] Fix issue where if there's no sub-header but there is a header, there needs to be more spacing before the form --- mailchimp_widget.php | 1 + 1 file changed, 1 insertion(+) diff --git a/mailchimp_widget.php b/mailchimp_widget.php index 965509b..659cbbd 100644 --- a/mailchimp_widget.php +++ b/mailchimp_widget.php @@ -83,6 +83,7 @@ function mailchimp_sf_signup_form( $args = array() ) { line-height: 1.4em; margin-bottom: 0.75em; } + .mc_custom_border_hdr, #mc_subheader { line-height: 1.25em; margin: 18px 0; From c287ab0b33dec47f17eb3a51c3cf5aff2c1a9bc9 Mon Sep 17 00:00:00 2001 From: Darin Kotter Date: Mon, 1 Jul 2024 14:06:24 -0600 Subject: [PATCH 02/48] Only set bottom margin, not top --- mailchimp_widget.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mailchimp_widget.php b/mailchimp_widget.php index 659cbbd..51920c4 100644 --- a/mailchimp_widget.php +++ b/mailchimp_widget.php @@ -86,7 +86,7 @@ function mailchimp_sf_signup_form( $args = array() ) { .mc_custom_border_hdr, #mc_subheader { line-height: 1.25em; - margin: 18px 0; + margin-bottom: 18px; } .mc_merge_var { margin-bottom: 1.0em; From 85009c77432262ca610d7865d38dd96968c9ea73 Mon Sep 17 00:00:00 2001 From: Darin Kotter Date: Mon, 1 Jul 2024 14:11:02 -0600 Subject: [PATCH 03/48] Bump up the default font size for list items --- views/css/frontend.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/css/frontend.php b/views/css/frontend.php index dd8ee09..2bb4b89 100644 --- a/views/css/frontend.php +++ b/views/css/frontend.php @@ -75,7 +75,7 @@ padding-left: 0; } ul.mc_list li { - font-size: 12px; + font-size: 14px; } #ui-datepicker-div .ui-datepicker-year { display: none; From 6b0f66211b0dbe1bc2566959b217ece396617d8c Mon Sep 17 00:00:00 2001 From: Darin Kotter Date: Mon, 1 Jul 2024 14:31:15 -0600 Subject: [PATCH 04/48] Remove class property that isn't used. If a GET request gives us an error, return that so we can display it --- lib/mailchimp/mailchimp.php | 11 ++++------- mailchimp.php | 2 +- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/lib/mailchimp/mailchimp.php b/lib/mailchimp/mailchimp.php index a310ba0..c69bc6e 100644 --- a/lib/mailchimp/mailchimp.php +++ b/lib/mailchimp/mailchimp.php @@ -17,13 +17,6 @@ class MailChimp_API { */ public $key; - /** - * The API key - * - * @var string - */ - public $api_key; - /** * The API url * @@ -101,6 +94,10 @@ public function get( $endpoint, $count = 10, $fields = array() ) { $request = wp_remote_get( $url, $args ); + if ( is_wp_error( $request ) ) { + return $request; + } + if ( is_array( $request ) && 200 === $request['response']['code'] ) { return json_decode( $request['body'], true ); } elseif ( is_array( $request ) && $request['response']['code'] ) { diff --git a/mailchimp.php b/mailchimp.php index e2e8e4d..74a1566 100644 --- a/mailchimp.php +++ b/mailchimp.php @@ -1223,7 +1223,7 @@ function mailchimp_sf_verify_key( $api ) { // Might as well set this data if we have it already. $valid_roles = array( 'owner', 'admin', 'manager' ); - if ( in_array( $user['role'], $valid_roles, true ) ) { + if ( isset( $user['role'] ) && in_array( $user['role'], $valid_roles, true ) ) { update_option( 'mc_api_key', $api->key ); update_option( 'mc_user', $user ); update_option( 'mc_datacenter', $api->datacenter ); From dd0c82832a0068057d521e796c6df1c9a9e50107 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Mon, 5 Aug 2024 18:54:33 +0530 Subject: [PATCH 05/48] Initial oAuth connect implementation. --- css/admin.css | 8 +++ includes/mailchimp-admin.php | 134 +++++++++++++++++++++++++++++++++++ js/admin.js | 108 ++++++++++++++++++++++++++++ mailchimp.php | 17 +++++ views/setup_page.php | 5 ++ 5 files changed, 272 insertions(+) create mode 100644 includes/mailchimp-admin.php create mode 100644 js/admin.js diff --git a/css/admin.css b/css/admin.css index 1a8e1cc..6a768d5 100644 --- a/css/admin.css +++ b/css/admin.css @@ -254,3 +254,11 @@ th.mailchimp-connect { #mc-message { margin-top: 26px; } + +/** + * Mailchimp OAuth CSS + */ +.mailchimp-sf-oauth-section .oauth-error { + display: block; + color: #db3a1b; +} diff --git a/includes/mailchimp-admin.php b/includes/mailchimp-admin.php new file mode 100644 index 0000000..6ca0281 --- /dev/null +++ b/includes/mailchimp-admin.php @@ -0,0 +1,134 @@ + esc_html__( 'You do not have permission to perform this action.', 'mailchimp' ) ) ); + } + + // Generate a secret and send it to the OAuth server. + $secret = uniqid( 'mailchimp_sf_' ); + $args = array( + 'domain' => site_url(), + 'secret' => $secret, + ); + + $options = array( + 'headers' => array( + 'Content-type' => 'application/json', + ), + 'body' => wp_json_encode( $args ), + ); + + $response = wp_remote_post( $this->oauth_url . '/api/start', $options ); + + // Check for errors. + if ( $response instanceof WP_Error ) { + wp_send_json_error( $response ); + } + + // Send the response to the front-end. + if ( 201 === $response['response']['code'] && ! empty( $response['body'] ) ) { + set_site_transient( 'mailchimp_sf_oauth_secret', $secret, 60 * 60 ); + $result = json_decode( $response['body'], true ); + wp_send_json_success( $result ); + } else { + if ( ! empty( $response['response'] ) ) { + $response = $response['response']; + } + wp_send_json_error( $response ); + } + } + + /** + * Finish the OAuth process. + * This function is called via AJAX. + * This function finishes the OAuth process by the sending temporary token back to the oAuth server. + */ + public function finish_oauth_process() { + // Validate the nonce and permissions. + if ( + ! current_user_can( 'manage_options' ) || + ! isset( $_POST['nonce'] ) || + ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'mailchimp_sf_oauth_finish_nonce' ) + ) { + wp_send_json_error( array( 'message' => esc_html__( 'You do not have permission to perform this action.', 'mailchimp' ) ) ); + } + + $token = isset( $_POST['token'] ) ? sanitize_text_field( wp_unslash( $_POST['token'] ) ) : ''; + $args = array( + 'domain' => site_url(), + 'secret' => get_site_transient( 'mailchimp_sf_oauth_secret' ), + 'token' => $token, + ); + + $options = array( + 'headers' => array( + 'Content-type' => 'application/json', + ), + 'body' => wp_json_encode( $args ), + ); + $response = wp_remote_post( $this->oauth_url . '/api/finish', $options ); + + // Check for errors. + if ( $response instanceof WP_Error ) { + wp_send_json_error( $response ); + } + + if ( 200 === $response['response']['code'] ) { + delete_option( 'mc_api_key' ); + delete_option( 'mailchimp_sf_access_token' ); + delete_option( 'mailchimp_sf_data_center' ); + + delete_site_transient( 'mailchimp_sf_oauth_secret' ); + + // Save the access token and data center. + $result = json_decode( $response['body'], true ); + if ( $result && ! empty( $result['access_token'] ) && ! empty( $result['data_center'] ) ) { + update_option( 'mailchimp_sf_data_center', $result['data_center'] ); + update_option( 'mailchimp_sf_access_token', $result['access_token'] ); + + wp_send_json_success( true ); + } else { + wp_send_json_error( array( 'message' => esc_html__( 'Invalid response from the server.', 'mailchimp' ) ) ); + } + } else { + wp_send_json_error( $response ); + } + } +} diff --git a/js/admin.js b/js/admin.js new file mode 100644 index 0000000..51d8586 --- /dev/null +++ b/js/admin.js @@ -0,0 +1,108 @@ +/* eslint-disable no-console */ +(function ($) { + const params = window.mailchimp_sf_admin_params || {}; + const oauthBaseUrl = 'https://woocommerce.mailchimpapp.com'; + + /** + * Open Mailchimp OAuth popup. + * + * @param {string} token - Token from the Oauth service. + */ + function openMailChimpOauthPopup(token) { + const startUrl = `${oauthBaseUrl}/auth/start/${token}`; + const width = 800; + const height = 600; + const screenSizes = window.screen || { width: 1024, height: 768 }; + const left = (screenSizes.width - width) / 2; + const top = (screenSizes.height - height) / 4; + const windowOptions = `toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no, width=${ + width + }, height=${height}, top=${top}, left=${left}, domain=${oauthBaseUrl.replace('https://', '')}`; + + // Open Mailchimp OAuth popup. + const popup = window.open(startUrl, params.oauth_window_name, windowOptions); + + if (popup == null) { + // TODO: Handle popup blocked. + console.error('Popup blocked. Please enable popups for this site.'); + } else { + // Handle popup opened. + const oauthInterval = window.setInterval(function () { + if (popup.closed) { + // Clear interval. + window.clearInterval(oauthInterval); + // TODO: Hide/show error/loading messages. + + // Check status of OAuth connection. + $.post(`${oauthBaseUrl}/api/status/${token}`, function (statusData) { + if (statusData && statusData.status === 'accepted') { + const finishData = { + action: 'mailchimp_sf_oauth_finish', + nonce: params.oauth_finish_nonce, + token, + }; + + // Finish OAuth connection and save token. + $.post(params.ajax_url, finishData, function (finishResponse) { + if (finishResponse.success) { + // Token is saved in the database, reload the page to reflect the changes. + window.location.reload(); + } else { + console.log( + 'Error calling OAuth finish endpoint. Data:', + finishResponse, + ); + } + }).fail(function () { + console.error('Error calling OAuth finish endpoint.'); + }); + } else { + console.log( + 'Error calling OAuth status endpoint. No credentials provided at login popup? Data:', + statusData, + ); + } + }).fail(function () { + console.error('Error calling OAuth status endpoint.'); + }); + } + }, 250); + } + } + + $(window).on('load', function () { + // Mailchimp OAuth connection. + $('#mailchimp_sf_oauth_connect').click(function () { + $('.mailchimp-sf-oauth-section .oauth-error').html(''); + $.post( + params.ajax_url, + { + action: 'mailchimp_sf_oauth_start', + nonce: params.oauth_start_nonce, + }, + function (response) { + if (response.success && response.data && response.data.token) { + // Open Mailchimp OAuth popup. + openMailChimpOauthPopup(response.data.token); + } else { + // eslint-disable-next-line no-console + console.error(response.data); + if (response.data && response.data.message) { + $('.mailchimp-sf-oauth-section .oauth-error').html( + response.data.message, + ); + } else { + $('.mailchimp-sf-oauth-section .oauth-error').html( + 'An error occurred. Please try again.', + ); + } + } + }, + ).fail(function () { + $('.mailchimp-sf-oauth-section .oauth-error').html( + 'An error occurred. Please try again.', + ); + }); + }); + }); +})(jQuery); diff --git a/mailchimp.php b/mailchimp.php index 74a1566..e087bab 100644 --- a/mailchimp.php +++ b/mailchimp.php @@ -58,6 +58,11 @@ // Upgrade routines. require_once 'mailchimp_upgrade.php'; +// Init Admin functions +require_once plugin_dir_path( __FILE__ ) . 'includes/mailchimp-admin.php'; +$admin = new MailChimp_Admin(); +$admin->init(); + /** * Do the following plugin setup steps here * @@ -146,6 +151,18 @@ function mailchimp_admin_page_scripts( $hook_suffix ) { wp_enqueue_style( 'mailchimp_sf_admin_css', MCSF_URL . 'css/admin.css', array(), true ); wp_enqueue_script( 'showMe', MCSF_URL . 'js/hidecss.js', array( 'jquery' ), MCSF_VER, true ); + wp_enqueue_script( 'mailchimp_sf_admin', MCSF_URL . 'js/admin.js', array( 'jquery' ), MCSF_VER, true ); + + wp_localize_script( + 'mailchimp_sf_admin', + 'mailchimp_sf_admin_params', + array( + 'ajax_url' => esc_url( admin_url( 'admin-ajax.php' ) ), + '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' ), + ) + ); } add_action( 'admin_enqueue_scripts', 'mailchimp_admin_page_scripts', 10, 1 ); diff --git a/views/setup_page.php b/views/setup_page.php index 9929753..3f970ed 100644 --- a/views/setup_page.php +++ b/views/setup_page.php @@ -75,6 +75,11 @@ +
+ + +

+
From 0c98dbb130f545af99c1cf2d1537600e9a13817d Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Tue, 6 Aug 2024 16:21:28 +0530 Subject: [PATCH 06/48] UI updates and added loader on connect page. --- js/admin.js | 38 +++++++++++++++++++++++++------------- views/setup_page.php | 26 ++++++++++++++++---------- 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/js/admin.js b/js/admin.js index 51d8586..febb25d 100644 --- a/js/admin.js +++ b/js/admin.js @@ -2,6 +2,8 @@ (function ($) { const params = window.mailchimp_sf_admin_params || {}; const oauthBaseUrl = 'https://woocommerce.mailchimpapp.com'; + const spinner = '.mailchimp-sf-oauth-connect-wrapper .spinner'; + const errorSelector = '.mailchimp-sf-oauth-section .oauth-error'; /** * Open Mailchimp OAuth popup. @@ -31,7 +33,6 @@ if (popup.closed) { // Clear interval. window.clearInterval(oauthInterval); - // TODO: Hide/show error/loading messages. // Check status of OAuth connection. $.post(`${oauthBaseUrl}/api/status/${token}`, function (statusData) { @@ -52,9 +53,17 @@ 'Error calling OAuth finish endpoint. Data:', finishResponse, ); + if (finishResponse.data && finishResponse.data.message) { + $(errorSelector).html(finishResponse.data.message); + } else { + $(errorSelector).html(params.generic_error); + } + $(errorSelector).show(); } }).fail(function () { console.error('Error calling OAuth finish endpoint.'); + $(errorSelector).html(params.generic_error); + $(errorSelector).show(); }); } else { console.log( @@ -62,8 +71,12 @@ statusData, ); } + $(spinner).removeClass('is-active'); }).fail(function () { + $(errorSelector).html(params.generic_error); + $(errorSelector).show(); console.error('Error calling OAuth status endpoint.'); + $(spinner).removeClass('is-active'); }); } }, 250); @@ -73,7 +86,10 @@ $(window).on('load', function () { // Mailchimp OAuth connection. $('#mailchimp_sf_oauth_connect').click(function () { - $('.mailchimp-sf-oauth-section .oauth-error').html(''); + $(errorSelector).hide(); + $(errorSelector).html(''); + $(spinner).addClass('is-active'); + $.post( params.ajax_url, { @@ -85,23 +101,19 @@ // Open Mailchimp OAuth popup. openMailChimpOauthPopup(response.data.token); } else { - // eslint-disable-next-line no-console - console.error(response.data); if (response.data && response.data.message) { - $('.mailchimp-sf-oauth-section .oauth-error').html( - response.data.message, - ); + $(errorSelector).html(response.data.message); } else { - $('.mailchimp-sf-oauth-section .oauth-error').html( - 'An error occurred. Please try again.', - ); + $(errorSelector).html(params.generic_error); } + $(errorSelector).show(); + $(spinner).removeClass('is-active'); } }, ).fail(function () { - $('.mailchimp-sf-oauth-section .oauth-error').html( - 'An error occurred. Please try again.', - ); + $(errorSelector).html(params.generic_error); + $(errorSelector).show(); + $(spinner).removeClass('is-active'); }); }); }); diff --git a/views/setup_page.php b/views/setup_page.php index 3f970ed..4a28c46 100644 --- a/views/setup_page.php +++ b/views/setup_page.php @@ -39,13 +39,10 @@ API Key. Paste your Mailchimp API key, and click Connect to continue.', + 'To get started, we\'ll need to connect your Mailchimp account. Click Connect Account to continue.', 'mailchimp' ), [ - 'a' => [ - 'href' => [], - ], 'strong' => [], ] ); @@ -60,7 +57,7 @@ ); ?>

-
+
@@ -75,11 +72,20 @@
-
- - -

-
+
+
+ + + + + +
+
+ + +
+
+
From b69e65059e65412bbc4f385ff556f388e5ecbee3 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Tue, 6 Aug 2024 16:49:51 +0530 Subject: [PATCH 07/48] Add verify access token logic. --- ...mp-admin.php => class-mailchimp-admin.php} | 74 ++++++++++++++++--- 1 file changed, 62 insertions(+), 12 deletions(-) rename includes/{mailchimp-admin.php => class-mailchimp-admin.php} (63%) diff --git a/includes/mailchimp-admin.php b/includes/class-mailchimp-admin.php similarity index 63% rename from includes/mailchimp-admin.php rename to includes/class-mailchimp-admin.php index 6ca0281..e42a967 100644 --- a/includes/mailchimp-admin.php +++ b/includes/class-mailchimp-admin.php @@ -1,18 +1,26 @@ $response->get_error_message() ) ); } // Send the response to the front-end. @@ -107,21 +115,26 @@ public function finish_oauth_process() { // Check for errors. if ( $response instanceof WP_Error ) { - wp_send_json_error( $response ); + wp_send_json_error( array( 'message' => $response->get_error_message() ) ); } if ( 200 === $response['response']['code'] ) { - delete_option( 'mc_api_key' ); - delete_option( 'mailchimp_sf_access_token' ); - delete_option( 'mailchimp_sf_data_center' ); - - delete_site_transient( 'mailchimp_sf_oauth_secret' ); - // Save the access token and data center. $result = json_decode( $response['body'], true ); if ( $result && ! empty( $result['access_token'] ) && ! empty( $result['data_center'] ) ) { - update_option( 'mailchimp_sf_data_center', $result['data_center'] ); - update_option( 'mailchimp_sf_access_token', $result['access_token'] ); + // Clean up the old data. + delete_option( 'mailchimp_sf_access_token' ); + delete_option( 'mailchimp_sf_data_center' ); + + delete_site_transient( 'mailchimp_sf_oauth_secret' ); + + // Verify the token. + $verify = $this->verify_and_save_oauth_token( $result['access_token'], $result['data_center'] ); + + if ( is_wp_error( $verify ) ) { + // If there is an error, send it back to the front-end. + wp_send_json_error( array( 'message' => $verify->get_error_message() ) ); + } wp_send_json_success( true ); } else { @@ -131,4 +144,41 @@ public function finish_oauth_process() { wp_send_json_error( $response ); } } + + /** + * Verify and save the OAuth token. + * + * @param string $access_token The token to verify. + * @param string $data_center The data center to verify. + * @return mixed + */ + public function verify_and_save_oauth_token( $access_token, $data_center ) { + try { + $api = new MailChimp_API( $access_token, $data_center ); + } catch ( Exception $e ) { + $msg = $e->getMessage(); + return new WP_Error( 'mailchimp-sf-invalid-token', $msg ); + } + + $user = $api->get( '' ); + if ( is_wp_error( $user ) ) { + return $user; + } + + // Might as well set this data if we have it already. + $valid_roles = array( 'owner', 'admin', 'manager' ); + if ( isset( $user['role'] ) && in_array( $user['role'], $valid_roles, true ) ) { + $data_encryption = new MailChimp_Data_Encryption(); + $access_token = $data_encryption->encrypt( $access_token ); + + update_option( 'mailchimp_sf_access_token', $access_token ); + update_option( 'mailchimp_sf_data_center', $data_center ); + update_option( 'mc_user', $user ); + return true; + + } else { + $msg = esc_html__( 'API Key must belong to "Owner", "Admin", or "Manager."', 'mailchimp' ); + return new WP_Error( 'mailchimp-sf-invalid-role', $msg ); + } + } } From 33cb1e4cb2ae8ca8fbd27cb5b3af50ffafbc526e Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Tue, 6 Aug 2024 16:51:54 +0530 Subject: [PATCH 08/48] Add encryption class. --- includes/class-mailchimp-data-encryption.php | 147 +++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 includes/class-mailchimp-data-encryption.php diff --git a/includes/class-mailchimp-data-encryption.php b/includes/class-mailchimp-data-encryption.php new file mode 100644 index 0000000..fd1e89a --- /dev/null +++ b/includes/class-mailchimp-data-encryption.php @@ -0,0 +1,147 @@ +key = $this->get_default_key(); + $this->salt = $this->get_default_salt(); + } + + /** + * Encrypts a value. + * + * If a user-based key is set, that key is used. Otherwise the default key is used. + * + * @since x.x.x + * + * @param string $value Value to encrypt. + * @return string|bool Encrypted value, or false on failure. + */ + public function encrypt( $value ) { + if ( ! extension_loaded( 'openssl' ) ) { + return $value; + } + + $method = 'aes-256-ctr'; + $ivlen = openssl_cipher_iv_length( $method ); + $iv = openssl_random_pseudo_bytes( $ivlen ); + + $raw_value = openssl_encrypt( $value . $this->salt, $method, $this->key, 0, $iv ); + if ( ! $raw_value ) { + return false; + } + + return base64_encode( $iv . $raw_value ); + } + + /** + * Decrypts a value. + * + * If a user-based key is set, that key is used. Otherwise the default key is used. + * + * @since x.x.x + * + * @param string $raw_value Value to decrypt. + * @return string|bool Decrypted value, or false on failure. + */ + public function decrypt( $raw_value ) { + if ( ! extension_loaded( 'openssl' ) || ! is_string( $raw_value ) ) { + return $raw_value; + } + + $decoded_value = base64_decode( $raw_value, true ); + + if ( false === $decoded_value ) { + return $raw_value; + } + + $method = 'aes-256-ctr'; + $ivlen = openssl_cipher_iv_length( $method ); + $iv = substr( $decoded_value, 0, $ivlen ); + + $decoded_value = substr( $decoded_value, $ivlen ); + + $value = openssl_decrypt( $decoded_value, $method, $this->key, 0, $iv ); + if ( ! $value || substr( $value, - strlen( $this->salt ) ) !== $this->salt ) { + return false; + } + + return substr( $value, 0, - strlen( $this->salt ) ); + } + + /** + * Gets the default encryption key to use. + * + * @since x.x.x + * + * @return string Default (not user-based) encryption key. + */ + private function get_default_key() { + if ( defined( 'MAILCHIMP_SF_ENCRYPTION_KEY' ) && '' !== MAILCHIMP_SF_ENCRYPTION_KEY ) { + return MAILCHIMP_SF_ENCRYPTION_KEY; + } + + if ( defined( 'LOGGED_IN_KEY' ) && '' !== LOGGED_IN_KEY ) { + return LOGGED_IN_KEY; + } + + // If this is reached, you're either not on a live site or have a serious security issue. + return 'vJgwa_qf0u(k!uir[rB);g;DciNAKuX;+q&`A+z&m6kX3Y|$q.U3:Q>!$)6CA+=O'; + } + + /** + * Gets the default encryption salt to use. + * + * @since 1.0.0 + * + * @return string Encryption salt. + */ + private function get_default_salt() { + if ( defined( 'MAILCHIMP_SF_ENCRYPTION_SALT' ) && '' !== MAILCHIMP_SF_ENCRYPTION_SALT ) { + return MAILCHIMP_SF_ENCRYPTION_SALT; + } + + if ( defined( 'LOGGED_IN_SALT' ) && '' !== LOGGED_IN_SALT ) { + return LOGGED_IN_SALT; + } + + // If this is reached, you're either not on a live site or have a serious security issue. + return '|qhC}/w6q+$V`H>wM:AtNpg/{s)g Date: Tue, 6 Aug 2024 17:10:54 +0530 Subject: [PATCH 09/48] Show modal to users for blocked-popup. --- css/admin.css | 5 ++++ includes/class-mailchimp-data-encryption.php | 2 +- js/admin.js | 27 ++++++++++++++++++-- views/setup_page.php | 8 ++++++ 4 files changed, 39 insertions(+), 3 deletions(-) diff --git a/css/admin.css b/css/admin.css index 6a768d5..99e826e 100644 --- a/css/admin.css +++ b/css/admin.css @@ -262,3 +262,8 @@ th.mailchimp-connect { display: block; color: #db3a1b; } + +.mailchimp-sf-oauth-connect-wrapper { + display: flex; + text-align: right; +} diff --git a/includes/class-mailchimp-data-encryption.php b/includes/class-mailchimp-data-encryption.php index fd1e89a..0002998 100644 --- a/includes/class-mailchimp-data-encryption.php +++ b/includes/class-mailchimp-data-encryption.php @@ -128,7 +128,7 @@ private function get_default_key() { /** * Gets the default encryption salt to use. * - * @since 1.0.0 + * @since x.x.x * * @return string Encryption salt. */ diff --git a/js/admin.js b/js/admin.js index febb25d..20ec471 100644 --- a/js/admin.js +++ b/js/admin.js @@ -25,8 +25,31 @@ const popup = window.open(startUrl, params.oauth_window_name, windowOptions); if (popup == null) { - // TODO: Handle popup blocked. - console.error('Popup blocked. Please enable popups for this site.'); + // Show modal if popup is blocked. + $('#login-popup-blocked-modal').dialog({ + modal: true, + title: params.modal_title, + width: 480, + buttons: [ + { + text: params.modal_button_cancel, + class: 'button-secondary', + click() { + $(this).dialog('close'); + }, + }, + { + text: params.modal_button_try_again, + class: 'button-primary', + click() { + $(this).dialog('close'); + openMailChimpOauthPopup(token); + }, + style: 'margin-left: 10px;', + }, + ], + }); + $(spinner).removeClass('is-active'); } else { // Handle popup opened. const oauthInterval = window.setInterval(function () { diff --git a/views/setup_page.php b/views/setup_page.php index 4a28c46..167f662 100644 --- a/views/setup_page.php +++ b/views/setup_page.php @@ -86,6 +86,14 @@ + From 4124d3660d172e54e39fe4f7ab2134ff7423ee2b Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Tue, 6 Aug 2024 19:11:29 +0530 Subject: [PATCH 10/48] Update API class to use access token if it is available. --- includes/class-mailchimp-admin.php | 4 +-- lib/mailchimp/mailchimp.php | 57 +++++++++++++++++++++++------- mailchimp.php | 46 +++++++++++++++++++----- views/setup_page.php | 2 +- 4 files changed, 84 insertions(+), 25 deletions(-) diff --git a/includes/class-mailchimp-admin.php b/includes/class-mailchimp-admin.php index e42a967..e00a843 100644 --- a/includes/class-mailchimp-admin.php +++ b/includes/class-mailchimp-admin.php @@ -124,7 +124,7 @@ public function finish_oauth_process() { if ( $result && ! empty( $result['access_token'] ) && ! empty( $result['data_center'] ) ) { // Clean up the old data. delete_option( 'mailchimp_sf_access_token' ); - delete_option( 'mailchimp_sf_data_center' ); + delete_option( 'mc_datacenter' ); delete_site_transient( 'mailchimp_sf_oauth_secret' ); @@ -172,7 +172,7 @@ public function verify_and_save_oauth_token( $access_token, $data_center ) { $access_token = $data_encryption->encrypt( $access_token ); update_option( 'mailchimp_sf_access_token', $access_token ); - update_option( 'mailchimp_sf_data_center', $data_center ); + update_option( 'mc_datacenter', $data_center ); update_option( 'mc_user', $user ); return true; diff --git a/lib/mailchimp/mailchimp.php b/lib/mailchimp/mailchimp.php index c69bc6e..02c600f 100644 --- a/lib/mailchimp/mailchimp.php +++ b/lib/mailchimp/mailchimp.php @@ -10,6 +10,13 @@ */ class MailChimp_API { + /** + * The access token. + * + * @var string + */ + public $access_token; + /** * The API key * @@ -34,26 +41,34 @@ class MailChimp_API { /** * Initialize the class * - * @param string $api_key The API key. - * @throws Exception If no api key is set + * @param string $access_token Access token or API key. If data center is not provided, we'll assume that this is an API key. + * @param string $data_center The data center. If not provided, we'll assume the data center is in the API key itself. + * @throws Exception If no api key or access token is set */ - public function __construct( $api_key ) { - $api_key = trim( $api_key ); - if ( ! $api_key ) { + public function __construct( $access_token, $data_center = '' ) { + $access_token = trim( $access_token ); + if ( ! $access_token ) { throw new Exception( esc_html( sprintf( - /* translators: %s: api key */ - __( 'Invalid API Key: %s', 'mailchimp' ), - $api_key + /* translators: %s: access token */ + __( 'Invalid Access Token or API key: %s', 'mailchimp' ), + $access_token ) ) ); } - $this->key = $api_key; - $dc = explode( '-', $api_key ); - $this->datacenter = empty( $dc[1] ) ? 'us1' : $dc[1]; + // No data center provided, so we'll assume it's in the API key. + if ( ! $data_center ) { + $this->key = $access_token; + $dc = explode( '-', $access_token ); + $this->datacenter = empty( $dc[1] ) ? 'us1' : $dc[1]; + } else { + $this->access_token = $access_token; + $this->datacenter = $data_center; + } + $this->api_url = 'https://' . $this->datacenter . '.api.mailchimp.com/3.0/'; } @@ -84,12 +99,20 @@ public function get( $endpoint, $count = 10, $fields = array() ) { $url .= "?{$query_params}"; } + $headers = array(); + // If we have an access token, use that, otherwise use the API key. + if ( $this->access_token ) { + $headers['Authorization'] = 'Bearer ' . $this->access_token; + } else { + $headers['Authorization'] = 'apikey ' . $this->key; + } + $args = array( 'timeout' => 5, 'redirection' => 5, 'httpversion' => '1.1', 'user-agent' => 'Mailchimp WordPress Plugin/' . get_bloginfo( 'url' ), - 'headers' => array( 'Authorization' => 'apikey ' . $this->key ), + 'headers' => $headers, ); $request = wp_remote_get( $url, $args ); @@ -120,13 +143,21 @@ public function get( $endpoint, $count = 10, $fields = array() ) { public function post( $endpoint, $body, $method = 'POST' ) { $url = $this->api_url . $endpoint; + $headers = array(); + // If we have an access token, use that, otherwise use the API key. + if ( $this->access_token ) { + $headers['Authorization'] = 'Bearer ' . $this->access_token; + } else { + $headers['Authorization'] = 'apikey ' . $this->key; + } + $args = array( 'method' => $method, 'timeout' => 5, 'redirection' => 5, 'httpversion' => '1.1', 'user-agent' => 'Mailchimp WordPress Plugin/' . get_bloginfo( 'url' ), - 'headers' => array( 'Authorization' => 'apikey ' . $this->key ), + 'headers' => $headers, 'body' => wp_json_encode( $body ), ); $request = wp_remote_post( $url, $args ); diff --git a/mailchimp.php b/mailchimp.php index e087bab..30b2112 100644 --- a/mailchimp.php +++ b/mailchimp.php @@ -49,6 +49,9 @@ require_once $path . 'lib/mailchimp/mailchimp.php'; } +// Encryption utility class. +require_once plugin_dir_path( __FILE__ ) . 'includes/class-mailchimp-data-encryption.php'; + // includes the widget code so it can be easily called either normally or via ajax require_once 'mailchimp_widget.php'; @@ -58,8 +61,8 @@ // Upgrade routines. require_once 'mailchimp_upgrade.php'; -// Init Admin functions -require_once plugin_dir_path( __FILE__ ) . 'includes/mailchimp-admin.php'; +// Init Admin functions. +require_once plugin_dir_path( __FILE__ ) . 'includes/class-mailchimp-admin.php'; $admin = new MailChimp_Admin(); $admin->init(); @@ -149,18 +152,22 @@ function mailchimp_admin_page_scripts( $hook_suffix ) { return; } - wp_enqueue_style( 'mailchimp_sf_admin_css', MCSF_URL . 'css/admin.css', array(), true ); + wp_enqueue_style( 'mailchimp_sf_admin_css', MCSF_URL . 'css/admin.css', array( 'wp-jquery-ui-dialog' ), true ); wp_enqueue_script( 'showMe', MCSF_URL . 'js/hidecss.js', array( 'jquery' ), MCSF_VER, true ); - wp_enqueue_script( 'mailchimp_sf_admin', MCSF_URL . 'js/admin.js', array( 'jquery' ), MCSF_VER, true ); + wp_enqueue_script( 'mailchimp_sf_admin', MCSF_URL . 'js/admin.js', array( 'jquery', 'jquery-ui-dialog' ), MCSF_VER, true ); wp_localize_script( 'mailchimp_sf_admin', 'mailchimp_sf_admin_params', array( - 'ajax_url' => esc_url( admin_url( 'admin-ajax.php' ) ), - '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' ), + 'ajax_url' => esc_url( admin_url( 'admin-ajax.php' ) ), + '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' ), ) ); } @@ -263,7 +270,7 @@ function mailchimp_sf_request_handler() { } // erase auth information - $options = array( 'mc_api_key', 'mc_sopresto_user', 'mc_sopresto_public_key', 'mc_sopresto_secret_key' ); + $options = array( 'mc_api_key', 'mailchimp_sf_access_token', 'mc_datacenter', 'mc_sopresto_user', 'mc_sopresto_public_key', 'mc_sopresto_secret_key' ); mailchimp_sf_delete_options( $options ); break; case 'change_form_settings': @@ -398,6 +405,14 @@ function mailchimp_sf_auth_nonce_salt() { * @return MailChimp_API | false */ function mailchimp_sf_get_api() { + // Check for the access token first. + $access_token = mailchimp_sf_get_access_token(); + $data_center = get_option( 'mc_datacenter' ); + if ( ! empty( $access_token ) && ! empty( $data_center ) ) { + return new MailChimp_API( $access_token, $data_center ); + } + + // Check for the API key if the access token is not available. $key = get_option( 'mc_api_key' ); if ( $key ) { return new MailChimp_API( $key ); @@ -1410,3 +1425,16 @@ function mailchimp_sf_create_nonce( $action = -1 ) { return substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 ); } + +/** + * Get Mailchimp Access Token. + * + * @since x.x.x + * @return string|bool + */ +function mailchimp_sf_get_access_token() { + $access_token = get_option( 'mailchimp_sf_access_token' ); + $data_encryption = new MailChimp_Data_Encryption(); + + return $data_encryption->decrypt( $access_token ); +} diff --git a/views/setup_page.php b/views/setup_page.php index 167f662..bc3a62f 100644 --- a/views/setup_page.php +++ b/views/setup_page.php @@ -31,7 +31,7 @@ } // If we don't have an API Key, do a login form -if ( ! $user || ! get_option( 'mc_api_key' ) ) { +if ( ! $user || ( ! get_option( 'mc_api_key' ) && ! mailchimp_sf_get_access_token() ) ) { ?>

From c9cf283b336e3f85c8a29c88cd98ac5dbd7ac35a Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Tue, 6 Aug 2024 19:52:08 +0530 Subject: [PATCH 11/48] Fix spacing issue. --- includes/class-mailchimp-admin.php | 2 +- includes/class-mailchimp-data-encryption.php | 4 ++-- lib/mailchimp/mailchimp.php | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/includes/class-mailchimp-admin.php b/includes/class-mailchimp-admin.php index e00a843..53288ff 100644 --- a/includes/class-mailchimp-admin.php +++ b/includes/class-mailchimp-admin.php @@ -5,7 +5,7 @@ * @package Mailchimp */ - // Exit if accessed directly. +// Exit if accessed directly. if ( ! defined( 'ABSPATH' ) ) { exit; } diff --git a/includes/class-mailchimp-data-encryption.php b/includes/class-mailchimp-data-encryption.php index 0002998..b904b23 100644 --- a/includes/class-mailchimp-data-encryption.php +++ b/includes/class-mailchimp-data-encryption.php @@ -67,7 +67,7 @@ public function encrypt( $value ) { return false; } - return base64_encode( $iv . $raw_value ); + return base64_encode( $iv . $raw_value ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode } /** @@ -85,7 +85,7 @@ public function decrypt( $raw_value ) { return $raw_value; } - $decoded_value = base64_decode( $raw_value, true ); + $decoded_value = base64_decode( $raw_value, true ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode if ( false === $decoded_value ) { return $raw_value; diff --git a/lib/mailchimp/mailchimp.php b/lib/mailchimp/mailchimp.php index 02c600f..ffdc9ec 100644 --- a/lib/mailchimp/mailchimp.php +++ b/lib/mailchimp/mailchimp.php @@ -61,15 +61,15 @@ public function __construct( $access_token, $data_center = '' ) { // No data center provided, so we'll assume it's in the API key. if ( ! $data_center ) { - $this->key = $access_token; - $dc = explode( '-', $access_token ); - $this->datacenter = empty( $dc[1] ) ? 'us1' : $dc[1]; + $this->key = $access_token; + $dc = explode( '-', $access_token ); + $this->datacenter = empty( $dc[1] ) ? 'us1' : $dc[1]; } else { - $this->access_token = $access_token; - $this->datacenter = $data_center; + $this->access_token = $access_token; + $this->datacenter = $data_center; } - $this->api_url = 'https://' . $this->datacenter . '.api.mailchimp.com/3.0/'; + $this->api_url = 'https://' . $this->datacenter . '.api.mailchimp.com/3.0/'; } /** From 416face19f39b4eefff87a6aceb20dcd24d73aee Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Tue, 6 Aug 2024 20:10:49 +0530 Subject: [PATCH 12/48] Remove existing login form. --- css/admin.css | 1 - mailchimp.php | 18 ------------------ views/setup_page.php | 16 ---------------- 3 files changed, 35 deletions(-) diff --git a/css/admin.css b/css/admin.css index 99e826e..cfb42fc 100644 --- a/css/admin.css +++ b/css/admin.css @@ -265,5 +265,4 @@ th.mailchimp-connect { .mailchimp-sf-oauth-connect-wrapper { display: flex; - text-align: right; } diff --git a/mailchimp.php b/mailchimp.php index 30b2112..c08d4ac 100644 --- a/mailchimp.php +++ b/mailchimp.php @@ -241,24 +241,6 @@ function mailchimp_sf_add_pages() { function mailchimp_sf_request_handler() { if ( isset( $_POST['mcsf_action'] ) ) { switch ( $_POST['mcsf_action'] ) { - case 'login': - $key = isset( $_POST['mailchimp_sf_api_key'] ) ? trim( sanitize_text_field( wp_unslash( $_POST['mailchimp_sf_api_key'] ) ) ) : ''; - - try { - $api = new MailChimp_API( $key ); - } catch ( Exception $e ) { - $msg = '' . $e->getMessage() . ''; - mailchimp_sf_global_msg( $msg ); - break; - } - - $key = mailchimp_sf_verify_key( $api ); - if ( is_wp_error( $key ) ) { - $msg = '' . $key->get_error_message() . ''; - mailchimp_sf_global_msg( $msg ); - } - - break; case 'logout': // Check capability & Verify nonce if ( diff --git a/views/setup_page.php b/views/setup_page.php index bc3a62f..6d985b7 100644 --- a/views/setup_page.php +++ b/views/setup_page.php @@ -57,22 +57,6 @@ ); ?>

-
- - - - - - - - -
- - - - -
-
From 796e63056c336d2d954cfd25322ce8359447ddff Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Tue, 6 Aug 2024 20:11:53 +0530 Subject: [PATCH 13/48] Fix js lint error. --- js/admin.js | 1 + 1 file changed, 1 insertion(+) diff --git a/js/admin.js b/js/admin.js index 20ec471..7ff6002 100644 --- a/js/admin.js +++ b/js/admin.js @@ -140,4 +140,5 @@ }); }); }); + // eslint-disable-next-line no-undef })(jQuery); From 5daf4f380457d8a8ec95f877627f2a5b4a090261 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Wed, 7 Aug 2024 12:42:35 +0530 Subject: [PATCH 14/48] Remove use of string template from js. --- js/admin.js | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/js/admin.js b/js/admin.js index 7ff6002..dd8bc62 100644 --- a/js/admin.js +++ b/js/admin.js @@ -1,4 +1,4 @@ -/* eslint-disable no-console */ +/* eslint-disable prefer-template, no-console */ (function ($) { const params = window.mailchimp_sf_admin_params || {}; const oauthBaseUrl = 'https://woocommerce.mailchimpapp.com'; @@ -11,15 +11,23 @@ * @param {string} token - Token from the Oauth service. */ function openMailChimpOauthPopup(token) { - const startUrl = `${oauthBaseUrl}/auth/start/${token}`; + const startUrl = oauthBaseUrl + '/auth/start/' + token; const width = 800; const height = 600; const screenSizes = window.screen || { width: 1024, height: 768 }; const left = (screenSizes.width - width) / 2; const top = (screenSizes.height - height) / 4; - const windowOptions = `toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no, width=${ - width - }, height=${height}, top=${top}, left=${left}, domain=${oauthBaseUrl.replace('https://', '')}`; + const windowOptions = + 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no, width=' + + width + + ', height=' + + height + + ', top=' + + top + + ', left=' + + left + + ', domain=' + + oauthBaseUrl.replace('https://', ''); // Open Mailchimp OAuth popup. const popup = window.open(startUrl, params.oauth_window_name, windowOptions); @@ -58,7 +66,8 @@ window.clearInterval(oauthInterval); // Check status of OAuth connection. - $.post(`${oauthBaseUrl}/api/status/${token}`, function (statusData) { + const statusUrl = oauthBaseUrl + '/api/status/' + token; + $.post(statusUrl, function (statusData) { if (statusData && statusData.status === 'accepted') { const finishData = { action: 'mailchimp_sf_oauth_finish', @@ -83,18 +92,20 @@ } $(errorSelector).show(); } + $(spinner).removeClass('is-active'); }).fail(function () { console.error('Error calling OAuth finish endpoint.'); $(errorSelector).html(params.generic_error); $(errorSelector).show(); + $(spinner).removeClass('is-active'); }); } else { console.log( 'Error calling OAuth status endpoint. No credentials provided at login popup? Data:', statusData, ); + $(spinner).removeClass('is-active'); } - $(spinner).removeClass('is-active'); }).fail(function () { $(errorSelector).html(params.generic_error); $(errorSelector).show(); From 54d1725b3f05c1236a98160efb77fe73ed5dd816 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Wed, 7 Aug 2024 12:44:15 +0530 Subject: [PATCH 15/48] Display error message in case of lists API fail. --- views/setup_page.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/views/setup_page.php b/views/setup_page.php index 6d985b7..52c938f 100644 --- a/views/setup_page.php +++ b/views/setup_page.php @@ -131,6 +131,23 @@ get( 'lists', 100, array( 'fields' => 'lists.id,lists.name,lists.email_type_option' ) ); + + if ( is_wp_error( $lists ) ) { + ?> +
+ get_error_message() ) + ); + ?> +
+ + Date: Wed, 7 Aug 2024 15:47:18 +0530 Subject: [PATCH 16/48] Added admin notice for the invalid/revoked token. --- includes/class-mailchimp-admin.php | 29 +++++++++++++++++++++++++++++ lib/mailchimp/mailchimp.php | 10 ++++++++++ mailchimp.php | 2 +- 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/includes/class-mailchimp-admin.php b/includes/class-mailchimp-admin.php index 53288ff..daf0556 100644 --- a/includes/class-mailchimp-admin.php +++ b/includes/class-mailchimp-admin.php @@ -29,6 +29,7 @@ class MailChimp_Admin { * Initialize the class */ public function init() { + add_action( 'admin_notices', array( $this, 'admin_notices' ) ); add_action( 'wp_ajax_mailchimp_sf_oauth_start', array( $this, 'start_oauth_process' ) ); add_action( 'wp_ajax_mailchimp_sf_oauth_finish', array( $this, 'finish_oauth_process' ) ); } @@ -124,6 +125,7 @@ public function finish_oauth_process() { if ( $result && ! empty( $result['access_token'] ) && ! empty( $result['data_center'] ) ) { // Clean up the old data. delete_option( 'mailchimp_sf_access_token' ); + delete_option( 'mailchimp_sf_auth_error' ); delete_option( 'mc_datacenter' ); delete_site_transient( 'mailchimp_sf_oauth_secret' ); @@ -181,4 +183,31 @@ public function verify_and_save_oauth_token( $access_token, $data_center ) { return new WP_Error( 'mailchimp-sf-invalid-role', $msg ); } } + + /** + * Display admin notices. + * + * @since x.x.x + */ + public function admin_notices() { + // display a notice if the access token is invalid/revoked. + if ( get_option( 'mailchimp_sf_auth_error', false ) && current_user_can( 'manage_options' ) && get_option( 'mailchimp_sf_access_token', '' ) ) { + ?> +
+

+ tag, %2$s - tag */ + __( 'Heads up! There may be a problem with your connection to Mailchimp. Please %1$sre-connect%2$s your Mailchimp account to fix the issue.', 'mailchimp' ), + '', + '' + ); + + echo wp_kses( $message, array( 'a' => array( 'href' => array() ) ) ); + ?> +

+
+ get_error_message() ); } + // Check if Access Token is invalid/revoked. + if ( is_array( $request ) && in_array( $request['response']['code'], array( 401, 403 ), true ) ) { + update_option( 'mailchimp_sf_auth_error', true ); + } + $body = json_decode( $request['body'], true ); $merges = get_option( 'mc_merge_vars' ); $field_name = ''; diff --git a/mailchimp.php b/mailchimp.php index c08d4ac..cdf40ee 100644 --- a/mailchimp.php +++ b/mailchimp.php @@ -252,7 +252,7 @@ function mailchimp_sf_request_handler() { } // erase auth information - $options = array( 'mc_api_key', 'mailchimp_sf_access_token', 'mc_datacenter', 'mc_sopresto_user', 'mc_sopresto_public_key', 'mc_sopresto_secret_key' ); + $options = array( 'mc_api_key', 'mailchimp_sf_access_token', 'mc_datacenter', 'mailchimp_sf_auth_error', 'mc_sopresto_user', 'mc_sopresto_public_key', 'mc_sopresto_secret_key' ); mailchimp_sf_delete_options( $options ); break; case 'change_form_settings': From 3f893f275c85635cfc9baf6398c86f8239d7f92b Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Wed, 7 Aug 2024 16:32:25 +0530 Subject: [PATCH 17/48] Display spinner on try-again popup. --- js/admin.js | 1 + 1 file changed, 1 insertion(+) diff --git a/js/admin.js b/js/admin.js index dd8bc62..0d08006 100644 --- a/js/admin.js +++ b/js/admin.js @@ -51,6 +51,7 @@ class: 'button-primary', click() { $(this).dialog('close'); + $(spinner).addClass('is-active'); openMailChimpOauthPopup(token); }, style: 'margin-left: 10px;', From c520dd3945ec4730931a46548deb56ff267190bc Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Wed, 7 Aug 2024 20:00:01 +0530 Subject: [PATCH 18/48] Increase request timeout to 10 seconds. --- lib/mailchimp/mailchimp.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/mailchimp/mailchimp.php b/lib/mailchimp/mailchimp.php index 99b28a5..bbe077e 100644 --- a/lib/mailchimp/mailchimp.php +++ b/lib/mailchimp/mailchimp.php @@ -108,7 +108,7 @@ public function get( $endpoint, $count = 10, $fields = array() ) { } $args = array( - 'timeout' => 5, + 'timeout' => 10, 'redirection' => 5, 'httpversion' => '1.1', 'user-agent' => 'Mailchimp WordPress Plugin/' . get_bloginfo( 'url' ), @@ -158,7 +158,7 @@ public function post( $endpoint, $body, $method = 'POST' ) { $args = array( 'method' => $method, - 'timeout' => 5, + 'timeout' => 10, 'redirection' => 5, 'httpversion' => '1.1', 'user-agent' => 'Mailchimp WordPress Plugin/' . get_bloginfo( 'url' ), From 23112f91eb5cc9fe43be4ad896f762edf0ba6d02 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Wed, 7 Aug 2024 21:26:42 +0530 Subject: [PATCH 19/48] Apply suggestions from code review Co-authored-by: Darin Kotter --- includes/class-mailchimp-admin.php | 6 ++++-- includes/class-mailchimp-data-encryption.php | 6 +++--- views/setup_page.php | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/includes/class-mailchimp-admin.php b/includes/class-mailchimp-admin.php index daf0556..166ce9d 100644 --- a/includes/class-mailchimp-admin.php +++ b/includes/class-mailchimp-admin.php @@ -37,8 +37,9 @@ public function init() { /** * Start the OAuth process. + * * This function is called via AJAX. - * It start the OAuth process by the calling the oAuth middleware server and responding the response to the front-end. + * It starts the OAuth process by the calling the OAuth middleware server and sending the response to the front-end. */ public function start_oauth_process() { // Validate the nonce and permissions. @@ -86,8 +87,9 @@ public function start_oauth_process() { /** * Finish the OAuth process. + * * This function is called via AJAX. - * This function finishes the OAuth process by the sending temporary token back to the oAuth server. + * This function finishes the OAuth process by the sending a temporary token back to the OAuth server. */ public function finish_oauth_process() { // Validate the nonce and permissions. diff --git a/includes/class-mailchimp-data-encryption.php b/includes/class-mailchimp-data-encryption.php index b904b23..11b36f6 100644 --- a/includes/class-mailchimp-data-encryption.php +++ b/includes/class-mailchimp-data-encryption.php @@ -2,7 +2,7 @@ /** * Class responsible for encrypting and decrypting data. * - * @package Mailchimp + * @package Mailchimp */ // Exit if accessed directly. @@ -121,7 +121,7 @@ private function get_default_key() { return LOGGED_IN_KEY; } - // If this is reached, you're either not on a live site or have a serious security issue. + // Ideally this default is never used but we have it just in case. return 'vJgwa_qf0u(k!uir[rB);g;DciNAKuX;+q&`A+z&m6kX3Y|$q.U3:Q>!$)6CA+=O'; } @@ -141,7 +141,7 @@ private function get_default_salt() { return LOGGED_IN_SALT; } - // If this is reached, you're either not on a live site or have a serious security issue. + // Ideally this default is never used but we have it just in case. return '|qhC}/w6q+$V`H>wM:AtNpg/{s)g From 4073bbb556f707bb65d7feff150a355fba64d6dc Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Wed, 7 Aug 2024 22:52:11 +0530 Subject: [PATCH 21/48] Add note in readme for the encryption constants. --- README.md | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a7f4e0d..53bc742 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ > Add a Mailchimp signup form widget to your WordPress site. -[![Support Level](https://img.shields.io/badge/support-active-green.svg?label=Support)](#support-level) [![GPL-2.0-or-later License](https://img.shields.io/github/license/mailchimp/wordpress?label=License)](https://github.com/mailchimp/wordpress/blob/develop/LICENSE.md) ![WordPress Plugin Version](https://img.shields.io/wordpress/plugin/v/mailchimp?label=Version) ![WordPress Minimum](https://img.shields.io/wordpress/plugin/wp-version/mailchimp?label=WordPress%20minimum) ![PHP Minimum](https://img.shields.io/wordpress/plugin/required-php/mailchimp?label=PHP%20minimum) ![WordPress Tested Up To](https://img.shields.io/wordpress/plugin/tested/mailchimp?label=WordPress) [![E2E Cypress Tests](https://github.com/mailchimp/wordpress/actions/workflows/e2e.yml/badge.svg)](https://github.com/mailchimp/wordpress/actions/workflows/e2e.yml) [![PHP Compatibility](https://github.com/mailchimp/wordpress/actions/workflows/php-compat.yml/badge.svg)](https://github.com/mailchimp/wordpress/actions/workflows/php-compat.yml) [![PHP Linting](https://github.com/mailchimp/wordpress/actions/workflows/phpcs.yml/badge.svg)](https://github.com/mailchimp/wordpress/actions/workflows/phpcs.yml) [![JS Linting](https://github.com/mailchimp/wordpress/actions/workflows/eslint.yml/badge.svg)](https://github.com/mailchimp/wordpress/actions/workflows/eslint.yml) +[![Support Level](https://img.shields.io/badge/support-active-green.svg?label=Support)](#support-level) [![GPL-2.0-or-later License](https://img.shields.io/github/license/mailchimp/wordpress?label=License)](https://github.com/mailchimp/wordpress/blob/develop/LICENSE.md) ![WordPress Plugin Version](https://img.shields.io/wordpress/plugin/v/mailchimp?label=Version) ![WordPress Minimum](https://img.shields.io/wordpress/plugin/wp-version/mailchimp?label=WordPress%20minimum) ![PHP Minimum](https://img.shields.io/wordpress/plugin/required-php/mailchimp?label=PHP%20minimum) ![WordPress Tested Up To](https://img.shields.io/wordpress/plugin/tested/mailchimp?label=WordPress) [![E2E Cypress Tests](https://github.com/mailchimp/wordpress/actions/workflows/e2e.yml/badge.svg)](https://github.com/mailchimp/wordpress/actions/workflows/e2e.yml) [![PHP Compatibility](https://github.com/mailchimp/wordpress/actions/workflows/php-compat.yml/badge.svg)](https://github.com/mailchimp/wordpress/actions/workflows/php-compat.yml) [![PHP Linting](https://github.com/mailchimp/wordpress/actions/workflows/phpcs.yml/badge.svg)](https://github.com/mailchimp/wordpress/actions/workflows/phpcs.yml) [![JS Linting](https://github.com/mailchimp/wordpress/actions/workflows/eslint.yml/badge.svg)](https://github.com/mailchimp/wordpress/actions/workflows/eslint.yml) ## Overview @@ -24,6 +24,21 @@ WordPress.com compatibility is limited to Business tier users only. [How to add No, only one form should exist per page, no matter the display type (widget, shortcode, or block). +## Access token Encryption + +The plugin stores the OAuth access token in the WordPress database and encrypts it for security. To ensure encryption and decryption work properly, the plugin needs access to certain security constants that should remain unchanged. + +By default, the plugin uses the `LOGGED_IN_KEY` and `LOGGED_IN_SALT` constants from the wp-config.php file. These usually work well. However, if another plugin or mechanism regularly updates these constants, the plugin will have trouble decrypting the access token and you’ll need to reconnect your Mailchimp account. + +To prevent such issues, it is recommended to define two additional constants in your wp-config.php file: `MAILCHIMP_SF_ENCRYPTION_KEY` and `MAILCHIMP_SF_ENCRYPTION_SALT`. These constants should consist of a combination of characters, preferably at least 32 characters long. Once set, these values should not be changed. For strong values, you can copy some of the values from https://api.wordpress.org/secret-key/1.1/salt/ and use them. You should have additional code like the following in your wp-config.php file: + +```php +define( 'MAILCHIMP_SF_ENCRYPTION_KEY', 'put your unique phrase here' ); +define( 'MAILCHIMP_SF_ENCRYPTION_SALT', 'put your unique phrase here' ); +``` + +If you add these constants after the plugin is already configured, the plugin will use the new constants, which may cause issues. To avoid this, you can copy the values from `LOGGED_IN_KEY` and `LOGGED_IN_SALT` to `MAILCHIMP_SF_ENCRYPTION_KEY` and `MAILCHIMP_SF_ENCRYPTION_SALT`. If you prefer new values, you will need to reconnect your Mailchimp account. + ## Installation This section describes how to install the plugin and get started using it. @@ -43,7 +58,7 @@ This section describes how to install the plugin and get started using it. If you have a custom coded sidebar or bells and whistles that prevent enabling widgets through the WordPress GUI, complete these steps instead. -WordPress v2.8 or higher: +WordPress v2.8 or higher: ` [mailchimpsf_form] ` If you are adding it inside a php code block, pop this in: From e7bc6f121f2fa6739f3a53f96be4155ec81a5886 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Wed, 7 Aug 2024 23:01:19 +0530 Subject: [PATCH 22/48] readme.txt updates. --- README.md | 12 ++++++------ readme.txt | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 53bc742..cfe49ca 100644 --- a/README.md +++ b/README.md @@ -18,12 +18,6 @@ WordPress.com compatibility is limited to Business tier users only. [How to add ![Configuring extra fields on your Signup Form (optional)](https://github.com/mailchimp/wordpress/blob/develop/.wordpress-org/screenshot-4.jpg?raw=true) -## Frequently Asked Questions - -### Can I have multiple forms on one page? - -No, only one form should exist per page, no matter the display type (widget, shortcode, or block). - ## Access token Encryption The plugin stores the OAuth access token in the WordPress database and encrypts it for security. To ensure encryption and decryption work properly, the plugin needs access to certain security constants that should remain unchanged. @@ -39,6 +33,12 @@ define( 'MAILCHIMP_SF_ENCRYPTION_SALT', 'put your unique phrase here' ); If you add these constants after the plugin is already configured, the plugin will use the new constants, which may cause issues. To avoid this, you can copy the values from `LOGGED_IN_KEY` and `LOGGED_IN_SALT` to `MAILCHIMP_SF_ENCRYPTION_KEY` and `MAILCHIMP_SF_ENCRYPTION_SALT`. If you prefer new values, you will need to reconnect your Mailchimp account. +## Frequently Asked Questions + +### Can I have multiple forms on one page? + +No, only one form should exist per page, no matter the display type (widget, shortcode, or block). + ## Installation This section describes how to install the plugin and get started using it. diff --git a/readme.txt b/readme.txt index 2918f3c..4490777 100644 --- a/readme.txt +++ b/readme.txt @@ -16,6 +16,21 @@ After installation, you’ll log in with your API key, select your Mailchimp lis WordPress.com compatibility is limited to Business tier users only. [How to add a signup form if you have a WordPress.com site](https://mailchimp.com/help/ways-to-add-a-signup-form-in-wordpress/). +=== Access token Encryption === + +The plugin stores the OAuth access token in the WordPress database and encrypts it for security. To ensure encryption and decryption work properly, the plugin needs access to certain security constants that should remain unchanged. + +By default, the plugin uses the `LOGGED_IN_KEY` and `LOGGED_IN_SALT` constants from the wp-config.php file. These usually work well. However, if another plugin or mechanism regularly updates these constants, the plugin will have trouble decrypting the access token and you’ll need to reconnect your Mailchimp account. + +To prevent such issues, it is recommended to define two additional constants in your wp-config.php file: `MAILCHIMP_SF_ENCRYPTION_KEY` and `MAILCHIMP_SF_ENCRYPTION_SALT`. These constants should consist of a combination of characters, preferably at least 32 characters long. Once set, these values should not be changed. For strong values, you can copy some of the values from https://api.wordpress.org/secret-key/1.1/salt/ and use them. You should have additional code like the following in your wp-config.php file: + +` +define( 'MAILCHIMP_SF_ENCRYPTION_KEY', 'put your unique phrase here' ); +define( 'MAILCHIMP_SF_ENCRYPTION_SALT', 'put your unique phrase here' ); +` + +If you add these constants after the plugin is already configured, the plugin will use the new constants, which may cause issues. To avoid this, you can copy the values from `LOGGED_IN_KEY` and `LOGGED_IN_SALT` to `MAILCHIMP_SF_ENCRYPTION_KEY` and `MAILCHIMP_SF_ENCRYPTION_SALT`. If you prefer new values, you will need to reconnect your Mailchimp account. + == Frequently Asked Questions == = Can I have multiple forms on one page? = From 19e72d1b04da4625e44ad0835b8ef930a69aabfd Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Wed, 7 Aug 2024 23:11:22 +0530 Subject: [PATCH 23/48] Wording updates on connect page. --- views/setup_page.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/views/setup_page.php b/views/setup_page.php index 1e1189e..134b858 100644 --- a/views/setup_page.php +++ b/views/setup_page.php @@ -35,11 +35,12 @@ ?>

-

+

+

Connect Account to continue.', + 'Please click the Connect Account button to connect this WordPress site with your Mailchimp account. Once you complete the Mailchimp login in the pop-up window that appears, this page will refresh to show the Mailchimp List Subscribe Form settings.', 'mailchimp' ), [ From 1198ebb147414b62d2255ade9557f25603fc03d3 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Wed, 7 Aug 2024 23:37:41 +0530 Subject: [PATCH 24/48] Show notice for re-connect incase of token decryption fail. --- mailchimp.php | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/mailchimp.php b/mailchimp.php index ab9db62..de7d340 100644 --- a/mailchimp.php +++ b/mailchimp.php @@ -1415,8 +1415,18 @@ function mailchimp_sf_create_nonce( $action = -1 ) { * @return string|bool */ function mailchimp_sf_get_access_token() { - $access_token = get_option( 'mailchimp_sf_access_token' ); + $access_token = get_option( 'mailchimp_sf_access_token' ); + if ( empty( $access_token ) ) { + return false; + } + $data_encryption = new Mailchimp_Data_Encryption(); + $access_token = $data_encryption->decrypt( $access_token ); + + // If decryption fails, display notice to user to re-authenticate. + if ( false === $access_token ) { + update_option( 'mailchimp_sf_auth_error', true ); + } - return $data_encryption->decrypt( $access_token ); + return $access_token; } From c2c918917e56734f416ba6477221ca39434c15fb Mon Sep 17 00:00:00 2001 From: Darin Kotter Date: Wed, 7 Aug 2024 14:38:30 -0600 Subject: [PATCH 25/48] Readme updates --- README.md | 12 ++++++------ readme.txt | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index cfe49ca..41e20b0 100644 --- a/README.md +++ b/README.md @@ -18,20 +18,20 @@ WordPress.com compatibility is limited to Business tier users only. [How to add ![Configuring extra fields on your Signup Form (optional)](https://github.com/mailchimp/wordpress/blob/develop/.wordpress-org/screenshot-4.jpg?raw=true) -## Access token Encryption +## Access Token Encryption -The plugin stores the OAuth access token in the WordPress database and encrypts it for security. To ensure encryption and decryption work properly, the plugin needs access to certain security constants that should remain unchanged. +Starting in version 1.6.0, authentication has changed to use OAuth. As part of this process, we retrieve an access token that can be used to make API requests. To provide a high-level of security, this access token is encrypted before being stored in the WordPress database. In order to ensure this access token can be decrypted when used, the plugin relies on certain security constants that should remain unchanged. -By default, the plugin uses the `LOGGED_IN_KEY` and `LOGGED_IN_SALT` constants from the wp-config.php file. These usually work well. However, if another plugin or mechanism regularly updates these constants, the plugin will have trouble decrypting the access token and you’ll need to reconnect your Mailchimp account. +With no additional configuration, we use the standard `LOGGED_IN_KEY` and `LOGGED_IN_SALT` constants that are normally set in your site's `wp-config.php` file. Some sites make use of security plugins that rotate these constants on a periodic basis. When this happens, we won't be able to decrypt the access token and you’ll need to reconnect your Mailchimp account to generate a new access token. -To prevent such issues, it is recommended to define two additional constants in your wp-config.php file: `MAILCHIMP_SF_ENCRYPTION_KEY` and `MAILCHIMP_SF_ENCRYPTION_SALT`. These constants should consist of a combination of characters, preferably at least 32 characters long. Once set, these values should not be changed. For strong values, you can copy some of the values from https://api.wordpress.org/secret-key/1.1/salt/ and use them. You should have additional code like the following in your wp-config.php file: +To prevent such issues, it is recommended to define two additional constants in your site's `wp-config.php` file: `MAILCHIMP_SF_ENCRYPTION_KEY` and `MAILCHIMP_SF_ENCRYPTION_SALT`. These constants should consist of a combination of characters, preferably at least 32 characters long. Once set, these values should not be changed. For strong values, you can copy some of the values from [here](https://api.wordpress.org/secret-key/1.1/salt/) and use them. You'll end up with additional code like the following in your `wp-config.php` file: ```php define( 'MAILCHIMP_SF_ENCRYPTION_KEY', 'put your unique phrase here' ); define( 'MAILCHIMP_SF_ENCRYPTION_SALT', 'put your unique phrase here' ); ``` -If you add these constants after the plugin is already configured, the plugin will use the new constants, which may cause issues. To avoid this, you can copy the values from `LOGGED_IN_KEY` and `LOGGED_IN_SALT` to `MAILCHIMP_SF_ENCRYPTION_KEY` and `MAILCHIMP_SF_ENCRYPTION_SALT`. If you prefer new values, you will need to reconnect your Mailchimp account. +If these constants are added after you've already authenticated with Mailchimp, you will need to reconnect your account. To avoid this, you can copy the values from `LOGGED_IN_KEY` and `LOGGED_IN_SALT` (if they exist) to `MAILCHIMP_SF_ENCRYPTION_KEY` and `MAILCHIMP_SF_ENCRYPTION_SALT` respectively. ## Frequently Asked Questions @@ -56,7 +56,7 @@ This section describes how to install the plugin and get started using it. ### Advanced -If you have a custom coded sidebar or bells and whistles that prevent enabling widgets through the WordPress GUI, complete these steps instead. +If you have a custom coded sidebar or bells and whistles that prevent enabling widgets through the WordPress GUI, complete these steps instead. WordPress v2.8 or higher: ` [mailchimpsf_form] ` diff --git a/readme.txt b/readme.txt index 4490777..4b34a7e 100644 --- a/readme.txt +++ b/readme.txt @@ -16,20 +16,20 @@ After installation, you’ll log in with your API key, select your Mailchimp lis WordPress.com compatibility is limited to Business tier users only. [How to add a signup form if you have a WordPress.com site](https://mailchimp.com/help/ways-to-add-a-signup-form-in-wordpress/). -=== Access token Encryption === +=== Access Token Encryption === -The plugin stores the OAuth access token in the WordPress database and encrypts it for security. To ensure encryption and decryption work properly, the plugin needs access to certain security constants that should remain unchanged. +Starting in version 1.6.0, authentication has changed to use OAuth. As part of this process, we retrieve an access token that can be used to make API requests. To provide a high-level of security, this access token is encrypted before being stored in the WordPress database. In order to ensure this access token can be decrypted when used, the plugin relies on certain security constants that should remain unchanged. -By default, the plugin uses the `LOGGED_IN_KEY` and `LOGGED_IN_SALT` constants from the wp-config.php file. These usually work well. However, if another plugin or mechanism regularly updates these constants, the plugin will have trouble decrypting the access token and you’ll need to reconnect your Mailchimp account. +With no additional configuration, we use the standard `LOGGED_IN_KEY` and `LOGGED_IN_SALT` constants that are normally set in your site's `wp-config.php` file. Some sites make use of security plugins that rotate these constants on a periodic basis. When this happens, we won't be able to decrypt the access token and you’ll need to reconnect your Mailchimp account to generate a new access token. -To prevent such issues, it is recommended to define two additional constants in your wp-config.php file: `MAILCHIMP_SF_ENCRYPTION_KEY` and `MAILCHIMP_SF_ENCRYPTION_SALT`. These constants should consist of a combination of characters, preferably at least 32 characters long. Once set, these values should not be changed. For strong values, you can copy some of the values from https://api.wordpress.org/secret-key/1.1/salt/ and use them. You should have additional code like the following in your wp-config.php file: +To prevent such issues, it is recommended to define two additional constants in your site's `wp-config.php` file: `MAILCHIMP_SF_ENCRYPTION_KEY` and `MAILCHIMP_SF_ENCRYPTION_SALT`. These constants should consist of a combination of characters, preferably at least 32 characters long. Once set, these values should not be changed. For strong values, you can copy some of the values from [here](https://api.wordpress.org/secret-key/1.1/salt/) and use them. You'll end up with additional code like the following in your `wp-config.php` file: ` define( 'MAILCHIMP_SF_ENCRYPTION_KEY', 'put your unique phrase here' ); define( 'MAILCHIMP_SF_ENCRYPTION_SALT', 'put your unique phrase here' ); ` -If you add these constants after the plugin is already configured, the plugin will use the new constants, which may cause issues. To avoid this, you can copy the values from `LOGGED_IN_KEY` and `LOGGED_IN_SALT` to `MAILCHIMP_SF_ENCRYPTION_KEY` and `MAILCHIMP_SF_ENCRYPTION_SALT`. If you prefer new values, you will need to reconnect your Mailchimp account. +If these constants are added after you've already authenticated with Mailchimp, you will need to reconnect your account. To avoid this, you can copy the values from `LOGGED_IN_KEY` and `LOGGED_IN_SALT` (if they exist) to `MAILCHIMP_SF_ENCRYPTION_KEY` and `MAILCHIMP_SF_ENCRYPTION_SALT` respectively. == Frequently Asked Questions == @@ -52,7 +52,7 @@ This section describes how to install the plugin and get started using it. = Advanced = -If you have a custom coded sidebar or bells and whistles that prevent enabling widgets through the WordPress GUI, complete these steps instead. +If you have a custom coded sidebar or bells and whistles that prevent enabling widgets through the WordPress GUI, complete these steps instead. WordPress v2.8 or higher: ` [mailchimpsf_form] ` From ed0edf06363317cd5a80ec080e6082e77322a199 Mon Sep 17 00:00:00 2001 From: Darin Kotter Date: Wed, 7 Aug 2024 14:53:36 -0600 Subject: [PATCH 26/48] Minor formatting cleanup --- includes/class-mailchimp-admin.php | 15 +++++++++------ includes/class-mailchimp-data-encryption.php | 4 ++-- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/includes/class-mailchimp-admin.php b/includes/class-mailchimp-admin.php index dc3a201..064bb7f 100644 --- a/includes/class-mailchimp-admin.php +++ b/includes/class-mailchimp-admin.php @@ -1,6 +1,6 @@

@@ -221,7 +224,7 @@ public function admin_notices() { /** * Sanitize variables using sanitize_text_field. * - * Arrays are sanitized recursively, Non-scalar values are ignored. + * Arrays are sanitized recursively, non-scalar values are ignored. * * @param string|array $data Data to sanitize. * @return string|array diff --git a/includes/class-mailchimp-data-encryption.php b/includes/class-mailchimp-data-encryption.php index d7ef9ce..3853bea 100644 --- a/includes/class-mailchimp-data-encryption.php +++ b/includes/class-mailchimp-data-encryption.php @@ -46,7 +46,7 @@ public function __construct() { /** * Encrypts a value. * - * If a user-based key is set, that key is used. Otherwise the default key is used. + * If a user-based key is set, that is used. Otherwise the default key is used. * * @since x.x.x * @@ -73,7 +73,7 @@ public function encrypt( $value ) { /** * Decrypts a value. * - * If a user-based key is set, that key is used. Otherwise the default key is used. + * If a user-based key is set, that is used. Otherwise the default key is used. * * @since x.x.x * From 3afeb8878443856b1c9b7641760ddc8a7fd08ef1 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Thu, 8 Aug 2024 11:44:00 +0530 Subject: [PATCH 27/48] Use oauth url from the server side. --- includes/class-mailchimp-admin.php | 34 +++++++++++++++++++++++++++++ js/admin.js | 7 +++--- mailchimp.php | 35 ------------------------------ 3 files changed, 37 insertions(+), 39 deletions(-) diff --git a/includes/class-mailchimp-admin.php b/includes/class-mailchimp-admin.php index dc3a201..f0e7d4d 100644 --- a/includes/class-mailchimp-admin.php +++ b/includes/class-mailchimp-admin.php @@ -32,6 +32,8 @@ public function init() { add_action( 'admin_notices', array( $this, 'admin_notices' ) ); add_action( 'wp_ajax_mailchimp_sf_oauth_start', array( $this, 'start_oauth_process' ) ); add_action( 'wp_ajax_mailchimp_sf_oauth_finish', array( $this, 'finish_oauth_process' ) ); + + add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_page_scripts' ) ); } @@ -233,4 +235,36 @@ public function sanitize_data( $data ) { return is_scalar( $data ) ? sanitize_text_field( $data ) : $data; } } + + /** + * Enqueue scripts/styles for the Mailchimp admin page + * + * @param string $hook_suffix The current admin page. + * @return void + */ + public function enqueue_admin_page_scripts( $hook_suffix ) { + if ( 'toplevel_page_mailchimp_sf_options' !== $hook_suffix ) { + return; + } + + wp_enqueue_style( 'mailchimp_sf_admin_css', MCSF_URL . 'css/admin.css', array( 'wp-jquery-ui-dialog' ), true ); + wp_enqueue_script( 'showMe', MCSF_URL . 'js/hidecss.js', array( 'jquery' ), MCSF_VER, true ); + wp_enqueue_script( 'mailchimp_sf_admin', MCSF_URL . 'js/admin.js', array( 'jquery', 'jquery-ui-dialog' ), MCSF_VER, true ); + + wp_localize_script( + 'mailchimp_sf_admin', + 'mailchimp_sf_admin_params', + 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' ), + ) + ); + } } diff --git a/js/admin.js b/js/admin.js index b33e9da..6e24563 100644 --- a/js/admin.js +++ b/js/admin.js @@ -1,7 +1,6 @@ /* eslint-disable prefer-template, no-console */ (function ($) { const params = window.mailchimp_sf_admin_params || {}; - const oauthBaseUrl = 'https://woocommerce.mailchimpapp.com'; const spinner = '.mailchimp-sf-oauth-connect-wrapper .spinner'; const errorSelector = '.mailchimp-sf-oauth-section .oauth-error'; @@ -11,7 +10,7 @@ * @param {string} token - Token from the Oauth service. */ function openMailchimpOauthPopup(token) { - const startUrl = oauthBaseUrl + '/auth/start/' + token; + const startUrl = params.oauth_url + '/auth/start/' + token; const width = 800; const height = 600; const screenSizes = window.screen || { width: 1024, height: 768 }; @@ -27,7 +26,7 @@ ', left=' + left + ', domain=' + - oauthBaseUrl.replace('https://', ''); + params.oauth_url.replace('https://', ''); // Open Mailchimp OAuth popup. const popup = window.open(startUrl, params.oauth_window_name, windowOptions); @@ -67,7 +66,7 @@ window.clearInterval(oauthInterval); // Check status of OAuth connection. - const statusUrl = oauthBaseUrl + '/api/status/' + token; + const statusUrl = params.oauth_url + '/api/status/' + token; $.post(statusUrl, function (statusData) { if (statusData && statusData.status === 'accepted') { const finishData = { diff --git a/mailchimp.php b/mailchimp.php index de7d340..166ae43 100644 --- a/mailchimp.php +++ b/mailchimp.php @@ -140,41 +140,6 @@ function mailchimp_sf_load_resources() { } } - -/** - * Loads resources for the Mailchimp admin page - * - * @param string $hook_suffix The current admin page. - * @return void - */ -function mailchimp_admin_page_scripts( $hook_suffix ) { - if ( 'toplevel_page_mailchimp_sf_options' !== $hook_suffix ) { - return; - } - - wp_enqueue_style( 'mailchimp_sf_admin_css', MCSF_URL . 'css/admin.css', array( 'wp-jquery-ui-dialog' ), true ); - wp_enqueue_script( 'showMe', MCSF_URL . 'js/hidecss.js', array( 'jquery' ), MCSF_VER, true ); - wp_enqueue_script( 'mailchimp_sf_admin', MCSF_URL . 'js/admin.js', array( 'jquery', 'jquery-ui-dialog' ), MCSF_VER, true ); - - wp_localize_script( - 'mailchimp_sf_admin', - 'mailchimp_sf_admin_params', - array( - 'ajax_url' => esc_url( admin_url( 'admin-ajax.php' ) ), - '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' ), - ) - ); -} - -add_action( 'admin_enqueue_scripts', 'mailchimp_admin_page_scripts', 10, 1 ); - - /** * Loads jQuery Datepicker for the date-pick class **/ From 01deb4b20daef617448226eb761cefd194ec9d5e Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Thu, 8 Aug 2024 12:18:34 +0530 Subject: [PATCH 28/48] Upgrade "@10up/cypress-wp-utils" to 0.4.0 --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index a821dba..c8b99b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@wordpress/server-side-render": "^5.2.0" }, "devDependencies": { - "@10up/cypress-wp-utils": "^0.3.0", + "@10up/cypress-wp-utils": "^0.4.0", "@wordpress/env": "^10.2.0", "10up-toolkit": "^6.2.0", "cypress": "^13.12.0", @@ -42,9 +42,9 @@ } }, "node_modules/@10up/cypress-wp-utils": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@10up/cypress-wp-utils/-/cypress-wp-utils-0.3.0.tgz", - "integrity": "sha512-iMjvca50TerMCY9M9vL0FIE+80ye5YohaQp3XvhgUgQdc4LS51X2fH+lhdb0uRmBTiUQcISazEvWGJxV7DeTbw==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@10up/cypress-wp-utils/-/cypress-wp-utils-0.4.0.tgz", + "integrity": "sha512-7cNELIX6ml5V9JEU83iEyQ6dkZ77ImdR5HKjUP4oyArQogPVcFPUnokU7GInH8DicqXbESrrkxZ0IfnNtNWh+A==", "dev": true, "engines": { "node": ">=12.0" diff --git a/package.json b/package.json index 193021c..8b247b7 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "build": "10up-toolkit build" }, "devDependencies": { - "@10up/cypress-wp-utils": "^0.3.0", + "@10up/cypress-wp-utils": "^0.4.0", "@wordpress/env": "^10.2.0", "10up-toolkit": "^6.2.0", "cypress": "^13.12.0", From 3aed9d26d187ab841602c8f62eddedaa8fbba8bb Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Thu, 8 Aug 2024 16:52:42 +0530 Subject: [PATCH 29/48] Upgrade cypress to 13.13.2 --- package-lock.json | 12 ++++++------ package.json | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index c8b99b8..57bc189 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "@10up/cypress-wp-utils": "^0.4.0", "@wordpress/env": "^10.2.0", "10up-toolkit": "^6.2.0", - "cypress": "^13.12.0", + "cypress": "^13.13.2", "cypress-mochawesome-reporter": "^3.8.2", "mochawesome-json-to-md": "^1.3.5" } @@ -9912,13 +9912,13 @@ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "node_modules/cypress": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.12.0.tgz", - "integrity": "sha512-udzS2JilmI9ApO/UuqurEwOvThclin5ntz7K0BtnHBs+tg2Bl9QShLISXpSEMDv/u8b6mqdoAdyKeZiSqKWL8g==", + "version": "13.13.2", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.13.2.tgz", + "integrity": "sha512-PvJQU33933NvS1StfzEb8/mu2kMy4dABwCF+yd5Bi7Qly1HOVf+Bufrygee/tlmty/6j5lX+KIi8j9Q3JUMbhA==", "dev": true, "hasInstallScript": true, "dependencies": { - "@cypress/request": "^3.0.0", + "@cypress/request": "^3.0.1", "@cypress/xvfb": "^1.2.4", "@types/sinonjs__fake-timers": "8.1.1", "@types/sizzle": "^2.3.2", @@ -9957,7 +9957,7 @@ "request-progress": "^3.0.0", "semver": "^7.5.3", "supports-color": "^8.1.1", - "tmp": "~0.2.1", + "tmp": "~0.2.3", "untildify": "^4.0.0", "yauzl": "^2.10.0" }, diff --git a/package.json b/package.json index 8b247b7..2c3fd71 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ }, "scripts": { "cypress:open": "cypress open --config-file tests/cypress/config.js --e2e --browser chrome", - "cypress:run": "cypress run --config-file tests/cypress/config.js", + "cypress:run": "cypress run --config-file tests/cypress/config.js --e2e --browser chrome", "env": "wp-env", "env:start": "wp-env start", "env:stop": "wp-env stop", @@ -34,7 +34,7 @@ "@10up/cypress-wp-utils": "^0.4.0", "@wordpress/env": "^10.2.0", "10up-toolkit": "^6.2.0", - "cypress": "^13.12.0", + "cypress": "^13.13.2", "cypress-mochawesome-reporter": "^3.8.2", "mochawesome-json-to-md": "^1.3.5" }, From 1b0159378b4a163abe74b87df500055560e9b40a Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Thu, 8 Aug 2024 16:53:04 +0530 Subject: [PATCH 30/48] Added admin tests. --- tests/cypress/e2e/admin.test.js | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/tests/cypress/e2e/admin.test.js b/tests/cypress/e2e/admin.test.js index 1f9a545..65d5f4a 100644 --- a/tests/cypress/e2e/admin.test.js +++ b/tests/cypress/e2e/admin.test.js @@ -1,10 +1,22 @@ -describe( 'Admin can login and make sure plugin is activated', () => { - before( () => { +/* eslint-disable no-undef */ +describe('Admin can login and make sure plugin is activated', () => { + before(() => { cy.login(); - } ); + }); - it( 'Can deactivate and activate plugin?', () => { - cy.deactivatePlugin( 'mailchimp' ); - cy.activatePlugin( 'mailchimp' ); - } ); -} ); + it('Can deactivate and activate plugin?', () => { + cy.deactivatePlugin('mailchimp'); + cy.activatePlugin('mailchimp'); + }); + + it('Can see "Mailchimp" menu and Can visit "Mailchimp" settings page.', () => { + cy.visit('/wp-admin/'); + + // Check Mailchimp menu. + cy.get('#adminmenu li#toplevel_page_mailchimp_sf_options').contains('Mailchimp'); + + // Check Heading + cy.get('#adminmenu li#toplevel_page_mailchimp_sf_options').click(); + cy.get('#wpbody .mailchimp-header h1').contains('Mailchimp List Subscribe Form'); + }); +}); From 74fae4c5f133474d66f173ee9feaa9a64aee1243 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Thu, 8 Aug 2024 16:53:37 +0530 Subject: [PATCH 31/48] Added connect to mailchimp test. --- tests/cypress/e2e/connect.test.js | 37 +++++++++++++++++++++++++++++++ tests/cypress/support/commands.js | 25 +++++++++++++++++++++ tests/cypress/support/index.js | 3 +++ 3 files changed, 65 insertions(+) create mode 100644 tests/cypress/e2e/connect.test.js diff --git a/tests/cypress/e2e/connect.test.js b/tests/cypress/e2e/connect.test.js new file mode 100644 index 0000000..79f027d --- /dev/null +++ b/tests/cypress/e2e/connect.test.js @@ -0,0 +1,37 @@ +/* eslint-disable no-undef */ +describe('Admin can connect to "Mailchimp" Account', () => { + before(() => { + cy.login(); + }); + + it('Can connect to "Mailchimp" using OAuth flow.', () => { + cy.visit('/wp-admin/admin.php?page=mailchimp_sf_options'); + + // Logout if already connected. + cy.get('body').then(($body) => { + if ($body.find('input[value="Logout"]').length > 0) { + cy.get('input[value="Logout"]').click(); + } + }); + + // Check Mailchimp menu. + cy.get('#mailchimp_sf_oauth_connect').should('exist'); + + // Enable popup capture. + cy.capturePopup(); + + cy.get('#mailchimp_sf_oauth_connect').click(); + cy.wait(5000); + + cy.popup().find('input#username').clear().type(Cypress.env('MAILCHIMP_USERNAME')); + cy.popup().find('input#password').clear().type(Cypress.env('MAILCHIMP_PASSWORD')); + cy.popup().find('button[type="submit"]').click(); + cy.wait(8000); // Not a best practice, but did not find a better way to handle this. + + cy.popup().find('input#submitButton').click(); + cy.wait(10000); + + cy.get('.mc-user h3').contains('Logged in as: '); + cy.get('input[value="Logout"]').should('exist'); + }); +}); diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js index 119ab03..95025e3 100644 --- a/tests/cypress/support/commands.js +++ b/tests/cypress/support/commands.js @@ -23,3 +23,28 @@ // // -- This will overwrite an existing command -- // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) + +const state = {}; + +/** + * Intercepts calls to window.open() to keep a reference to the new window + */ +Cypress.Commands.add('capturePopup', () => { + cy.window().then((win) => { + const { open } = win; + cy.stub(win, 'open').callsFake((...params) => { + // Capture the reference to the popup + state.popup = open(...params); + return state.popup; + }); + }); +}); + +/** + * Returns a wrapped body of a captured popup + */ +Cypress.Commands.add('popup', () => { + console.log('state', state.popup); + const popup = Cypress.$(state.popup.document); + return cy.wrap(popup.contents().find('body')); +}); diff --git a/tests/cypress/support/index.js b/tests/cypress/support/index.js index d8895b6..5652037 100644 --- a/tests/cypress/support/index.js +++ b/tests/cypress/support/index.js @@ -15,6 +15,9 @@ import '@10up/cypress-wp-utils'; +// Import commands.js using ES2015 syntax: +import './commands'; + beforeEach( () => { cy.session( 'login', cy.login, { cacheAcrossSpecs: true, From d61ab47f93ec98d3d3ce638113d45089f4faa5ab Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Thu, 8 Aug 2024 17:35:09 +0530 Subject: [PATCH 32/48] Add settings, shortcode and block tests. --- tests/cypress/e2e/settings.test.js | 83 ++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 tests/cypress/e2e/settings.test.js diff --git a/tests/cypress/e2e/settings.test.js b/tests/cypress/e2e/settings.test.js new file mode 100644 index 0000000..71350a0 --- /dev/null +++ b/tests/cypress/e2e/settings.test.js @@ -0,0 +1,83 @@ +/* eslint-disable no-undef */ +describe('Admin can update plugin settings', () => { + let shortcodePostId = 0; + let blockPostId = 0; + + before(() => { + cy.login(); + }); + + it('Admin can see list and save it', () => { + cy.visit('/wp-admin/admin.php?page=mailchimp_sf_options'); + + cy.get('.mc-h2').contains('Your Lists'); + cy.get('#mc_list_id').select('10up'); + cy.get('input[value="Update List"]').click(); + cy.get('#mc-message .success_msg b').contains('Success!'); + }); + + it('Admin can create a Signup form using the shortcode', () => { + const postTitle = 'Mailchimp signup form - shortcode'; + const beforeSave = () => { + cy.insertBlock('core/shortcode').then((id) => { + cy.get(`#${id} .blocks-shortcode__textarea`).type('[mailchimpsf_form]'); + }); + }; + cy.createPost({ title: postTitle, content: '', beforeSave }).then((post) => { + if (post) { + shortcodePostId = post.id; + cy.visit(`/?p=${shortcodePostId}`); + cy.get('#mc_signup').should('exist'); + cy.get('#mc_mv_EMAIL').should('exist'); + cy.get('#mc_signup_submit').should('exist'); + cy.get('#mc_signup_submit').click(); + cy.get('.mc_error_msg').should('exist'); + cy.get('.mc_error_msg').contains(': This value should not be blank.'); + } + }); + }); + + it('Admin can create a Signup form using Mailchimp block', () => { + const postTitle = 'Mailchimp signup form - Block'; + const beforeSave = () => { + cy.insertBlock('mailchimp/mailchimp'); + }; + cy.createPost({ title: postTitle, content: '', beforeSave }).then((post) => { + if (post) { + blockPostId = post.id; + cy.visit(`/?p=${shortcodePostId}`); + cy.get('#mc_signup').should('exist'); + cy.get('#mc_mv_EMAIL').should('exist'); + cy.get('#mc_signup_submit').should('exist'); + cy.get('#mc_signup_submit').click(); + cy.get('.mc_error_msg').should('exist'); + cy.get('.mc_error_msg').contains(': This value should not be blank.'); + } + }); + }); + + it('Admin can set content options for signup form', () => { + cy.visit('/wp-admin/admin.php?page=mailchimp_sf_options'); + + // Set content options + const header = 'Subscribe to our newsletter'; + const subHeader = + 'Join our mailing list to receive the latest news and updates from our team.'; + const button = 'Subscribe Now'; + cy.get('#mc_header_content').clear().type(header); + cy.get('#mc_subheader_content').clear().type(subHeader); + cy.get('#mc_submit_text').clear().type(button); + cy.get('input[value="Update Subscribe Form Settings"]').first().click(); + + // Verify content options + cy.visit(`/?p=${shortcodePostId}`); + cy.get('.mc_custom_border_hdr').contains(header); + cy.get('#mc_subheader').contains(subHeader); + cy.get('#mc_signup_submit').contains(button); + + cy.visit(`/?p=${blockPostId}`); + cy.get('.mc_custom_border_hdr').contains(header); + cy.get('#mc_subheader').contains(subHeader); + cy.get('#mc_signup_submit').contains(button); + }); +}); From ee0884eb9695b8880e159eab2f4f63daf8ea0fe9 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Thu, 8 Aug 2024 18:06:56 +0530 Subject: [PATCH 33/48] Added tests for remove CSS and custom styling. --- tests/cypress/e2e/settings.test.js | 55 ++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/tests/cypress/e2e/settings.test.js b/tests/cypress/e2e/settings.test.js index 71350a0..51df7f1 100644 --- a/tests/cypress/e2e/settings.test.js +++ b/tests/cypress/e2e/settings.test.js @@ -80,4 +80,59 @@ describe('Admin can update plugin settings', () => { cy.get('#mc_subheader').contains(subHeader); cy.get('#mc_signup_submit').contains(button); }); + + it('Admin can remove mailchimp CSS', () => { + // Remove mailchimp CSS. + cy.visit('/wp-admin/admin.php?page=mailchimp_sf_options'); + cy.get('#mc_nuke_all_styles').check(); + cy.get('input[value="Update Subscribe Form Settings"]').first().click(); + + // Verify + cy.visit(`/?p=${shortcodePostId}`); + cy.get('#mc_subheader').should('not.have.css', 'margin-bottom', '18px'); + + cy.visit(`/?p=${blockPostId}`); + cy.get('#mc_subheader').should('not.have.css', 'margin-bottom', '18px'); + + // Enable mailchimp CSS. + cy.visit('/wp-admin/admin.php?page=mailchimp_sf_options'); + cy.get('#mc_nuke_all_styles').uncheck(); + cy.get('input[value="Update Subscribe Form Settings"]').first().click(); + + // Verify + cy.visit(`/?p=${shortcodePostId}`); + cy.get('#mc_subheader').should('have.css', 'margin-bottom', '18px'); + + cy.visit(`/?p=${blockPostId}`); + cy.get('#mc_subheader').should('have.css', 'margin-bottom', '18px'); + }); + + it('Admin can set custom styling on signup form', () => { + // Remove mailchimp CSS. + cy.visit('/wp-admin/admin.php?page=mailchimp_sf_options'); + cy.get('#mc_custom_style').check(); + cy.get('#mc_form_border_width').clear().type('10'); + cy.get('#mc_form_border_color').clear().type('000000'); + cy.get('#mc_form_text_color').clear().type('FF0000'); + cy.get('#mc_form_background').clear().type('00FF00'); + cy.get('input[value="Update Subscribe Form Settings"]').first().click(); + + // Verify + cy.visit(`/?p=${shortcodePostId}`); + cy.get('#mc_signup form').should('have.css', 'border-width', '10px'); + cy.get('#mc_signup form').should('have.css', 'border-color', 'rgb(0, 0, 0)'); + cy.get('#mc_signup form').should('have.css', 'color', 'rgb(255, 0, 0)'); + cy.get('#mc_signup form').should('have.css', 'background-color', 'rgb(0, 255, 0)'); + + cy.visit(`/?p=${blockPostId}`); + cy.get('#mc_signup form').should('have.css', 'border-width', '10px'); + cy.get('#mc_signup form').should('have.css', 'border-color', 'rgb(0, 0, 0)'); + cy.get('#mc_signup form').should('have.css', 'color', 'rgb(255, 0, 0)'); + cy.get('#mc_signup form').should('have.css', 'background-color', 'rgb(0, 255, 0)'); + + // Enable mailchimp CSS. + cy.visit('/wp-admin/admin.php?page=mailchimp_sf_options'); + cy.get('#mc_custom_style').uncheck(); + cy.get('input[value="Update Subscribe Form Settings"]').first().click(); + }); }); From 5bfe73e23042d858014304ba50c9a6aa540c8550 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Thu, 8 Aug 2024 18:45:34 +0530 Subject: [PATCH 34/48] Added some more settings tests. --- tests/cypress/e2e/settings.test.js | 118 +++++++++++++++++++++-------- 1 file changed, 86 insertions(+), 32 deletions(-) diff --git a/tests/cypress/e2e/settings.test.js b/tests/cypress/e2e/settings.test.js index 51df7f1..d19c76f 100644 --- a/tests/cypress/e2e/settings.test.js +++ b/tests/cypress/e2e/settings.test.js @@ -70,15 +70,12 @@ describe('Admin can update plugin settings', () => { cy.get('input[value="Update Subscribe Form Settings"]').first().click(); // Verify content options - cy.visit(`/?p=${shortcodePostId}`); - cy.get('.mc_custom_border_hdr').contains(header); - cy.get('#mc_subheader').contains(subHeader); - cy.get('#mc_signup_submit').contains(button); - - cy.visit(`/?p=${blockPostId}`); - cy.get('.mc_custom_border_hdr').contains(header); - cy.get('#mc_subheader').contains(subHeader); - cy.get('#mc_signup_submit').contains(button); + [shortcodePostId, blockPostId].forEach((postId) => { + cy.visit(`/?p=${postId}`); + cy.get('.mc_custom_border_hdr').contains(header); + cy.get('#mc_subheader').contains(subHeader); + cy.get('#mc_signup_submit').contains(button); + }); }); it('Admin can remove mailchimp CSS', () => { @@ -88,11 +85,10 @@ describe('Admin can update plugin settings', () => { cy.get('input[value="Update Subscribe Form Settings"]').first().click(); // Verify - cy.visit(`/?p=${shortcodePostId}`); - cy.get('#mc_subheader').should('not.have.css', 'margin-bottom', '18px'); - - cy.visit(`/?p=${blockPostId}`); - cy.get('#mc_subheader').should('not.have.css', 'margin-bottom', '18px'); + [shortcodePostId, blockPostId].forEach((postId) => { + cy.visit(`/?p=${postId}`); + cy.get('#mc_subheader').should('not.have.css', 'margin-bottom', '18px'); + }); // Enable mailchimp CSS. cy.visit('/wp-admin/admin.php?page=mailchimp_sf_options'); @@ -100,15 +96,14 @@ describe('Admin can update plugin settings', () => { cy.get('input[value="Update Subscribe Form Settings"]').first().click(); // Verify - cy.visit(`/?p=${shortcodePostId}`); - cy.get('#mc_subheader').should('have.css', 'margin-bottom', '18px'); - - cy.visit(`/?p=${blockPostId}`); - cy.get('#mc_subheader').should('have.css', 'margin-bottom', '18px'); + [shortcodePostId, blockPostId].forEach((postId) => { + cy.visit(`/?p=${postId}`); + cy.get('#mc_subheader').should('have.css', 'margin-bottom', '18px'); + }); }); it('Admin can set custom styling on signup form', () => { - // Remove mailchimp CSS. + // Enable custom styling and set values. cy.visit('/wp-admin/admin.php?page=mailchimp_sf_options'); cy.get('#mc_custom_style').check(); cy.get('#mc_form_border_width').clear().type('10'); @@ -118,21 +113,80 @@ describe('Admin can update plugin settings', () => { cy.get('input[value="Update Subscribe Form Settings"]').first().click(); // Verify - cy.visit(`/?p=${shortcodePostId}`); - cy.get('#mc_signup form').should('have.css', 'border-width', '10px'); - cy.get('#mc_signup form').should('have.css', 'border-color', 'rgb(0, 0, 0)'); - cy.get('#mc_signup form').should('have.css', 'color', 'rgb(255, 0, 0)'); - cy.get('#mc_signup form').should('have.css', 'background-color', 'rgb(0, 255, 0)'); - - cy.visit(`/?p=${blockPostId}`); - cy.get('#mc_signup form').should('have.css', 'border-width', '10px'); - cy.get('#mc_signup form').should('have.css', 'border-color', 'rgb(0, 0, 0)'); - cy.get('#mc_signup form').should('have.css', 'color', 'rgb(255, 0, 0)'); - cy.get('#mc_signup form').should('have.css', 'background-color', 'rgb(0, 255, 0)'); + [shortcodePostId, blockPostId].forEach((postId) => { + cy.visit(`/?p=${postId}`); + cy.get('#mc_signup form').should('have.css', 'border-width', '10px'); + cy.get('#mc_signup form').should('have.css', 'border-color', 'rgb(0, 0, 0)'); + cy.get('#mc_signup form').should('have.css', 'color', 'rgb(255, 0, 0)'); + cy.get('#mc_signup form').should('have.css', 'background-color', 'rgb(0, 255, 0)'); + }); - // Enable mailchimp CSS. + // Reset cy.visit('/wp-admin/admin.php?page=mailchimp_sf_options'); cy.get('#mc_custom_style').uncheck(); cy.get('input[value="Update Subscribe Form Settings"]').first().click(); }); + + it('Admin can set Merge Fields Included settings', () => { + // Remove mailchimp CSS. + cy.visit('/wp-admin/admin.php?page=mailchimp_sf_options'); + cy.get('#mc_mv_FNAME').uncheck(); + cy.get('#mc_mv_LNAME').uncheck(); + cy.get('input[value="Update Subscribe Form Settings"]').first().click(); + + // Verify + [shortcodePostId, blockPostId].forEach((postId) => { + cy.visit(`/?p=${postId}`); + cy.get('#mc_mv_FNAME').should('not.exist'); + cy.get('#mc_mv_LNAME').should('not.exist'); + }); + + // Reset + cy.visit('/wp-admin/admin.php?page=mailchimp_sf_options'); + cy.get('#mc_mv_FNAME').check(); + cy.get('#mc_mv_LNAME').check(); + cy.get('input[value="Update Subscribe Form Settings"]').first().click(); + + // Verify + [shortcodePostId, blockPostId].forEach((postId) => { + cy.visit(`/?p=${postId}`); + cy.get('#mc_mv_FNAME').should('exist'); + cy.get('#mc_mv_LNAME').should('exist'); + }); + }); + + it('Admin can set list options settings', () => { + // Remove mailchimp CSS. + cy.visit('/wp-admin/admin.php?page=mailchimp_sf_options'); + cy.get('#mc_use_javascript').uncheck(); + cy.get('#mc_use_datepicker').uncheck(); + cy.get('#mc_use_unsub_link').check(); + cy.get('input[value="Update Subscribe Form Settings"]').first().click(); + + // Verify + [shortcodePostId, blockPostId].forEach((postId) => { + cy.visit(`/?p=${postId}`); + cy.get('#mc_submit_type').should('have.value', 'html'); + cy.get('#mc_mv_BIRTHDAY').should('not.have.class', 'hasDatepicker'); + cy.get('#mc_mv_BIRTHDAY').click(); + cy.get('#ui-datepicker-div').should('not.exist'); + cy.get('#mc_unsub_link').should('exist'); + }); + + // Reset + cy.visit('/wp-admin/admin.php?page=mailchimp_sf_options'); + cy.get('#mc_use_javascript').check(); + cy.get('#mc_use_datepicker').check(); + cy.get('#mc_use_unsub_link').uncheck(); + cy.get('input[value="Update Subscribe Form Settings"]').first().click(); + + [shortcodePostId, blockPostId].forEach((postId) => { + cy.visit(`/?p=${postId}`); + cy.get('#mc_submit_type').should('have.value', 'js'); + cy.get('#mc_mv_BIRTHDAY').should('have.class', 'hasDatepicker'); + cy.get('#mc_mv_BIRTHDAY').click(); + cy.get('#ui-datepicker-div').should('exist'); + cy.get('#mc_unsub_link').should('not.exist'); + }); + }); }); From a63ad6072a678bbb44994765c7bd2a54b87f7b60 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Thu, 8 Aug 2024 18:51:12 +0530 Subject: [PATCH 35/48] Add logout tests. --- tests/cypress/e2e/settings.test.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/cypress/e2e/settings.test.js b/tests/cypress/e2e/settings.test.js index d19c76f..8d06de2 100644 --- a/tests/cypress/e2e/settings.test.js +++ b/tests/cypress/e2e/settings.test.js @@ -189,4 +189,13 @@ describe('Admin can update plugin settings', () => { cy.get('#mc_unsub_link').should('not.exist'); }); }); + + it('Admin can logout', () => { + cy.visit('/wp-admin/admin.php?page=mailchimp_sf_options'); + cy.get('#mailchimp_sf_oauth_connect').should('not.exist'); + cy.get('input[value="Logout"]').click(); + + // connect to "Mailchimp" Account button should be visible. + cy.get('#mailchimp_sf_oauth_connect').should('exist'); + }); }); From 91f09c2b9a23549dddefe035050b6b804f688fc5 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Thu, 8 Aug 2024 19:08:48 +0530 Subject: [PATCH 36/48] Updated E2E workflow file. --- .github/workflows/e2e.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index ec60018..db40edc 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -13,6 +13,9 @@ jobs: cypress: name: ${{ matrix.core.name }} runs-on: ubuntu-latest + env: + CYPRESS_MAILCHIMP_USERNAME: ${{ secrets.MAILCHIMP_USERNAME }} + CYPRESS_MAILCHIMP_PASSWORD: ${{ secrets.MAILCHIMP_PASSWORD }} strategy: matrix: core: From 6e7dc0830a682835b4965918ba2eaad9192c5147 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Thu, 8 Aug 2024 19:09:01 +0530 Subject: [PATCH 37/48] Readme updates. --- README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/README.md b/README.md index 41e20b0..35fa33e 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,23 @@ Currently we have the plugin configured so it can be translated and the followin * sv_SE - Swedish in Sweden (thanks to [Sebastian Johnsson](http://www.agiley.se/) for contributing) * tr_TR - Turkish in Turkey (thanks to [Hakan E.](http://kazancexpert.com/) for contributing) +## E2E tests +The `tests` directory contains end-to-end tests for the project, utilizing Cypress to run tests in an environment created using wp-env. + +### Pre-requisites +- Node.js v20 +- Docker +- Create an account in [Mailchimp](https://mailchimp.com/) + +### Run E2E tests in local +1. Run `npm install`. +2. Run `npm run build`. +3. Run `npm run env:start`. +4. Set Mailchimp credentials as environment variables: + - run `export CYPRESS_MAILCHIMP_USERNAME="your mailchimp username"` + - run `export CYPRESS_MAILCHIMP_PASSWORD="your mailchimp password"` +5. Run `npm run cypress:run`. You can also run `npm run cypress:open` to run tests in UI mode. + ## Support Level **Active:** Mailchimp is actively working on this, and we expect to continue work for the foreseeable future including keeping tested up to the most recent version of WordPress. Bug reports, feature requests, questions, and pull requests are welcome. From a1c810284ee6a6ee85f10dbe148be54996d06e0d Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Thu, 8 Aug 2024 20:04:11 +0530 Subject: [PATCH 38/48] Trigger E2E tests. --- tests/cypress/e2e/connect.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cypress/e2e/connect.test.js b/tests/cypress/e2e/connect.test.js index 79f027d..411bcd9 100644 --- a/tests/cypress/e2e/connect.test.js +++ b/tests/cypress/e2e/connect.test.js @@ -29,7 +29,7 @@ describe('Admin can connect to "Mailchimp" Account', () => { cy.wait(8000); // Not a best practice, but did not find a better way to handle this. cy.popup().find('input#submitButton').click(); - cy.wait(10000); + cy.wait(10000); // Not a best practice, but did not find a better way to handle this. cy.get('.mc-user h3').contains('Logged in as: '); cy.get('input[value="Logout"]').should('exist'); From 554fa372ba3fb9d8e91333053320adb5a45c328d Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Thu, 8 Aug 2024 20:28:02 +0530 Subject: [PATCH 39/48] Add block name in insertBlock command. --- tests/cypress/e2e/connect.test.js | 4 ++-- tests/cypress/e2e/settings.test.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/cypress/e2e/connect.test.js b/tests/cypress/e2e/connect.test.js index 411bcd9..e29942a 100644 --- a/tests/cypress/e2e/connect.test.js +++ b/tests/cypress/e2e/connect.test.js @@ -21,12 +21,12 @@ describe('Admin can connect to "Mailchimp" Account', () => { cy.capturePopup(); cy.get('#mailchimp_sf_oauth_connect').click(); - cy.wait(5000); + cy.wait(6000); cy.popup().find('input#username').clear().type(Cypress.env('MAILCHIMP_USERNAME')); cy.popup().find('input#password').clear().type(Cypress.env('MAILCHIMP_PASSWORD')); cy.popup().find('button[type="submit"]').click(); - cy.wait(8000); // Not a best practice, but did not find a better way to handle this. + cy.wait(10000); // Not a best practice, but did not find a better way to handle this. cy.popup().find('input#submitButton').click(); cy.wait(10000); // Not a best practice, but did not find a better way to handle this. diff --git a/tests/cypress/e2e/settings.test.js b/tests/cypress/e2e/settings.test.js index 8d06de2..a6d411b 100644 --- a/tests/cypress/e2e/settings.test.js +++ b/tests/cypress/e2e/settings.test.js @@ -40,7 +40,7 @@ describe('Admin can update plugin settings', () => { it('Admin can create a Signup form using Mailchimp block', () => { const postTitle = 'Mailchimp signup form - Block'; const beforeSave = () => { - cy.insertBlock('mailchimp/mailchimp'); + cy.insertBlock('mailchimp/mailchimp', 'Mailchimp List Subscribe Form'); }; cy.createPost({ title: postTitle, content: '', beforeSave }).then((post) => { if (post) { From 57c0058b8ea70d13a3970992a25fc9bfc4e6b2b4 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Fri, 9 Aug 2024 12:44:53 +0530 Subject: [PATCH 40/48] Addressed improvements feedback. --- includes/class-mailchimp-admin.php | 2 +- lib/mailchimp/mailchimp.php | 2 ++ mailchimp.php | 10 ++++++++++ mailchimp_widget.php | 4 ++++ views/setup_page.php | 2 +- 5 files changed, 18 insertions(+), 2 deletions(-) diff --git a/includes/class-mailchimp-admin.php b/includes/class-mailchimp-admin.php index 1a243a9..0322d52 100644 --- a/includes/class-mailchimp-admin.php +++ b/includes/class-mailchimp-admin.php @@ -206,7 +206,7 @@ public function admin_notices() { // Display a notice if the access token is invalid/revoked. ?> -

+

- +
From c633d39d877f8293698bfd9ea34143706b4ca0c8 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Fri, 9 Aug 2024 13:30:50 +0530 Subject: [PATCH 41/48] Some improvements in settings tests. --- tests/cypress/e2e/settings.test.js | 48 +++++++++++++++--------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/tests/cypress/e2e/settings.test.js b/tests/cypress/e2e/settings.test.js index a6d411b..6b78c6b 100644 --- a/tests/cypress/e2e/settings.test.js +++ b/tests/cypress/e2e/settings.test.js @@ -1,7 +1,7 @@ /* eslint-disable no-undef */ describe('Admin can update plugin settings', () => { - let shortcodePostId = 0; - let blockPostId = 0; + let shortcodePostURL = '/mailchimp-signup-form-shortcode'; + let blockPostPostURL = '/mailchimp-signup-form-block'; before(() => { cy.login(); @@ -25,8 +25,8 @@ describe('Admin can update plugin settings', () => { }; cy.createPost({ title: postTitle, content: '', beforeSave }).then((post) => { if (post) { - shortcodePostId = post.id; - cy.visit(`/?p=${shortcodePostId}`); + shortcodePostURL = `/?p=${post.id}`; + cy.visit(shortcodePostURL); cy.get('#mc_signup').should('exist'); cy.get('#mc_mv_EMAIL').should('exist'); cy.get('#mc_signup_submit').should('exist'); @@ -42,10 +42,10 @@ describe('Admin can update plugin settings', () => { const beforeSave = () => { cy.insertBlock('mailchimp/mailchimp', 'Mailchimp List Subscribe Form'); }; - cy.createPost({ title: postTitle, content: '', beforeSave }).then((post) => { - if (post) { - blockPostId = post.id; - cy.visit(`/?p=${shortcodePostId}`); + cy.createPost({ title: postTitle, content: '', beforeSave }).then((postBlock) => { + if (postBlock) { + blockPostPostURL = `/?p=${postBlock.id}`; + cy.visit(blockPostPostURL); cy.get('#mc_signup').should('exist'); cy.get('#mc_mv_EMAIL').should('exist'); cy.get('#mc_signup_submit').should('exist'); @@ -70,8 +70,8 @@ describe('Admin can update plugin settings', () => { cy.get('input[value="Update Subscribe Form Settings"]').first().click(); // Verify content options - [shortcodePostId, blockPostId].forEach((postId) => { - cy.visit(`/?p=${postId}`); + [shortcodePostURL, blockPostPostURL].forEach((url) => { + cy.visit(url); cy.get('.mc_custom_border_hdr').contains(header); cy.get('#mc_subheader').contains(subHeader); cy.get('#mc_signup_submit').contains(button); @@ -85,8 +85,8 @@ describe('Admin can update plugin settings', () => { cy.get('input[value="Update Subscribe Form Settings"]').first().click(); // Verify - [shortcodePostId, blockPostId].forEach((postId) => { - cy.visit(`/?p=${postId}`); + [shortcodePostURL, blockPostPostURL].forEach((url) => { + cy.visit(url); cy.get('#mc_subheader').should('not.have.css', 'margin-bottom', '18px'); }); @@ -96,8 +96,8 @@ describe('Admin can update plugin settings', () => { cy.get('input[value="Update Subscribe Form Settings"]').first().click(); // Verify - [shortcodePostId, blockPostId].forEach((postId) => { - cy.visit(`/?p=${postId}`); + [shortcodePostURL, blockPostPostURL].forEach((url) => { + cy.visit(url); cy.get('#mc_subheader').should('have.css', 'margin-bottom', '18px'); }); }); @@ -113,8 +113,8 @@ describe('Admin can update plugin settings', () => { cy.get('input[value="Update Subscribe Form Settings"]').first().click(); // Verify - [shortcodePostId, blockPostId].forEach((postId) => { - cy.visit(`/?p=${postId}`); + [shortcodePostURL, blockPostPostURL].forEach((url) => { + cy.visit(url); cy.get('#mc_signup form').should('have.css', 'border-width', '10px'); cy.get('#mc_signup form').should('have.css', 'border-color', 'rgb(0, 0, 0)'); cy.get('#mc_signup form').should('have.css', 'color', 'rgb(255, 0, 0)'); @@ -135,8 +135,8 @@ describe('Admin can update plugin settings', () => { cy.get('input[value="Update Subscribe Form Settings"]').first().click(); // Verify - [shortcodePostId, blockPostId].forEach((postId) => { - cy.visit(`/?p=${postId}`); + [shortcodePostURL, blockPostPostURL].forEach((url) => { + cy.visit(url); cy.get('#mc_mv_FNAME').should('not.exist'); cy.get('#mc_mv_LNAME').should('not.exist'); }); @@ -148,8 +148,8 @@ describe('Admin can update plugin settings', () => { cy.get('input[value="Update Subscribe Form Settings"]').first().click(); // Verify - [shortcodePostId, blockPostId].forEach((postId) => { - cy.visit(`/?p=${postId}`); + [shortcodePostURL, blockPostPostURL].forEach((url) => { + cy.visit(url); cy.get('#mc_mv_FNAME').should('exist'); cy.get('#mc_mv_LNAME').should('exist'); }); @@ -164,8 +164,8 @@ describe('Admin can update plugin settings', () => { cy.get('input[value="Update Subscribe Form Settings"]').first().click(); // Verify - [shortcodePostId, blockPostId].forEach((postId) => { - cy.visit(`/?p=${postId}`); + [shortcodePostURL, blockPostPostURL].forEach((url) => { + cy.visit(url); cy.get('#mc_submit_type').should('have.value', 'html'); cy.get('#mc_mv_BIRTHDAY').should('not.have.class', 'hasDatepicker'); cy.get('#mc_mv_BIRTHDAY').click(); @@ -180,8 +180,8 @@ describe('Admin can update plugin settings', () => { cy.get('#mc_use_unsub_link').uncheck(); cy.get('input[value="Update Subscribe Form Settings"]').first().click(); - [shortcodePostId, blockPostId].forEach((postId) => { - cy.visit(`/?p=${postId}`); + [shortcodePostURL, blockPostPostURL].forEach((url) => { + cy.visit(url); cy.get('#mc_submit_type').should('have.value', 'js'); cy.get('#mc_mv_BIRTHDAY').should('have.class', 'hasDatepicker'); cy.get('#mc_mv_BIRTHDAY').click(); From 1caefec31ad3cc1a6f85332955576890fa25f6a7 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Fri, 9 Aug 2024 15:07:51 +0530 Subject: [PATCH 42/48] Fix shortcode form create issue. --- tests/cypress/e2e/settings.test.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/cypress/e2e/settings.test.js b/tests/cypress/e2e/settings.test.js index 6b78c6b..ceb4406 100644 --- a/tests/cypress/e2e/settings.test.js +++ b/tests/cypress/e2e/settings.test.js @@ -20,7 +20,10 @@ describe('Admin can update plugin settings', () => { const postTitle = 'Mailchimp signup form - shortcode'; const beforeSave = () => { cy.insertBlock('core/shortcode').then((id) => { - cy.get(`#${id} .blocks-shortcode__textarea`).type('[mailchimpsf_form]'); + cy.getBlockEditor() + .find(`#${id} .blocks-shortcode__textarea`) + .clear() + .type('[mailchimpsf_form]'); }); }; cy.createPost({ title: postTitle, content: '', beforeSave }).then((post) => { @@ -41,6 +44,7 @@ describe('Admin can update plugin settings', () => { const postTitle = 'Mailchimp signup form - Block'; const beforeSave = () => { cy.insertBlock('mailchimp/mailchimp', 'Mailchimp List Subscribe Form'); + cy.wait(500); }; cy.createPost({ title: postTitle, content: '', beforeSave }).then((postBlock) => { if (postBlock) { From 86e94893c12a2e083f8f1cd2bf4eb9bd3be9e070 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Fri, 9 Aug 2024 15:13:55 +0530 Subject: [PATCH 43/48] Add retry in run mode. --- tests/cypress/config.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/cypress/config.js b/tests/cypress/config.js index b881418..0720819 100644 --- a/tests/cypress/config.js +++ b/tests/cypress/config.js @@ -25,6 +25,10 @@ module.exports = defineConfig( { supportFile: 'tests/cypress/support/index.js', defaultCommandTimeout: 20000, }, + retries: { + runMode: 2, + openMode: 0, + }, } ); /** From 3921ba60c98119508ad75c0b1a0ed0e3216addf9 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Fri, 9 Aug 2024 15:31:59 +0530 Subject: [PATCH 44/48] Update E2E workflow to use zip built by generate zip action. --- .github/workflows/e2e.yml | 19 ++++++++++++-- tests/bin/set-core-version.js | 48 ++++++++++++++++++++++------------- 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index db40edc..9985810 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -10,7 +10,11 @@ on: - develop jobs: + build: + uses: mailchimp/wordpress/.github/workflows/build-release-zip.yml@develop + cypress: + needs: build name: ${{ matrix.core.name }} runs-on: ubuntu-latest env: @@ -27,6 +31,16 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - name: Download build zip + uses: actions/download-artifact@v4 + with: + name: ${{ github.event.repository.name }} + path: ${{ github.event.repository.name }} + + - name: Display structure of downloaded files + run: ls -R + working-directory: ${{ github.event.repository.name }} + - uses: actions/setup-node@v4 with: node-version-file: '.nvmrc' @@ -34,8 +48,8 @@ jobs: - name: Install dependencies run: npm ci - - name: Set the core version - run: ./tests/bin/set-core-version.js ${{ matrix.core.version }} + - name: Set the core version and plugins config + run: ./tests/bin/set-core-version.js --core=${{ matrix.core.version }} --plugins=./${{ github.event.repository.name }} - name: Set up WP environment run: npm run env:start @@ -44,6 +58,7 @@ jobs: run: npm run cypress:run - name: Update summary + if: always() run: | npx mochawesome-merge ./tests/cypress/reports/*.json -o tests/cypress/reports/mochawesome.json rm -rf ./tests/cypress/reports/mochawesome-*.json diff --git a/tests/bin/set-core-version.js b/tests/bin/set-core-version.js index f320701..abe61c4 100755 --- a/tests/bin/set-core-version.js +++ b/tests/bin/set-core-version.js @@ -1,34 +1,46 @@ #!/usr/bin/env node const fs = require( 'fs' ); -const { exit } = require( 'process' ); -const path = `${ process.cwd() }/.wp-env.override.json`; +const path = `${ process.cwd() }/.wp-env.json`; -// eslint-disable-next-line import/no-dynamic-require -const config = fs.existsSync( path ) ? require( path ) : {}; +let config = fs.existsSync( path ) ? require( path ) : { plugins: [ '.' ] }; -const args = process.argv.slice( 2 ); +const args = {}; +process.argv + .slice(2, process.argv.length) + .forEach( arg => { + if (arg.slice(0,2) === '--') { + const param = arg.split('='); + const paramName = param[0].slice(2,param[0].length); + const paramValue = param.length > 1 ? param[1] : true; + args[paramName] = paramValue; + } + }); -if ( args.length === 0 ) exit( 0 ); +if ( ! args.core && ! args.plugins ) { + return; +} + +if ( 'latest' === args.core ) { + delete args.core; +} -if ( args[ 0 ] === 'latest' ) { - if ( fs.existsSync( path ) ) { - fs.unlinkSync( path ); - } - exit( 0 ); +if( Object.keys(args).length === 0 ) { + return; } -config.core = args[ 0 ]; +if ( args.plugins ) { + args.plugins = args.plugins.split(','); +} -// eslint-disable-next-line no-useless-escape -if ( ! config.core.match( /^WordPress\/WordPress\#/ ) ) { - config.core = `WordPress/WordPress#${ config.core }`; +config = { + ...config, + ...args, } try { - fs.writeFileSync( path, JSON.stringify( config ) ); + fs.writeFileSync( path, JSON.stringify( config ) ); } catch ( err ) { - // eslint-disable-next-line no-console - console.error( err ); + console.error( err ); } From 4fe1d6e88c0f8ecd792c6beb20987771fa0f7643 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Fri, 9 Aug 2024 15:56:48 +0530 Subject: [PATCH 45/48] Try fix connect tests in trunk env. --- tests/cypress/e2e/connect.test.js | 14 ++++++++++---- tests/cypress/support/commands.js | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/tests/cypress/e2e/connect.test.js b/tests/cypress/e2e/connect.test.js index e29942a..89c71bf 100644 --- a/tests/cypress/e2e/connect.test.js +++ b/tests/cypress/e2e/connect.test.js @@ -23,12 +23,18 @@ describe('Admin can connect to "Mailchimp" Account', () => { cy.get('#mailchimp_sf_oauth_connect').click(); cy.wait(6000); - cy.popup().find('input#username').clear().type(Cypress.env('MAILCHIMP_USERNAME')); - cy.popup().find('input#password').clear().type(Cypress.env('MAILCHIMP_PASSWORD')); - cy.popup().find('button[type="submit"]').click(); + cy.popup() + .find('input#username') + .clear() + .type(Cypress.env('MAILCHIMP_USERNAME'), { force: true }); + cy.popup() + .find('input#password') + .clear() + .type(Cypress.env('MAILCHIMP_PASSWORD'), { force: true }); + cy.popup().find('button[type="submit"]').click({ force: true }); cy.wait(10000); // Not a best practice, but did not find a better way to handle this. - cy.popup().find('input#submitButton').click(); + cy.popup().find('input#submitButton').click({ force: true }); cy.wait(10000); // Not a best practice, but did not find a better way to handle this. cy.get('.mc-user h3').contains('Logged in as: '); diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js index 95025e3..8d89823 100644 --- a/tests/cypress/support/commands.js +++ b/tests/cypress/support/commands.js @@ -1,3 +1,4 @@ +/* eslint-disable no-undef */ // *********************************************** // This example commands.js shows you how to // create various custom commands and overwrite @@ -44,7 +45,6 @@ Cypress.Commands.add('capturePopup', () => { * Returns a wrapped body of a captured popup */ Cypress.Commands.add('popup', () => { - console.log('state', state.popup); const popup = Cypress.$(state.popup.document); return cy.wrap(popup.contents().find('body')); }); From 41757747b0c5453996822eb5aaac3b95e2860ab6 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Tue, 13 Aug 2024 11:27:07 +0530 Subject: [PATCH 46/48] Add admin notice for the API key deprecation. --- includes/class-mailchimp-admin.php | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/includes/class-mailchimp-admin.php b/includes/class-mailchimp-admin.php index 0322d52..1a3ebc1 100644 --- a/includes/class-mailchimp-admin.php +++ b/includes/class-mailchimp-admin.php @@ -196,9 +196,32 @@ public function verify_and_save_oauth_token( $access_token, $data_center ) { * @since x.x.x */ public function admin_notices() { + if ( ! current_user_can( 'manage_options' ) ) { + return; + } + + // Display a deprecation notice if the user is using an API key to connect with Mailchimp. + if ( get_option( 'mc_api_key', '' ) && ! get_option( 'mailchimp_sf_access_token', '' ) && mailchimp_sf_should_display_form() ) { + ?> +
+

+ tag, %2$s - tag */ + __( 'Heads up! It looks like you\'re using an API key to connect with Mailchimp, which is now deprecated. Please log out and reconnect your Mailchimp account using the new OAuth authentication by clicking the "Connect Account" button on the %1$splugin settings%2$s page.', 'mailchimp' ), + '', + '' + ); + + echo wp_kses( $message, array( 'a' => array( 'href' => array() ) ) ); + ?> +

+
+ Date: Tue, 13 Aug 2024 10:50:36 -0600 Subject: [PATCH 47/48] Update since statements --- includes/class-mailchimp-admin.php | 6 +++--- includes/class-mailchimp-data-encryption.php | 16 ++++++++-------- mailchimp.php | 4 ++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/includes/class-mailchimp-admin.php b/includes/class-mailchimp-admin.php index 1a3ebc1..26a62c3 100644 --- a/includes/class-mailchimp-admin.php +++ b/includes/class-mailchimp-admin.php @@ -13,14 +13,14 @@ /** * Class Mailchimp_Admin * - * @since x.x.x + * @since 1.6.0 */ class Mailchimp_Admin { /** * The OAuth base endpoint * - * @since x.x.x + * @since 1.6.0 * @var string */ private $oauth_url = 'https://woocommerce.mailchimpapp.com'; @@ -193,7 +193,7 @@ public function verify_and_save_oauth_token( $access_token, $data_center ) { /** * Display admin notices. * - * @since x.x.x + * @since 1.6.0 */ public function admin_notices() { if ( ! current_user_can( 'manage_options' ) ) { diff --git a/includes/class-mailchimp-data-encryption.php b/includes/class-mailchimp-data-encryption.php index 3853bea..85fcb15 100644 --- a/includes/class-mailchimp-data-encryption.php +++ b/includes/class-mailchimp-data-encryption.php @@ -13,14 +13,14 @@ /** * Class Mailchimp_Data_Encryption * - * @since x.x.x + * @since 1.6.0 */ class Mailchimp_Data_Encryption { /** * Key to use for encryption. * - * @since x.x.x + * @since 1.6.0 * @var string */ private $key; @@ -28,7 +28,7 @@ class Mailchimp_Data_Encryption { /** * Salt to use for encryption. * - * @since x.x.x + * @since 1.6.0 * @var string */ private $salt; @@ -36,7 +36,7 @@ class Mailchimp_Data_Encryption { /** * Constructor. * - * @since x.x.x + * @since 1.6.0 */ public function __construct() { $this->key = $this->get_default_key(); @@ -48,7 +48,7 @@ public function __construct() { * * If a user-based key is set, that is used. Otherwise the default key is used. * - * @since x.x.x + * @since 1.6.0 * * @param string $value Value to encrypt. * @return string|bool Encrypted value, or false on failure. @@ -75,7 +75,7 @@ public function encrypt( $value ) { * * If a user-based key is set, that is used. Otherwise the default key is used. * - * @since x.x.x + * @since 1.6.0 * * @param string $raw_value Value to decrypt. * @return string|bool Decrypted value, or false on failure. @@ -108,7 +108,7 @@ public function decrypt( $raw_value ) { /** * Gets the default encryption key to use. * - * @since x.x.x + * @since 1.6.0 * * @return string Default (not user-based) encryption key. */ @@ -128,7 +128,7 @@ private function get_default_key() { /** * Gets the default encryption salt to use. * - * @since x.x.x + * @since 1.6.0 * * @return string Encryption salt. */ diff --git a/mailchimp.php b/mailchimp.php index 2b5daa2..8150397 100644 --- a/mailchimp.php +++ b/mailchimp.php @@ -1376,7 +1376,7 @@ function mailchimp_sf_create_nonce( $action = -1 ) { /** * Get Mailchimp Access Token. * - * @since x.x.x + * @since 1.6.0 * @return string|bool */ function mailchimp_sf_get_access_token() { @@ -1399,7 +1399,7 @@ function mailchimp_sf_get_access_token() { /** * Should display Mailchimp Signup form. * - * @since x.x.x + * @since 1.6.0 * @return bool */ function mailchimp_sf_should_display_form() { From a3d10a982f2fefe755f5f2fa2cad4440a590c52c Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Tue, 20 Aug 2024 18:26:47 +0530 Subject: [PATCH 48/48] Make the API authentication error message more general. --- lib/mailchimp/mailchimp.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/mailchimp/mailchimp.php b/lib/mailchimp/mailchimp.php index 39861b5..e3bb099 100644 --- a/lib/mailchimp/mailchimp.php +++ b/lib/mailchimp/mailchimp.php @@ -128,6 +128,7 @@ public function get( $endpoint, $count = 10, $fields = array() ) { // Check if Access Token is invalid/revoked. if ( in_array( $request['response']['code'], array( 401, 403 ), true ) ) { update_option( 'mailchimp_sf_auth_error', true ); + return new WP_Error( 'mailchimp-auth-error', esc_html__( 'Authentication failed.', 'mailchimp' ) ); } $error = json_decode( $request['body'], true );