hasPermission($permission)) { return TRUE; } return FALSE; } /** * Instantiates a Mailchimp library object. * * @return \Mailchimp\Mailchimp * Drupal Mailchimp library object. */ function mailchimp_get_api_object($classname = 'MailchimpApiUser') { $object = \Drupal::service('mailchimp.client_factory')->getByClassNameOrNull($classname); if (!$object) { \Drupal::messenger()->addError('Failed to load Mailchimp PHP library. Please refer to the installation requirements.'); return NULL; } $config = \Drupal::config('mailchimp.settings'); if (!$config->get('test_mode') && !$object->hasApiAccess()) { $mc_oauth_url = Url::fromRoute('mailchimp.admin.oauth'); \Drupal::messenger()->addError(t('Unable to connect to Mailchimp API. Visit @oauth_settings_page to authenticate or uncheck "Use OAuth Authentication" and add an api_key below (deprecated).', ['@oauth_settings_page' => Link::fromTextAndUrl(t('OAuth Settings page'), $mc_oauth_url)->toString()])); return NULL; } return $object; } /** * 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 " . 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 == MailchimpLists::MEMBER_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 == MailchimpLists::MEMBER_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, $tags = NULL) { $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, 'tags' => $tags, ]; return mailchimp_addto_queue('mailchimp_subscribe_process', $args); } return mailchimp_subscribe_process($list_id, $email, $merge_vars, $interests, $double_optin, $format, $language, $gdpr_consent, $tags); } /** * 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, $tags = NULL) { $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, ]); } } if ($double_optin) { $msg = $config->get('optin_check_email_msg'); if ($msg) { \Drupal::messenger()->addStatus($msg, FALSE); } } // Add or update member tags. if ($tags) { $tags = explode(',', (string) $tags); $tags = array_map('trim', $tags); try { $mc_lists->addTagsMember($list_id, $tags, $email); } catch (ClientException $e) { \Drupal::logger('mailchimp')->error('An error occurred while adding tags for this email({email}) to Mailchimp: {message}', [ 'message' => $e->getMessage(), 'email' => $email, ]); } } } 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, $tags); } \Drupal::logger('mailchimp')->error('An error occurred subscribing {email} to list {list}. "{message}"', [ 'email' => $email, 'list' => $list_id, 'message' => $e->getMessage(), ]); } 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, $tags = NULL) { $config = \Drupal::config('mailchimp.settings'); if ($config->get('cron')) { $args = [ 'list_id' => $list_id, 'email' => $email, 'merge_vars' => $merge_vars, 'interests' => $interests, 'tags' => $tags, '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, $tags); } /** * 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, $tags = NULL) { $config = \Drupal::config('mailchimp.settings'); $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, $tags); } \Drupal::logger('mailchimp')->error('An error occurred updating {email} on list {list}. "{message}"', [ 'email' => $email, 'list' => $list_id, 'message' => $e->getMessage(), ]); } if ($double_optin) { $msg = $config->get('optin_check_email_msg'); if ($msg) { \Drupal::messenger()->addStatus($msg, FALSE); } } // Add or update member tags if ($tags) { $tags = explode(',', (string) $tags); $tags = array_map('trim', $tags); try { $mcapi->addTagsMember($list_id, $tags, $email); } catch (ClientException $e) { \Drupal::logger('mailchimp')->error('An error occurred while adding tags for this email({email}) to Mailchimp: {message}', [ 'message' => $e->getMessage(), 'email' => $email, ]); } } 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 = MailchimpLists::MEMBER_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->updateMember($list_id, $email, ['status' => MailchimpLists::MEMBER_STATUS_UNSUBSCRIBED]); \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']; } /** * 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 the webhook endpoint URL. * * @return string * The endpoint URL. */ function mailchimp_webhook_url($hash = NULL, $lang = NULL) { if (is_null($hash)) { $hash = \Drupal::config('mailchimp.settings')->get('webhook_hash'); } if (is_null($lang)) { $lang = \Drupal::languageManager()->getDefaultLanguage()->getId(); } return Url::fromRoute( 'mailchimp.webhook_endpoint', [ 'hash' => $hash, 'language' => $lang, ] )->setAbsolute()->toString(); } /** * 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' => $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 = $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' => $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 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] = t($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; if (!empty($mergevar->options->size)) { $input['#size'] = $mergevar->options->size; } else { $input['#size'] = 25; } $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($batch_limit = NULL) { $queue = \Drupal::queue(MAILCHIMP_QUEUE_CRON); $queue->createQueue(); $queue_count = $queue->numberOfItems(); if ($queue_count > 0) { $config = \Drupal::config('mailchimp.settings'); if (is_null($batch_limit)) { $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++; } } return $batch_size ?? $queue_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'); $current_path = \Drupal::service('path.current')->getPath(); $current_alias = \Drupal::service('path_alias.manager')->getAliasByPath($current_path); if ($connected_site_paths && (\Drupal::service('path.matcher')->matchPath($current_path, $connected_site_paths) || \Drupal::service('path.matcher')->matchPath($current_alias, $connected_site_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(), ]); } } } } }