From ed57a66d6cd8eb52efee42a6ff471b91f3ae3b88 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rapha=C3=ABl=20Droz?=
Date: Mon, 20 Mar 2023 11:37:21 -0300
Subject: [PATCH] 5.2
---
change_log.txt | 23 +-
class-gf-mailchimp.php | 566 ++++++++++++++++++++++++----
css/form_settings.css | 20 +-
css/form_settings.min.css | 2 +-
includes/class-gf-mailchimp-api.php | 100 +++--
mailchimp.php | 6 +-
6 files changed, 608 insertions(+), 109 deletions(-)
diff --git a/change_log.txt b/change_log.txt
index b2708aa..2c644fb 100644
--- a/change_log.txt
+++ b/change_log.txt
@@ -1,3 +1,24 @@
+### 5.2 | 2023-02-15
+- Added Gravity Forms license key to oAuth process.
+- Added support for async (background) feed processing to improve form submission performance.
+- Fixed an issue that prevents the Marketing Permission setting from being applied to users in certain situations.
+- Fixed an issue where the opt-in email is not sending to already pending members when they resubscribe.
+
+### 5.1 | 2022-05-11
+- Fixed an issue where the save settings button isn't visible when creating or editing a feed.
+- Fixed a display issue with some conditional logic feed settings.
+- Fixed an issue where API calls are being made on all admin pages when checking for deprecated keys.
+
+
+### 5.0 | 2021-09-29
+- Updated the authorization flow to connect to the Mailchimp API via Oauth.
+
+
+### 4.9 | 2021-04-28
+- Fixed an issue where conditional logic is not correctly identifying matching selections when forms contain a multi-select field.
+- Fixed an issue where the add-on icon is missing on the form settings screen in Gravity Forms 2.5.
+
+
### 4.8 | 2020-09-09
- Added support for Gravity Forms 2.5.
- Fixed birthday merge fields no longer being sent in the correct format expected by the Mailchimp API.
@@ -298,4 +319,4 @@
- Split logic from Feeds Add-On.
- Implemented automatic upgrade.
- Implemented list page checkboxes (for bulk actions).
-- Implemented active/inactive icons on list page.
\ No newline at end of file
+- Implemented active/inactive icons on list page.
diff --git a/class-gf-mailchimp.php b/class-gf-mailchimp.php
index 588f972..0fc5856 100755
--- a/class-gf-mailchimp.php
+++ b/class-gf-mailchimp.php
@@ -17,6 +17,8 @@
*/
class GFMailChimp extends GFFeedAddOn {
+ const POST_ACTION = 'gravityformsmailchimp_disconnect';
+
/**
* Contains an instance of this class, if available.
*
@@ -26,6 +28,15 @@ class GFMailChimp extends GFFeedAddOn {
*/
private static $_instance = null;
+ /**
+ * Enabling background feed processing.
+ *
+ * @since 5.1.1
+ *
+ * @var bool
+ */
+ protected $_async_feed_processing = true;
+
/**
* Defines the version of the Mailchimp Add-On.
*
@@ -231,6 +242,23 @@ public function init() {
}
+ /**
+ * Add actions for admin_init
+ *
+ * @since 4.10
+ *
+ * @return void
+ */
+ public function init_admin() {
+ parent::init_admin();
+
+ add_action( 'admin_init', array( $this, 'maybe_update_auth_creds' ) );
+ if ( GFForms::is_gravity_page() ) {
+ add_action( 'admin_init', array( $this, 'warn_for_deprecated_key' ) );
+ }
+ add_action( 'admin_post_' . self::POST_ACTION, array( $this, 'handle_disconnection' ) );
+ }
+
/**
* Remove unneeded settings.
*
@@ -260,11 +288,14 @@ public function styles() {
$styles = array(
array(
- 'handle' => $this->_slug . '_form_settings',
+ 'handle' => 'gravityformsmailchimp_form_settings',
'src' => $this->get_base_url() . "/css/form_settings{$min}.css",
'version' => $this->_version,
'enqueue' => array(
- array( 'admin_page' => array( 'form_settings' ) ),
+ array(
+ 'admin_page' => array( 'plugin_settings', 'form_settings' ),
+ 'tab' => $this->_slug,
+ ),
),
),
);
@@ -282,13 +313,119 @@ public function styles() {
*/
public function get_menu_icon() {
- return file_get_contents( $this->get_base_path() . '/images/menu-icon.svg' );
+ return $this->is_gravityforms_supported( '2.5-beta-4' ) ? 'gform-icon--mailchimp' : 'dashicons-admin-generic';
+
+ }
+
+
+ // # OAUTH SETTINGS -----------------------------------------------------------------------------------------------
+
+ /**
+ * Get the authorization payload data.
+ *
+ * Returns the auth POST request if it's present, otherwise attempts to return a recent transient cache.
+ *
+ * @since 3.10
+ *
+ * @return array
+ */
+ private function get_oauth_payload() {
+ $payload = array_filter(
+ array(
+ 'auth_payload' => rgpost( 'auth_payload' ),
+ 'state' => rgpost( 'state' ),
+ 'auth_error' => rgpost( 'auth_error' ),
+ )
+ );
+
+ if ( count( $payload ) === 2 || isset( $payload['auth_error'] ) ) {
+ return $payload;
+ }
+
+ $payload = get_transient( "gravityapi_response_{$this->_slug}" );
+ if ( ! is_array( $payload ) ) {
+ return array();
+ }
+
+ delete_transient( "gravityapi_response_{$this->_slug}" );
+
+ return $payload;
}
+ /**
+ * Update Auth Creds if they have changed.
+ *
+ * @since 4.10
+ *
+ * @return void
+ */
+ public function maybe_update_auth_creds() {
+ $payload = $this->get_oauth_payload();
+ // No payload, bail.
+ if ( empty( $payload ) ) {
+ return;
+ }
+
+ // Auth Error form API - log and bail.
+ if ( isset( $payload['auth_error'] ) ) {
+ $this->add_error_notice( __METHOD__, 'error authenticating with the API Server' );
+
+ return;
+ }
+
+ $state = $payload['state'];
+ $auth_payload = json_decode( $payload['auth_payload'], true );
+ // State didn't pass our nonce - log and bail.
+ if ( $state !== get_transient( "gravityapi_request_{$this->_slug}" ) ) {
+ $this->add_error_notice( __METHOD__, 'could not verify the state value from the API Server.' );
+ return;
+ }
+
+ // Incorrect/missing auth data - log and bail.
+ if ( ! isset( $auth_payload['access_token'] ) || ! isset( $auth_payload['server_prefix'] ) ) {
+ $this->add_error_notice( __METHOD__, 'missing access_token or server_prefix in API response.' );
+
+ return;
+ }
+
+ // Store the auth payload.
+ $this->update_plugin_settings( $auth_payload );
+ }
+
+ /**
+ * Add an error notice to admin if something goes awry. Also logs error to error_log.
+ *
+ * @since 4.10
+ *
+ * @param string $method The method being called.
+ * @param string $message The message to display.
+ *
+ * @return void
+ */
+ private function add_error_notice( $method, $message ) {
+ add_action( 'admin_notices', function () {
+ $message = __( 'Could not authenticate with Mailchimp.', 'gravityformsmailchimp' );
+
+ printf( '', esc_html( $message ) );
+ } );
+
+ $this->log_error( $method . ': ' . $message );
+ }
+
+ /**
+ * Get the authentication state, which was created from a wp nonce.
+ *
+ * @since 4.10
+ *
+ * @return string
+ */
+ private function get_authentication_state_action() {
+ return 'gform_mailchimp_authentication_state';
+ }
// # PLUGIN SETTINGS -----------------------------------------------------------------------------------------------
@@ -304,19 +441,20 @@ public function plugin_settings_fields() {
return array(
array(
- 'description' => '' .
- sprintf(
- esc_html__( 'Mailchimp makes it easy to send email newsletters to your customers, manage your subscriber audiences, and track campaign performance. Use Gravity Forms to collect customer information and automatically add it to your Mailchimp subscriber audience. If you don\'t have a Mailchimp account, you can %1$ssign up for one here.%2$s', 'gravityformsmailchimp' ),
- '', ''
- )
- . '
',
+ // translators: %1 is an opening tag, and %2 is a closing tag.
+ 'description' => '' . sprintf( esc_html__( 'Mailchimp makes it easy to send email newsletters to your customers, manage your subscriber audiences, and track campaign performance. Use Gravity Forms to collect customer information and automatically add it to your Mailchimp subscriber audience. If you don\'t have a Mailchimp account, you can %1$ssign up for one here.%2$s', 'gravityformsmailchimp' ), '', '' ) . '
',
'fields' => array(
+
array(
- 'name' => 'apiKey',
- 'label' => esc_html__( 'Mailchimp API Key', 'gravityformsmailchimp' ),
- 'type' => 'text',
- 'class' => 'medium',
+ 'name' => 'connection',
+ 'type' => 'html',
'feedback_callback' => array( $this, 'initialize_api' ),
+ 'html' => array( $this, 'render_connection_button' ),
+ 'callback' => array( $this, 'render_connection_button' ),
+ ),
+ array(
+ 'type' => 'save',
+ 'class' => 'hidden',
),
),
),
@@ -324,8 +462,169 @@ public function plugin_settings_fields() {
}
+ /**
+ * Render the Connection Button on the Settings Page.
+ *
+ * @since 4.10
+ *
+ * @return string
+ */
+ public function render_connection_button() {
+ $valid = $this->is_valid_connection();
+
+ if ( ! $valid ) {
+ $nonce = wp_create_nonce( $this->get_authentication_state_action() );
+ $transient_name = 'gravityapi_request_' . $this->_slug;
+ if ( get_transient( $transient_name ) ) {
+ delete_transient( $transient_name );
+ }
+ set_transient( $transient_name, $nonce, 10 * MINUTE_IN_SECONDS );
+ }
+
+ $before = $this->get_before_button_content( $valid );
+ $button = $this->get_button_content( $valid );
+ $after = $this->get_after_button_content( $valid );
+
+ if ( version_compare( GFForms::$version, '2.5', '<' ) ) {
+ echo $before . $button . $after;
+
+ return;
+ }
+
+ return $before . $button . $after;
+ }
+
+ /**
+ * Get the markup to display before the connect button.
+ *
+ * @since 4.10
+ *
+ * @param bool $valid Whether the current connection is valid.
+ *
+ * @return string
+ */
+ private function get_before_button_content( $valid ) {
+ $html = '';
+
+ if ( ! $valid ) {
+ return '';
+ }
+
+ $account = $this->api->account_details();
+ $name = isset( $account['account_name'] ) ? $account['account_name'] : false;
+ $html .= '';
+
+ if ( $name ) {
+ $html .= esc_html__( 'Connected to Mailchimp as: ', 'gravityformsmailchimp' );
+ $html .= esc_html( $name ) . '
';
+ } else {
+ $html .= esc_html__( 'Connected to Mailchimp.', 'gravityformsmailchimp' );
+ $html .= '
';
+ }
+
+ /**
+ * Allows third-party code to modify the HTML content which appears before the Connect button.
+ *
+ * @since 4.10
+ *
+ * @param string $html The current HTML markup.
+ * @param bool $valid Whether the current API connection is valid (connected and using oAuth).
+ *
+ * @return string
+ */
+ return apply_filters( 'gform_mailchimp_before_connect_button', $html, $valid );
+ }
+
+ /**
+ * Get the markup to display the connect button.
+ *
+ * @since 4.10
+ *
+ * @param bool $valid Whether the current connection is valid.
+ *
+ * @return string
+ */
+ private function get_button_content( $valid ) {
+ $html = sprintf(
+ '%2$s',
+ $valid ? $this->get_disconnect_url() : $this->get_connect_url(),
+ $valid ? __( 'Disconnect from Mailchimp', 'gravityformsmailchimp' ) : __( 'Connect to Mailchimp', 'gravityformsmailchimp' ),
+ '_self',
+ $valid ? 'gform-button--secondary' : 'gform-button--primary'
+ );
+
+ /**
+ * Allows third-party code to modify the Connect button HTML markup.
+ *
+ * @since 4.10
+ *
+ * @param string $html The current button HTML markup.
+ * @param bool $valid Whether the current API connection is valid (connected and using oAuth).
+ *
+ * @return string
+ */
+ return apply_filters( 'gform_mailchimp_connect_button', $html, $valid );
+ }
+
+ /**
+ * Get the markup to display after the connect button.
+ *
+ * @since 4.10
+ *
+ * @param bool $valid Whether the current connection is valid.
+ *
+ * @return string
+ */
+ private function get_after_button_content( $valid ) {
+ if ( ! $valid ) {
+ return '';
+ }
+
+ $html = '';
+ // translators: %1 is an opening tag, and %2 is a closing tag.
+ $html .= sprintf( __( 'In order to remove this site from your Mailchimp account, you\'ll need to remove it from your Mailchimp Account. %1$sLearn More.%2$s' ), '', '' );
+ $html .= '
';
+
+ /**
+ * Allows third-party code to modify the HTML content which appears after the Connect button.
+ *
+ * @since 4.10
+ *
+ * @param string $html The current HTML markup.
+ * @param bool $valid Whether the current API connection is valid (connected and using oAuth).
+ *
+ * @return string
+ */
+ return apply_filters( 'gform_mailchimp_after_connect_button', $html, $valid );
+ }
+
+ /**
+ * Get the correct disconnect URL
+ *
+ * @since 4.10
+ *
+ * @return string
+ */
+ private function get_disconnect_url() {
+ return add_query_arg( array( 'action' => self::POST_ACTION ), admin_url( 'admin-post.php' ) );
+ }
+
+ /**
+ * Get the correct connect URL
+ *
+ * @since 4.10
+ *
+ * @return string
+ */
+ private function get_connect_url() {
+ $settings_url = urlencode( admin_url( 'admin.php?page=gf_settings&subview=' . $this->_slug ) );
+ $connect_url = sprintf( '%1$s/auth/mailchimp', GRAVITY_API_URL );
+ $nonce = wp_create_nonce( $this->get_authentication_state_action() );
+
+ return add_query_arg( array( 'redirect_to' => $settings_url, 'state' => $nonce, 'license' => GFCommon::get_key() ), $connect_url );
+ }
// # FEED SETTINGS -------------------------------------------------------------------------------------------------
@@ -417,11 +716,11 @@ public function feed_settings_fields() {
),
),
array(
- 'name' => 'tags',
- 'type' => 'text',
- 'class' => 'medium merge-tag-support mt-position-right mt-hide_all_fields',
- 'label' => esc_html__( 'Tags', 'gravityformsmailchimp' ),
- 'tooltip' => sprintf(
+ 'name' => 'tags',
+ 'type' => 'text',
+ 'class' => 'medium merge-tag-support mt-position-right mt-hide_all_fields',
+ 'label' => esc_html__( 'Tags', 'gravityformsmailchimp' ),
+ 'tooltip' => sprintf(
'%s
%s',
esc_html__( 'Tags', 'gravityformsmailchimp' ),
esc_html__( 'Associate tags to your Mailchimp contacts with a comma separated list (e.g. new lead, Gravity Forms, web source). Commas within a merge tag value will be created as a single tag.', 'gravityformsmailchimp' )
@@ -443,7 +742,10 @@ public function feed_settings_fields() {
esc_html__( 'When conditional logic is enabled, form submissions will only be exported to Mailchimp when the conditions are met. When disabled all form submissions will be exported.', 'gravityformsmailchimp' )
),
),
- array( 'type' => 'save' ),
+ array(
+ 'type' => 'save',
+ 'dependency' => 'mailchimpList',
+ ),
),
),
);
@@ -769,6 +1071,7 @@ public function settings_interest_categories( $field, $echo = true ) {
// If no categories are found, return.
if ( empty( $categories ) ) {
$this->log_debug( __METHOD__ . '(): No categories found.' );
+
return '';
}
@@ -848,7 +1151,7 @@ public function interest_category_condition( $setting_name_root ) {
$is_enabled = $this->get_setting( $condition_enabled_setting ) == '1';
$container_style = ! $is_enabled ? "style='display:none;'" : '';
- $str = "