'MailChimp MCAPI', 'vendor url' => 'http://apidocs.mailchimp.com/api/1.3', 'download url' => 'http://apidocs.mailchimp.com/api/downloads/mailchimp-api-class.zip', 'version arguments' => array( 'file' => 'MCAPI.class.php', // Version 1.3 'pattern' => '/\$version = \"((\d+)\.(\d+))\"/', ), 'files' => array( 'php' => array('MCAPI.class.php'), ), ); return $libraries; } /** * Implements hook_menu(). */ function mailchimp_menu() { $items = array(); $items['admin/config/services/mailchimp'] = array( 'title' => 'MailChimp', 'description' => 'Manage MailChimp Settings.', 'page callback' => 'drupal_get_form', 'page arguments' => array('mailchimp_admin_settings'), 'access arguments' => array('administer mailchimp'), 'file' => 'includes/mailchimp.admin.inc', 'type' => MENU_NORMAL_ITEM, ); $items['admin/config/services/mailchimp/global'] = array( 'title' => 'Global Settings', 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => 10, ); $items['mailchimp/webhook'] = array( 'title' => 'MailChimp webhooks endpoint', 'page callback' => 'mailchimp_process_webhook', 'access callback' => 'mailchimp_process_webhook_access', 'access arguments' => array(2), 'type' => MENU_CALLBACK, ); return $items; } /** * Implements hook_permission(). */ function mailchimp_permission() { return array( 'administer mailchimp' => array( 'title' => t('administer mailchimp'), 'description' => t('TODO Add a description for administer mailchimp'), ), ); } /** * 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 * Indicates subscription status. */ 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 given list. */ function mailchimp_subscribe_user($list, $email, $merge_vars, $message = TRUE, $mcapi = NULL) { $success = FALSE; if ($mcapi || $mcapi = mailchimp_get_api_object()) { $double_optin = $list->settings['doublein']; $success = $mcapi->listSubscribe($list->mc_list_id, $email, $merge_vars, 'html', $double_optin, TRUE); if ($message && $success && $double_optin) { drupal_set_message( t( 'You have chosen to subscribe to %list. An email will be sent to your address. Click the link in the email to confirm the subscription.', array('%list' => $list->label) ) ); } elseif ($message && $success) { drupal_set_message(t('You have subscribed to %list.', array('%list' => $list->label))); } // Clear user cache, just in case there's some cruft leftover: mailchimp_cache_clear_user($list->mc_list_id, $email); if ($success) { module_invoke_all('mailchimp_subscribe_user', $list, $email, $merge_vars); watchdog( 'mailchimp', '@email was subscribed to list @list.', array('@email' => $merge_vars['EMAIL'], '@list' => $list->label), WATCHDOG_NOTICE ); } else { watchdog( 'mailchimp', 'A problem occurred subscribing @email to list @list. Message: @msg', array( '@email' => $merge_vars['EMAIL'], '@list' => $list->label, '@msg' => $mcapi->errorMessage, ), WATCHDOG_WARNING ); } } return $success; } /** * Update a user in a given list. */ function mailchimp_update_user($list, $email, $merge_vars, $message = TRUE, $mcapi = NULL) { $success = FALSE; if ($mcapi || $mcapi = mailchimp_get_api_object()) { $success = $mcapi->listUpdateMember($list->mc_list_id, $email, $merge_vars); if ($success && $message) { drupal_set_message(t('You have updated your settings in %list.', array('%list' => $list->label))); } // Clear user cache: mailchimp_cache_clear_user($list->mc_list_id, $email); if ($success) { watchdog( 'mailchimp', '@email was updated in list @list.', array('@email' => $merge_vars['EMAIL'], '@list' => $list->label), WATCHDOG_NOTICE ); } else { watchdog( 'mailchimp', 'A problem occurred subscribing @email to list @list. Message: @msg', array( '@email' => $merge_vars['EMAIL'], '@list' => $list->label, '@msg' => $mcapi->errorMessage, ), WATCHDOG_WARNING ); } } return $success; } /** * Unsubscribe a user from the given list. * * @param stdclass $list * A mailchimp lists entity. * @param string $email * Email address to be unsubscribed. * @param bool $message * Whether or not to display a message in the web interface. * @param object $mcapi * Mailchimp API object if one is already loaded. * @param bool $delete * Indicates whether an email should be deleted or just unsubscribed. * * @return bool * Indicates whether unsubscribe was successful. */ function mailchimp_unsubscribe_user($list, $email, $message = TRUE, $mcapi = NULL, $delete = FALSE) { $success = FALSE; if ($mcapi || $mcapi = mailchimp_get_api_object()) { if (mailchimp_is_subscribed($list->mc_list_id, $email)) { $success = $mcapi->listUnsubscribe($list->mc_list_id, $email, $delete, FALSE, FALSE); if ($success) { module_invoke_all('mailchimp_unsubscribe_user', $list, $email); if ($message) { drupal_set_message(t('You have unsubscribed from %list.', array('%list' => $list->label))); } } // Clear user cache: mailchimp_cache_clear_user($list->mc_list_id, $email); } } return $success; } /** * Get a Mailchimp API object for communication with the mailchimp server. * * @return MCAPI|NULL * A valid MCAPI implementation or NULL if no one found. */ function mailchimp_get_api_object() { $library = libraries_load('mailchimp'); if (!$library['installed']) { $msg = 'Failed to load MailChimp PHP library. Please refer to the installation requirements.'; watchdog('mailchimp', $msg, array(), WATCHDOG_ERROR); drupal_set_message(t($msg), 'error'); return NULL; } $api_key = variable_get('mailchimp_api_key', ''); // We allow the class name to be overridden, following the example of core's // mailsystem, in order to use alternate MailChimp classes. The bundled tests // use this approach to extend the MailChimp class with a test server. $classname = variable_get('mailchimp_api_classname', 'MailChimp'); $q = new $classname($api_key); // Set the timeout to something that won't take down the Drupal site: $q->setTimeout(60); // Specify if a secure connection should be used with the API: $q->useSecure(variable_get('mailchimp_use_secure', TRUE)); if ($q->errorCode) { watchdog('mailchimp', 'MCAPI Error: %errmsg', array('%errmsg' => $q->errorMessage), WATCHDOG_ERROR); return NULL; } return $q; } /** * Return 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 arrays. */ function mailchimp_get_lists($list_ids = array(), $reset = FALSE) { $cache = $reset ? NULL : cache_get('mailchimp_lists'); $lists = array(); // Return cached lists: if ($cache) { $lists = $cache->data; } // Query lists from the MC API and store in cache: else { if ($q = mailchimp_get_api_object()) { $result = $q->lists(array(), 0, 100); if ($result['total'] > 0) { foreach ($result['data'] as $list) { // Append mergefields: $list['mergevars'] = $q->listMergeVars($list['id']); // Append interest groups: $list['intgroups'] = $q->listInterestGroupings($list['id']); $lists[$list['id']] = $list; } } } uasort($lists, '_mailchimp_list_cmp'); cache_set('mailchimp_lists', $lists, 'cache', CACHE_TEMPORARY); } // Filter by given ids: if (!empty($list_ids)) { foreach ($lists as $key => $list) { if (!in_array($key, $list_ids)) { unset($lists[$key]); } } } return $lists; } /** * Wrapper around mailchimp_get_lists() to return a single list. * * @param string $list_id * The unique ID of the list provided by MailChimp. * * @return array * A list array formatted as indicated in the MailChimp API documentation. */ function mailchimp_get_list($list_id) { $lists = mailchimp_get_lists(array($list_id)); return reset($lists); } /** * Get the MailChimp memberinfo for a given email address and list. * * Results are cached in the cache_mailchimp_user bin which is cleared by the MC * web hooks system when needed. * * @param string $list_id * @param string $email * @param bool $reset * * @return array * memberinfo array. */ function mailchimp_get_memberinfo($list_id, $email, $reset = FALSE) { $cache = $reset ? NULL : cache_get($list_id . '-' . $email, 'cache_mailchimp_user'); $memberinfo = array(); // Return cached lists: if ($cache) { $memberinfo = $cache->data; } // Query lists from the MC API and store in cache: else { if ($q = mailchimp_get_api_object()) { $result = $q->listMemberInfo($list_id, array($email)); if ($result['success']) { $memberinfo = reset($result['data']); } } cache_set($list_id . '-' . $email, $memberinfo, 'cache_mailchimp_user', CACHE_PERMANENT); } return $memberinfo; } /** * Sets the member info in the cache. * * @param string $list_id * @param string $email */ function mailchimp_set_memberinfo($list_id, $email) { mailchimp_get_memberinfo($list_id, $email, TRUE); } /** * Clear a mailchimp user memberinfo cache. * * @param string $list_id * @param $email */ function mailchimp_cache_clear_user($list_id, $email) { cache_clear_all($list_id . '-' . $email, 'cache_mailchimp_user'); } /** * Clear a mailchimp activity cache. */ function mailchimp_cache_clear_list_activity($list_id) { cache_clear_all('mailchimp_activity_' . $list_id, 'cache'); } /** * Clear a mailchimp activity cache. */ function mailchimp_cache_clear_campaign($campaign_id) { cache_clear_all('mailchimp_campaign_' . $campaign_id, 'cache'); } /** * Implements hook_flush_caches(). */ function mailchimp_flush_caches() { return array('cache_mailchimp_user'); } /** * Access callback for mailchimp_process_webhook(). */ function mailchimp_process_webhook_access($key) { return $key == mailchimp_webhook_key(); } /** * Process a webhook post from MailChimp. */ function mailchimp_process_webhook() { $data = $_POST['data']; $type = $_POST['type']; switch ($type) { case 'unsubscribe': case 'profile': case 'cleaned': mailchimp_cache_clear_user($data['list_id'], $data['email']); mailchimp_set_memberinfo($data['list_id'], $data['email']); break; case 'upemail': mailchimp_cache_clear_user($data['list_id'], $data['old_email']); mailchimp_set_memberinfo($data['list_id'], $data['new_email']); 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. module_invoke_all('mailchimp_process_webhook', $type, $data); // Log event: watchdog( 'mailchimp', 'Webhook type @type has been processed.', array('@type' => $type), WATCHDOG_INFO ); } /** * Generate a key to include in the webhook url based on a hash. * * @return string * The key. */ function mailchimp_webhook_key() { return drupal_hash_base64($GLOBALS['base_url'] . drupal_get_private_key() . drupal_get_hash_salt()); } /** * Generate the webhook endpoint URL. * * @return string * The endpoint URL. */ function mailchimp_webhook_url() { return $GLOBALS['base_url'] . '/mailchimp/webhook/' . mailchimp_webhook_key(); } /** * Helper function used by uasort() to sort lists alphabetically by name. * * @param array $a * An array representing the first list. * @param array $b * An array 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 MCAPI::campaigns() to return data for a given campaign. * * Data is stored in the temporary cache. * * @param string $campaign_id * @param bool $reset * * @return mixed * Array of campaign data or NULL if not found. */ function mailchimp_get_campaign_data($campaign_id, $reset = FALSE) { $cache = $reset ? NULL : cache_get('mailchimp_campaign_' . $campaign_id); // Return cached lists: if ($cache) { $campaign_data = $cache->data; } else { $q = mailchimp_get_api_object(); $filters = array( 'campaign_id' => $campaign_id, ); $result_sandwich = $q->campaigns($filters); $campaign_data = $result_sandwich['data'][0]; cache_set('mailchimp_campaign_' . $campaign_id, $campaign_data, 'cache', CACHE_TEMPORARY); } return $campaign_data; } /** * Wrapper around MCAPI::campaignsForEmail(). * * Returns all IDs of campaigns that have included a given email address. * * @param string $email * * @return array * campaign IDs. */ function mailchimp_get_campaigns_for_email($email) { $q = mailchimp_get_api_object(); $campaign_list = $q->campaignsForEmail($email); return $campaign_list; } /** * Returns an array of lists that the user has been a member of. * * Includes a value indicating whether they are currently a subscribed or not. * * @param string $email * * @return array * Containing 2 arrays -- one ('lists') of all lists that have made use of * this email address (list_id -> list_data[]). One ('campaigns') of all * campaigns that have included this email (campaign_id -> campaign_data[]) * * We include the campaign data because we need it to get accurate list * activity history anyway, and we want to keep the data handy to avoid * excessive API calls. */ function mailchimp_get_lists_by_email($email) { $campaign_ids = mailchimp_get_campaigns_for_email($email); $lists = mailchimp_get_lists(); $filtered_lists = array(); if ($campaign_ids) { // Iterate through campaigns, add each campaign's list as array index/value. foreach ($campaign_ids as $campaign_id) { $campaign_data = mailchimp_get_campaign_data($campaign_id); $filtered_lists['lists'][$campaign_data['list_id']] = $lists[$campaign_data['list_id']]; $filtered_lists['campaigns'][$campaign_id] = $campaign_data; } } return $filtered_lists; }