/**
  * Function to actually build the form
  *
  * @return None
  * @access public
  */
 public function buildQuickForm()
 {
     $this->addFormRule(array('CRM_Mailchimp_Form_Setting', 'formRule'), $this);
     CRM_Core_Resources::singleton()->addStyleFile('uk.co.vedaconsulting.mailchimp', 'css/mailchimp.css');
     $webhook_url = CRM_Utils_System::url('civicrm/mailchimp/webhook', 'reset=1', TRUE, NULL, FALSE, TRUE);
     $this->assign('webhook_url', 'Webhook URL - ' . $webhook_url);
     // Add the API Key Element
     $this->addElement('text', 'api_key', ts('API Key'), array('size' => 48));
     // Add the User Security Key Element
     $this->addElement('text', 'security_key', ts('Security Key'), array('size' => 24));
     // Add Enable or Disable Debugging
     $enableOptions = array(1 => ts('Yes'), 0 => ts('No'));
     $this->addRadio('enable_debugging', ts('Enable Debugging'), $enableOptions, NULL);
     // Create the Submit Button.
     $buttons = array(array('type' => 'submit', 'name' => ts('Save & Test')));
     $groups = CRM_Mailchimp_Utils::getGroupsToSync(array(), null, $membership_only = TRUE);
     foreach ($groups as $group_id => $details) {
         $list = new Mailchimp_Lists(CRM_Mailchimp_Utils::mailchimp());
         $webhookoutput = $list->webhooks($details['list_id']);
         if ($webhookoutput[0]['sources']['api'] == 1) {
             CRM_Mailchimp_Utils::checkDebug('CRM_Mailchimp_Form_Setting - API is set in Webhook setting for listID', $details['list_id']);
             $listID = $details['list_id'];
             CRM_Core_Session::setStatus(ts('API is set in Webhook setting for listID %1', array(1 => $listID)), ts('Error'), 'error');
             break;
         }
     }
     // Add the Buttons.
     $this->addButtons($buttons);
 }
 public function __construct($list_id)
 {
     $this->list_id = $list_id;
     $this->group_details = CRM_Mailchimp_Utils::getGroupsToSync($groupIDs = [], $list_id, $membership_only = FALSE);
     foreach ($this->group_details as $group_id => $group_details) {
         if (empty($group_details['category_id'])) {
             $this->membership_group_id = $group_id;
         }
     }
     if (empty($this->membership_group_id)) {
         throw new InvalidArgumentException("Failed to find mapped membership group for list '{$list_id}'");
     }
     // Also cache without the membership group, i.e. interest groups only.
     $this->interest_group_details = $this->group_details;
     unset($this->interest_group_details[$this->membership_group_id]);
 }
 static function manageCiviCRMGroupSubcription($contactID = array(), $requestData, $action)
 {
     CRM_Mailchimp_Utils::checkDebug('Start- CRM_Mailchimp_Page_WebHook manageCiviCRMGroupSubcription $contactID= ', $contactID);
     CRM_Mailchimp_Utils::checkDebug('Start- CRM_Mailchimp_Page_WebHook manageCiviCRMGroupSubcription $requestData= ', $requestData);
     CRM_Mailchimp_Utils::checkDebug('Start- CRM_Mailchimp_Page_WebHook manageCiviCRMGroupSubcription $requestType= ', $action);
     if (empty($contactID) || empty($requestData['list_id']) || empty($action)) {
         return NULL;
     }
     $listID = $requestData['list_id'];
     $groupContactRemoves = $groupContactAdditions = array();
     // Deal with subscribe/unsubscribe.
     // We need the CiviCRM membership group for this list.
     $groups = CRM_Mailchimp_Utils::getGroupsToSync(array(), $listID, $membership_only = TRUE);
     $allGroups = CRM_Mailchimp_Utils::getGroupsToSync(array(), $listID, $membership_only = FALSE);
     if (!$groups) {
         // This list is not mapped to a group in CiviCRM.
         return NULL;
     }
     $_ = array_keys($groups);
     $membershipGroupID = $_[0];
     if ($action == 'subscribe') {
         $groupContactAdditions[$membershipGroupID][] = $contactID;
     } elseif ($action == 'unsubscribe') {
         $groupContactRemoves[$membershipGroupID][] = $contactID;
         $mcGroupings = array();
         foreach (empty($requestData['merges']['GROUPINGS']) ? array() : $requestData['merges']['GROUPINGS'] as $grouping) {
             foreach (explode(', ', $grouping['groups']) as $group) {
                 $mcGroupings[$grouping['id']][$group] = 1;
             }
         }
         foreach ($allGroups as $groupID => $details) {
             if ($groupID != $membershipGroupID && $details['is_mc_update_grouping']) {
                 if (!empty($mcGroupings[$details['grouping_id']][$details['group_name']])) {
                     $groupContactRemoves[$groupID][] = $contactID;
                 }
             }
         }
     }
     // Now deal with all the groupings that are mapped to CiviCRM groups for this list
     // and that have the allow MC updates flag set.
     /* Sample groupings from MC:
      *
      *     [GROUPINGS] => Array(
      *       [0] => Array(
      *           [id] => 11365
      *           [name] => CiviCRM
      *           [groups] => special
      *       ))
      * Re-map to mcGroupings[grouping_id][group_name] = 1;
      */
     $mcGroupings = array();
     foreach (empty($requestData['merges']['GROUPINGS']) ? array() : $requestData['merges']['GROUPINGS'] as $grouping) {
         foreach (explode(', ', $grouping['groups']) as $group) {
             $mcGroupings[$grouping['id']][$group] = 1;
         }
     }
     $groups = CRM_Mailchimp_Utils::getGroupsToSync(array(), $listID, $membership_only = FALSE);
     CRM_Mailchimp_Utils::checkDebug('Middle- CRM_Mailchimp_Page_WebHook manageCiviCRMGroupSubcription $groups ', $groups);
     CRM_Mailchimp_Utils::checkDebug('Middle- CRM_Mailchimp_Page_WebHook manageCiviCRMGroupSubcription $mcGroupings ', $mcGroupings);
     foreach ($groups as $groupID => $details) {
         if ($groupID != $membershipGroupID && $details['is_mc_update_grouping']) {
             // This is a group we allow updates for.
             if (empty($mcGroupings[$details['grouping_id']][$details['group_name']])) {
                 $groupContactRemoves[$groupID][] = $contactID;
             } else {
                 $groupContactAdditions[$groupID][] = $contactID;
             }
         }
     }
     // Add contacts to groups, if anything to do.
     foreach ($groupContactAdditions as $groupID => $contactIDs) {
         CRM_Contact_BAO_GroupContact::addContactsToGroup($contactIDs, $groupID, 'Admin', 'Added');
     }
     // Remove contacts from groups, if anything to do.
     foreach ($groupContactRemoves as $groupID => $contactIDs) {
         CRM_Contact_BAO_GroupContact::removeContactsFromGroup($contactIDs, $groupID, 'Admin', 'Removed');
     }
     CRM_Mailchimp_Utils::checkDebug('End - CRM_Mailchimp_Page_WebHook manageCiviCRMGroupSubcription $groupContactRemoves ', $groupContactRemoves);
     CRM_Mailchimp_Utils::checkDebug('End - CRM_Mailchimp_Page_WebHook manageCiviCRMGroupSubcription $groupContactAdditions ', $groupContactAdditions);
     CRM_Mailchimp_Utils::checkDebug('End - CRM_Mailchimp_Page_WebHook manageCiviCRMGroupSubcription $contactID= ', $contactID);
     CRM_Mailchimp_Utils::checkDebug('End - CRM_Mailchimp_Page_WebHook manageCiviCRMGroupSubcription $requestData= ', $requestData);
     CRM_Mailchimp_Utils::checkDebug('End - CRM_Mailchimp_Page_WebHook manageCiviCRMGroupSubcription $requestType= ', $action);
 }
/**
 * Implementation of hook_civicrm_post
 *
 * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_post
 */
function mailchimp_civicrm_post($op, $objectName, $objectId, &$objectRef)
{
    /***** NO BULK EMAILS (User Opt Out) *****/
    if ($objectName == 'Individual' || $objectName == 'Organization' || $objectName == 'Household') {
        // Contact Edited
        if ($op == 'edit' || $op == 'create') {
            if ($objectRef->is_opt_out == 1) {
                $action = 'unsubscribe';
            } else {
                $action = 'subscribe';
            }
            // Get all groups, the contact is subscribed to
            $civiGroups = CRM_Contact_BAO_GroupContact::getGroupList($objectId);
            $civiGroups = array_keys($civiGroups);
            if (empty($civiGroups)) {
                return;
            }
            // Get mailchimp details
            $groups = CRM_Mailchimp_Utils::getGroupsToSync($civiGroups);
            if (!empty($groups)) {
                // Loop through all groups and unsubscribe the email address from mailchimp
                foreach ($groups as $groupId => $groupDetails) {
                    CRM_Mailchimp_Utils::subscribeOrUnsubsribeToMailchimpList($groupDetails, $objectId, $action);
                }
            }
        }
    }
    /***** Contacts added/removed/deleted from CiviCRM group *****/
    if ($objectName == 'GroupContact') {
        // FIXME: Dirty hack to skip hook
        require_once 'CRM/Core/Session.php';
        $session = CRM_Core_Session::singleton();
        $skipPostHook = $session->get('skipPostHook');
        // Added/Removed/Deleted - This works for both bulk action and individual add/remove/delete
        if (($op == 'create' || $op == 'edit' || $op == 'delete') && empty($skipPostHook)) {
            // Decide mailchimp action based on $op
            // Add / Rejoin Group
            if ($op == 'create' || $op == 'edit') {
                $action = 'subscribe';
            } elseif ($op == 'delete') {
                $action = 'unsubscribe';
            }
            // Get mailchimp details for the group
            $groups = CRM_Mailchimp_Utils::getGroupsToSync(array($objectId));
            // Proceed only if the group is configured with mailing list/groups
            if (!empty($groups[$objectId])) {
                // Loop through all contacts added/removed from the group
                foreach ($objectRef as $contactId) {
                    // Subscribe/Unsubscribe in Mailchimp
                    CRM_Mailchimp_Utils::subscribeOrUnsubsribeToMailchimpList($groups[$objectId], $contactId, $action);
                }
            }
        }
    }
}
/**
 * Implementation of hook_civicrm_post
 *
 * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_post
 */
function mailchimp_civicrm_post($op, $objectName, $objectId, &$objectRef)
{
    if (!CRM_Mailchimp_Utils::$post_hook_enabled) {
        // Post hook is disabled at this point in the running.
        return;
    }
    /***** NO BULK EMAILS (User Opt Out) *****/
    if ($objectName == 'Individual' || $objectName == 'Organization' || $objectName == 'Household') {
        // Contact Edited
        // @todo artfulrobot: I don't understand the cases this is dealing with.
        //                    Perhaps it was trying to check that if someone's been
        //                    marked as 'opt out' then they're unsubscribed from all
        //                    mailings. I could not follow the logic though -
        //                    without tests in place I thought it was better
        //                    disabled.
        if (FALSE) {
            if ($op == 'edit' || $op == 'create') {
                if ($objectRef->is_opt_out == 1) {
                    $action = 'unsubscribe';
                } else {
                    $action = 'subscribe';
                }
                // Get all groups, the contact is subscribed to
                $civiGroups = CRM_Contact_BAO_GroupContact::getGroupList($objectId);
                $civiGroups = array_keys($civiGroups);
                if (empty($civiGroups)) {
                    return;
                }
                // Get mailchimp details
                $groups = CRM_Mailchimp_Utils::getGroupsToSync($civiGroups);
                if (!empty($groups)) {
                    // Loop through all groups and unsubscribe the email address from mailchimp
                    foreach ($groups as $groupId => $groupDetails) {
                        // method removed. CRM_Mailchimp_Utils::subscribeOrUnsubsribeToMailchimpList($groupDetails, $objectId, $action);
                    }
                }
            }
        }
    }
    /***** Contacts added/removed/deleted from CiviCRM group *****/
    if ($objectName == 'GroupContact') {
        // Determine if the action being taken needs to affect Mailchimp at all.
        if ($op == 'view') {
            // Nothing changed; nothing to do.
            return;
        }
        // Get mailchimp details for the group.
        // $objectId here means CiviCRM group Id.
        $groups = CRM_Mailchimp_Utils::getGroupsToSync(array($objectId));
        if (empty($groups[$objectId])) {
            // This group has nothing to do with Mailchimp.
            return;
        }
        // The updates we need to make can be complex.
        // If someone left/joined a group synced as the membership group for a
        // Mailchimp list, then that's a subscribe/unsubscribe option.
        // If however it was a group synced to an interest in Mailchimp, then
        // the join/leave on the CiviCRM side only means updating interests on the
        // Mailchimp side, not a subscribe/unsubscribe.
        // There is also the case that somone's been put into an interest group, but
        // is not in the membership group, which should not result in them being
        // subscribed at MC.
        if ($groups[$objectId]['interest_id']) {
            // This is a change to an interest grouping.
            // We only need update Mailchimp about this if the contact is in the
            // membership group.
            $list_id = $groups[$objectId]['list_id'];
            // find membership group, then find out if the contact is in that group.
            $membership_group_details = CRM_Mailchimp_Utils::getGroupsToSync(array(), $list_id, TRUE);
            $result = civicrm_api3('Contact', 'getsingle', ['return' => 'group', 'contact_id' => $objectRef[0]]);
            if (!CRM_Mailchimp_Utils::getGroupIds($result['groups'], $membership_group_details)) {
                // This contact is not in the membership group, so don't bother telling
                // Mailchimp about a change in their interests.
                return;
            }
        }
        // Finally this hook is useful for small changes only; if you just added
        // thousands of people to a group then this is NOT the way to tell Mailchimp
        // about it as it would require thousands of separate API calls. This would
        // probably cause big problems (like hitting the API rate limits, or
        // crashing CiviCRM due to PHP max execution times etc.). Such updates must
        // happen in the more controlled bulk update (push).
        if (count($objectRef) > 1) {
            // Limit application to one contact only.
            CRM_Core_Session::setStatus(ts('You have made a bulk update that means CiviCRM contacts and Mailchimp are no longer in sync. You should do an "Update Mailchimp from CiviCRM" sync to ensure the changes you have made are applied at Mailchimp.'), ts('Update Mailchimp from CiviCRM required.'));
            return;
        }
        // Trigger mini sync for this person and this list.
        $sync = new CRM_Mailchimp_Sync($groups[$objectId]['list_id']);
        $sync->updateMailchimpFromCiviSingleContact($objectRef[0]);
    }
}
 /**
  * New contacts from Mailchimp need bringing into CiviCRM.
  */
 static function syncPullUpdates(CRM_Queue_TaskContext $ctx, $listID)
 {
     // Prepare the groups that we need to update
     $stats[$listID]['added'] = $stats[$listID]['removed'] = 0;
     // We need the membership group and any groups mapped to interest groupings with the allow MC updates option set.
     $membership_group_id = FALSE;
     $updatable_grouping_groups = array();
     foreach (CRM_Mailchimp_Utils::getGroupsToSync(array(), $listID) as $groupID => $details) {
         if (!$details['grouping_id']) {
             $membership_group_id = $groupID;
         } elseif ($details['is_mc_update_grouping']) {
             // This group is one that we allow Mailchimp to update CiviCRM with.
             $updatable_grouping_groups[$groupID] = $details;
         }
     }
     // all Mailchimp table
     $dao = CRM_Core_DAO::executeQuery("SELECT m.*, c.groupings c_groupings\n      FROM tmp_mailchimp_push_m m\n      LEFT JOIN tmp_mailchimp_push_c c ON m.email = c.email\n      ;");
     // Loop the $dao object creating/finding contacts in CiviCRM.
     $groupContactRemoves = $groupContact = array();
     while ($dao->fetch()) {
         $params = array('FNAME' => $dao->first_name, 'LNAME' => $dao->last_name, 'EMAIL' => $dao->email);
         // Update/create contact.
         $contact_id = CRM_Mailchimp_Utils::updateContactDetails($params);
         if ($contact_id) {
             // Ensure the contact is in the membership group.
             if (!$dao->c_groupings) {
                 // This contact was not found in the CiviCRM table.
                 // Therefore they are not in the membership group.
                 // (actually they could have an email problem as well, but that's OK).
                 // Add them into the membership group.
                 $groupContact[$membership_group_id][] = $contact_id;
                 $civi_groupings = array();
                 $stats[$listID]['added']++;
             } else {
                 // This contact is in C and MC, but has differences.
                 // unpack the group membership from CiviCRM.
                 $civi_groupings = unserialize($dao->c_groupings);
             }
             // unpack the group membership reported by MC
             $mc_groupings = unserialize($dao->groupings);
             // Now sort out the grouping_groups for those we are supposed to allow updates for
             foreach ($updatable_grouping_groups as $groupID => $details) {
                 // Should this person be in this grouping:group according to MC?
                 if (!empty($mc_groupings[$details['grouping_id']][$details['group_id']])) {
                     // They should be in this group.
                     if (empty($civi_groupings[$details['grouping_id']][$details['group_id']])) {
                         // But they're not! Plan to add them in.
                         $groupContact[$groupID][] = $contact_id;
                     }
                 } else {
                     // They should NOT be in this group.
                     if (!empty($civi_groupings[$details['grouping_id']][$details['group_id']])) {
                         // But they ARE. Plan to remove them.
                         $groupContactRemoves[$groupID][] = $contact_id;
                     }
                 }
             }
         }
     }
     // And now, what if a contact is not in the Mailchimp list? We must remove them from the membership group.
     $dao = CRM_Core_DAO::executeQuery("SELECT c.contact_id\n      FROM tmp_mailchimp_push_c c\n      WHERE NOT EXISTS (\n        SELECT m.email FROM tmp_mailchimp_push_m m WHERE m.email=c.email\n      );");
     // Loop the $dao object creating/finding contacts in CiviCRM.
     while ($dao->fetch()) {
         $groupContactRemoves[$membership_group_id][] = $dao->contact_id;
         $stats[$listID]['removed']++;
     }
     // Log group contacts which are going to be added to CiviCRM
     CRM_Core_Error::debug_var('Mailchimp $groupContact= ', $groupContact);
     // FIXME: dirty hack setting a variable in session to skip post hook
     require_once 'CRM/Core/Session.php';
     $session = CRM_Core_Session::singleton();
     $session->set('skipPostHook', 'yes');
     if ($groupContact) {
         // We have some contacts to add into groups...
         foreach ($groupContact as $groupID => $contactIDs) {
             CRM_Contact_BAO_GroupContact::addContactsToGroup($contactIDs, $groupID, 'Admin', 'Added');
         }
     }
     // Log group contacts which are going to be removed from CiviCRM
     CRM_Core_Error::debug_var('Mailchimp $groupContactRemoves= ', $groupContactRemoves);
     if ($groupContactRemoves) {
         // We have some contacts to add into groups...
         foreach ($groupContactRemoves as $groupID => $contactIDs) {
             CRM_Contact_BAO_GroupContact::removeContactsFromGroup($contactIDs, $groupID, 'Admin', 'Removed');
         }
     }
     // FIXME: unset variable in session
     $session->set('skipPostHook', '');
     static::updatePullStats($stats);
     // Finally, finish up by removing the two temporary tables
     CRM_Core_DAO::executeQuery("DROP TABLE tmp_mailchimp_push_m;");
     CRM_Core_DAO::executeQuery("DROP TABLE tmp_mailchimp_push_c;");
     return CRM_Queue_Task::TASK_SUCCESS;
 }
/**
 * Mailchimp Get CiviCRM Group Mailchimp settings (Mailchimp List Id and Group)
 *
 * @param array $params
 * @return array API result descriptor
 * @see civicrm_api3_create_success
 * @see civicrm_api3_create_error
 * @throws API_Exception
 */
function civicrm_api3_mailchimp_getcivicrmgroupmailchimpsettings($params)
{
    $groupIds = empty($params['ids']) ? array() : explode(',', $params['ids']);
    $groups = CRM_Mailchimp_Utils::getGroupsToSync($groupIds);
    return civicrm_api3_create_success($groups);
}
/**
 * CiviCRM to Mailchimp Sync
 *
 * @param array $params
 * @return array API result descriptor
 * @see civicrm_api3_create_success
 * @see civicrm_api3_create_error
 * @throws API_Exception
 */
function civicrm_api3_mailchimp_sync($params)
{
    $groups = CRM_Mailchimp_Utils::getGroupsToSync(array(), null, $membership_only = TRUE);
    foreach ($groups as $group_id => $details) {
        $list = new Mailchimp_Lists(CRM_Mailchimp_Utils::mailchimp());
        $webhookoutput = $list->webhooks($details['list_id']);
        if ($webhookoutput[0]['sources']['api'] == 1) {
            return civicrm_api3_create_error('civicrm_api3_mailchimp_sync -  API is set in Webhook setting for listID ' . $details['list_id'] . ' Please uncheck API');
        }
    }
    $result = $pullResult = array();
    // Do pull first from mailchimp to CiviCRM
    $pullRunner = CRM_Mailchimp_Form_Pull::getRunner($skipEndUrl = TRUE);
    if ($pullRunner) {
        $pullResult = $pullRunner->runAll();
    }
    // Do push from CiviCRM to mailchimp
    $runner = CRM_Mailchimp_Form_Sync::getRunner($skipEndUrl = TRUE);
    if ($runner) {
        $result = $runner->runAll();
    }
    if ($pullResult['is_error'] == 0 && $result['is_error'] == 0) {
        return civicrm_api3_create_success();
    } else {
        return civicrm_api3_create_error();
    }
}
 /**
  * Collect CiviCRM data into temporary working table.
  */
 static function syncCollectCiviCRM($listID)
 {
     CRM_Mailchimp_Utils::checkDebug('Start-CRM_Mailchimp_Form_Sync syncCollectCiviCRM $listID= ', $listID);
     // Nb. these are temporary tables but we don't use TEMPORARY table because they are
     // needed over multiple sessions because of queue.
     CRM_Core_DAO::executeQuery("DROP TABLE IF EXISTS tmp_mailchimp_push_c;");
     $dao = CRM_Core_DAO::executeQuery("CREATE TABLE tmp_mailchimp_push_c (\n        contact_id INT(10) UNSIGNED NOT NULL,\n        email_id INT(10) UNSIGNED NOT NULL,\n        email VARCHAR(200),\n        first_name VARCHAR(100),\n        last_name VARCHAR(100),\n        hash CHAR(32),\n        groupings VARCHAR(4096),\n        PRIMARY KEY (email_id, email, hash)\n        );");
     // Cheekily access the database directly to obtain a prepared statement.
     $db = $dao->getDatabaseConnection();
     $insert = $db->prepare('INSERT INTO tmp_mailchimp_push_c VALUES(?, ?, ?, ?, ?, ?, ?)');
     //create table for mailchim civicrm syn errors
     $dao = CRM_Core_DAO::executeQuery("CREATE TABLE IF NOT EXISTS mailchimp_civicrm_syn_errors (\n        id int(11) NOT NULL AUTO_INCREMENT,\n        email VARCHAR(200),\n        error VARCHAR(200),\n        error_count int(10),\n        group_id int(20),\n        list_id VARCHAR(20),\n        PRIMARY KEY (id)\n        );");
     // We need to know what groupings we have maps to.
     // We only care about CiviCRM groups that are mapped to this MC List:
     $mapped_groups = CRM_Mailchimp_Utils::getGroupsToSync(array(), $listID);
     // First, get all subscribers from the membership group for this list.
     // ... Find CiviCRM group id for the membership group.
     // ... And while we're at it, build an SQL-safe array of groupIds for groups mapped to groupings.
     //     (we use that later)
     $membership_group_id = FALSE;
     $grouping_group_ids = array('normal' => array(), 'smart' => array());
     $default_info = array();
     foreach ($mapped_groups as $group_id => $details) {
         CRM_Contact_BAO_GroupContactCache::loadAll($group_id);
         if (!$details['grouping_id']) {
             $membership_group_id = $group_id;
         } else {
             $grouping_group_ids[$details['civigroup_uses_cache'] ? 'smart' : 'normal'][] = (int) $group_id;
             $default_info[$details['grouping_id']][$details['group_id']] = FALSE;
         }
     }
     $grouping_group_ids['smart'] = implode(',', $grouping_group_ids['smart']);
     $grouping_group_ids['normal'] = implode(',', $grouping_group_ids['normal']);
     if (!$membership_group_id) {
         throw new Exception("No CiviCRM group is mapped to determine membership of Mailchimp list {$listID}");
     }
     // ... Load all subscribers in $groupContact object
     if (!($groupContact = CRM_Mailchimp_Utils::getGroupContactObject($membership_group_id))) {
         CRM_Mailchimp_Utils::checkDebug('get group contact= ', $groupContact);
         throw new Exception("No CiviCRM group is mapped to determine membership of Mailchimp list {$listID}. CiviCRM group {$membership_group_id} failed to load");
     }
     // Now we iterate through the subscribers, collecting data about the other mapped groups
     // This is pretty inefficient :-(
     while ($groupContact->fetch()) {
         // Find the contact, for the name fields
         $contact = new CRM_Contact_BAO_Contact();
         $contact->id = $groupContact->contact_id;
         $contact->is_deleted = 0;
         if (!$contact->find(TRUE)) {
             continue;
         }
         // Find their primary (bulk) email
         $email = new CRM_Core_BAO_Email();
         $email->contact_id = $groupContact->contact_id;
         $email->is_primary = TRUE;
         if (!$email->find(TRUE)) {
             continue;
         }
         // If no email, it's like they're not there.
         if (!$email->email || $email->on_hold || $contact->is_opt_out || $contact->do_not_email) {
             //@todo update stats.
             continue;
         }
         // Find out if they're in any groups that we care about.
         // Start off as not in the groups...
         $info = $default_info;
         // We can do this with two queries, one for normal groups, one for smart groups.
         // Normal groups.
         if ($grouping_group_ids['normal']) {
             $groupContact2 = new CRM_Contact_BAO_GroupContact();
             $groupContact2->contact_id = $groupContact->contact_id;
             $groupContact2->whereAdd("status = 'Added'");
             $groupContact2->whereAdd("group_id IN ({$grouping_group_ids['normal']})");
             $groupContact2->find();
             while ($groupContact2->fetch()) {
                 // need MC grouping_id and group_id
                 $details = $mapped_groups[$groupContact2->group_id];
                 $info[$details['grouping_id']][$details['group_id']] = TRUE;
             }
             unset($groupContact2);
         }
         // Smart groups
         if ($grouping_group_ids['smart']) {
             $groupContactCache = new CRM_Contact_BAO_GroupContactCache();
             $groupContactCache->contact_id = $groupContact->contact_id;
             $groupContactCache->whereAdd("group_id IN ({$grouping_group_ids['smart']})");
             $groupContactCache->find();
             while ($groupContactCache->fetch()) {
                 // need MC grouping_id and group_id
                 $details = $mapped_groups[$groupContactCache->group_id];
                 $info[$details['grouping_id']][$details['group_id']] = TRUE;
             }
             unset($groupContactCache);
         }
         // OK we should now have all the info we need.
         // Serialize the grouping array for SQL storage - this is the fastest way.
         $info = serialize($info);
         // we're ready to store this but we need a hash that contains all the info
         // for comparison with the hash created from the CiviCRM data (elsewhere).
         //          email,           first name,      last name,      groupings
         $hash = md5($email->email . $contact->first_name . $contact->last_name . $info);
         // run insert prepared statement
         $db->execute($insert, array($contact->id, $email->id, $email->email, $contact->first_name, $contact->last_name, $hash, $info));
     }
     // Tidy up.
     $db->freePrepared($insert);
     // count
     $dao = CRM_Core_DAO::executeQuery("SELECT COUNT(*) c  FROM tmp_mailchimp_push_c");
     $dao->fetch();
     CRM_Mailchimp_Utils::checkDebug('End-CRM_Mailchimp_Form_Sync syncCollectCiviCRM $listID= ', $listID);
     return $dao->c;
 }
 /**
  * Check that list problems are spotted.
  *
  * 1. Test for missing webhooks.
  * 2. Test for error if the list is not found at Mailchimp.
  * 3. Test for network error.
  *
  * @depends testGetMCInterestGroupings
  */
 public function testCheckGroupsConfig()
 {
     //
     // Test 1
     //
     // The default mock list does not have any webhooks set.
     $api_prophecy = $this->prophesize('CRM_Mailchimp_Api3');
     CRM_Mailchimp_Utils::setMailchimpApi($api_prophecy->reveal());
     $api_prophecy->get('/lists/dummylistid/webhooks');
     $groups = CRM_Mailchimp_Utils::getGroupsToSync([static::$civicrm_group_id_membership]);
     $warnings = CRM_Mailchimp_Utils::checkGroupsConfig($groups);
     $this->assertEquals(1, count($warnings));
     $this->assertContains(ts('Need to create a webhook'), $warnings[0]);
     //
     // Test 2
     //
     $api_prophecy = $this->prophesize('CRM_Mailchimp_Api3');
     CRM_Mailchimp_Utils::setMailchimpApi($api_prophecy->reveal());
     $api_prophecy->get('/lists/dummylistid/webhooks')->will(function ($args) {
         // Need to mock a 404 response.
         $this->response = (object) ['http_code' => 404, 'data' => []];
         $this->request = (object) ['method' => 'GET'];
         throw new CRM_Mailchimp_RequestErrorException($this->reveal(), "Not found");
     });
     $groups = CRM_Mailchimp_Utils::getGroupsToSync([static::$civicrm_group_id_membership]);
     $warnings = CRM_Mailchimp_Utils::checkGroupsConfig($groups);
     $this->assertEquals(1, count($warnings));
     $this->assertContains(ts('The Mailchimp list that this once worked with has been deleted'), $warnings[0]);
     //
     // Test 3
     //
     $api_prophecy = $this->prophesize('CRM_Mailchimp_Api3');
     CRM_Mailchimp_Utils::setMailchimpApi($api_prophecy->reveal());
     $api_prophecy->get('/lists/dummylistid/webhooks')->will(function ($args) {
         // Need to mock a network error
         $this->response = (object) ['http_code' => 500, 'data' => []];
         throw new CRM_Mailchimp_NetworkErrorException($this->reveal(), "Someone unplugged internet");
     });
     $groups = CRM_Mailchimp_Utils::getGroupsToSync([static::$civicrm_group_id_membership]);
     $warnings = CRM_Mailchimp_Utils::checkGroupsConfig($groups);
     $this->assertEquals(1, count($warnings));
     $this->assertContains(ts('Problems (possibly temporary)'), $warnings[0]);
     $this->assertContains(ts('Someone unplugged internet'), $warnings[0]);
     // We did not change anything on the fixture.
     static::$fixture_should_be_reset = FALSE;
 }
 /**
  * Set up the queue.
  */
 public static function getRunner($skipEndUrl = FALSE, $dry_run = FALSE)
 {
     // Setup the Queue
     $queue = CRM_Queue_Service::singleton()->create(array('name' => self::QUEUE_NAME, 'type' => 'Sql', 'reset' => TRUE));
     // reset push stats
     $stats = ['dry_run' => $dry_run];
     CRM_Core_BAO_Setting::setItem($stats, CRM_Mailchimp_Form_Setting::MC_SETTING_GROUP, 'push_stats');
     // We need to process one list at a time.
     $groups = CRM_Mailchimp_Utils::getGroupsToSync(array(), null, $membership_only = TRUE);
     CRM_Mailchimp_Utils::checkDebug('CRM_Mailchimp_Form_Sync getRunner $groups= ', $groups);
     // Each list is a task.
     $listCount = 0;
     foreach ($groups as $group_id => $details) {
         if (empty($details['list_name'])) {
             // This list has been deleted at Mailchimp, or for some other reason we
             // could not access its name. Best not to sync it.
             continue;
         }
         $stats[$details['list_id']] = ['c_count' => 0, 'mc_count' => 0, 'in_sync' => 0, 'updates' => 0, 'additions' => 0, 'unsubscribes' => 0];
         $identifier = "List " . $listCount++ . " " . $details['civigroup_title'];
         $task = new CRM_Queue_Task(['CRM_Mailchimp_Form_Sync', 'syncPushList'], [$details['list_id'], $identifier, $dry_run], "{$identifier}: collecting data from CiviCRM.");
         // Add the Task to the Queue
         $queue->createItem($task);
     }
     if (count($stats) == 1) {
         // Nothing to do. (only key is 'dry_run')
         return FALSE;
     }
     // Setup the Runner
     $runnerParams = array('title' => ($dry_run ? ts('Dry Run: ') : '') . ts('Mailchimp Push Sync: update Mailchimp from CiviCRM'), 'queue' => $queue, 'errorMode' => CRM_Queue_Runner::ERROR_ABORT, 'onEndUrl' => CRM_Utils_System::url(self::END_URL, self::END_PARAMS, TRUE, NULL, FALSE));
     // Skip End URL to prevent redirect
     // if calling from cron job
     if ($skipEndUrl == TRUE) {
         unset($runnerParams['onEndUrl']);
     }
     $runner = new CRM_Queue_Runner($runnerParams);
     static::updatePushStats($stats);
     CRM_Mailchimp_Utils::checkDebug('End-CRM_Mailchimp_Form_Sync getRunner $identifier= ', $identifier);
     return $runner;
 }
 /**
  * Check all mapped groups' lists.
  *
  * Nb. this does not output anything itself so we can test it works. It is
  * used by the settings page.
  *
  * @param null|Array $groups array of membership groups to check, or NULL to
  *                   check all.
  *
  * @return Array of message strings that should be output with CRM_Core_Error
  * or such.
  *
  */
 public static function checkGroupsConfig($groups = NULL)
 {
     if ($groups === NULL) {
         $groups = CRM_Mailchimp_Utils::getGroupsToSync(array(), null, $membership_only = TRUE);
     }
     if (!is_array($groups)) {
         throw new InvalidArgumentException("expected array argument, if provided");
     }
     $api = CRM_Mailchimp_Utils::getMailchimpApi();
     $warnings = [];
     // Check all our groups do not have the sources:API set in the webhook, and
     // that they do have the webhook set.
     foreach ($groups as $group_id => $details) {
         $group_settings_link = "<a href='/civicrm/group?reset=1&action=update&id={$group_id}' >" . htmlspecialchars($details['civigroup_title']) . "</a>";
         $message_prefix = ts('CiviCRM group "%1" (Mailchimp list %2): ', [1 => $group_settings_link, 2 => $details['list_id']]);
         try {
             $test_warnings = CRM_Mailchimp_Utils::configureList($details['list_id'], $dry_run = TRUE);
             foreach ($test_warnings as $_) {
                 $warnings[] = $message_prefix . $_;
             }
         } catch (CRM_Mailchimp_NetworkErrorException $e) {
             $warnings[] = $message_prefix . ts("Problems (possibly temporary) fetching details from Mailchimp. ") . $e->getMessage();
         } catch (CRM_Mailchimp_RequestErrorException $e) {
             $message = $e->getMessage();
             if ($e->response->http_code == 404) {
                 // A little more helpful than "resource not found".
                 $warnings[] = $message_prefix . ts("The Mailchimp list that this once worked with has " . "been deleted on Mailchimp. Please edit the CiviCRM group settings to " . "either specify a different Mailchimp list that exists, or to remove " . "the Mailchimp integration for this group.");
             } else {
                 $warnings[] = $message_prefix . ts("Problems fetching details from Mailchimp. ") . $e->getMessage();
             }
         }
     }
     if ($warnings) {
         CRM_Core_Error::debug_log_message('Mailchimp list check warnings' . var_export($warnings, 1));
     }
     return $warnings;
 }