de.propeace.mailinglistsync/Civi/Mailinglistsync/MailingListRecipient.php
2025-03-28 19:07:48 +01:00

529 lines
14 KiB
PHP

<?php
declare(strict_types=1);
namespace Civi\Mailinglistsync;
use Civi\Api4\Contact;
use Civi\Mailinglistsync\Exceptions\MailinglistException;
use CRM_Mailinglistsync_ExtensionUtil as E;
class MailingListRecipient {
public const CUSTOM_GROUP_NAME = 'Active_Directory';
protected ?string $sid;
protected ?int $contactId;
protected ?string $firstName;
protected ?string $lastName;
protected ?string $email;
/**
* Constructor.
*
* @param string|null $sid
* @param int|null $contact_id
* @param string|null $first_name
* @param string|null $last_name
* @param string|null $email
*/
public function __construct(
string $sid = NULL,
int $contact_id = NULL,
string $first_name = NULL,
string $last_name = NULL ,
string $email = NULL,
) {
$this->contactId = $contact_id;
$this->firstName = $first_name;
$this->lastName = $last_name;
$this->email = $email;
$this->sid = $sid;
$this->updateData = [];
}
/**
* Data to be updated. Use the `save()` method to apply the update.
*
* @var array
*/
private array $updateData;
/**
* Create a new contact and return the recipient.
*
* @param string $firstName
* @param string $lastName
* @param string $email
* @param string $sid
* @param string $locationType
*
* @return \Civi\Mailinglistsync\MailingListRecipient
* @throws \Civi\Mailinglistsync\Exceptions\MailinglistException
*/
public static function createContact(
string $email,
string $sid,
string $locationType,
string $firstName = '',
string $lastName = '',
): self {
// Try to create a new contact
try {
$contact = \Civi\Api4\Contact::create(FALSE)
->addValue('contact_type', 'Individual')
->addValue('Active_Directory.SID', $sid)
->addChain(
'Email.create',
\Civi\Api4\Email::create(FALSE)
->addValue('contact_id', '$id')
->addValue('email', $email)
->addValue('location_type_id:name', $locationType),
);
// If contact has a firstname, add it to the chain
if (!empty($firstName)) {
$contact->addValue('first_name', $firstName);
}
// If contact has a lastname, add it to the chain
if (!empty($lastName)) {
$contact->addValue('last_name', $lastName);
}
// If contact has no firstname or lastname, add email as display name
if (empty($firstName) && empty($lastName)) {
$contact->addValue('display_name', $email);
}
// Execute the query
$contact->execute();
}
catch (\Exception $e) {
throw new MailinglistException(
"Failed to create contact: {$e->getMessage()}",
MailinglistException::ERROR_CODE_CREATE_CONTACT_FAILED,
);
}
return new static(
sid: $sid,
contact_id: $contact['id'],
first_name: $firstName,
last_name: $lastName,
email: $email,
);
}
/**
* Get a recipient by its email address.
*
* @param string $email
*
* @return MailingListRecipient
* @throws \Civi\Mailinglistsync\Exceptions\MailinglistException
*/
public static function getContactIdEmail(string $email): MailingListRecipient {
try {
$recipientData = Contact::get()
->addSelect('id', 'first_name', 'last_name', 'email.email', 'Active_Directory.SID')
->addJoin('Email AS email', 'LEFT', ['id', '=', 'email.contact_id'])
->addWhere('email.email', '=', $email)
->addGroupBy('id')
->execute()
->first();
}
catch (\Exception $e) {
throw new MailinglistException(
"Could not get recipient by email '{$email}': {$e->getMessage()}",
MailinglistException::ERROR_CODE_GET_RECIPIENTS_FAILED
);
}
try {
$recipient = new MailingListRecipient(
sid: $recipientData['Active_Directory.SID'],
contact_id: $recipientData['id'],
first_name: $recipientData['first_name'],
last_name: $recipientData['last_name'],
email: $recipientData['email.email'],
);
} catch (\Exception $e) {
throw new MailinglistException(
"Could not create recipient object for contact with id '{$recipientData['id']}': {$e->getMessage()}",
MailinglistException::ERROR_CODE_GET_RECIPIENTS_FAILED
);
}
return $recipient;
}
/**
* Get a list of group mailing lists the contact is subscribed to.
*
* @return ?array
* @throws \Civi\Mailinglistsync\Exceptions\MailinglistException
*/
public function getMailingLists(): ?array {
$mailingLists = [];
try {
$groups = \Civi\Api4\GroupContact::get()
->addSelect('group_id')
->addJoin(
'Group AS group', 'INNER',
['group_id', '=', 'group.id'],
['group.' . GroupMailingList::CUSTOM_GROUP_NAME . '.Enable_mailing_list', '=', 1],
)
->addWhere('contact_id', '=', $this->getContactId())
->addWhere('status', '=', 'Added')
->execute()
->getArrayCopy();
} catch (\Exception $e) {
throw new MailinglistException(
'Failed to get group mailing lists',
MailinglistException::ERROR_CODE_GET_GROUP_MAILING_LISTS_FAILED,
);
}
foreach ($groups as $group) {
$mailingList = new GroupMailingList($group['group_id']);
if ($mailingList->isADGroup()) {
$mailingList = new ADGroupMailingList($group['group_id']); ;
}
$mailingLists[] = $mailingList;
}
return $mailingLists;
}
/**
* Get a list of AD group mailing lists the contact is subscribed to.
*
* @return ?array
* @throws \Civi\Mailinglistsync\Exceptions\MailinglistException
*/
public function getAdMailingLists(): ?array {
$mailingLists = [];
try {
$groups = \Civi\Api4\GroupContact::get()
->addSelect('group_id')
->addJoin(
'Group AS group', 'INNER',
['group_id', '=', 'group.id'],
['group.' . GroupMailingList::CUSTOM_GROUP_NAME . '.Enable_mailing_list', '=', 1],
['group.' . GroupMailingList::CUSTOM_GROUP_NAME . '.Active_Directory_SID', 'IS NOT EMPTY'],
)
->addWhere('contact_id', '=', $this->getContactId())
->addWhere('status', '=', 'Added')
->execute()
->getArrayCopy();
} catch (\Exception $e) {
throw new MailinglistException(
'Failed to get AD group mailing lists',
MailinglistException::ERROR_CODE_GET_AD_GROUP_MAILING_LISTS_FAILED,
);
}
foreach ($groups as $group) {
$mailingLists[] = new ADGroupMailingList($group['group_id']);
}
return $mailingLists;
}
/**
* Get a list of event mailing lists the contact is subscribed to.
*
* @return ?array
* @throws \Civi\Mailinglistsync\Exceptions\MailinglistException
*/
public function getEventMailingLists(): ?array {
$mailingLists = [];
try {
$groups = \Civi\Api4\Participant::get()
->addSelect('event_id')
->addJoin(
'Event AS event', 'INNER',
['event_id', '=', 'event.id'],
['event.' . EventMailingList::CUSTOM_GROUP_NAME . '.Enable_mailing_list', '=', 1],
)
->addWhere('contact_id', '=', $this->getContactId())
->addWhere('status_id', 'IN', EventMailingList::getEnabledParticipantStatus())
->execute()
->getArrayCopy();
}
catch (\Exception $e) {
throw new MailinglistException(
'Failed to get event mailing lists',
MailinglistException::ERROR_CODE_GET_EVENT_MAILING_LISTS_FAILED,
);
}
foreach ($groups as $group) {
$mailingLists[] = new EventMailingList($group['group_id']);
}
return $mailingLists;
}
/**
* Get the contact ID.
*
* @return int|NULL
*/
public function getContactId(): ?int {
return $this->contactId;
}
/**
* Get the first name of the contact.
*
* @return string|NULL
*/
public function getFirstName(): ?string {
return $this->firstName;
}
/**
* Get the last name of the contact.
*
* @return string|NULL
*/
public function getLastName(): ?string {
return $this->lastName;
}
/**
* Get the email address of the contact.
*
* @return string|NULL
*/
public function getEmail(): ?string {
return $this->email;
}
/**
* Get the SID of the contact.
*
* @return string|NULL
*/
public function getSid(): ?string {
return $this->sid;
}
/**
* Update the first name of the contact.
*
* @param string $firstName
*
* @return void
*/
public function updateFirstName(string $firstName): void {
$this->updateData += ['first_name' => $firstName];
}
/**
* Update the last name of the contact.
*
* @param string $lastName
*
* @return void
*/
public function updateLastName(string $lastName): void {
$this->updateData += ['last_name' => $lastName];
}
/**
* Update the email address of the contact.
*
* @throws \Civi\Mailinglistsync\Exceptions\MailinglistException
*/
public function updateEmail(string $email): void {
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new MailinglistException(
"Invalid email address '$email'",
MailinglistException::ERROR_CODE_INVALID_EMAIL_ADDRESS,
);
}
$this->updateData += ['email' => $email];
}
/**
* Update the SID of the contact.
*
* @param string $sid
*
* @return void
*/
public function updateSid(string $sid): void {
$this->updateData += [self::CUSTOM_GROUP_NAME . '.SID' => $sid];
}
/**
* Save the changes marked using the setter methods.
*
* @throws \Civi\Mailinglistsync\Exceptions\MailinglistException
*/
public function save(): void {
if (empty($this->updateData)) {
return;
}
$contactValues = ['first_name', 'last_name', self::CUSTOM_GROUP_NAME . '.SID'];
$contactUpdates = array_intersect_key($this->updateData, array_flip($contactValues));
if (!empty($contactUpdates)) {
try {
\Civi\Api4\Contact::update()
->setValues($contactUpdates)
->addWhere('id', '=', $this->getContactId())
->execute();
\Civi::log()->debug(
"Updated contact '{$this->getContactId()}' with data: " . json_encode($this->updateData)
);
}
catch (\Exception $e) {
\Civi::log()->error(
"Failed to update contact '{$this->getContactId()}': $e"
);
throw new MailinglistException(
"Failed to update contact: {$e->getMessage()}",
MailinglistException::ERROR_CODE_UPDATE_ENTITY_FAILED,
);
}
}
// Update email address
if (!empty($this->updateData['email'])) {
try {
\Civi\Api4\Email::update()
->addValue('email', $this->updateData['email'])
->addWhere('contact_id', '=', $this->getContactId())
->execute();
\Civi::log()->debug(
"Updated email address for contact '{$this->getContactId()}'"
);
}
catch (\Exception $e) {
\Civi::log()->error(
"Failed to update email address for contact '{$this->getContactId()}': $e"
);
throw new MailinglistException(
"Failed to update email address: {$e->getMessage()}",
MailinglistException::ERROR_CODE_UPDATE_ENTITY_FAILED,
);
}
}
try {
self::createSyncActivity(
$this->getContactId(),
'Completed',
E::ts('Contact updated'),
E::ts('The contact data was updated by the mailing list synchronization: %1', [
1 => json_encode($this->updateData)
])
);
}
catch (\Exception $e) {
\Civi::log()->error(
"Failed to create activity for contact '{$this->getContactId()}': $e"
);
throw new MailinglistException(
"Failed to create activity for contact '{$this->getContactId()}': $e",
MailinglistException::ERROR_CODE_CREATE_ACTIVITY_FAILED,
);
}
}
/**
* Get a list of email location types for the contact.
*
* @return array
* @throws \Civi\Mailinglistsync\Exceptions\MailinglistException
*/
public function getEmailLocationTypes(): array {
try {
$result = \Civi\Api4\Email::get()
->addSelect('location_type_id:name')
->addWhere('contact_id', '=', $this->getContactId())
->execute()
->getArrayCopy();
return array_column($result, 'location_type_id:name');
}
catch (\Exception $e) {
throw new MailinglistException(
'Failed to get email location types',
MailinglistException::ERROR_CODE_GET_EMAIL_LOCATION_TYPES_FAILED,
);
}
}
/**
* Create an e-mail address for the contact.
*
* @param string $email
* @param string $locationType
*
* @return array|NULL
* @throws \Civi\Mailinglistsync\Exceptions\MailinglistException
*/
public function createEmail(string $email, string $locationType): ?array {
try {
$result = \Civi\Api4\Email::create(FALSE)
->addValue('contact_id', $this->getContactId())
->addValue('email', $email)
->addValue('location_type_id:name', $locationType)
->execute()
->first();
}
catch (\Exception $e) {
throw new MailinglistException(
'Failed to create email',
MailinglistException::ERROR_CODE_CREATE_EMAIL_ADDRESS_FAILED,
);
}
$this->email = $email;
return $result;
}
/**
* Add a synchronization activity to a contact.
*
* @param int $targetContactId
* @param string $status
* @param string $subject
* @param string $details
*
* @return void
* @throws \Civi\Mailinglistsync\Exceptions\MailinglistException
*/
public static function createSyncActivity(
int $targetContactId,
string $status,
string $subject,
string $details,
): void {
try {
\Civi\Api4\Activity::create(FALSE)
->addValue('source_contact_id', getSessionUser())
->addValue('activity_type_id:name', 'Mailing_List_Synchronization_Activity')
->addValue('subject', $subject)
->addValue('details', $details)
->addValue('status_id:name', ucfirst($status))
->addValue('target_contact_id', $targetContactId)
->execute()->first();
}
catch (\Exception $e) {
throw new MailinglistException(
"Failed to create contact activity: $e",
MailinglistException::ERROR_CODE_CREATE_ACTIVITY_FAILED,
);
}
}
}