/**
  *
  */
 public function testUpdateCiviFromMailchimpContactLogic()
 {
     $cases = [['label' => 'Test no changes', 'mailchimp' => ['first_name' => 'x', 'last_name' => 'y'], 'civi' => ['first_name' => 'x', 'last_name' => 'y'], 'expected' => []], ['label' => 'Test first name changes', 'mailchimp' => ['first_name' => 'a', 'last_name' => 'y'], 'civi' => ['first_name' => 'x', 'last_name' => 'y'], 'expected' => ['first_name' => 'a']], ['label' => 'Test first name provide', 'mailchimp' => ['first_name' => 'a', 'last_name' => 'y'], 'civi' => ['first_name' => '', 'last_name' => 'y'], 'expected' => ['first_name' => 'a']], ['label' => 'Test first name no clobber', 'mailchimp' => ['first_name' => '', 'last_name' => 'y'], 'civi' => ['first_name' => 'x', 'last_name' => 'y'], 'expected' => []], ['label' => 'Test last name changes', 'mailchimp' => ['last_name' => 'a', 'first_name' => 'y'], 'civi' => ['last_name' => 'x', 'first_name' => 'y'], 'expected' => ['last_name' => 'a']], ['label' => 'Test last name provide', 'mailchimp' => ['last_name' => 'a', 'first_name' => 'y'], 'civi' => ['last_name' => '', 'first_name' => 'y'], 'expected' => ['last_name' => 'a']], ['label' => 'Test last name no clobber', 'mailchimp' => ['last_name' => '', 'first_name' => 'y'], 'civi' => ['last_name' => 'x', 'first_name' => 'y'], 'expected' => []]];
     foreach ($cases as $case) {
         extract($case);
         $result = CRM_Mailchimp_Sync::updateCiviFromMailchimpContactLogic($mailchimp, $civi);
         $this->assertEquals($expected, $result, "FAILED: {$label}");
     }
 }
/**
 * 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]);
    }
}
 /**
  * Tests the removeInSync method.
  *
  */
 public function testRemoveInSync()
 {
     // Create empty tables.
     CRM_Mailchimp_Sync::createTemporaryTableForMailchimp();
     CRM_Mailchimp_Sync::createTemporaryTableForCiviCRM();
     // Prepare the mock for the subscription the post hook will do.
     $api_prophecy = $this->prophesize('CRM_Mailchimp_Api3');
     CRM_Mailchimp_Utils::setMailchimpApi($api_prophecy->reveal());
     $api_prophecy->get('/lists/dummylistid/interest-categories', Argument::any())->willReturn(json_decode('{"http_code":200,"data":{"categories":[{"id":"categoryid","title":"' . static::MC_INTEREST_CATEGORY_TITLE . '"}]}}'));
     $api_prophecy->get("/lists/dummylistid/interest-categories/categoryid/interests", Argument::any())->willReturn(json_decode('{"http_code":200,"data":{"interests":[{"id":"interestId1","name":"' . static::MC_INTEREST_NAME_1 . '"},{"id":"interestId2","name":"' . static::MC_INTEREST_NAME_2 . '"}]}}'));
     $api_prophecy->get('/lists', ['fields' => 'lists.id,lists.name', 'count' => 10000])->willReturn(json_decode(json_encode(['http_code' => 200, 'data' => ['lists' => []]])));
     $sync = new CRM_Mailchimp_Sync(static::$test_list_id);
     // Test 1.
     //
     // Delete records from both tables when there's a cid_guess--contact link
     // and the hash is the same.
     CRM_Core_DAO::executeQuery("INSERT INTO tmp_mailchimp_push_c (email, hash, contact_id) VALUES\n      ('*****@*****.**', 'aaaaaaaaaaaaaaaa', 1),\n      ('*****@*****.**', 'aaaaaaaaaaaaaaaa', 2);");
     CRM_Core_DAO::executeQuery("INSERT INTO tmp_mailchimp_push_m (email, hash, cid_guess) VALUES\n      ('*****@*****.**', 'aaaaaaaaaaaaaaaa', 1),\n      ('*****@*****.**', 'aaaaaaaaaaaaaaaa', 2);");
     $result = $sync->removeInSync('pull');
     $this->assertEquals(2, $result);
     $this->assertEquals(0, $sync->countMailchimpMembers());
     $this->assertEquals(0, $sync->countCiviCrmMembers());
     // Test 2.
     //
     // Check different hashes stops removals.
     CRM_Core_DAO::executeQuery("INSERT INTO tmp_mailchimp_push_c (email, hash, contact_id) VALUES\n      ('*****@*****.**', 'different', 1),\n      ('*****@*****.**', 'different', 2);");
     CRM_Core_DAO::executeQuery("INSERT INTO tmp_mailchimp_push_m (email, hash, cid_guess) VALUES\n      ('*****@*****.**', 'aaaaaaaaaaaaaaaa', 1),\n      ('*****@*****.**', 'aaaaaaaaaaaaaaaa', 2);");
     $result = $sync->removeInSync('pull');
     $this->assertEquals(0, $result);
     $this->assertEquals(2, $sync->countMailchimpMembers());
     $this->assertEquals(2, $sync->countCiviCrmMembers());
     // Test 3.
     //
     // Check nothing removed if no cid-contact match.
     CRM_Mailchimp_Sync::createTemporaryTableForMailchimp();
     CRM_Mailchimp_Sync::createTemporaryTableForCiviCRM();
     CRM_Core_DAO::executeQuery("INSERT INTO tmp_mailchimp_push_c (email, hash, contact_id) VALUES\n      ('*****@*****.**', 'aaaaaaaaaaaaaaaa', 1),\n      ('*****@*****.**', 'aaaaaaaaaaaaaaaa', 2);");
     CRM_Core_DAO::executeQuery("INSERT INTO tmp_mailchimp_push_m (email, hash, cid_guess) VALUES\n      ('*****@*****.**', 'aaaaaaaaaaaaaaaa', 0),\n      ('*****@*****.**', 'aaaaaaaaaaaaaaaa', NULL);");
     $result = $sync->removeInSync('pull');
     $this->assertEquals(0, $result);
     $this->assertEquals(2, $sync->countMailchimpMembers());
     $this->assertEquals(2, $sync->countCiviCrmMembers());
     // Test 4.
     //
     // Check duplicate civi contact deleted.
     CRM_Mailchimp_Sync::createTemporaryTableForMailchimp();
     CRM_Mailchimp_Sync::createTemporaryTableForCiviCRM();
     CRM_Core_DAO::executeQuery("INSERT INTO tmp_mailchimp_push_c (email, hash, contact_id) VALUES\n      ('*****@*****.**', 'Xaaaaaaaaaaaaaaa', 1),\n      ('*****@*****.**', 'Yaaaaaaaaaaaaaaa', 2);");
     CRM_Core_DAO::executeQuery("INSERT INTO tmp_mailchimp_push_m (email, hash, cid_guess) VALUES\n      ('*****@*****.**', 'bbbbbbbbbbbbbbbb', 1);");
     $result = $sync->removeInSync('push');
     $this->assertEquals(1, $result);
     $this->assertEquals(1, $sync->countMailchimpMembers());
     $this->assertEquals(1, $sync->countCiviCrmMembers());
     // Test 5.
     //
     // Check duplicate civi contact NOT deleted when in pull mode.
     CRM_Mailchimp_Sync::createTemporaryTableForMailchimp();
     CRM_Mailchimp_Sync::createTemporaryTableForCiviCRM();
     CRM_Core_DAO::executeQuery("INSERT INTO tmp_mailchimp_push_c (email, hash, contact_id) VALUES\n      ('*****@*****.**', 'Xaaaaaaaaaaaaaaa', 1),\n      ('*****@*****.**', 'Yaaaaaaaaaaaaaaa', 2);");
     CRM_Core_DAO::executeQuery("INSERT INTO tmp_mailchimp_push_m (email, hash, cid_guess) VALUES\n      ('*****@*****.**', 'bbbbbbbbbbbbbbbb', 1);");
     $result = $sync->removeInSync('pull');
     $this->assertEquals(0, $result);
     $this->assertEquals(1, $sync->countMailchimpMembers());
     $this->assertEquals(2, $sync->countCiviCrmMembers());
     // Test 5: one contact should be removed because it's in sync, the other
     // because it's a duplicate.
     //
     // Check duplicate civi contact deleted.
     CRM_Mailchimp_Sync::createTemporaryTableForMailchimp();
     CRM_Mailchimp_Sync::createTemporaryTableForCiviCRM();
     CRM_Core_DAO::executeQuery("INSERT INTO tmp_mailchimp_push_c (email, hash, contact_id) VALUES\n      ('*****@*****.**', 'aaaaaaaaaaaaaaaa', 1),\n      ('*****@*****.**', 'Yaaaaaaaaaaaaaaa', 2);");
     CRM_Core_DAO::executeQuery("INSERT INTO tmp_mailchimp_push_m (email, hash, cid_guess) VALUES\n      ('*****@*****.**', 'aaaaaaaaaaaaaaaa', 1);");
     $result = $sync->removeInSync('push');
     $this->assertEquals(2, $result);
     $this->assertEquals(0, $sync->countMailchimpMembers());
     $this->assertEquals(0, $sync->countCiviCrmMembers());
     CRM_Mailchimp_Sync::dropTemporaryTables();
 }
 /**
  * Find/create, and update.
  *
  * - "[list_id]": "a6b5da1054",
  * - "[email]": "*****@*****.**",
  * - "[merges][FNAME]": "MailChimp",
  * - "[merges][LNAME]": "API",
  * - "[merges][INTERESTS]": "Group1,Group2",
  *
  */
 public function findOrCreateSubscribeAndUpdate()
 {
     $this->findOrCreateContact();
     // Check whether names have changed.
     $contact = civicrm_api3('Contact', 'getsingle', ['contact_id' => $this->contact_id]);
     $edits = CRM_Mailchimp_Sync::updateCiviFromMailchimpContactLogic(['first_name' => empty($this->request_data['merges']['FNAME']) ? '' : $this->request_data['merges']['FNAME'], 'last_name' => empty($this->request_data['merges']['LNAME']) ? '' : $this->request_data['merges']['LNAME']], $contact);
     if ($edits) {
         // We do need to make some changes.
         civicrm_api3('Contact', 'create', ['contact_id' => $this->contact_id] + $edits);
     }
     // Contact has just subscribed, we'll need to add them to the list.
     civicrm_api3('GroupContact', 'create', ['contact_id' => $this->contact_id, 'group_id' => $this->sync->membership_group_id, 'status' => 'Added']);
     $this->updateInterestsFromMerges();
 }
 /**
  * Tests the guessContactIdsByNameAndEmail method with deleted contacts in the
  * mix.
  *
  */
 public function testGuessContactIdsByNameAndEmailIgnoresDeletedContacts()
 {
     //
     // Test 1: Primary case: only one contact matches on name+email but it's
     // deleted. Should not match.
     //
     CRM_Mailchimp_Sync::createTemporaryTableForMailchimp();
     CRM_Core_DAO::executeQuery("INSERT INTO tmp_mailchimp_push_m (email, first_name, last_name)\n      VALUES (%1, %2, %3);", [1 => [static::$civicrm_contact_1['email'], 'String'], 2 => [static::$civicrm_contact_1['first_name'], 'String'], 3 => [static::$civicrm_contact_1['last_name'], 'String']]);
     // Delete (trash) the contact.
     civicrm_api3('Contact', 'delete', ['contact_id' => static::$civicrm_contact_1['contact_id']]);
     $result = CRM_Mailchimp_Sync::guessContactIdsByNameAndEmail();
     $this->assertEquals(0, $result);
     //
     // Test 2: Check if contact 2 shares the email address and name
     //
     // Contact 2 should be matched.
     // change contact2's name.
     civicrm_api3('Contact', 'create', ['contact_id' => static::$civicrm_contact_2['contact_id'], 'first_name' => static::$civicrm_contact_1['first_name'], 'last_name' => static::$civicrm_contact_1['last_name']]);
     // and email.
     civicrm_api3('Email', 'create', ['contact_id' => static::$civicrm_contact_2['contact_id'], 'email' => static::$civicrm_contact_1['email'], 'is_billing' => 1]);
     CRM_Mailchimp_Sync::dropTemporaryTables();
     CRM_Mailchimp_Sync::createTemporaryTableForMailchimp();
     CRM_Core_DAO::executeQuery("INSERT INTO tmp_mailchimp_push_m (email, first_name, last_name)\n      VALUES (%1, %2, %3);", [1 => [static::$civicrm_contact_1['email'], 'String'], 2 => [static::$civicrm_contact_1['first_name'], 'String'], 3 => [static::$civicrm_contact_1['last_name'], 'String']]);
     $result = CRM_Mailchimp_Sync::guessContactIdsByNameAndEmail();
     $this->assertEquals(1, $result);
     // Check the matched record did match contact 2.
     $result = CRM_Core_DAO::singleValueQuery('SELECT COUNT(*) c FROM tmp_mailchimp_push_m
   WHERE email = %1 AND cid_guess = ' . static::$civicrm_contact_2['contact_id'], [1 => [static::$civicrm_contact_1['email'], 'String']]);
     $this->assertEquals(1, $result);
     // Test 3: a third contact matches name and email - no longer unique, should
     // not match.
     $contact3 = civicrm_api3('Contact', 'create', ['contact_type' => 'Individual', 'first_name' => static::C_CONTACT_1_FIRST_NAME, 'last_name' => static::C_CONTACT_1_LAST_NAME, 'email' => static::$civicrm_contact_1['email']]);
     CRM_Mailchimp_Sync::dropTemporaryTables();
     CRM_Mailchimp_Sync::createTemporaryTableForMailchimp();
     CRM_Core_DAO::executeQuery("INSERT INTO tmp_mailchimp_push_m (email, first_name, last_name)\n      VALUES (%1, %2, %3);", [1 => [static::$civicrm_contact_1['email'], 'String'], 2 => [static::$civicrm_contact_1['first_name'], 'String'], 3 => [static::$civicrm_contact_1['last_name'], 'String']]);
     $result = CRM_Mailchimp_Sync::guessContactIdsByNameAndEmail();
     // Remove 3rd contact.
     civicrm_api3('Contact', 'delete', ['contact_id' => $contact3['id'], 'skip_undelete' => 1]);
     // check it did not match.
     $this->assertEquals(0, $result);
 }
 /**
  * Batch update Mailchimp with new contacts that need to be subscribed, or
  * have changed data including unsubscribes.
  */
 public static function syncPushToMailchimp(CRM_Queue_TaskContext $ctx, $listID, $dry_run)
 {
     CRM_Mailchimp_Utils::checkDebug('Start-CRM_Mailchimp_Form_Sync syncPushAdd $listID= ', $listID);
     // Do the batch update. Might take a while :-O
     $sync = new CRM_Mailchimp_Sync($listID);
     $sync->dry_run = $dry_run;
     // this generates updates and unsubscribes
     $stats[$listID] = $sync->updateMailchimpFromCivi();
     // Finally, finish up by removing the two temporary tables
     //CRM_Mailchimp_Sync::dropTemporaryTables();
     static::updatePushStats($stats);
     return CRM_Queue_Task::TASK_SUCCESS;
 }
 /**
  * New contacts and profile changes need bringing into CiviCRM.
  */
 public static function syncPullFromMailchimp(CRM_Queue_TaskContext $ctx, $listID, $dry_run)
 {
     // Do the batch update. Might take a while :-O
     $sync = new CRM_Mailchimp_Sync($listID);
     $sync->dry_run = $dry_run;
     // this generates updates and group changes.
     $stats[$listID] = $sync->updateCiviFromMailchimp();
     // Finally, finish up by removing the two temporary tables
     // @todo re-enable this: CRM_Mailchimp_Sync::dropTemporaryTables();
     static::updatePullStats($stats);
     return CRM_Queue_Task::TASK_SUCCESS;
 }
 /**
  * Check interests are properly mapped as groups are changed and that
  * collectMailchimp and collectCiviCrm work as expected.
  *
  *
  * This uses the posthook, which in turn uses
  * updateMailchimpFromCiviSingleContact.
  *
  * If all is working then at that point both collections should match.
  *
  */
 public function testSyncInterestGroupings()
 {
     $api = CRM_Mailchimp_Utils::getMailchimpApi();
     try {
         // Add them to the interest group (this should not trigger a Mailchimp
         // update as they are not in thet membership list yet).
         $this->joinGroup(static::$civicrm_contact_1, static::$civicrm_group_id_interest_1);
         // The post hook should subscribe this person and set their interests.
         $this->joinMembershipGroup(static::$civicrm_contact_1);
         // Check their interest group was set.
         $result = $api->get("/lists/" . static::$test_list_id . "/members/" . static::$civicrm_contact_1['subscriber_hash'], ['fields' => 'status,interests'])->data;
         $this->assertEquals((object) [static::$test_interest_id_1 => TRUE, static::$test_interest_id_2 => FALSE], $result->interests);
         // Remove them to the interest group.
         $this->removeGroup(static::$civicrm_contact_1, static::$civicrm_group_id_interest_1);
         // Check their interest group was unset.
         $result = $api->get("/lists/" . static::$test_list_id . "/members/" . static::$civicrm_contact_1['subscriber_hash'], ['fields' => 'status,interests'])->data;
         $this->assertEquals((object) [static::$test_interest_id_1 => FALSE, static::$test_interest_id_2 => FALSE], $result->interests);
         // Add them to the 2nd interest group.
         // While this is a dull test, we assume it works if the other interest
         // group one did, it leaves the fixture with one on and one off which is a
         // good mix for the next test.
         $this->joinGroup(static::$civicrm_contact_1, static::$civicrm_group_id_interest_2);
         // Check their interest group was set.
         $result = $api->get("/lists/" . static::$test_list_id . "/members/" . static::$civicrm_contact_1['subscriber_hash'], ['fields' => 'status,interests'])->data;
         $this->assertEquals((object) [static::$test_interest_id_1 => FALSE, static::$test_interest_id_2 => TRUE], $result->interests);
         // Now check collections work.
         $sync = new CRM_Mailchimp_Sync(static::$test_list_id);
         $sync->collectCiviCrm('push');
         $this->assertEquals(1, $sync->countCiviCrmMembers());
         $sync->collectMailchimp('push');
         $this->assertEquals(1, $sync->countMailchimpMembers());
         $matches = $sync->matchMailchimpMembersToContacts();
         $this->assertEquals(['bySubscribers' => 1, 'byUniqueEmail' => 0, 'byNameEmail' => 0, 'bySingle' => 0, 'totalMatched' => 1, 'newContacts' => 0, 'failures' => 0], $matches);
         // This should return 1
         $dao = CRM_Core_DAO::executeQuery("SELECT * FROM tmp_mailchimp_push_m");
         $dao->fetch();
         $mc = ['email' => $dao->email, 'first_name' => $dao->first_name, 'last_name' => $dao->last_name, 'interests' => $dao->interests, 'hash' => $dao->hash, 'cid_guess' => $dao->cid_guess];
         $dao = CRM_Core_DAO::executeQuery("SELECT * FROM tmp_mailchimp_push_c");
         $dao->fetch();
         $civi = ['email' => $dao->email, 'email_id' => $dao->email_id, 'contact_id' => $dao->contact_id, 'first_name' => $dao->first_name, 'last_name' => $dao->last_name, 'interests' => $dao->interests, 'hash' => $dao->hash];
         $this->assertEquals($civi['first_name'], $mc['first_name']);
         $this->assertEquals($civi['last_name'], $mc['last_name']);
         $this->assertEquals($civi['email'], $mc['email']);
         $this->assertEquals($civi['interests'], $mc['interests']);
         $this->assertEquals($civi['hash'], $mc['hash']);
         // As the records are in sync, they should be and deleted.
         $in_sync = $sync->removeInSync('push');
         $this->assertEquals(1, $in_sync);
         // Now check the tables are both empty.
         $this->assertEquals(0, $sync->countMailchimpMembers());
         $this->assertEquals(0, $sync->countCiviCrmMembers());
     } catch (CRM_Mailchimp_Exception $e) {
         // Spit out request and response for debugging.
         print "Request:\n";
         print_r($e->request);
         print "Response:\n";
         print_r($e->response);
         // re-throw exception.
         throw $e;
     }
 }
 /**
  * Removes from the temporary tables those records that do not need processing
  * because they are identical.
  *
  * In *push* mode this will also remove any rows in the CiviCRM temp table
  * where there's an email match in the mailchimp table but the cid_guess is
  * different. This is to cover the case when two contacts in CiviCRM have the
  * same email and both are added to the membership group. Without this the
  * Push operation would attempt to craeate a 2nd Mailchimp member but with the
  * email address that's already on the list. This would mean the names kept
  * getting flipped around since it would be updating the same member twice -
  * very confusing.
  *
  * So for deleting the contacts from the CiviCRM table on *push* we avoid
  * this. However on *pull* we leave the contact in the table - they will then
  * get removed from the group, leaving just the single contact/member with
  * that particular email address.
  *
  * @param string $mode pull|push.
  * @return int
  */
 public function removeInSync($mode)
 {
     // In push mode, delete duplicate CiviCRM contacts.
     $doubles = 0;
     if ($mode == 'push') {
         $doubles = CRM_Mailchimp_Sync::runSqlReturnAffectedRows('DELETE c
      FROM tmp_mailchimp_push_c c
      INNER JOIN tmp_mailchimp_push_m m ON c.email=m.email AND m.cid_guess != c.contact_id;
     ');
         if ($doubles) {
             CRM_Mailchimp_Utils::checkDebug("removeInSync removed {$doubles} contacts who are in the membership group but have the same email address as another contact that is also in the membership group.");
         }
     }
     // Delete records have the same hash - these do not need an update.
     // count for testing purposes.
     $dao = CRM_Core_DAO::executeQuery("SELECT COUNT(c.email) co FROM tmp_mailchimp_push_m m\n      INNER JOIN tmp_mailchimp_push_c c ON m.cid_guess = c.contact_id AND m.hash = c.hash;");
     $dao->fetch();
     $count = $dao->co;
     if ($count > 0) {
         CRM_Core_DAO::executeQuery("DELETE m, c\n         FROM tmp_mailchimp_push_m m\n         INNER JOIN tmp_mailchimp_push_c c ON m.cid_guess = c.contact_id AND m.hash = c.hash;");
     }
     CRM_Mailchimp_Utils::checkDebug("removeInSync removed {$count} in-sync contacts.");
     return $count + $doubles;
 }