548 lines
19 KiB
PHP
548 lines
19 KiB
PHP
<?php
|
|
|
|
require_once 'mailinglistsync.civix.php';
|
|
|
|
use Civi\Api4\Event;
|
|
use Civi\Api4\Participant;
|
|
use Civi\Api4\Email;
|
|
use Civi\Mailinglistsync\Exceptions\MailinglistException;
|
|
use CRM_Mailinglistsync_ExtensionUtil as E;
|
|
use Civi\Mailinglistsync\EventMailingList;
|
|
use Civi\Mailinglistsync\GroupMailingList;
|
|
use function Civi\Mailinglistsync\getLocationTypes;
|
|
|
|
require_once 'Civi/Mailinglistsync/Utils.php';
|
|
|
|
/**
|
|
* Implements hook_civicrm_config().
|
|
*
|
|
* @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_config/
|
|
*/
|
|
function mailinglistsync_civicrm_config(&$config): void {
|
|
_mailinglistsync_civix_civicrm_config($config);
|
|
}
|
|
|
|
/**
|
|
* Implements hook_civicrm_install().
|
|
*
|
|
* @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_install
|
|
*/
|
|
function mailinglistsync_civicrm_install(): void {
|
|
_mailinglistsync_civix_civicrm_install();
|
|
}
|
|
|
|
/**
|
|
* Implements hook_civicrm_enable().
|
|
*
|
|
* @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_enable
|
|
*/
|
|
function mailinglistsync_civicrm_enable(): void {
|
|
_mailinglistsync_civix_civicrm_enable();
|
|
}
|
|
|
|
/**
|
|
* Implements validateForm() to validate custom fields in group and event forms.
|
|
*
|
|
* @throws \Civi\API\Exception\UnauthorizedException
|
|
* @throws \CRM_Core_Exception
|
|
*/
|
|
function mailinglistsync_civicrm_validateForm($formName, &$fields, &$files, &$form, &$errors): void {
|
|
// Validate custom fields in group and event forms.
|
|
if ($formName === 'CRM_Group_Form_Edit') {
|
|
if ($_REQUEST['action'] === 'delete' || $_REQUEST['action'] === 'update') {
|
|
$mailingList = new GroupMailingList(intval($_REQUEST['id']));
|
|
}
|
|
else {
|
|
$mailingList = new GroupMailingList();
|
|
}
|
|
}
|
|
elseif ($formName === 'CRM_Event_Form_ManageEvent_EventInfo') {
|
|
if ($_REQUEST['action'] === 'delete' || $_REQUEST['action'] === 'update') {
|
|
$mailingList = new EventMailingList(intval($_REQUEST['id']));
|
|
}
|
|
else {
|
|
$mailingList = new EventMailingList();
|
|
}
|
|
}
|
|
if (!empty($mailingList)) {
|
|
// We cannot delete the corresponding mlmmj mailing list in the post hook
|
|
// so we have to deal with it here
|
|
if ($_REQUEST['action'] === 'delete' && $mailingList->isEnabled()) {
|
|
try {
|
|
$mailingList->delete();
|
|
}
|
|
catch (MailinglistException $e) {
|
|
$errorCode = $e->getCode();
|
|
|
|
if ($errorCode !== MailinglistException::ERROR_CODE_DELETE_EMAIL_ADDRESS_FAILED) {
|
|
\Civi::log(E::LONG_NAME)->error($e->getMessage());
|
|
}
|
|
CRM_Core_Session::setStatus(
|
|
E::ts('Failed to delete mlmmj mailing list: %1', [1 => $e->getMessage()]),
|
|
E::ts('Deletion failed'),
|
|
'alert',
|
|
);
|
|
}
|
|
}
|
|
|
|
// Handle group and event mailing list updates which affect the mlmmj mailing list
|
|
if ($_REQUEST['action'] === 'update' && $mailingList->isEnabled()) {
|
|
$customFields = $mailingList::translateCustomFields($fields);
|
|
|
|
// If enabled changes from TRUE to FALSE, delete the mlmmj mailing list
|
|
if ($customFields['Enable_mailing_list']['value'] === "0") {
|
|
try {
|
|
$mailingList->delete();
|
|
}
|
|
catch (MailinglistException $e) {
|
|
$errorCode = $e->getCode();
|
|
if ($errorCode !== MailinglistException::ERROR_CODE_DELETE_EMAIL_ADDRESS_FAILED) {
|
|
\Civi::log(E::LONG_NAME)->error($e->getMessage());
|
|
}
|
|
CRM_Core_Session::setStatus(
|
|
E::ts('Failed to delete mlmmj mailing list: %1', [1 => $e->getMessage()]),
|
|
E::ts('Deletion failed'),
|
|
'alert',
|
|
);
|
|
}
|
|
}
|
|
|
|
// If email address has changed, delete and mark group for creation
|
|
elseif ($customFields['E_mail_address']['value'] !== $mailingList->getEmailAddress()) {
|
|
try {
|
|
$mailingList->delete();
|
|
}
|
|
catch (MailinglistException $e) {
|
|
$errorCode = $e->getCode();
|
|
if ($errorCode !== MailinglistException::ERROR_CODE_DELETE_EMAIL_ADDRESS_FAILED) {
|
|
\Civi::log(E::LONG_NAME)->error($e->getMessage());
|
|
}
|
|
CRM_Core_Session::setStatus(
|
|
E::ts('Failed to delete mlmmj mailing list: %1', [1 => $e->getMessage()]),
|
|
E::ts('Deletion failed'),
|
|
'alert',
|
|
);
|
|
}
|
|
// Queue mailinglist for synchronization with mlmmj
|
|
if ($mailingList::class === GroupMailingList::class) {
|
|
$queue = \Civi::queue('propeace-mailinglist-group-queue', [
|
|
'type' => 'SqlParallel',
|
|
'is_autorun' => FALSE,
|
|
'reset' => FALSE,
|
|
'error' => 'abort',
|
|
]);
|
|
$queue->createItem(new CRM_Queue_Task(
|
|
// callback
|
|
['Civi\Mailinglistsync\QueueHelper', 'storeInstance'],
|
|
// arguments
|
|
[$mailingList->getId(), $mailingList::class],
|
|
// title
|
|
"Sync Group ID '{$mailingList->getId()}'"
|
|
));
|
|
}
|
|
elseif ($mailingList::class === EventMailingList::class) {
|
|
$queue = \Civi::queue('propeace-mailinglist-event-queue', [
|
|
'type' => 'SqlParallel',
|
|
'is_autorun' => FALSE,
|
|
'reset' => FALSE,
|
|
'error' => 'abort',
|
|
]);
|
|
$queue->createItem(new CRM_Queue_Task(
|
|
// callback
|
|
['Civi\Mailinglistsync\QueueHelper', 'storeInstance'],
|
|
// arguments
|
|
[$mailingList->getId(), $mailingList::class],
|
|
// title
|
|
"Sync Event ID '{$mailingList->getId()}'"
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Validate custom fields
|
|
$mailingList::validateCustomFields($fields, $form, $errors);
|
|
}
|
|
|
|
// Check permission to alter group membership via form
|
|
if ($formName === 'CRM_Contact_Form_GroupContact' || $formName === 'CRM_Contact_Form_Task_AddToGroup') {
|
|
$mailingList = new GroupMailingList($fields['group_id']);
|
|
_mailinglistsync_check_group_membership_permissions($mailingList, $errors);
|
|
}
|
|
elseif ($formName === 'CRM_Event_Form_Participant') {
|
|
$mailingList = new EventMailingList($fields['event_id']);
|
|
_mailinglistsync_check_event_membership_permissions($mailingList, $errors);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implements hook_civicrm_post() to check on permissions to alter mailing list
|
|
* groups and sync group with mlmmj.
|
|
*
|
|
* @throws \Civi\API\Exception\UnauthorizedException
|
|
* @throws \Civi\Mailinglistsync\Exceptions\MailinglistException
|
|
* @throws \CRM_Core_Exception
|
|
*/
|
|
function mailinglistsync_civicrm_post(string $op, string $objectName, int $objectId, &$objectRef) {
|
|
if ($op === 'delete' || $op === 'edit' || $op === 'create') {
|
|
// Check on groups mailing list
|
|
// Note: I wonder why $objectId refers to the group instead of the group contact here.
|
|
if ($objectName === 'GroupContact') {
|
|
$mailingList = new GroupMailingList($objectRef->group_id ?? $objectId);
|
|
|
|
// Check permission to alter group membership
|
|
if ($mailingList->isEnabled() && !CRM_Core_Permission::check('manage_group_mailinglists')) {
|
|
CRM_Core_Session::setStatus(
|
|
E::ts('You do not have permission to manage memberships of mailing list groups.'),
|
|
E::ts('Permission Denied'),
|
|
'alert',
|
|
);
|
|
throw new MailinglistException(
|
|
'Permission to manage group membership denied',
|
|
MailinglistException::ERROR_CODE_PERMISSION_DENIED
|
|
);
|
|
}
|
|
|
|
// Check permission to alter AD group membership
|
|
if ($mailingList->isADGroup() && !CRM_Core_Permission::check('manage_ad_mailinglists')) {
|
|
CRM_Core_Session::setStatus(
|
|
E::ts('You do not have permission to manage memberships of AD mailing list groups.'),
|
|
E::ts('Permission Denied'),
|
|
'alert',
|
|
);
|
|
throw new MailinglistException(
|
|
'Permission to manage AD group membership denied',
|
|
MailinglistException::ERROR_CODE_PERMISSION_DENIED
|
|
);
|
|
}
|
|
}
|
|
|
|
// Check on event mailing lists
|
|
elseif ($objectName === 'Participant') {
|
|
$eventId = $objectRef->event_id ?? Event::get()
|
|
->addSelect('event_id')
|
|
->addWhere('id', '=', $objectId)
|
|
->execute()
|
|
->first()['event_id'];
|
|
$mailingList = new EventMailingList($eventId);
|
|
|
|
// Check permission to alter event mailing list if it is not a deletion
|
|
if ($op !== 'delete'
|
|
&& $mailingList->isEnabled()
|
|
&& !CRM_Core_Permission::check('manage_event_mailinglists')) {
|
|
CRM_Core_Session::setStatus(
|
|
E::ts('You do not have permission to manage event mailing lists.'),
|
|
E::ts('Permission Denied'),
|
|
'alert',
|
|
);
|
|
throw new MailinglistException(
|
|
'Permission to manage event mailing list denied',
|
|
MailinglistException::ERROR_CODE_PERMISSION_DENIED
|
|
);
|
|
}
|
|
}
|
|
|
|
// If this is an e-mail address of a location type used for mailing lists
|
|
if ($objectName === 'Email' && in_array((int) $objectRef->location_type_id, array_keys(getLocationTypes()))) {
|
|
// Ensure that only one e-mail address of this location type is set for this contact
|
|
$result = Email::get()
|
|
->addSelect('contact_id', 'contact.display_name')
|
|
->addJoin('LocationType AS location_type', 'INNER',
|
|
['location_type_id', '=', 'location_type.id'])
|
|
->addWhere('location_type_id', '=', $objectRef->location_type_id)
|
|
->addWhere('contact_id', '=', $objectRef->contact_id)
|
|
->addWhere('id', '!=', $objectRef->id)
|
|
->execute()
|
|
->first();
|
|
|
|
if (!empty($result) && $op != 'delete') {
|
|
throw new MailinglistException(
|
|
"An e-mail address of a mailing list type is already used for this contact",
|
|
MailinglistException::ERROR_CODE_UPDATE_EMAIL_ADDRESS_FAILED
|
|
);
|
|
}
|
|
|
|
// Ensure that this email address is only used for one contact
|
|
$results = Email::get()
|
|
->addSelect('contact.id', 'contact.display_name')
|
|
->addJoin('Contact AS contact', 'LEFT', [
|
|
'contact_id',
|
|
'=',
|
|
'contact.id',
|
|
])
|
|
->addWhere('contact_id', '!=', $objectRef->contact_id)
|
|
->addWhere('email', '=', $objectRef->email)
|
|
->addWhere('location_type_id', '=', $objectRef->location_type_id)
|
|
->execute()
|
|
->getArrayCopy();
|
|
|
|
if (!empty($results) && $op != 'delete') {
|
|
// Delete e-mail address if it is not allowed
|
|
// TODO: This is a workaround. We should prevent the creation of the e-mail address in the first place.
|
|
_mailinglistsync_delete_email_address((int) $objectRef->id);
|
|
|
|
$contacts = [];
|
|
foreach ($results as $result) {
|
|
$contactId = $result['contact.id'];
|
|
$contactName = $result['contact.display_name'];
|
|
$contactUrl = CRM_Utils_System::url('civicrm/contact/view', 'reset=1&cid=' . $contactId, TRUE);
|
|
$contacts[] = "<a href='$contactUrl'>$contactName (id: $contactId)</a>";
|
|
}
|
|
$message = count($contacts) > 1
|
|
? E::ts(
|
|
"This e-mail address is already used for other contacts: %1",
|
|
[1 => implode(', ', $contacts)]
|
|
)
|
|
: E::ts(
|
|
"This e-mail address is already used for another contact: %1",
|
|
[1 => array_pop($contacts)]
|
|
);
|
|
CRM_Core_Session::setStatus(
|
|
E::ts($message),
|
|
E::ts('E-Mail Address Already Used'),
|
|
'error',
|
|
['unique' => TRUE]
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Synchronize group and event mailing lists whenever they change by adding
|
|
* them to the queue which will be processed by the cron job.
|
|
*
|
|
* @throws \Civi\API\Exception\UnauthorizedException
|
|
* @throws \Civi\Mailinglistsync\Exceptions\MailinglistException
|
|
* @throws \CRM_Core_Exception
|
|
*/
|
|
function mailinglistsync_civicrm_postCommit(string $op, string $objectName, int $objectId, &$objectRef) {
|
|
// Sync groups mailing list
|
|
// Note: I wonder why $objectId refers to the group instead of the group contact here.
|
|
if ($objectName === 'GroupContact') {
|
|
$mailingList = new GroupMailingList($objectRef->group_id ?? $objectId);
|
|
if ($mailingList->isEnabled()) {
|
|
// Queue group for synchronization with mlmmj
|
|
$queue = \Civi::queue('propeace-mailinglist-group-queue', [
|
|
'type' => 'SqlParallel',
|
|
'is_autorun' => FALSE,
|
|
'reset' => FALSE,
|
|
'error' => 'abort',
|
|
]);
|
|
$queue->createItem(new CRM_Queue_Task(
|
|
// callback
|
|
['Civi\Mailinglistsync\QueueHelper', 'storeInstance'],
|
|
// arguments
|
|
[$mailingList->getId(), $mailingList::class],
|
|
// title
|
|
"Sync Group ID '{$mailingList->getId()}'"
|
|
));
|
|
}
|
|
}
|
|
|
|
elseif ($objectName === 'Group') {
|
|
$mailingList = new GroupMailingList($objectId);
|
|
if ($mailingList->isEnabled()) {
|
|
// Queue group for synchronization with mlmmj
|
|
$queue = \Civi::queue('propeace-mailinglist-group-queue', [
|
|
'type' => 'SqlParallel',
|
|
'is_autorun' => FALSE,
|
|
'reset' => FALSE,
|
|
'error' => 'abort',
|
|
]);
|
|
$queue->createItem(new CRM_Queue_Task(
|
|
// callback
|
|
['Civi\Mailinglistsync\QueueHelper', 'storeInstance'],
|
|
// arguments
|
|
[$mailingList->getId(), $mailingList::class],
|
|
// title
|
|
"Sync Group ID '{$mailingList->getId()}'"
|
|
));
|
|
}
|
|
}
|
|
|
|
// Sync event mailing lists
|
|
elseif ($objectName === 'Participant') {
|
|
$eventId = $objectRef->event_id ?? Participant::get()
|
|
->addSelect('event_id')
|
|
->addWhere('id', '=', $objectId)
|
|
->execute()
|
|
->first()['event_id'];
|
|
$mailingList = new EventMailingList($eventId);
|
|
if ($mailingList->isEnabled()) {
|
|
// Queue event for synchronization with mlmmj
|
|
$queue = \Civi::queue('propeace-mailinglist-event-queue', [
|
|
'type' => 'SqlParallel',
|
|
'is_autorun' => FALSE,
|
|
'reset' => FALSE,
|
|
'error' => 'abort',
|
|
]);
|
|
$queue->createItem(new CRM_Queue_Task(
|
|
// callback
|
|
['Civi\Mailinglistsync\QueueHelper', 'storeInstance'],
|
|
// arguments
|
|
[$mailingList->getId(), $mailingList::class],
|
|
// title
|
|
"Sync Event ID '{$mailingList->getId()}'"
|
|
));
|
|
}
|
|
}
|
|
|
|
elseif ($objectName === 'Event') {
|
|
$mailingList = new EventMailingList($objectId);
|
|
if ($mailingList->isEnabled()) {
|
|
// Queue event for synchronization with mlmmj
|
|
$queue = \Civi::queue('propeace-mailinglist-event-queue', [
|
|
'type' => 'SqlParallel',
|
|
'is_autorun' => FALSE,
|
|
'reset' => FALSE,
|
|
'error' => 'abort',
|
|
]);
|
|
$queue->createItem(new CRM_Queue_Task(
|
|
// callback
|
|
['Civi\Mailinglistsync\QueueHelper', 'storeInstance'],
|
|
// arguments
|
|
[$mailingList->getId(), $mailingList::class],
|
|
// title
|
|
"Sync Event ID '{$mailingList->getId()}'"
|
|
));
|
|
}
|
|
}
|
|
|
|
if ($objectName === 'GroupContact') {
|
|
$mailingList = new GroupMailingList($objectRef->group_id ?? $objectId);
|
|
if ($mailingList->isEnabled()) {
|
|
// Queue group for synchronization with mlmmj
|
|
$queue = \Civi::queue('propeace-mailinglist-group-queue', [
|
|
'type' => 'SqlParallel',
|
|
'is_autorun' => FALSE,
|
|
'reset' => FALSE,
|
|
'error' => 'abort',
|
|
]);
|
|
$queue->createItem(new CRM_Queue_Task(
|
|
// callback
|
|
['Civi\Mailinglistsync\QueueHelper', 'storeInstance'],
|
|
// arguments
|
|
[$mailingList->getId(), $mailingList::class],
|
|
// title
|
|
"Sync Group ID '{$mailingList->getId()}'"
|
|
));
|
|
}
|
|
}
|
|
|
|
// Sync e-mail addresses
|
|
elseif ($objectName === 'Email') {
|
|
// If an email address update comes from an api call, the
|
|
// objectRef->location_type_id is not set. So we have to get it from the
|
|
// database.
|
|
if (empty($objectRef->location_type_id)) {
|
|
$locationTypeId = \Civi\Api4\Email::get()
|
|
->addSelect('location_type_id')
|
|
->addWhere('id', '=', $objectRef->id)
|
|
->addWhere('location_type_id', 'IN', array_keys(getLocationTypes()))
|
|
->execute()
|
|
->first()['location_type_id'];
|
|
}
|
|
if (
|
|
!empty($locationTypeId) ||
|
|
in_array((int) $objectRef->location_type_id, array_keys(getLocationTypes()))
|
|
) {
|
|
// Queue email address for synchronization with mlmmj
|
|
$queue = \Civi::queue('propeace-mailinglist-email-queue', [
|
|
'type' => 'SqlParallel',
|
|
'is_autorun' => FALSE,
|
|
'reset' => FALSE,
|
|
'error' => 'abort',
|
|
]);
|
|
$queue->createItem(new CRM_Queue_Task(
|
|
// callback
|
|
['Civi\Mailinglistsync\QueueHelper', 'storeEmail'],
|
|
// arguments
|
|
[$objectRef->email],
|
|
// title
|
|
"Sync E-Mail '$objectRef->email'"
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implementation of hook_civicrm_permission
|
|
*
|
|
* @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_permission/
|
|
*/
|
|
function mailinglistsync_civicrm_permission(&$permissions) {
|
|
$permissions['manage_ad_mailinglists'] = [
|
|
'label' => E::SHORT_NAME . ': ' . E::ts('Manage AD Mailing Lists'),
|
|
'description' => E::ts('Manage mailing list groups originating from Active Directory'),
|
|
'disabled' => FALSE,
|
|
];
|
|
$permissions['manage_ad_address_type'] = [
|
|
'label' => E::SHORT_NAME . ': ' . E::ts('Manage AD Addresses'),
|
|
'description' => E::ts('Manage addresses reserved for data originating from Active Directory'),
|
|
'disabled' => FALSE,
|
|
];
|
|
$permissions['manage_group_mailinglists'] = [
|
|
'label' => E::SHORT_NAME . ': ' . E::ts('Manage Group Mailing Lists'),
|
|
'description' => E::ts('Manage groups used as mailing lists'),
|
|
'disabled' => FALSE,
|
|
];
|
|
$permissions['manage_event_mailinglists'] = [
|
|
'label' => E::SHORT_NAME . ': ' . E::ts('Manage Event Mailing Lists'),
|
|
'description' => E::ts('Manage events used as mailing lists'),
|
|
'disabled' => FALSE,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Check permissions to alter group membership.
|
|
*
|
|
* @param \Civi\Mailinglistsync\GroupMailingList $mailingList
|
|
* @param $errors
|
|
*
|
|
* @return void
|
|
*/
|
|
function _mailinglistsync_check_group_membership_permissions(GroupMailingList $mailingList, &$errors): void {
|
|
if ($mailingList->isEnabled() && !CRM_Core_Permission::check('manage_group_mailinglists')) {
|
|
$errors['group_id'] = E::ts('You do not have permission to manage group membership.');
|
|
}
|
|
if ($mailingList->isADGroup() && !CRM_Core_Permission::check('manage_ad_mailinglists')) {
|
|
$errors['group_id'] = E::ts('You do not have permission to manage AD group membership.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check permissions to alter event membership.
|
|
*
|
|
* @param \Civi\Mailinglistsync\EventMailingList $mailingList
|
|
* @param $errors
|
|
*
|
|
* @return void
|
|
*/
|
|
function _mailinglistsync_check_event_membership_permissions(EventMailingList $mailingList, &$errors): void {
|
|
if ($mailingList->isEnabled() && !CRM_Core_Permission::check('manage_event_mailinglists')) {
|
|
$errors['event_id'] = E::ts('You do not have permission to manage event membership.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Unfortunately, we cannot prevent the creation of a new e-mail address within
|
|
* the post hook, so we have to delete it again if it is not allowed.
|
|
*
|
|
* @param int $emailId
|
|
*
|
|
* @return void
|
|
* @throws \Civi\Mailinglistsync\Exceptions\MailinglistException
|
|
*/
|
|
function _mailinglistsync_delete_email_address(int $emailId): void {
|
|
try {
|
|
$email = Email::delete()
|
|
->addWhere('id', '=', $emailId)
|
|
->execute();
|
|
}
|
|
catch (\Exception $e) {
|
|
throw new MailinglistException(
|
|
"Failed to delete e-mail address: {$e}",
|
|
MailinglistException::ERROR_CODE_DELETE_EMAIL_ADDRESS_FAILED
|
|
);
|
|
}
|
|
}
|