addSelect('id') ->addWhere(static::CUSTOM_GROUP_NAME . '.Active_Directory_SID', '=', $sid) ->execute() ->first()['id']; $groups = \CRM_Contact_BAO_Group::getGroups(['id' => $id]); $groupId = array_pop($groups); return $groupId ? new self($groupId) : NULL; } /** * Synchronize recipients with a list of SIDs and e-mail addresses. * * @param array $recipients * * @return array A description of the changes made * * @throws \Civi\Mailinglistsync\Exceptions\MailinglistException */ public function syncRecipients(array $recipients): array { $adGroupMembers = $this->getRecipients(); // Add new recipients $recipientsAdded = []; foreach ($recipients as $recipient) { if (empty($adGroupMembers[$recipient['sid']] ?? NULL)) { $recipientsAdded[$recipient['sid']] = $this->addRecipient($recipient); } } // Remove recipients that are no longer in the list $recipientsRemoved = []; foreach ($adGroupMembers as $adGroupMember) { /* @var \Civi\Mailinglistsync\MailingListRecipient $adGroupMember */ if (!in_array($adGroupMember->getSid(), array_column($recipients, 'sid'))) { $recipientsRemoved[$adGroupMember->getSid()] = $this->removeRecipient($adGroupMember); } } // Update recipients $recipientsUpdated = []; $recipientAttributesMap = [ // Map recipient attribute names to method names (get/set) 'first_name' => 'FirstName', 'last_name' => 'LastName', 'email' => 'Email', ]; foreach ($recipients as $recipient) { $changed = FALSE; // Find the group member by SID $adGroupMemberArray = array_filter( $adGroupMembers, fn($adGroupMember) => $adGroupMember->getSid() === $recipient['sid'] ); $count = count($adGroupMemberArray); if ($count === 0) { continue; } elseif ($count > 1) { throw new MailinglistException( "Multiple recipients with the same SID", MailinglistException::ERROR_CODE_MULTIPLE_RECIPIENTS ); } /* @var \Civi\Mailinglistsync\MailingListRecipient $adGroupMember */ $adGroupMember = array_pop($adGroupMemberArray); // Update attributes if they have changed foreach ($recipientAttributesMap as $attrName => $methodName) { $changed = self::updateRecipient( $adGroupMember, $recipient, $attrName, fn() => $adGroupMember->{"get$methodName"}(), fn($value) => $adGroupMember->{"set$methodName"}($value), $recipientsUpdated, ) || $changed; } // Apply changes if ($changed) { $adGroupMember->save(); } } // Count errors $errorCount = count(array_filter( $recipientsAdded, fn($recipient) => $recipient['is_error'] )); $errorCount += count(array_filter( $recipientsRemoved, fn($recipient) => $recipient['is_error'] )); $errorCount += count(array_filter( $recipientsUpdated, fn($recipient) => array_filter($recipient, fn($attr) => $attr['is_error']) )); return [ 'error_count' => $errorCount, 'recipients_added' => $recipientsAdded, 'recipients_removed' => $recipientsRemoved, 'recipients_updated' => $recipientsUpdated, ]; } /** * Get a list of recipients indexed by SID. * * @override GroupMailingList::getRecipients() * @return array * @throws \Civi\Mailinglistsync\Exceptions\MailinglistException */ public function getRecipients(): array { $recipients = parent::getRecipients(); return array_column($recipients, null, fn($recipient) => $recipient->getSid()); } private function addRecipient(array $recipient): array { $result = ['recipient' => $recipient]; try { // Try to find an existing contact $contactSearch = $this->findExistingContact( firstName: $recipient['first_name'], lastName: $recipient['last_name'], email: $recipient['email'], ); if ($contactSearch !== NULL) { $result['is_error'] = FALSE; $result['recipient']['contact_id'] = $contactSearch['contact']['id']; $result['recipient']['found_by'] = $contactSearch['found_by']; $result['recipient']['contact_created'] = FALSE; return $result; } // Create a new contact if none was found $newRecipient = MailingListRecipient::createContact( firstName: $recipient['first_name'], lastName: $recipient['last_name'], email: $recipient['email'], sid: $recipient['sid'], ); $result['is_error'] = FALSE; $result['recipient']['contact_id'] = $newRecipient->getContactId(); $result['recipient']['contact_created'] = TRUE; return $result; } catch (MailinglistException $e) { $error = $e->getLogMessage(); \Civi::log(E::LONG_NAME)->error($error); $result['is_error'] = TRUE; $result['error_message'] = $error; $result['recipient']['contact_created'] = FALSE; return $result; } } /** * Helper function to update recipient data dynamically. * OMG, is this DRY! * * @param \Civi\Mailinglistsync\MailingListRecipient $old * @param array $new * @param string $attributeName * @param callable $getter * @param callable $setter * @param array $changes * * @return bool TRUE if the recipient was updated, FALSE otherwise */ private static function updateRecipient( MailingListRecipient $old, array $new, string $attributeName, callable $getter, callable $setter, array &$changes, ): bool { $updated = FALSE; if ($new[$attributeName] !== $getter()) { $error = NULL; $oldValue = $getter(); try { $setter($new[$attributeName]); $updated = TRUE; } catch (MailinglistException $e) { \Civi::log(E::LONG_NAME)->error($e->getLogMessage()); $error = $e->getLogMessage(); } finally { $changes[$old['sid']][$attributeName] = [ 'is_error' => $error !== NULL, 'old' => $oldValue, 'new' => $new[$attributeName], ]; if ($error !== NULL) { $changes[$old['sid']][$attributeName]['error'] = $error; } } } return $updated; } }