From 2edf04dfcca8c41df13df26ca9d0bf128d589666 Mon Sep 17 00:00:00 2001
From: Hangzhi Yu <hangzhi@protonmail.com>
Date: Mon, 7 Feb 2022 23:57:18 +0100
Subject: [PATCH] Create person and link it to person invitation when invite by
 email is used

---
 CHANGELOG.rst            |  3 +++
 aleksis/core/forms.py    | 27 +++++++++++++++++++++++++++
 aleksis/core/models.py   |  2 +-
 aleksis/core/settings.py |  2 ++
 4 files changed, 33 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 2449b5d35..b69711b1d 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -12,6 +12,9 @@ Unreleased
 Fixed
 ~~~~~
 
+* No person was created and linked to the PersonInvitation object when invite by e-mail is used
+* No valid data in the second e-mail field of the signup form when it was disabled
+* Invitation options were displayed even when the feature was disabled
 * Inviting newly created persons for registration failed
 * [Docker] Do not clear cache in migration container die to session invalidation issues
 * Notification email about user changes was broken
diff --git a/aleksis/core/forms.py b/aleksis/core/forms.py
index 87071a8fd..ef4184812 100644
--- a/aleksis/core/forms.py
+++ b/aleksis/core/forms.py
@@ -18,6 +18,7 @@ from dj_cleavejs import CleaveWidget
 from django_select2.forms import ModelSelect2MultipleWidget, ModelSelect2Widget, Select2Widget
 from dynamic_preferences.forms import PreferenceForm
 from guardian.shortcuts import assign_perm
+from invitations.forms import InviteForm
 from material import Fieldset, Layout, Row
 
 from .mixins import ExtensibleForm, SchoolTermRelatedExtensibleForm
@@ -29,6 +30,7 @@ from .models import (
     GroupType,
     OAuthApplication,
     Person,
+    PersonInvitation,
     SchoolTerm,
 )
 from .registries import (
@@ -410,6 +412,31 @@ class InvitationCodeForm(forms.Form):
         self.fields["code"].widget = CleaveWidget(blocks=blocks, delimiter="-", uppercase=True)
 
 
+class PersonCreateInviteForm(InviteForm):
+    """Custom form to create a person and invite them."""
+
+    first_name = forms.CharField(label=_("First name"), required=True)
+    last_name = forms.CharField(label=_("Last name"), required=True)
+
+    layout = Layout(
+        Row("first_name", "last_name"),
+        Row("email"),
+    )
+
+    def clean_email(self):
+        if Person.objects.filter(email=self.cleaned_data["email"]).exists():
+            raise ValidationError(_("A person is using this e-mail address"))
+        return super().clean_email()
+
+    def save(self, email):
+        person = Person.objects.create(
+            first_name=self.cleaned_data["first_name"],
+            last_name=self.cleaned_data["last_name"],
+            email=email,
+        )
+        return PersonInvitation.create(email=email, person=person)
+
+
 class SelectPermissionForm(forms.Form):
     """Select a permission to assign."""
 
diff --git a/aleksis/core/models.py b/aleksis/core/models.py
index c0f03d347..812a9d254 100644
--- a/aleksis/core/models.py
+++ b/aleksis/core/models.py
@@ -1156,7 +1156,7 @@ class PersonInvitation(AbstractBaseInvitation, PureDjangoModel):
         packet_size = get_site_preferences()["auth__invite_code_packet_size"]
         code = generate_random_code(length, packet_size)
 
-        instance = cls._default_manager.create(email=email, inviter=inviter, key=code, **kwargs)
+        instance = cls.objects.create(email=email, inviter=inviter, key=code, **kwargs)
         return instance
 
     key_expired = Invitation.key_expired
diff --git a/aleksis/core/settings.py b/aleksis/core/settings.py
index aaf341ef6..4bcdab6d4 100644
--- a/aleksis/core/settings.py
+++ b/aleksis/core/settings.py
@@ -371,6 +371,8 @@ INVITATIONS_INVITATION_EXPIRY = _settings.get("auth.invitation.expiry", 3)
 INVITATIONS_EMAIL_SUBJECT_PREFIX = ACCOUNT_EMAIL_SUBJECT_PREFIX
 # Use custom invitation model
 INVITATIONS_INVITATION_MODEL = "core.PersonInvitation"
+# Use custom invitation form
+INVITATIONS_INVITE_FORM = "aleksis.core.forms.PersonCreateInviteForm"
 # Display error message if invitation code is invalid
 INVITATIONS_GONE_ON_ACCEPT_ERROR = False
 # Mark invitation as accepted after signup
-- 
GitLab