Skip to content

Commit

Permalink
Move checking for existing participants out of event registration forms
Browse files Browse the repository at this point in the history
  • Loading branch information
jensschuppe committed Aug 26, 2024
1 parent e6c7a8d commit 489ff75
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 64 deletions.
114 changes: 114 additions & 0 deletions CRM/Event/BAO/Participant.php
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,120 @@ public static function deleteParticipant($id) {
return $participant;
}

/**
* Retrieves existing participants.
*
* @param int $contactId
* The contact ID of participants to find.
* @param int $eventId
* The event ID of participants to find.
* @param bool $onlyCounted
* Whether to only consider registrations with a status with "is_counted".
* @param bool $includeOnWaitlist
* Whether to consider registrations with status "On waitlist" when restricing to "is_counted".
* @param array $excludeStatus
* A list of registration status to not consider (e.g. for ignoring cancelled registrations).
* @param array $filterRoleIds
* A list of participant role IDs to filter for. Registrations with other roles will not be considered.
* @param bool $includeTest
* Whether to include test participants.
*
* @return array<int,array{id:int,"status_id:name":string}>
* An array of participants (a subset of attributes) matching the given criteria, keyed by ID.
* @throws \CRM_Core_Exception
* @throws \Civi\API\Exception\UnauthorizedException
*
* @internal
*/
public static function findExistingParticipants(
int $contactId,
int $eventId,
bool $onlyCounted = TRUE,
bool $includeOnWaitlist = TRUE,
array $excludeStatus = ['Cancelled'],
array $filterRoleIds = [],
bool $includeTest = FALSE
) {
$query = \Civi\Api4\Participant::get(FALSE)
->addSelect('id', 'status_id:name')
->addWhere('contact_id', '=', $contactId)
->addWhere('event_id', '=', $eventId);

if ($onlyCounted) {
$query
->addJoin('ParticipantStatusType AS participant_status_type', 'LEFT');
$clauses = [
['participant_status_type.is_counted', '=', TRUE],
];
if ($includeOnWaitlist) {
$clauses = ['participant_status_type.name', '=', 'On waitlist'];
}
$query->addClause('OR', $clauses);
}

if ([] !== $excludeStatus) {
$query->addWhere('status_id:name', 'NOT IN', $excludeStatus);
}

if ([] !== $filterRoleIds) {
$query->addWhere('role_id', 'IN', $filterRoleIds);
}

if (!$includeTest) {
$query->addWhere('is_test', '=', FALSE);
}

$result = $query->execute();
return $result->getArrayCopy();
}

/**
* Checks for existing participants.
* Can be used during validation of new event registrations to check for duplicates.
*
* @param int $contactId
* The contact ID of participants to find.
* @param int $eventId
* The event ID of participants to find.
* @param bool $onlyCounted
* Whether to only consider registrations with a status with "is_counted".
* @param bool $includeOnWaitlist
* Whether to consider registrations with status "On waitlist" when restricing to "is_counted".
* @param array $excludeStatus
* A list of registration status to not consider (e.g. for ignoring cancelled registrations).
* @param array $filterRoleIds
* A list of participant role IDs to filter for. Registrations with other roles will not be considered.
* @param bool $includeTest
* Whether to include test participants.
*
* @return bool
* @throws \CRM_Core_Exception
* @throws \Civi\API\Exception\UnauthorizedException
*
* @internal
*/
public static function exists(
int $contactId,
int $eventId,
bool $onlyCounted = TRUE,
bool $includeOnWaitlist = TRUE,
array $excludeStatus = ['Cancelled'],
array $filterRoleIds = [],
bool $includeTest = FALSE
) {
return count(
self::findExistingParticipants(
$contactId,
$eventId,
$onlyCounted,
$includeOnWaitlist,
$excludeStatus,
$filterRoleIds,
$includeTest
)
) > 0;
}

/**
* Checks duplicate participants.
*
Expand Down
25 changes: 15 additions & 10 deletions CRM/Event/Form/Participant.php
Original file line number Diff line number Diff line change
Expand Up @@ -779,16 +779,21 @@ public static function formRule($values, $files, $self) {
$event->id = $eventId;
$event->find(TRUE);

if (!$event->allow_same_participant_emails && !empty($contactId) && !empty($eventId)) {
$cancelledStatusID = CRM_Core_PseudoConstant::getKey('CRM_Event_BAO_Participant', 'status_id', 'Cancelled');
$dupeCheck = new CRM_Event_BAO_Participant();
$dupeCheck->contact_id = $contactId;
$dupeCheck->event_id = $eventId;
$dupeCheck->whereAdd("status_id != {$cancelledStatusID} ");
$dupeCheck->find(TRUE);
if (!empty($dupeCheck->id)) {
$errorMsg['event_id'] = ts('This contact has already been assigned to this event.');
}
if (
!$event->allow_same_participant_emails
&& !empty($contactId)
&& !empty($eventId)
&& CRM_Event_BAO_Participant::exists(
$contactId,
$eventId,
FALSE,
TRUE,
['Cancelled'],
[],
TRUE
)
) {
$errorMsg['event_id'] = ts('This contact has already been assigned to this event.');
}
}
return empty($errorMsg) ? TRUE : $errorMsg;
Expand Down
95 changes: 46 additions & 49 deletions CRM/Event/Form/Registration/Register.php
Original file line number Diff line number Diff line change
Expand Up @@ -939,7 +939,7 @@ public function postProcess() {
* @param bool $isAdditional
* Treat isAdditional participants a bit differently.
*
* @return int
* @return bool|void
*/
public static function checkRegistration($fields, $form, $isAdditional = FALSE) {
// CRM-3907, skip check for preview registrations
Expand All @@ -953,57 +953,54 @@ public static function checkRegistration($fields, $form, $isAdditional = FALSE)
$contactID = self::getRegistrationContactID($fields, $form, $isAdditional);

if ($contactID) {
$participant = new CRM_Event_BAO_Participant();
$participant->contact_id = $contactID;
$participant->event_id = $form->_values['event']['id'];
if (!empty($fields['participant_role']) && is_numeric($fields['participant_role'])) {
$participant->role_id = $fields['participant_role'];
}
else {
$participant->role_id = $form->_values['event']['default_role_id'];
}
$participant->is_test = 0;
$participant->find();
// Event#30 - Anyone whose status type has `is_counted` OR is on the waitlist should be considered as registered.
$statusTypes = CRM_Event_PseudoConstant::participantStatus(NULL, 'is_counted = 1') + CRM_Event_PseudoConstant::participantStatus(NULL, "name = 'On waitlist'");
while ($participant->fetch()) {
if (array_key_exists($participant->status_id, $statusTypes)) {
if (!$isAdditional && !$form->_values['event']['allow_same_participant_emails']) {
$registerUrl = CRM_Utils_System::url('civicrm/event/register',
"reset=1&id={$form->_values['event']['id']}&cid=0"
);
if ($form->_pcpId) {
$registerUrl .= '&pcpId=' . $form->_pcpId;
}
$registrationType = (CRM_Event_PseudoConstant::getKey('CRM_Event_BAO_Participant', 'participant_status_id', 'On waitlist') == $participant->status_id) ? 'waitlisted' : 'registered';
if ($registrationType == 'waitlisted') {
$status = ts("It looks like you are already waitlisted for this event. If you want to change your registration, or you feel that you've received this message in error, please contact the site administrator.");
}
else {
$status = ts("It looks like you are already registered for this event. If you want to change your registration, or you feel that you've received this message in error, please contact the site administrator.");
}
$status .= ' ' . ts('You can also <a href="%1">register another participant</a>.', [1 => $registerUrl]);
CRM_Core_Session::singleton()->setStatus($status, '', 'alert');
$url = CRM_Utils_System::url('civicrm/event/info',
"reset=1&id={$form->_values['event']['id']}&noFullMsg=true"
);
if ($form->_action & CRM_Core_Action::PREVIEW) {
$url .= '&action=preview';
}
$existingParticipants = CRM_Event_BAO_Participant::findExistingParticipants(
$contactID,
$form->_values['event']['id'],
TRUE,
TRUE,
[],
[
is_numeric($fields['participant_role'] ?? NULL) ? $fields['participant_role'] : $form->_values['event']['default_role_id'],
]
);
if (
count($existingParticipants) > 0
&& !$isAdditional
&& !$form->_values['event']['allow_same_participant_emails']
) {
$registerUrl = CRM_Utils_System::url('civicrm/event/register',
"reset=1&id={$form->_values['event']['id']}&cid=0"
);
if ($form->_pcpId) {
$registerUrl .= '&pcpId=' . $form->_pcpId;
}
$registrationType = (CRM_Event_PseudoConstant::getKey('CRM_Event_BAO_Participant', 'participant_status_id', 'On waitlist') == reset($existingParticipants)['status_id:name']) ? 'waitlisted' : 'registered';
if ($registrationType == 'waitlisted') {
$status = ts("It looks like you are already waitlisted for this event. If you want to change your registration, or you feel that you've received this message in error, please contact the site administrator.");
}
else {
$status = ts("It looks like you are already registered for this event. If you want to change your registration, or you feel that you've received this message in error, please contact the site administrator.");
}
$status .= ' ' . ts('You can also <a href="%1">register another participant</a>.', [1 => $registerUrl]);
CRM_Core_Session::singleton()->setStatus($status, '', 'alert');
$url = CRM_Utils_System::url('civicrm/event/info',
"reset=1&id={$form->_values['event']['id']}&noFullMsg=true"
);
if ($form->_action & CRM_Core_Action::PREVIEW) {
$url .= '&action=preview';
}

if ($form->_pcpId) {
$url .= '&pcpId=' . $form->_pcpId;
}
if ($form->_pcpId) {
$url .= '&pcpId=' . $form->_pcpId;
}

CRM_Utils_System::redirect($url);
}
CRM_Utils_System::redirect($url);
}

if ($isAdditional) {
$status = ts("It looks like this participant is already registered for this event. If you want to change your registration, or you feel that you've received this message in error, please contact the site administrator.");
CRM_Core_Session::singleton()->setStatus($status, '', 'alert');
return $participant->id;
}
}
if ($isAdditional) {
$status = ts("It looks like this participant is already registered for this event. If you want to change your registration, or you feel that you've received this message in error, please contact the site administrator.");
CRM_Core_Session::singleton()->setStatus($status, '', 'alert');
return TRUE;
}
}
}
Expand Down
16 changes: 11 additions & 5 deletions CRM/Event/Form/Task/Register.php
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,17 @@ public function postProcess(): void {
$duplicateContacts = 0;
foreach ($this->_contactIds as $k => $dupeCheckContactId) {
// Eliminate contacts that have already been assigned to this event.
$dupeCheck = new CRM_Event_BAO_Participant();
$dupeCheck->contact_id = $dupeCheckContactId;
$dupeCheck->event_id = $event_id;
$dupeCheck->find(TRUE);
if (!empty($dupeCheck->id)) {
if (
CRM_Event_BAO_Participant::exists(
$dupeCheckContactId,
$event_id,
FALSE,
TRUE,
[],
[],
TRUE
)
) {
$duplicateContacts++;
if (!$allowSameParticipantEmails) {
unset($this->_contactIds[$k]);
Expand Down

0 comments on commit 489ff75

Please sign in to comment.