Skip to content
Snippets Groups Projects
Commit 23816324 authored by Owen Bush's avatar Owen Bush Committed by Owen Bush
Browse files

Issue #3183483 by owenbush, ryankavalsky: Check For Existing Registration by Email

parent aa34b3ea
No related branches found
Tags 2.0.0-beta3
No related merge requests found
......@@ -220,6 +220,7 @@ function recurring_events_registration_update_8005() {
'format' => 'enabled-disabled',
],
])
->setDefaultValue(0)
->setDisplayConfigurable('view', FALSE);
$definition_update_manager->installFieldStorageDefinition('status', 'registrant', 'registrant', $status);
}
......@@ -259,3 +260,100 @@ function recurring_events_registration_update_8006(&$sandbox) {
$sandbox['#finished'] = 1;
}
}
/**
* Update the event_registration field type to add unique email field.
*/
function recurring_events_registration_update_8007() {
$entity_type_manager = \Drupal::entityTypeManager();
$storage = $entity_type_manager->getStorage('eventseries');
$bundle_definition = $entity_type_manager->getDefinition('eventseries');
$id_key = $bundle_definition->getKey('id');
$revision_key = $bundle_definition->getKey('revision');
$table_name = $storage->getDataTable() ?: $storage->getBaseTable();
$revision_table_name = $storage->getRevisionDataTable() ?: $storage->getRevisionTable();
$database = \Drupal::database();
$definition_manager = \Drupal::entityDefinitionUpdateManager();
// We need to grab all the current data for registration settings for an event
// before we remove the registration field, before re-adding it with our new
// unique email field.
$reg_fields = [
'event_registration__value',
'event_registration__end_value',
'event_registration__registration',
'event_registration__registration_type',
'event_registration__registration_dates',
'event_registration__capacity',
'event_registration__waitlist',
'event_registration__instance_schedule_open',
'event_registration__instance_schedule_open_amount',
'event_registration__instance_schedule_open_units',
'event_registration__instance_schedule_close',
'event_registration__instance_schedule_close_amount',
'event_registration__instance_schedule_close_units',
'event_registration__instance_schedule_close_type',
];
$registration_values = $database->select($table_name)
->fields($table_name, [$id_key, $revision_key] + $reg_fields)
->execute()
->fetchAll();
// We need to NULL out all the current values, so that we can remove the field
// without warning.
$update_fields = array_fill_keys($reg_fields, NULL);
$database->update($table_name)
->fields($update_fields)
->execute();
// Remove the existing registration field.
$field_storage_definition = $definition_manager->getFieldStorageDefinition('event_registration', 'eventseries');
$definition_manager->uninstallFieldStorageDefinition($field_storage_definition);
// Create a new instance of the registration field and install it.
$storage_definition = BaseFieldDefinition::create('event_registration')
->setName('event_registration')
->setLabel(t('Event Registration'))
->setDescription('The event registration configuration.')
->setDisplayConfigurable('form', TRUE)
->setDisplayConfigurable('view', TRUE)
->setRevisionable(TRUE)
->setTranslatable(FALSE)
->setCardinality(1)
->setRequired(FALSE)
->setDisplayOptions('form', [
'type' => 'event_registration',
'weight' => 10,
]);
\Drupal::entityDefinitionUpdateManager()
->installFieldStorageDefinition('event_registration', 'eventseries', 'eventseries', $storage_definition);
// Restore previously stored values.
if (!empty($registration_values)) {
foreach ($registration_values as $value) {
$values_to_restore = [
$id_key => $value->{$id_key},
$revision_key => $value->{$revision_key},
];
foreach ($reg_fields as $field) {
$values_to_restore[$field] = rtrim($value->{$field}, 's');
}
$values_to_restore['event_registration__unique_email_address'] = 0;
$database->update($table_name)
->fields($values_to_restore)
->condition($id_key, $value->{$id_key})
->execute();
$database->update($revision_table_name)
->fields($values_to_restore)
->condition($id_key, $value->{$id_key})
->execute();
}
}
}
......@@ -262,6 +262,7 @@ class Registrant extends EditorialContentEntityBase implements RegistrantInterfa
],
'weight' => 120,
])
->setDefaultValue(0)
->setDisplayConfigurable('form', TRUE);
return $fields;
......
......@@ -414,6 +414,17 @@ class RegistrantForm extends ContentEntityForm {
$entity->setWaitlist($form_state->getValue('add_to_waitlist'));
}
}
$unique_email_address = $this->creationService->registrationUniqueEmailAddress();
if ($unique_email_address) {
$email_address = $form_state->getValue('email');
$ignored_registrant_id = ($entity->isNew() ? NULL : (int) $entity->id());
$existing_registration_id = $this->creationService->hasUserRegisteredByEmail($email_address[0]['value'], $ignored_registrant_id);
if ($existing_registration_id) {
// If a registration already exists for the email display an error.
$form_state->setErrorByName('email', $this->t("You've already registered for this event."));
}
}
}
/**
......
......@@ -89,6 +89,12 @@ class EventRegistration extends DateRangeItem {
'length' => 255,
];
$schema['columns']['unique_email_address'] = [
'type' => 'int',
'default' => 0,
'unsigned' => TRUE,
];
return $schema;
}
......@@ -108,12 +114,13 @@ class EventRegistration extends DateRangeItem {
$instance_schedule_close_amount = $this->get('instance_schedule_close_amount')->getValue();
$instance_schedule_close_units = $this->get('instance_schedule_close_units')->getValue();
$instance_schedule_close_type = $this->get('instance_schedule_close_type')->getValue();
$unique_email_address = $this->get('unique_email_address')->getValue();
return parent::isEmpty() && empty($registration) && empty($registration_type)
&& empty($registration_dates) && empty($capacity) && empty($waitlist)
&& empty($instance_schedule_open) && empty($instance_schedule_open_amount)
&& empty($instance_schedule_open_units) && empty($instance_schedule_close)
&& empty($instance_schedule_close_amount) && empty($instance_schedule_close_units)
&& empty($instance_schedule_close_type);
&& empty($instance_schedule_close_type) && empty($unique_email_address);
}
/**
......@@ -170,6 +177,10 @@ class EventRegistration extends DateRangeItem {
->setLabel(t('Instance Registration Close Type'))
->setDescription(t('Select when to close registrations (type).'));
$properties['unique_email_address'] = DataDefinition::create('boolean')
->setLabel(t('Restrict registration to once per email address?'))
->setDescription(t('Select whether to prevent a single email address from registering multiple times for the same event.'));
return $properties;
}
......
......@@ -77,11 +77,24 @@ class EventRegistrationWidget extends DateRangeDefaultWidget {
'#default_value' => $items[$delta]->registration ?: '',
];
$element['unique_email_address'] = [
'#type' => 'checkbox',
'#title' => $this->t('Restrict registration to once per email address?'),
'#description' => $this->t('Select this box to only allow an email address to register for an event one time.'),
'#weight' => 1,
'#default_value' => $items[$delta]->unique_email_address ?: '',
'#states' => [
'visible' => [
':input[name="event_registration[0][registration]"]' => ['checked' => TRUE],
],
],
];
$element['registration_type'] = [
'#type' => 'radios',
'#title' => $this->t('Registration Type'),
'#description' => $this->t('Select whether registrations are for the entire series, or for individual instances.'),
'#weight' => 1,
'#weight' => 2,
'#default_value' => $items[$delta]->registration_type ?: 'instance',
'#options' => [
'instance' => $this->t('Individual Event Registration'),
......@@ -98,7 +111,7 @@ class EventRegistrationWidget extends DateRangeDefaultWidget {
'#type' => 'radios',
'#title' => $this->t('Registration Dates'),
'#description' => $this->t('Choose between open or scheduled registration. Open registration ends when the event begins.'),
'#weight' => 2,
'#weight' => 3,
'#default_value' => $items[$delta]->registration_dates ?: 'open',
'#options' => [
'open' => $this->t('Open Registration'),
......@@ -114,7 +127,7 @@ class EventRegistrationWidget extends DateRangeDefaultWidget {
$element['series_registration'] = [
'#type' => 'fieldset',
'#title' => $this->t('Series Registration'),
'#weight' => 3,
'#weight' => 4,
'#states' => [
'visible' => [
':input[name="event_registration[0][registration]"]' => ['checked' => TRUE],
......@@ -135,7 +148,7 @@ class EventRegistrationWidget extends DateRangeDefaultWidget {
$element['instance_registration'] = [
'#type' => 'fieldset',
'#title' => $this->t('Instance Registration'),
'#weight' => 3,
'#weight' => 4,
'#states' => [
'visible' => [
':input[name="event_registration[0][registration]"]' => ['checked' => TRUE],
......@@ -260,7 +273,7 @@ class EventRegistrationWidget extends DateRangeDefaultWidget {
'#type' => 'number',
'#title' => $this->t('Total Number of Spaces Available'),
'#description' => $this->t('Maximum number of attendees available for each series, or individual event. Leave blank for unlimited.'),
'#weight' => 5,
'#weight' => 6,
'#default_value' => $items[$delta]->capacity ?: '',
'#min' => 0,
'#states' => [
......@@ -277,7 +290,7 @@ class EventRegistrationWidget extends DateRangeDefaultWidget {
'#type' => 'checkbox',
'#title' => $this->t('Enable Waiting List'),
'#description' => $this->t('Enable a waiting list if the number of registrations reaches capacity.'),
'#weight' => 6,
'#weight' => 7,
'#default_value' => $waitlist_default_value,
'#states' => [
'visible' => [
......@@ -364,6 +377,10 @@ class EventRegistrationWidget extends DateRangeDefaultWidget {
$item['instance_schedule_close_type'] = '';
}
if (empty($item['unique_email_address'])) {
$item['unique_email_address'] = 0;
}
}
$values = parent::massageFormValues($values, $form, $form_state);
return $values;
......
......@@ -672,4 +672,60 @@ class RegistrationCreationService {
return $token_help;
}
/**
* Has the email address registered for this event before.
*
* @param string $email
* The email address of the user.
* @param int $ignored_registrant_id
* The ID of the registrant to ignore during duplicate email checks.
*
* @return bool|int
* Return the registration ID if it exists otherwise return FALSE.
*/
public function hasUserRegisteredByEmail($email, $ignored_registrant_id = NULL) {
// Look through this series' registrations.
$existing_registration_id = FALSE;
$current_series_registrations = $this->retrieveAllSeriesRegisteredParties();
foreach ($current_series_registrations as $registration_id => $registration_record) {
if ($registration_id === $ignored_registrant_id) {
continue;
}
// Compare the event instance ID and email address.
if (($this->eventInstance->id() == $registration_record->get('eventinstance_id')->target_id)
&& ($this->cleanEmailAddress($email) == $this->cleanEmailAddress($registration_record->get('email')->value))) {
// Remember the existing registration ID and stop looking.
$existing_registration_id = $registration_id;
break;
}
}
return $existing_registration_id;
}
/**
* Clean the email address to handle plus- and dot-addressing.
*
* @param string $email
* The email address of the user.
*
* @return string
* The cleaned email address
*/
public function cleanEmailAddress($email) {
$email_address_parts = (isset($email) ? explode('@', $email) : ['', '']);
$email_address_clean = str_replace('.', '', $email_address_parts[0]) . '@' . $email_address_parts[1];
$email_address_clean = preg_replace('/\+.*@/', '@', $email_address_clean);
return (isset($email) ? strtolower($email_address_clean) : NULL);
}
/**
* Do email addresses have to be unique for this event?
*
* @return bool
* Whether or not unique email addresses are enforced for this event.
*/
public function registrationUniqueEmailAddress() {
return $this->eventSeries->event_registration->unique_email_address;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment