From d42f5bbcef73b88d60e77c77d4f138c0d152d5ae Mon Sep 17 00:00:00 2001
From: Tom Teichler <tom.teichler@teckids.org>
Date: Tue, 22 Feb 2022 23:49:50 +0100
Subject: [PATCH] Implement account regstration

---
 CHANGELOG.rst                                 |  5 +
 aleksis/apps/paweljong/forms.py               |  6 ++
 aleksis/apps/paweljong/menus.py               |  8 ++
 .../templates/paweljong/account_wizard.html   | 35 +++++++
 .../templates/paweljong/register_start.html   | 71 ++++++++++++++
 aleksis/apps/paweljong/urls.py                | 12 +++
 aleksis/apps/paweljong/views.py               | 93 ++++++++++++++++++-
 7 files changed, 226 insertions(+), 4 deletions(-)
 create mode 100644 aleksis/apps/paweljong/templates/paweljong/account_wizard.html
 create mode 100644 aleksis/apps/paweljong/templates/paweljong/register_start.html

diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 2ea9e00..bbda90d 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -9,6 +9,11 @@ and this project adheres to `Semantic Versioning`_.
 Unreleased
 ----------
 
+Added
+~~~~~
+
+* Implement account registration
+
 Fixed
 ~~~~~
 
diff --git a/aleksis/apps/paweljong/forms.py b/aleksis/apps/paweljong/forms.py
index 1fb58f1..8acc84a 100644
--- a/aleksis/apps/paweljong/forms.py
+++ b/aleksis/apps/paweljong/forms.py
@@ -445,3 +445,9 @@ class RegisterEventAccount(SignupForm, ExtensibleForm):
     first_name = forms.CharField(label=_("First name"))
     last_name = forms.CharField(label=_("Last name"))
     date_of_birth = forms.DateField(label=_("Date of birth"))
+
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+
+        self.fields["username"].help_text = _("The username must only contain lower case letters and numbers, and must begin with a letter.")
diff --git a/aleksis/apps/paweljong/menus.py b/aleksis/apps/paweljong/menus.py
index 227309d..4655f15 100644
--- a/aleksis/apps/paweljong/menus.py
+++ b/aleksis/apps/paweljong/menus.py
@@ -2,6 +2,14 @@ from django.utils.translation import gettext_lazy as _
 
 MENUS = {
     "NAV_MENU_CORE": [
+        {
+            "name": _("Register"),
+            "url": "register_account_start",
+            "icon": "how_to_reg",
+            "validators": [
+                "menu_generator.validators.is_anonymous",
+            ],
+        },
         {
             "name": _("Events"),
             "url": "#",
diff --git a/aleksis/apps/paweljong/templates/paweljong/account_wizard.html b/aleksis/apps/paweljong/templates/paweljong/account_wizard.html
new file mode 100644
index 0000000..9599cbd
--- /dev/null
+++ b/aleksis/apps/paweljong/templates/paweljong/account_wizard.html
@@ -0,0 +1,35 @@
+{% extends "core/base.html" %}
+{% load material_form i18n %}
+
+{% block browser_title %}{% trans "Registration" %}{% endblock %}
+{% block page_title %}{% trans "Registration" %}{% endblock %}
+
+{% block content %}
+
+ <form method="post">
+     {% csrf_token %}
+     {{ wizard.form.media }}
+     {{ wizard.management_form }}
+     {% if wizard.form.forms %}
+         {{ wizard.form.management_form }}
+         {% for form in wizard.form.forms %}
+             {% form form=form %}{% endform %}
+         {% endfor %}
+     {% else %}
+         {% form form=wizard.form %}{% endform %}
+     {% endif %}
+ 
+     {% if wizard.steps.prev %}
+         <button type="submit" value="{{ wizard.steps.first }}" class="btn waves-effect waves-light color-primary" name="wizard_goto_step">
+             <i class="material-icons left">first_page</i>
+             {% trans "first step" %}
+         </button>
+         <button type="submit" value="{{ wizard.steps.prev }}" class="btn waves-effect waves-light color-secondary" name="wizard_goto_step">
+             <i class="material-icons left">arrow_back_ios</i>
+             {% trans "previous step" %}
+         </button>
+     {% endif %}
+     {% trans "Next" as caption %}
+     {% include "core/partials/save_button.html" with caption=caption icon="navigate_next" %}
+ </form>
+{% endblock %}
diff --git a/aleksis/apps/paweljong/templates/paweljong/register_start.html b/aleksis/apps/paweljong/templates/paweljong/register_start.html
new file mode 100644
index 0000000..c4a19f8
--- /dev/null
+++ b/aleksis/apps/paweljong/templates/paweljong/register_start.html
@@ -0,0 +1,71 @@
+{% extends "core/base.html" %}
+{% load i18n static %}
+
+{% block browser_title %}{% blocktrans %}Register{% endblocktrans %}{% endblock %}
+
+{% block content %}
+
+ <div class="card">
+   <div class="card-content">
+     <div class="row">
+       <div class="col s12">
+         <span class="card-title">
+          {% trans "Registration" %}
+         </span>
+         <p>
+           {% blocktrans %}
+           Every person needs a personal e-mail address. This address must be one
+           that you, the participating child, check and read yourself, not one owned by your
+           parents.
+           {% endblocktrans %}
+         </p>
+         <p>
+           {% blocktrans %}
+           If you do not have a personal e-mail address yet, and do not want or cannot register
+           one with one of the "big" providers (keep in mind that Google, Outlook, etc. are
+           allowed from 16 years only, and often collect and analyse your private mail), you can
+           register an e-mail address with us.
+           {% endblocktrans %}
+         </p>
+       </div>
+     </div>
+   </div>
+   <div class="card-tabs">
+     <ul class="tabs tabs-fixed-width">
+       <li class="tab">
+         <a href="#without_email">
+           {% trans "I have a personal email address" %}
+         </a>
+       </li>
+       <li class="tab">
+         <a href="#with_email">
+            {% trans "I don't have a personal email address" %}
+         </a>
+       </li>
+     </ul>
+   </div>
+   <div class="card-content">
+     <div id="with_email">
+       <p>
+         {% blocktrans %}
+         You can now choose a personal e-mail address hosted on our servers.
+         For information about receiving mails, see <a
+         href="https://leopard.institute/pages/services.html">
+         https://leopard.institute/pages/services.html</a>
+         {% endblocktrans %}
+       </p>
+       <a href="{% url "set_email_needed_no_slug" %}">{% trans "Register now" %}</a>
+     </div>
+     <div id="without_email">
+       <p>
+         {% blocktrans %}
+         You will be asked for your existing, personal e-mail address. Please remember
+         that you should not use an address owned by your parents. If you do not have
+         a personal e-mail address, please choose the respective option instead!
+         {% endblocktrans %}
+       </p>
+       <a href="{% url "register_account" %}">{% trans "Register now" %}</a>
+     </div>
+   </div>
+ </div>
+{% endblock %}
diff --git a/aleksis/apps/paweljong/urls.py b/aleksis/apps/paweljong/urls.py
index 81901ec..c765dcf 100644
--- a/aleksis/apps/paweljong/urls.py
+++ b/aleksis/apps/paweljong/urls.py
@@ -26,6 +26,15 @@ condition_dict = {
     "register": views.is_person_anonymous,
 }
 
+account_forms = [
+    ("email", MailAddForm),
+    ("register", RegisterEventAccount),
+]
+
+account_conditions = {
+    "email": views.is_email_needed,
+}
+
 urlpatterns = [
     path("event/<slug:slug>/edit", views.EditEventView.as_view(), name="edit_event_by_slug"),
     path(
@@ -40,6 +49,9 @@ urlpatterns = [
         name="register_event_by_slug_start",
     ),
     path("misc/set_email_needed/<slug:slug>", views.set_email_needed, name="set_email_needed"),
+    path("misc/set_email_needed/", views.set_email_needed, name="set_email_needed_no_slug"),
+    path("account/register/start", views.AccountRegisterStart.as_view(), name="register_account_start"),
+    path("account/register", views.AccountRegisterWizardView.as_view(account_forms, condition_dict=account_conditions), name="register_account"),
     path("events/feed", views.UpcomingEventsRSSFeed(), name="upcoming_events_rss_feed"),
     path("events/create", views.CreateEventView.as_view(), name="create_event"),
     path("events/manage", views.manage_events, name="manage_events"),
diff --git a/aleksis/apps/paweljong/views.py b/aleksis/apps/paweljong/views.py
index 50ed08c..211c038 100644
--- a/aleksis/apps/paweljong/views.py
+++ b/aleksis/apps/paweljong/views.py
@@ -1,3 +1,5 @@
+from typing import Optional
+
 from django.conf import settings
 from django.contrib.auth import get_user_model
 from django.contrib.syndication.views import Feed
@@ -9,6 +11,7 @@ from django.utils.decorators import method_decorator
 from django.utils.text import slugify
 from django.utils.translation import ugettext as _
 from django.views.decorators.cache import never_cache
+from django.views.generic import TemplateView
 from django.views.generic.detail import DetailView
 
 import reversion
@@ -261,15 +264,17 @@ def is_person_anonymous(wizard):
     return wizard.request.user.is_anonymous
 
 
-def set_email_needed(request, slug):
+def set_email_needed(request, slug: Optional[str] = None):
     request.session["email_needed"] = True
 
-    return redirect("register_event_by_slug", slug)
+    if slug:
+        return redirect("register_event_by_slug", slug)
+    else:
+        return redirect("register_account")
 
 
 def is_email_needed(wizard):
-    return wizard.request.session.get("email_needed")
-
+    return wizard.request.session.pop("email_needed", None)
 
 TEMPLATES = {
     "email": "paweljong/event/register_wizard.html",
@@ -281,6 +286,82 @@ TEMPLATES = {
     "consent": "paweljong/event/register_wizard_consent.html",
 }
 
+class AccountRegisterWizardView(SessionWizardView):
+    template_name = "paweljong/account_wizard.html"
+    file_storage = settings.DEFAULT_FILE_STORAGE
+
+    def get_form_kwargs(self, step):
+        kwargs = super().get_form_kwargs()
+        if step == "email":
+            kwargs["request"] = self.request
+        return kwargs
+
+    def get_form_initial(self, step):
+
+        initial = self.initial_dict.get(step, {})
+
+        if step == "register":
+            cleaned_data_email = self.get_cleaned_data_for_step("email")
+            if cleaned_data_email:
+                domain = cleaned_data_email["domain"]
+                email = f"{cleaned_data_email['local_part']}@{domain.domain}"
+                initial.update(
+                    {
+                        "email": email,
+                        "email2": email,
+                    }
+                )
+
+        if step == "contact_details":
+            cleaned_data_register = self.get_cleaned_data_for_step("register")
+            if cleaned_data_register:
+                initial.update(
+                    {
+                        "first_name": cleaned_data_register["first_name"],
+                        "last_name": cleaned_data_register["last_name"],
+                        "email": cleaned_data_register["email"],
+                        "date_of_birth": cleaned_data_register["date_of_birth"],
+                    }
+                )
+
+        return self.initial_dict.get(step, initial)
+
+    def done(self, form_list, **kwargs):
+
+        cleaned_data_email = self.get_cleaned_data_for_step("email")
+        cleaned_data_register = self.get_cleaned_data_for_step("register")
+
+        # Create email address
+        if cleaned_data_email:
+            _email = MailAddress.objects.create(
+                local_part=cleaned_data_email["local_part"],
+                domain=cleaned_data_email["domain"],
+            )
+
+        # Create user
+        if cleaned_data_register:
+            user = User.objects.create(
+                username=cleaned_data_register["username"],
+                email=cleaned_data_register["email"],
+            )
+            user.set_password(cleaned_data_register["password1"])
+            user.save()
+        else:
+            user = self.request.user
+
+        person, created = Person.objects.get_or_create(
+            user=user,
+            defaults={
+                "email": cleaned_data_register["email"],
+                "first_name": cleaned_data_register["first_name"],
+                "last_name": cleaned_data_register["last_name"],
+                "date_of_birth": cleaned_data_register["date_of_birth"],
+            },
+
+        )
+
+        return redirect("index")
+
 
 class RegisterEventWizardView(SessionWizardView):
     template_name = "paweljong/event/register_wizard.html"
@@ -682,3 +763,7 @@ class UpcomingEventsRSSFeed(Feed):
 
     def item_description(self, item):
         return item.description
+
+class AccountRegisterStart(TemplateView):
+
+    template_name = "paweljong/register_start.html"
-- 
GitLab