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