mailchimp.module in Mailchimp 8
Mailchimp module.
File
mailchimp.moduleView source
<?php
/**
* @file
* Mailchimp module.
*/
use Drupal\Component\Utility\Crypt;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Markup;
use Drupal\Core\Site\Settings;
use Mailchimp\MailchimpLists;
define('MAILCHIMP_QUEUE_CRON', 'mailchimp');
define('MAILCHIMP_STATUS_SENT', 'sent');
define('MAILCHIMP_STATUS_SAVE', 'save');
define('MAILCHIMP_STATUS_PAUSED', 'paused');
define('MAILCHIMP_STATUS_SCHEDULE', 'schedule');
define('MAILCHIMP_STATUS_SENDING', 'sending');
if (!class_exists('Mailchimp\\Mailchimp')) {
include_once __DIR__ . '/lib/mailchimp-api-php/src/Mailchimp.php';
include_once __DIR__ . '/lib/mailchimp-api-php/src/MailchimpAPIException.php';
include_once __DIR__ . '/lib/mailchimp-api-php/src/MailchimpAutomations.php';
include_once __DIR__ . '/lib/mailchimp-api-php/src/MailchimpCampaigns.php';
include_once __DIR__ . '/lib/mailchimp-api-php/src/MailchimpConnectedSites.php';
include_once __DIR__ . '/lib/mailchimp-api-php/src/MailchimpEcommerce.php';
include_once __DIR__ . '/lib/mailchimp-api-php/src/MailchimpLists.php';
include_once __DIR__ . '/lib/mailchimp-api-php/src/MailchimpReports.php';
include_once __DIR__ . '/lib/mailchimp-api-php/src/MailchimpTemplates.php';
include_once __DIR__ . '/lib/mailchimp-api-php/src/http/MailchimpHttpClientInterface.php';
include_once __DIR__ . '/lib/mailchimp-api-php/src/http/MailchimpCurlHttpClient.php';
include_once __DIR__ . '/lib/mailchimp-api-php/src/http/MailchimpGuzzleHttpClient.php';
}
/**
* Access callback for mailchimp submodule menu items.
*/
function mailchimp_apikey_ready_access($permission) {
if (mailchimp_get_api_object() && \Drupal::currentUser()
->hasPermission($permission)) {
return TRUE;
}
return FALSE;
}
/**
* Instantiates a Mailchimp library object.
*
* @return \Mailchimp
* Drupal Mailchimp library object.
*/
function mailchimp_get_api_object($classname = 'Mailchimp') {
$mailchimp =& drupal_static(__FUNCTION__);
if (isset($mailchimp) && $mailchimp instanceof $classname) {
return $mailchimp;
}
$config = \Drupal::config('mailchimp.settings');
if ($config
->get('test_mode')) {
$classname = '\\Mailchimp\\Tests\\' . $classname;
}
else {
$classname = '\\Mailchimp\\' . $classname;
}
if (!class_exists($classname)) {
$msg = t('Failed to load Mailchimp PHP library. Please refer to the installation requirements.');
\Drupal::logger('mailchimp')
->error($msg);
\Drupal::messenger()
->addError($msg);
return NULL;
}
$api_key = $config
->get('api_key');
if (!strlen($api_key)) {
\Drupal::logger('mailchimp')
->error('Mailchimp Error: API Key cannot be blank.');
return NULL;
}
$http_options = [
'timeout' => $config
->get('api_timeout'),
'headers' => [
'User-Agent' => _mailchimp_get_user_agent(),
],
];
$mailchimp = new $classname($api_key, 'apikey', $http_options);
return $mailchimp;
}
/**
* Gets the user agent string for this installation of Mailchimp.
*
* @return string
* The user agent string.
*/
function _mailchimp_get_user_agent() {
$version = '8.x-1.x';
if (\Drupal::moduleHandler()
->moduleExists('system')) {
/** @var \Drupal\Core\Extension\ModuleExtensionList $extension_list */
$extension_list = \Drupal::service('extension.list.module');
$info = $extension_list
->getExtensionInfo('mailchimp');
if (!empty($info['version'])) {
$version = $info['version'];
}
}
$user_agent = "DrupalMailchimp/{$version} " . \GuzzleHttp\default_user_agent();
return $user_agent;
}
/**
* Returns a single list.
*
* @param string $list_id
* The unique ID of the list provided by Mailchimp.
*
* @return array
* Array of list data.
*/
function mailchimp_get_list($list_id) {
$lists = mailchimp_get_lists([
$list_id,
]);
return reset($lists);
}
/**
* Returns all Mailchimp lists for a given key. Lists are stored in the cache.
*
* @param array $list_ids
* An array of list IDs to filter the results by.
* @param bool $reset
* Force a cache reset.
*
* @return array
* An array of list data arrays.
*/
function mailchimp_get_lists(array $list_ids = [], $reset = FALSE) {
$lists = [];
$cache = \Drupal::cache('mailchimp');
$cached_data = $reset ? NULL : $cache
->get('lists');
// Return cached lists.
if ($cached_data) {
$lists = $cached_data->data;
}
else {
try {
/* @var \Mailchimp\MailchimpLists $mcapi */
$mcapi = mailchimp_get_api_object('MailchimpLists');
if ($mcapi != NULL) {
$result = $mcapi
->getLists([
'count' => 500,
]);
if ($result->total_items > 0) {
foreach ($result->lists as $list) {
$int_category_data = $mcapi
->getInterestCategories($list->id, [
'count' => 500,
]);
if ($int_category_data->total_items > 0) {
$list->intgroups = [];
foreach ($int_category_data->categories as $interest_category) {
$interest_data = $mcapi
->getInterests($list->id, $interest_category->id, [
'count' => 500,
]);
if ($interest_data->total_items > 0) {
$interest_category->interests = $interest_data->interests;
}
$list->intgroups[] = $interest_category;
}
}
$lists[$list->id] = $list;
// Append mergefields:
$mergefields = $mcapi
->getMergeFields($list->id, [
'count' => 500,
]);
if ($mergefields->total_items > 0) {
$lists[$list->id]->mergevars = $mergefields->merge_fields;
}
}
}
uasort($lists, '_mailchimp_list_cmp');
$cache
->set('lists', $lists);
}
} catch (\Exception $e) {
\Drupal::logger('mailchimp')
->error('An error occurred requesting list information from Mailchimp. "{message}"', [
'message' => $e
->getMessage(),
]);
}
}
// Filter by given IDs.
if (!empty($list_ids)) {
$filtered_lists = [];
foreach ($list_ids as $id) {
if (array_key_exists($id, $lists)) {
$filtered_lists[$id] = $lists[$id];
}
}
return $filtered_lists;
}
else {
return $lists;
}
}
/**
* Helper function used by uasort() to sort lists alphabetically by name.
*
* @param object $a
* An object representing the first list.
* @param object $b
* An object representing the second list.
*
* @return int
* One of the values -1, 0, 1
*/
function _mailchimp_list_cmp($a, $b) {
if ($a->name == $b->name) {
return 0;
}
return $a->name < $b->name ? -1 : 1;
}
/**
* Wrapper around MailchimpLists->getMergeFields().
*
* @param array $list_ids
* Array of Mailchimp list IDs.
* @param bool $reset
* Set to TRUE if mergevars should not be loaded from cache.
*
* @return array
* Struct describing mergevars for the specified lists.
*/
function mailchimp_get_mergevars(array $list_ids, $reset = FALSE) {
$mergevars = [];
$cache = \Drupal::cache('mailchimp');
if (!$reset) {
foreach ($list_ids as $key => $list_id) {
$cached_data = $cache
->get($list_id . '-mergevars');
// Get cached data and unset from our remaining lists to query.
if ($cached_data) {
$mergevars[$list_id] = $cached_data->data;
unset($list_ids[$key]);
}
}
}
// Get the uncached merge vars from Mailchimp.
if (count($list_ids)) {
/* @var \Mailchimp\MailchimpLists $mc_lists */
$mc_lists = mailchimp_get_api_object('MailchimpLists');
$list_id = NULL;
try {
if (!$mc_lists) {
throw new Exception('Cannot get merge vars without Mailchimp API. Check API key has been entered.');
}
foreach ($list_ids as $list_id) {
// Add default EMAIL merge var for all lists.
$mergevars[$list_id] = [
(object) [
'tag' => 'EMAIL',
'name' => t('Email Address'),
'type' => 'email',
'required' => TRUE,
'default_value' => '',
'public' => TRUE,
'display_order' => 1,
'options' => (object) [
'size' => 25,
],
],
];
$result = $mc_lists
->getMergeFields($list_id, [
'count' => 500,
]);
if ($result->total_items > 0) {
$mergevars[$list_id] = array_merge($mergevars[$list_id], $result->merge_fields);
}
$cache
->set($list_id . '-mergevars', $mergevars[$list_id]);
}
} catch (\Exception $e) {
\Drupal::logger('mailchimp')
->error('An error occurred requesting mergevars for list {list}. "{message}"', [
'list' => $list_id,
'message' => $e
->getMessage(),
]);
}
}
return $mergevars;
}
/**
* Get the Mailchimp member info for a given email address and list.
*
* Results are cached in the cache_mailchimp bin which is cleared by the
* Mailchimp web hooks system when needed.
*
* @param string $list_id
* The Mailchimp list ID to get member info for.
* @param string $email
* The Mailchimp user email address to load member info for.
* @param bool $reset
* Set to TRUE if member info should not be loaded from cache.
*
* @return object
* Member info object, empty if there is no valid info.
*/
function mailchimp_get_memberinfo($list_id, $email, $reset = FALSE) {
$cache = \Drupal::cache('mailchimp');
if (!$reset) {
$cached_data = $cache
->get($list_id . '-' . $email);
if ($cached_data) {
return $cached_data->data;
}
}
// Query lists from the MCAPI and store in cache:
$memberinfo = new stdClass();
/* @var \Mailchimp\MailchimpLists $mc_lists */
$mc_lists = mailchimp_get_api_object('MailchimpLists');
try {
if (!$mc_lists) {
throw new Exception('Cannot get member info without Mailchimp API. Check API key has been entered.');
}
$result = $mc_lists
->getMemberInfo($list_id, $email);
if (!empty($result->id)) {
$memberinfo = $result;
$cache
->set($list_id . '-' . $email, $memberinfo);
}
} catch (\Exception $e) {
// A 404 exception code means mailchimp does not have subscription
// information for given email address. This is not an error and we can
// cache this information.
if ($e
->getCode() == 404) {
$cache
->set($list_id . '-' . $email, $memberinfo);
}
else {
\Drupal::logger('mailchimp')
->error('An error occurred requesting memberinfo for {email} in list {list}. "{message}"', [
'email' => $email,
'list' => $list_id,
'message' => $e
->getMessage(),
]);
}
}
return $memberinfo;
}
/**
* Get the marketing permissions for a subscribed member.
*
* Simple wrapper around mailchimp_get_memberinfo().
*
* @param string $list_id
* Unique string identifier for the list on your MailChimp account.
* @param string $email
* Email address to check for on the identified MailChimp List.
* @param bool $reset
* Set to TRUE to ignore the cache. (Used heavily in testing functions.)
*
* @return array
* An array of marketing permissions, or an empty array if not subscribed
*/
function mailchimp_get_marketing_permissions($list_id, $email, $reset = FALSE) {
$memberinfo = mailchimp_get_memberinfo($list_id, $email, $reset);
if (isset($memberinfo->status) && $memberinfo->status == 'subscribed' && isset($memberinfo->marketing_permissions)) {
return $memberinfo->marketing_permissions;
}
return [];
}
/**
* Check if the given email is subscribed to the given list.
*
* Simple wrapper around mailchimp_get_memberinfo().
*
* @param string $list_id
* Unique string identifier for the list on your Mailchimp account.
* @param string $email
* Email address to check for on the identified Mailchimp List.
* @param bool $reset
* Set to TRUE to ignore the cache. (Used heavily in testing functions.)
*
* @return bool
* TRUE if subscribed, FALSE otherwise.
*/
function mailchimp_is_subscribed($list_id, $email, $reset = FALSE) {
$subscribed = FALSE;
$memberinfo = mailchimp_get_memberinfo($list_id, $email, $reset);
if (isset($memberinfo->status) && $memberinfo->status == 'subscribed') {
$subscribed = TRUE;
}
return $subscribed;
}
/**
* Subscribe a user to a Mailchimp list in real time or by adding to the queue.
*
* @see Mailchimp_Lists::subscribe()
*/
function mailchimp_subscribe($list_id, $email, $merge_vars = NULL, $interests = [], $double_optin = FALSE, $format = 'html', $language = NULL, $gdpr_consent = FALSE) {
$config = \Drupal::config('mailchimp.settings');
if (empty($language)) {
$language = \Drupal::languageManager()
->getCurrentLanguage()
->getId();
}
if ($config
->get('cron')) {
$args = [
'list_id' => $list_id,
'email' => $email,
'merge_vars' => $merge_vars,
'interests' => $interests,
'double_optin' => $double_optin,
'format' => $format,
'language' => $language,
'gdpr_consent' => $gdpr_consent,
];
return mailchimp_addto_queue('mailchimp_subscribe_process', $args);
}
return mailchimp_subscribe_process($list_id, $email, $merge_vars, $interests, $double_optin, $format, $language, $gdpr_consent);
}
/**
* Wrapper around Mailchimp_Lists::subscribe().
*
* @see Mailchimp_Lists::subscribe()
*/
function mailchimp_subscribe_process($list_id, $email, $merge_vars = NULL, $interests = [], $double_optin = FALSE, $format = 'html', $language = NULL, $gdpr_consent = FALSE) {
$config = \Drupal::config('mailchimp.settings');
$result = FALSE;
try {
/* @var \Mailchimp\MailchimpLists $mc_lists */
$mc_lists = mailchimp_get_api_object('MailchimpLists');
if (!$mc_lists) {
throw new Exception('Cannot subscribe to list without Mailchimp API. Check API key has been entered.');
}
$parameters = [
// If double opt-in is required, set member status to 'pending'.
'status' => $double_optin ? MailchimpLists::MEMBER_STATUS_PENDING : MailchimpLists::MEMBER_STATUS_SUBSCRIBED,
'email_type' => $format,
];
if (!empty($language)) {
$parameters['language'] = $language;
}
// Set interests.
if (!empty($interests)) {
$selected_interests = [];
foreach ($interests as $interest_group) {
// This could happen in case the selected interest group
// is set to display radio inputs. So we either do an
// explicit check here, or simply transform the single string
// value to an array in order to pass the condition check below.
if (!is_array($interest_group)) {
$interest_group = [
$interest_group => $interest_group,
];
}
foreach ($interest_group as $interest_id => $interest_status) {
$selected_interests[$interest_id] = $interest_status !== 0;
}
}
if (!empty($selected_interests)) {
$parameters['interests'] = (object) $selected_interests;
}
}
// Set merge fields.
if (!empty($merge_vars)) {
$parameters['merge_fields'] = (object) $merge_vars;
}
// Has GDPR consent been given?
if ($gdpr_consent) {
// If the member is already subscribed get the marketing permission id(s)
// for the list and enable them.
$marketing_permissions = mailchimp_get_marketing_permissions($list_id, $email);
$was_subscribed = FALSE;
if ($marketing_permissions) {
foreach ($marketing_permissions as $marketing_permission) {
$parameters['marketing_permissions'][] = [
'marketing_permission_id' => $marketing_permission->marketing_permission_id,
'enabled' => TRUE,
];
}
$was_subscribed = TRUE;
}
}
else {
// We need to make sure this is set.
$was_subscribed = FALSE;
}
// Add member to list.
$result = $mc_lists
->addOrUpdateMember($list_id, $email, $parameters);
if (isset($result->id)) {
\Drupal::moduleHandler()
->invokeAll('mailchimp_subscribe_success', [
$list_id,
$email,
$merge_vars,
]);
// Clear user cache, just in case there's some cruft leftover:
mailchimp_cache_clear_member($list_id, $email);
\Drupal::logger('mailchimp')
->notice('{email} was subscribed to list {list}.', [
'email' => $email,
'list' => $list_id,
]);
// For newly subscribed members set GDPR consent if it's been given.
if (!$was_subscribed && $gdpr_consent && !empty($result->marketing_permissions)) {
// If the member is already subscribed get the marketing permission
// id(s) for the list and enable them.
foreach ($result->marketing_permissions as $marketing_permission) {
$parameters['marketing_permissions'][] = [
'marketing_permission_id' => $marketing_permission->marketing_permission_id,
'enabled' => TRUE,
];
}
// Update the member.
$result = $mc_lists
->addOrUpdateMember($list_id, $email, $parameters);
if (!isset($result->id)) {
\Drupal::logger('mailchimp')
->warning('A problem occurred setting marketing permissions for {email} on list {list}.', [
'email' => $email,
'list' => $list_id,
]);
}
}
}
else {
if (!$config
->get('test_mode')) {
\Drupal::logger('mailchimp')
->warning('A problem occurred subscribing {email} to list {list}.', [
'email' => $email,
'list' => $list_id,
]);
}
}
} catch (\Exception $e) {
if ($e
->getCode() == '400' && strpos($e
->getMessage(), 'Member In Compliance State') !== FALSE && !$double_optin) {
\Drupal::logger('mailchimp')
->error('Detected "Member In Compliance State" subscribing {email} to list {list}. Trying again using double-opt in.', [
'email' => $email,
'list' => $list_id,
]);
return mailchimp_subscribe_process($list_id, $email, $merge_vars, $interests, TRUE, $format, $language, $gdpr_consent);
}
\Drupal::logger('mailchimp')
->error('An error occurred subscribing {email} to list {list}. "{message}"', [
'email' => $email,
'list' => $list_id,
'message' => $e
->getMessage(),
]);
}
if ($double_optin) {
\Drupal::messenger()
->addStatus(t('Please check your email to confirm your subscription.'), FALSE);
}
return $result;
}
/**
* Adds a Mailchimp subscription task to the queue.
*
* @param string $function
* The name of the function the queue runner should call.
* @param array $args
* The list of args to pass to the function.
*
* @return mixed
* Unique ID if item is successfully added to the queue, FALSE otherwise.
*/
function mailchimp_addto_queue($function, array $args) {
$queue = \Drupal::queue(MAILCHIMP_QUEUE_CRON);
$queue
->createQueue();
return $queue
->createItem([
'function' => $function,
'args' => $args,
]);
}
/**
* Update a members list subscription in real time or by adding to the queue.
*
* @see Mailchimp_Lists::updateMember()
*/
function mailchimp_update_member($list_id, $email, $merge_vars, $interests = [], $format = 'html', $double_optin = FALSE, $gdpr_consent = FALSE) {
$config = \Drupal::config('mailchimp.settings');
if ($config
->get('cron')) {
$args = [
'list_id' => $list_id,
'email' => $email,
'merge_vars' => $merge_vars,
'interests' => $interests,
'format' => $format,
'double_optin' => $double_optin,
'gdpr_consent' => $gdpr_consent,
];
return mailchimp_addto_queue('mailchimp_update_member_process', $args);
}
return mailchimp_update_member_process($list_id, $email, $merge_vars, $interests, $format, $double_optin, $gdpr_consent);
}
/**
* Wrapper around Mailchimp_Lists::updateMember().
*
* @see Mailchimp_Lists::updateMember()
*/
function mailchimp_update_member_process($list_id, $email, $merge_vars, $interests, $format, $double_optin = FALSE, $gdpr_consent = FALSE) {
$result = FALSE;
try {
/* @var \Mailchimp\MailchimpLists $mc_lists */
$mcapi = mailchimp_get_api_object('MailchimpLists');
$parameters = [
'status' => $double_optin ? MailchimpLists::MEMBER_STATUS_PENDING : MailchimpLists::MEMBER_STATUS_SUBSCRIBED,
'email_type' => $format,
];
// Set interests.
if (!empty($interests)) {
$selected_interests = [];
foreach ($interests as $interest_group) {
foreach ($interest_group as $interest_id => $interest_status) {
$selected_interests[$interest_id] = $interest_status !== 0;
}
}
if (!empty($selected_interests)) {
$parameters['interests'] = (object) $selected_interests;
}
}
// Set merge fields.
if (!empty($merge_vars)) {
$parameters['merge_fields'] = (object) $merge_vars;
}
// Has GDPR consent been given?
if ($gdpr_consent) {
// If the member is already subscribed get the marketing permission id(s)
// for the list and enable them.
$marketing_permissions = mailchimp_get_marketing_permissions($list_id, $email);
if ($marketing_permissions) {
foreach ($marketing_permissions as $marketing_permission) {
$parameters['marketing_permissions'][] = [
'marketing_permission_id' => $marketing_permission->marketing_permission_id,
'enabled' => TRUE,
];
}
}
}
// Update member.
$result = $mcapi
->updateMember($list_id, $email, $parameters);
if (isset($result->id)) {
\Drupal::logger('mailchimp')
->notice('{email} was updated in list {list_id}.', [
'email' => $email,
'list' => $list_id,
]);
// Clear user cache:
mailchimp_cache_clear_member($list_id, $email);
}
else {
\Drupal::logger('mailchimp')
->warning('A problem occurred updating {email} on list {list}.', [
'email' => $email,
'list' => $list_id,
]);
}
} catch (\Exception $e) {
if ($e
->getCode() == '400' && strpos($e
->getMessage(), 'Member In Compliance State') !== FALSE && !$double_optin) {
\Drupal::logger('mailchimp')
->error('Detected "Member In Compliance State" subscribing {email} to list {list}. Trying again using double-opt in.', [
'email' => $email,
'list' => $list_id,
]);
return mailchimp_update_member_process($list_id, $email, $merge_vars, $interests, $format, TRUE, $gdpr_consent);
}
\Drupal::logger('mailchimp')
->error('An error occurred updating {email} on list {list}. "{message}"', [
'email' => $email,
'list' => $list_id,
'message' => $e
->getMessage(),
]);
}
if ($double_optin) {
\Drupal::messenger()
->addStatus(t('Please check your email to confirm your subscription.'), FALSE);
}
return $result;
}
/**
* Retrieve all members of a given list with a given status.
*
* Note that this function can cause locking an is somewhat slow. It is not
* recommended unless you know what you are doing! See the MCAPI documentation.
*/
function mailchimp_get_members($list_id, $status = 'subscribed', $options = []) {
$results = FALSE;
$lock = \Drupal::lock();
if ($lock
->acquire('mailchimp_get_members', 60)) {
try {
/* @var \Mailchimp\MailchimpLists $mcapi */
$mcapi = mailchimp_get_api_object('MailchimpLists');
$options['status'] = $status;
$options['count'] = 500;
$results = $mcapi
->getMembers($list_id, $options);
} catch (\Exception $e) {
\Drupal::logger('mailchimp')
->error('An error occurred pulling member info for a list. "{message}"', [
'message' => $e
->getMessage(),
]);
}
$lock
->release('mailchimp_get_members');
}
return $results;
}
/**
* Batch updates a number of Mailchimp list members.
*
* @see Mailchimp_Lists::batchSubscribe()
*/
function mailchimp_batch_update_members($list_id, $batch) {
$results = FALSE;
try {
/* @var \Mailchimp\MailchimpLists $mc_lists */
$mc_lists = mailchimp_get_api_object('MailchimpLists');
if (!$mc_lists) {
throw new Exception('Cannot batch subscribe to list without Mailchimp API. Check API key has been entered.');
}
if (!empty($batch)) {
// Create a new batch update operation for each member.
foreach ($batch as $batch_data) {
// TODO: Remove 'advanced' earlier? Needed at all?
unset($batch_data['merge_vars']['advanced']);
$parameters = [
'email_type' => $batch_data['email_type'],
'merge_fields' => (object) $batch_data['merge_vars'],
];
$mc_lists
->addOrUpdateMember($list_id, $batch_data['email'], $parameters, TRUE);
}
// Process batch operations.
return $mc_lists
->processBatchOperations();
}
} catch (\Exception $e) {
\Drupal::logger('mailchimp')
->error('An error occurred performing batch subscribe/update. "{message}"', [
'message' => $e
->getMessage(),
]);
}
return $results;
}
/**
* Unsubscribes a member from a Mailchimp list.
*
* @see Mailchimp_Lists::unsubscribe()
*/
function mailchimp_unsubscribe($list_id, $email) {
$config = \Drupal::config('mailchimp.settings');
$result = FALSE;
if (mailchimp_is_subscribed($list_id, $email)) {
if ($config
->get('cron')) {
$result = mailchimp_addto_queue('mailchimp_unsubscribe_process', [
'list_id' => $list_id,
'email' => $email,
]);
}
else {
$result = mailchimp_unsubscribe_process($list_id, $email);
}
}
return $result;
}
/**
* Unsubscribes a member from a Mailchimp list.
*
* @see Mailchimp_Lists::unsubscribe()
*/
function mailchimp_unsubscribe_process($list_id, $email) {
try {
/* @var \Mailchimp\MailchimpLists $mc_lists */
$mc_lists = mailchimp_get_api_object('MailchimpLists');
if (!$mc_lists) {
throw new Exception('Cannot unsubscribe from list without Mailchimp API. Check API key has been entered.');
}
$mc_lists
->removeMember($list_id, $email);
\Drupal::moduleHandler()
->invokeAll('mailchimp_unsubscribe_success', [
$list_id,
$email,
]);
// Clear user cache:
mailchimp_cache_clear_member($list_id, $email);
return TRUE;
} catch (\Exception $e) {
\Drupal::logger('mailchimp')
->error('An error occurred unsubscribing {email} from list {list}. "{message}"', [
'email' => $email,
'list' => $list_id,
'message' => $e
->getMessage(),
]);
}
return FALSE;
}
/**
* Wrapper function to return data for a given campaign.
*
* Data is stored in the Mailchimp cache.
*
* @param string $campaign_id
* The ID of the campaign to get data for.
* @param bool $reset
* Set to TRUE if campaign data should not be loaded from cache.
*
* @return mixed
* Array of campaign data or FALSE if not found.
*/
function mailchimp_get_campaign_data($campaign_id, $reset = FALSE) {
$cache = \Drupal::cache('mailchimp');
$campaign_data = FALSE;
if (!$reset) {
$cached_data = $cache
->get('campaign_' . $campaign_id);
if ($cached_data) {
return $cached_data->data;
}
}
try {
/* @var \Mailchimp\MailchimpCampaigns $mcapi */
$mcapi = mailchimp_get_api_object('MailchimpCampaigns');
$response = $mcapi
->getCampaign($campaign_id);
if (!empty($response->id)) {
$campaign_data = $response;
$cache
->set('campaign_' . $campaign_id, $campaign_data);
}
} catch (\Exception $e) {
\Drupal::logger('mailchimp')
->error('An error occurred retrieving campaign data for {campaign}. "{message}"', [
'campaign' => $campaign_id,
'message' => $e
->getMessage(),
]);
}
return $campaign_data;
}
/**
* Returns all lists a given email address is currently subscribed to.
*
* @param string $email
* Email address to search.
*
* @return array
* Campaign structs containing id, web_id, name.
*/
function mailchimp_get_lists_for_email($email) {
try {
/* @var \Mailchimp\MailchimpLists $mcapi */
$mcapi = mailchimp_get_api_object('MailchimpLists');
$lists = $mcapi
->getListsForEmail($email);
} catch (\Exception $e) {
\Drupal::logger('mailchimp')
->error('An error occurred retreiving lists data for {email}. "{message}"', [
'email' => $email,
'message' => $e
->getMessage(),
]);
$lists = [];
}
return $lists;
}
/**
* Returns all webhooks for a given Mailchimp list ID.
*
* @see Mailchimp_Lists::webhooks()
*/
function mailchimp_webhook_get($list_id) {
try {
/* @var \Mailchimp\MailchimpLists $mc_lists */
$mc_lists = mailchimp_get_api_object('MailchimpLists');
$result = $mc_lists
->getWebhooks($list_id);
return $result->total_items > 0 ? $result->webhooks : FALSE;
} catch (\Exception $e) {
\Drupal::logger('mailchimp')
->error('An error occurred reading webhooks for list {list}. "{message}"', [
'list' => $list_id,
'message' => $e
->getMessage(),
]);
return FALSE;
}
}
/**
* Adds a webhook to a Mailchimp list.
*
* @param string $list_id
* The Mailchimp list ID to add a webhook for.
* @param string $url
* The URL of the webhook endpoint.
* @param array $events
* Associative array of events action to bool, indicating enabled status.
* @param array $sources
* Associative array of source name to bool, indicating source status.
*
* @return string
* The ID of the new webhook.
*
* @see Mailchimp_Lists::addWebhook()
*/
function mailchimp_webhook_add($list_id, $url, array $events = [], array $sources = []) {
try {
/* @var \Mailchimp\MailchimpLists $mc_lists */
$mc_lists = mailchimp_get_api_object('MailchimpLists');
if (!$mc_lists) {
throw new Exception('Cannot add webhook without Mailchimp API. Check API key has been entered.');
}
$parameters = [
'events' => (object) $events,
'sources' => (object) $sources,
];
$result = $mc_lists
->addWebhook($list_id, $url, $parameters);
return $result->id;
} catch (\Exception $e) {
\Drupal::logger('mailchimp')
->error('An error occurred adding webhook for list {list}. "{message}"', [
'list' => $list_id,
'message' => $e
->getMessage(),
]);
return FALSE;
}
}
/**
* Deletes a Mailchimp list webhook.
*
* @param string $list_id
* The ID of the Mailchimp list to delete the webhook from.
* @param string $url
* The URL of the webhook endpoint.
*
* @return bool
* TRUE if deletion was successful, FALSE otherwise.
*
* @see Mailchimp_Lists::webhookDel()
*/
function mailchimp_webhook_delete($list_id, $url) {
try {
/* @var \Mailchimp\MailchimpLists $mc_lists */
$mc_lists = mailchimp_get_api_object('MailchimpLists');
$result = $mc_lists
->getWebhooks($list_id);
if ($result->total_items > 0) {
foreach ($result->webhooks as $webhook) {
if ($webhook->url == $url) {
$mc_lists
->deleteWebhook($list_id, $webhook->id);
return TRUE;
}
}
}
return FALSE;
} catch (\Exception $e) {
\Drupal::logger('mailchimp')
->error('An error occurred deleting webhook for list {list}. "{message}"', [
'list' => $list_id,
'message' => $e
->getMessage(),
]);
return FALSE;
}
}
/**
* Clears a mailchimp user member info cache.
*
* @param string $list_id
* The list ID.
* @param string $email
* The email address.
*/
function mailchimp_cache_clear_member($list_id, $email) {
$cache = \Drupal::cache('mailchimp');
$cache
->delete($list_id . '-' . $email);
}
/**
* Clears a mailchimp activity cache.
*
* @param string $list_id
* The list ID.
*/
function mailchimp_cache_clear_list_activity($list_id) {
$cache = \Drupal::cache('mailchimp');
$cache
->delete('mailchimp_activity_' . $list_id);
}
/**
* Clears a mailchimp activity cache.
*
* @param string $campaign_id
* The campaign ID.
*/
function mailchimp_cache_clear_campaign($campaign_id) {
$cache = \Drupal::cache('mailchimp');
$cache
->delete('mailchimp_campaign_' . $campaign_id);
}
/**
* Implements hook_flush_caches().
*/
function mailchimp_flush_caches() {
return [
'mailchimp',
];
}
/**
* Access callback for mailchimp_process_webhook().
*
* @param string $key
* The incoming webhook key.
*
* @return bool
* Whether or not the key matches.
*/
function mailchimp_process_webhook_access($key) {
return $key == mailchimp_webhook_key();
}
/**
* Processes a webhook post from Mailchimp.
*/
function mailchimp_process_webhook() {
if (!isset($_POST)) {
return "Mailchimp Webhook Endpoint.";
}
$data = $_POST['data'];
$type = $_POST['type'];
switch ($type) {
case 'unsubscribe':
case 'profile':
case 'cleaned':
mailchimp_get_memberinfo($data['list_id'], $data['email'], TRUE);
break;
case 'upemail':
mailchimp_cache_clear_member($data['list_id'], $data['old_email']);
mailchimp_get_memberinfo($data['list_id'], $data['new_email'], TRUE);
break;
case 'campaign':
mailchimp_cache_clear_list_activity($data['list_id']);
mailchimp_cache_clear_campaign($data['id']);
break;
}
// Allow other modules to act on a webhook.
\Drupal::moduleHandler()
->invokeAll('mailchimp_process_webhook', [
$type,
$data,
]);
// Log event:
\Drupal::logger('mailchimp')
->info('Webhook type {type} has been processed.', [
'type' => $type,
]);
return NULL;
}
/**
* Generates a key to include in the webhook URL based on a hash.
*
* @return string
* The key.
*/
function mailchimp_webhook_key() {
return Crypt::hashBase64($GLOBALS['base_url'] . Drupal::service('private_key')
->get() . Settings::getHashSalt());
}
/**
* Generates the webhook endpoint URL.
*
* @return string
* The endpoint URL.
*/
function mailchimp_webhook_url() {
return $GLOBALS['base_url'] . '/mailchimp/webhook/' . mailchimp_webhook_key();
}
/**
* Helper function to generate form elements for a list's interest groups.
*
* @param object $list
* Mailchimp list object as returned by mailchimp_get_list().
* @param array $defaults
* Array of default values to use if no group subscription values already
* exist at Mailchimp.
* @param string $email
* Optional email address to pass to the MCAPI and retrieve existing values
* for use as defaults.
* @param string $mode
* Elements display mode:
* - "default" shows all groups except the hidden ones,
* - "admin" shows all groups including hidden ones,
* - "hidden" generates '#type' => 'value' elements using default values.
*
* @return array
* A collection of form elements, one per interest group.
*/
function mailchimp_interest_groups_form_elements($list, array $defaults = [], $email = NULL, $mode = 'default') {
$return = [];
if ($mode == 'hidden') {
foreach ($list->intgroups as $group) {
$return[$group->id] = [
'#type' => 'value',
'#value' => isset($defaults[$group->id]) ? $defaults[$group->id] : [],
];
}
return $return;
}
try {
foreach ($list->intgroups as $group) {
/* @var \Mailchimp\MailchimpLists $mc_lists */
$mc_lists = mailchimp_get_api_object('MailchimpLists');
$interest_data = $mc_lists
->getInterests($list->id, $group->id, [
'count' => 500,
]);
if (!empty($email)) {
$memberinfo = mailchimp_get_memberinfo($list->id, $email);
}
// phpcs:disable
$field_title = t($group->title);
// phpcs:enable
$field_description = NULL;
$value = NULL;
// Set the form field type:
switch ($group->type) {
case 'hidden':
$field_title .= ' (' . t('hidden') . ')';
$field_description = t('This group will not be visible to the end user. However you can set the default value and it will be actually used.');
if ($mode == 'admin') {
$field_type = 'checkboxes';
}
else {
$field_type = 'value';
$value = isset($defaults[$group->id]) ? $defaults[$group->id] : [];
}
break;
case 'radio':
$field_type = 'radios';
break;
case 'dropdown':
$field_type = 'select';
break;
default:
$field_type = $group->type;
}
// Extract the field options:
$options = [];
if ($field_type == 'select') {
$options[''] = '-- select --';
}
$default_values = [];
// Set interest options and default values.
foreach ($interest_data->interests as $interest) {
// phpcs:disable
$options[$interest->id] = t($interest->name);
// phpcs:enable
if (isset($memberinfo)) {
if (isset($memberinfo->interests->{$interest->id}) && $memberinfo->interests->{$interest->id} === TRUE) {
$default_values[$group->id][] = $interest->id;
}
}
elseif (!empty($defaults)) {
if ($group->type === 'radio') {
if (isset($defaults[$group->id]) && $defaults[$group->id] === $interest->id) {
$default_values[$group->id] = $interest->id;
}
}
else {
if (isset($defaults[$group->id][$interest->id]) && !empty($defaults[$group->id][$interest->id])) {
$default_values[$group->id][] = $interest->id;
}
}
}
}
$return[$group->id] = [
'#type' => $field_type,
'#title' => $field_title,
'#description' => $field_description,
'#options' => $options,
'#default_value' => isset($default_values[$group->id]) ? $default_values[$group->id] : [],
'#attributes' => [
'class' => [
'mailchimp-newsletter-interests-' . $list->id,
],
],
];
if ($value !== NULL) {
$return[$group->id]['#value'] = $value;
}
}
} catch (\Exception $e) {
\Drupal::logger('mailchimp')
->error('An error occurred generating interest group lists. "{message}"', [
'message' => $e
->getMessage(),
]);
}
return $return;
}
/**
* Convert Mailchimp form elements to Drupal Form API.
*
* @param object $mergevar
* The mailchimp-formatted form element to convert.
*
* @return array
* A properly formatted drupal form element.
*/
function mailchimp_insert_drupal_form_tag($mergevar) {
// Insert common FormAPI properties:
$input = [
'#weight' => $mergevar->display_order,
'#required' => $mergevar->required,
'#default_value' => $mergevar->default_value,
];
// phpcs:disable
$input['#title'] = t((string) $mergevar->name);
// phpcs:enable
switch ($mergevar->type) {
case 'address':
// Sub-array of address elements according to Mailchimp specs.
// https://apidocs.mailchimp.com/api/2.0/lists/subscribe.php
$input['#type'] = 'container';
$input['#tree'] = TRUE;
$input['addr1'] = [
'#title' => t('Address 1'),
'#type' => 'textfield',
];
$input['addr2'] = [
'#title' => t('Address 2'),
'#type' => 'textfield',
];
$input['city'] = [
'#title' => t('City'),
'#type' => 'textfield',
];
$input['state'] = [
'#title' => t('State'),
'#type' => 'textfield',
'#size' => 2,
'#maxlength' => 2,
];
$input['zip'] = [
'#title' => t('Zip'),
'#type' => 'textfield',
'#size' => 6,
'#maxlength' => 6,
];
$input['country'] = [
'#title' => t('Country'),
'#type' => 'textfield',
'#size' => 2,
'#maxlength' => 2,
];
break;
case 'dropdown':
// Dropdown is mapped to <select> element in Drupal Form API.
$input['#type'] = 'select';
// Creates options, we must delete array keys to have relevant information
// on Mailchimp.
$choices = [];
foreach ($mergevar->options->choices as $choice) {
$choices[$choice] = $choice;
}
$input['#options'] = $choices;
break;
case 'radio':
// Radio is mapped to <input type='radio' /> i.e. 'radios' element in
// Drupal Form API.
$input['#type'] = 'radios';
// Creates options, we must delete array keys to have relevant information
// on Mailchimp.
$choices = [];
foreach ($mergevar->options->choices as $choice) {
$choices[$choice] = $choice;
}
$input['#options'] = $choices;
break;
case 'email':
if (\Drupal::service('element_info')
->getInfo('emailfield', '#type')) {
// Set to an HTML5 email type if 'emailfield' is supported:
$input['#type'] = 'email';
}
else {
// Set to standard text type if 'emailfield' isn't defined:
$input['#type'] = 'textfield';
}
$input['#size'] = $mergevar->options->size;
$input['#element_validate'] = [
'mailchimp_validate_email',
];
break;
case 'date':
$input['#type'] = 'date';
break;
default:
// This is a standard input[type=text] or something we can't handle with
// Drupal FormAPI.
$input['#type'] = 'textfield';
if (isset($mergevar->options->size)) {
$input['#size'] = $mergevar->options->size;
}
break;
}
// Special cases for Mailchimp hidden defined fields:
if ($mergevar->public === FALSE) {
$input['#type'] = 'hidden';
}
return $input;
}
/**
* Implements hook_cron().
*
* Processes queued Mailchimp actions.
*/
function mailchimp_cron() {
$queue = \Drupal::queue(MAILCHIMP_QUEUE_CRON);
$queue
->createQueue();
$queue_count = $queue
->numberOfItems();
if ($queue_count > 0) {
$config = \Drupal::config('mailchimp.settings');
$batch_limit = $config
->get('batch_limit');
$batch_size = $queue_count < $batch_limit ? $queue_count : $batch_limit;
$count = 0;
while ($count < $batch_size) {
if ($item = $queue
->claimItem()) {
call_user_func_array($item->data['function'], $item->data['args']);
$queue
->deleteItem($item);
}
$count++;
}
}
}
/**
* Wrapper for email validation function in core.
*
* Necessary so email validation function can be added
* to forms as a value in the #element_validate array.
*
* @see \Egulias\EmailValidator\EmailValidator::isValid()
*/
function mailchimp_validate_email($mail, FormStateInterface $form_state) {
if (!\Drupal::service('email.validator')
->isValid($mail['#value'])) {
$form_state
->setError($mail, t('The email address %mail is not valid.', [
'%mail' => $mail['#value'],
]));
return FALSE;
}
return TRUE;
}
/**
* Implements hook_page_bottom().
*/
function mailchimp_page_bottom(array &$page_bottom) {
$config = \Drupal::config('mailchimp.settings');
// Insert JavaScript for Mailchimp Connected Sites, if enabled.
if (!empty($config
->get('enable_connected'))) {
// Limit JavaScript embed to pre-configured paths.
$connected_site_paths = $config
->get('connected_paths');
$valid_paths = explode("\r\n", $connected_site_paths);
$path = \Drupal::service('path.current')
->getPath();
$path = str_replace('/', '', $path);
if (\Drupal::service('path.matcher')
->isFrontPage() && in_array('<front>', $valid_paths) || in_array($path, $valid_paths)) {
$connected_site_id = $config
->get('connected_id');
if (!empty($connected_site_id)) {
try {
/* @var \Mailchimp\MailchimpConnectedSites $mc_connected */
$mc_connected = mailchimp_get_api_object('MailchimpConnectedSites');
// Verify Connected Site exists on the Mailchimp side and insert JS.
$connected_site = $mc_connected
->getConnectedSite($connected_site_id);
if (!empty($connected_site)) {
$mcjs = [
'#type' => 'markup',
'#markup' => Markup::create($connected_site->site_script->fragment),
];
$page_bottom['mailchimp_connected'] = $mcjs;
}
} catch (\Exception $e) {
\Drupal::logger('mailchimp')
->error('An error occurred while getting connected sites. "{message}"', [
'message' => $e
->getMessage(),
]);
}
}
}
}
}
Functions
Name | Description |
---|---|
mailchimp_addto_queue | Adds a Mailchimp subscription task to the queue. |
mailchimp_apikey_ready_access | Access callback for mailchimp submodule menu items. |
mailchimp_batch_update_members | Batch updates a number of Mailchimp list members. |
mailchimp_cache_clear_campaign | Clears a mailchimp activity cache. |
mailchimp_cache_clear_list_activity | Clears a mailchimp activity cache. |
mailchimp_cache_clear_member | Clears a mailchimp user member info cache. |
mailchimp_cron | Implements hook_cron(). |
mailchimp_flush_caches | Implements hook_flush_caches(). |
mailchimp_get_api_object | Instantiates a Mailchimp library object. |
mailchimp_get_campaign_data | Wrapper function to return data for a given campaign. |
mailchimp_get_list | Returns a single list. |
mailchimp_get_lists | Returns all Mailchimp lists for a given key. Lists are stored in the cache. |
mailchimp_get_lists_for_email | Returns all lists a given email address is currently subscribed to. |
mailchimp_get_marketing_permissions | Get the marketing permissions for a subscribed member. |
mailchimp_get_memberinfo | Get the Mailchimp member info for a given email address and list. |
mailchimp_get_members | Retrieve all members of a given list with a given status. |
mailchimp_get_mergevars | Wrapper around MailchimpLists->getMergeFields(). |
mailchimp_insert_drupal_form_tag | Convert Mailchimp form elements to Drupal Form API. |
mailchimp_interest_groups_form_elements | Helper function to generate form elements for a list's interest groups. |
mailchimp_is_subscribed | Check if the given email is subscribed to the given list. |
mailchimp_page_bottom | Implements hook_page_bottom(). |
mailchimp_process_webhook | Processes a webhook post from Mailchimp. |
mailchimp_process_webhook_access | Access callback for mailchimp_process_webhook(). |
mailchimp_subscribe | Subscribe a user to a Mailchimp list in real time or by adding to the queue. |
mailchimp_subscribe_process | Wrapper around Mailchimp_Lists::subscribe(). |
mailchimp_unsubscribe | Unsubscribes a member from a Mailchimp list. |
mailchimp_unsubscribe_process | Unsubscribes a member from a Mailchimp list. |
mailchimp_update_member | Update a members list subscription in real time or by adding to the queue. |
mailchimp_update_member_process | Wrapper around Mailchimp_Lists::updateMember(). |
mailchimp_validate_email | Wrapper for email validation function in core. |
mailchimp_webhook_add | Adds a webhook to a Mailchimp list. |
mailchimp_webhook_delete | Deletes a Mailchimp list webhook. |
mailchimp_webhook_get | Returns all webhooks for a given Mailchimp list ID. |
mailchimp_webhook_key | Generates a key to include in the webhook URL based on a hash. |
mailchimp_webhook_url | Generates the webhook endpoint URL. |
_mailchimp_get_user_agent | Gets the user agent string for this installation of Mailchimp. |
_mailchimp_list_cmp | Helper function used by uasort() to sort lists alphabetically by name. |