Problem/Motivation
From what I can tell, the current release of this module allows users to be registered for the same event instance multiple times. I'd like to make sure that any given email address can only register for an event one time.
Proposed resolution
recurring_events_registration\src\RegistrationCreationService.php:324
/**
* Has the email address registered for this event before.
*
* @param str $email
* The email address of the user.
*
* @return bool/int
* If the registration already exists, return the registration ID. Otherwise, return FALSE.
*/
public function hasUserRegisteredByEmail($email) {
$existing_registration_id = FALSE;
$current_registrations = $this->retrieveAllSeriesRegisteredParties();
foreach ($current_registrations as $registration_id => $registration_record) {
if (($this->eventSeries->id() == $registration_record->get("eventseries_id")->target_id)
&& ($this->eventInstance->id() == $registration_record->get("eventinstance_id")->target_id)
&& (strtolower($email) == strtolower($registration_record->get("email")->value))) {
$existing_registration_id = $registration_id;
break;
}
}
return $existing_registration_id;
}
recurring_events\modules\recurring_events_registration\src\Form\RegistrantForm.php:415
$existing_registration_id = $this->creationService->hasUserRegisteredByEmail($form["email"]["widget"][0]["value"]["#value"]);
if ($existing_registration_id) {
$message = $this->t("You've already registered for this event.");
$form_state->setRedirect('entity.registrant.add_form', ['eventinstance' => $event_instance_id]);
}
else {
...
}
Remaining tasks
- I don't love how expensive the hasUserRegisteredByEmail() by having to essentially load and scan through every registration record on the site.
- Is there a better way to get the user's email address instead of $form["email"]["widget"][0]["value"]["#value"]?
Comments
Comment #2
owenbush commentedI think the idea behind this is good. I am a little concerned however because some of the use-cases we have for the module distinctly need emails to not be unique per event. For example, Libraries where parents are registering on behalf of themselves and maybe one or more young children who do not have emails.
There is an idea floating around in another issue of allowing per-event restrictions on the number of registrants someone can create. Part of that discussion has been how to determine whether a person has registered before. If they are logged in you can check the author of the registrant, (or even in your use-case the email), but if they are not logged in you would have to rely on something else. There is a problem with being able to game the system when relying upon emails, because of the use of plus- and dot-addressing. So name@domain.com is the same email as name+foobar@domain.com. Also, firstlast@domain.com is the same account as first.last@domain.com. So if there are restrictions on emails, they are pretty easy to circumvent for people who know about these sorts of things.
That's not to say it is not worthwhile doing it, but that checking emails is not a fool-proof way of preventing multiple-registrations. The best way would be to use a Drupal account, but a lot of sites won't want to enforce a Drupal login to register.
Its food for thought for sure and I'd love to have your input on this.
Here is one of the other issues/threads about this: #3113980: Limit number of registrations allowed
Comment #3
owenbush commentedAs for your questions about efficiency of your code, you could use an entity query to query properties of the registrant - and eventseries_id, eventinstance_id and email are all basefields so they should be queryable.
You can get a query for a registrant like so:
I've not tested that, but I think it would work.
Comment #4
ryankavalsky commentedThat's a great point about the plus- and dot-addressing. I wasn't aware of that until now (my organization's email system doesn't support it), but like you said, it's easy to work with. While the entityQuery works for exact email address matches, it won't be able to compare a "clean" (i.e. no ".", no "+", and lowercase) already-registered email address to a "clean" trying-to-register email address, sort of like this:
After reading the other issue thread (#3113980: Limit number of registrations allowed), it sounds like limiting registrations to one per email address is still a good idea. I'm thinking we should make it an option when somebody creates/edits an event/series. Do you have suggestions/resources for how to add a new boolean unique_email_address field to that form? The code I've worked up adds the field, but doesn't store the field's value. I've created a new recurring_events_update_xxxx() to alter the schema, but my update didn't go through (sorry, I'm a newbie at _update_):
Failed: Drupal\Core\Database\DatabaseExceptionWrapper: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'eventseries_field_data.event_registration__unique_email_address' in 'field list':See the attached patch file for all changes I've made against 8.x-1.0-beta2. You'll also see that I've added a $message_type so depending on the registration result, the message can show either as a success, warning, or error.
I have some time in my schedule at the moment, so I'm happy to help in any way.
Comment #5
ryankavalsky commentedI've re-rolled this patch to correct some file formatting issues I was having, since I use PowerShell as my terminal:
If anybody else uses PowerShell, here's a script that allows you to create good diff files in one line:
Then, just run the following command:
creatediff [patch_file_name].patchComment #6
owenbush commentedI'm not against this patch at all, that's not why I have not merged it yet. It just needs some work like adding the field to the schema appropriately to get it to create the field in the database, and there is a @todo in the middle of this code which needs to be updated to check if duplicate emails are not allowed, that is hardcoded to TRUE at this point. So there is work to be done before this is mergeable and I'll try and see if I can get to it soon.
Comment #7
owenbush commentedThanks for your hard work on the patch. The patch would not apply and there were quite a few changes needed to make this work, so I have gone ahead and recreated the patch. If you have time, please take a moment to test this out. You will need to run database updates if you already have an installation with registration configured.
Comment #8
ryankavalsky commentedThanks @owenbush! I tried applying the patch against a fresh install of 2.0.0-beta1 and 2.0.0-beta2, but neither worked. Which release should I test the patch against?
Comment #9
owenbush commentedHey Ryan,
I'll check the patch soon and verify why this may be failing.
Comment #10
owenbush commentedSorry about the delay. I have figured out what is going on.
There is another issue: #3173249: Allow Registrants to be Versionable and Add Status which has this patch:
https://www.drupal.org/files/issues/2020-10-16/recurring_events-versiona...
Because that patch also touches upon some of the same things, you need to have that patch applied first. That patch has actually been merged but only into the dev version of the module - I have not cut a release yet.
And the patches only apply to the 2.0.0-beta2 and 8.x-1.0-beta2 versions of the module.
Comment #11
ryankavalsky commentedAfter applying the versionable registrants patch to a clean install of 2.0.0-beta1, I got the below error. Is there an extra comma on line 109?
Is currently:
Should be:
Error:
Comment #12
owenbush commentedThanks for finding that issue. I have actually merged a fix for that into the dev release, but just in case you need a patch for it, I have attached a patch here too. That patch is an additional patch to the other in this issue
Comment #13
ryankavalsky commentedWorks like a charm! Thanks for the patch and re-patch. I've now marked this as RTBC.
Comment #14
owenbush commentedComment #15
owenbush commentedComment #17
ryankavalsky commentedHi again! Just one more change to the patch. If somebody is editing their existing registration, we don't have to check for a duplicate email.
So, instead of:
It should be
Comment #18
owenbush commentedYou raise a good point about editing a registrant, however, I think the solution is the wrong one. We don't actually want to not check for duplicates when editing, we still do (for example if someone tries to change their email address to something else and that email has already registered).
What we likely want to do instead is to check to see whether any registrants (other than the currently edited one) already have that email address. This will stop the validation errors upon resaving an existing registrant, but also prevent the person changing their address to one already registered with another registrant.
Comment #19
ryankavalsky commentedThat's a great point. We can add a parameter to the hasUserRegisteredByEmail() function that will allow us to optionally exclude a registration ID when checking for duplicate email addresses. Then, when somebody is editing their existing registration, we'll just be sure to send over that existing registration ID as the ID to not be checked against.
RegistrationCreationService.php hasUserRegisteredByEmail:
RegistrantForm.php validateForm():
Comment #20
owenbush commentedAttached is a patch to deal with editing registrants.
Comment #21
ryankavalsky commentedThanks for putting out that patch! It didn't apply cleanly for me, for either 2.0.0-beta1 or 2.0.0-beta2. I was using the beta1 and beta2 code pulled fresh from the project page.
2.0.0-beta1:
2.0.0-beta2:
Comment #22
owenbush commentedIn all likelihood this does not apply to the latest releases because it was built against the latest dev versions of the module, which have had a lot of changes over the last few weeks.
I'm going to try cut a new beta release soon to capture all those changes.
Comment #25
owenbush commentedI am merging this now, so we can get this into the next beta, and ultimately release candidate, release. If there are issues we can address them with a new patch.