From 5498dc97c67906575971925135af6dcc122e4bc8 Mon Sep 17 00:00:00 2001
From: Tom Teichler <tom.teichler@teckids.org>
Date: Sun, 20 Feb 2022 18:03:01 +0100
Subject: [PATCH] Add term management

---
 aleksis/apps/paweljong/filters.py             |  7 +-
 aleksis/apps/paweljong/forms.py               | 52 +++++++--------
 .../apps/paweljong/migrations/0007_terms.py   | 65 +++++++++++++++++++
 .../0008_remove_terms_from_event.py           | 25 +++++++
 aleksis/apps/paweljong/model_extensions.py    | 10 ---
 aleksis/apps/paweljong/models.py              | 26 ++++----
 aleksis/apps/paweljong/tables.py              | 48 +++++---------
 .../templates/paweljong/term/create.html      | 19 ++++++
 .../templates/paweljong/term/edit.html        | 13 ++++
 .../templates/paweljong/term/list.html        | 14 ++++
 aleksis/apps/paweljong/urls.py                | 28 ++++++--
 11 files changed, 219 insertions(+), 88 deletions(-)
 create mode 100644 aleksis/apps/paweljong/migrations/0007_terms.py
 create mode 100644 aleksis/apps/paweljong/migrations/0008_remove_terms_from_event.py
 delete mode 100644 aleksis/apps/paweljong/model_extensions.py
 create mode 100644 aleksis/apps/paweljong/templates/paweljong/term/create.html
 create mode 100644 aleksis/apps/paweljong/templates/paweljong/term/edit.html
 create mode 100644 aleksis/apps/paweljong/templates/paweljong/term/list.html

diff --git a/aleksis/apps/paweljong/filters.py b/aleksis/apps/paweljong/filters.py
index be18c0e..3cbf0a0 100644
--- a/aleksis/apps/paweljong/filters.py
+++ b/aleksis/apps/paweljong/filters.py
@@ -5,7 +5,7 @@ from material import Layout, Row
 
 from aleksis.core.filters import MultipleCharFilter
 
-from .models import Event, EventRegistration, FeedbackAspect, Voucher
+from .models import Event, EventRegistration, FeedbackAspect, Voucher, Terms
 
 
 class EventRegistrationFilter(FilterSet):
@@ -53,6 +53,11 @@ class FeedbackAspectsFilter(FilterSet):
         model = FeedbackAspect
         fields = ["aspect"]
 
+class TermsFilter(FilterSet):
+    class Meta:
+        model = Terms
+        fields = ["title"]
+
 
 class EventFilter(FilterSet):
     class Meta:
diff --git a/aleksis/apps/paweljong/forms.py b/aleksis/apps/paweljong/forms.py
index 44c1fd6..176fe16 100644
--- a/aleksis/apps/paweljong/forms.py
+++ b/aleksis/apps/paweljong/forms.py
@@ -11,7 +11,7 @@ from phonenumber_field.formfields import PhoneNumberField
 from aleksis.core.mixins import ExtensibleForm
 from aleksis.core.models import Group, Person
 
-from .models import Event, EventRegistration, FeedbackAspect, Voucher
+from .models import Event, EventRegistration, FeedbackAspect, Voucher, Terms
 
 COMMENT_CHOICES = [
     ("first", _("Only first name")),
@@ -126,6 +126,7 @@ class EditEventForm(ExtensibleForm):
             Fieldset(_("Date data"), Row("date_event", "date_registration", "date_retraction")),
             Fieldset(_("Event details"), Row("cost", "max_participants"), "information"),
             Fieldset(_("Feedback aspects"), "feedback_aspects"),
+            Fieldset(_("Terms"), "terms"),
         ),
     )
 
@@ -143,6 +144,7 @@ class EditEventForm(ExtensibleForm):
             "cost",
             "max_participants",
             "feedback_aspects",
+            "terms",
             "information",
         ]
         widgets = {
@@ -154,6 +156,10 @@ class EditEventForm(ExtensibleForm):
                 search_fields=["aspect__icontains"],
                 attrs={"data-minimum-input-length": 0, "class": "browser-default"},
             ),
+            "terms": ModelSelect2MultipleWidget(
+                search_fields=["aspect__icontains"],
+                attrs={"data-minimum-input-length": 0, "class": "browser-default"},
+            ),
         }
 
 
@@ -249,7 +255,7 @@ class RegisterEventGuardians(ExtensibleForm):
 
 class RegisterEventContactDetails(ExtensibleForm):
     class Meta:
-        model = EventRegistration
+        model = Group
         fields = []
 
     layout = Layout(
@@ -427,34 +433,21 @@ class RegisterEventFinancial(ExtensibleForm):
 
 class RegisterEventConsent(ExtensibleForm):
 
-    layout = Layout(
-        Fieldset(
-            _("Declaration of consent"),
-            Row("accept_terms", "accept_data", "accept_general_terms"),
-        ),
-    )
-
     class Meta:
         model = EventRegistration
-        fields = ["accept_terms", "accept_data", "accept_general_terms"]
-        help_texts = {
-            "accept_terms": _(
-                "Parents: My child filled out the registration form themselves, and in my presence. "
-                "I agree with the participation, the terms of use and the terms and conditions. "
-                "I am aware that the registration is binding and that withdrawal is only possible "
-                "in exceptional cases with an important reason. In addition, I agree to pay the "
-                "participation fee in advance and agree to the reimbursement guidelines "
-                "mentioned above."
-            ),
-            "accept_data": _(
-                "I consent to the processing of my data as stated in the "
-                "terms of use and all the data provided is correct. If I am under the "
-                "age of 16, my parents also agree to this and I can prove this on request "
-                "(e.g. by making contact with my parents)."
-            ),
-            "accept_general_terms": _("I agree with the terms and conditions and have read them."),
-        }
+        fields = []
 
+    def __init__(self, event, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+
+        for field in event.terms.all():
+            field_instance = forms.BooleanField(
+                help_text=field.term,
+                required=True,
+            )
+            self.fields[field.title] = field_instance
+            node = Fieldset(f"{field.title}", f"{field.title}")
+            self.add_node_to_layout(node)
 
 class EditEventRegistrationForm(forms.ModelForm):
 
@@ -527,6 +520,11 @@ class EditFeedbackAspectForm(forms.ModelForm):
         model = FeedbackAspect
         exclude = []
 
+class EditTermForm(forms.ModelForm):
+    class Meta:
+        model = Terms
+        exclude = []
+
 
 class RegisterEventAccount(SignupForm, ExtensibleForm):
     """Form to register new user accounts."""
diff --git a/aleksis/apps/paweljong/migrations/0007_terms.py b/aleksis/apps/paweljong/migrations/0007_terms.py
new file mode 100644
index 0000000..71a1002
--- /dev/null
+++ b/aleksis/apps/paweljong/migrations/0007_terms.py
@@ -0,0 +1,65 @@
+# Generated by Django 3.2.12 on 2022-02-20 15:24
+
+import ckeditor.fields
+import django.contrib.sites.managers
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('sites', '0002_alter_domain_unique'),
+        ('paweljong', '0006_unique_constraints'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='eventregistration',
+            name='channel',
+        ),
+        migrations.AddField(
+            model_name='eventregistration',
+            name='school',
+            field=models.CharField(default='', max_length=255, verbose_name='Name of school'),
+            preserve_default=False,
+        ),
+        migrations.AddField(
+            model_name='eventregistration',
+            name='school_class',
+            field=models.CharField(default='', max_length=255, verbose_name='School class'),
+            preserve_default=False,
+        ),
+        migrations.AddField(
+            model_name='eventregistration',
+            name='school_place',
+            field=models.CharField(default='', max_length=255, verbose_name='Place of the school'),
+            preserve_default=False,
+        ),
+        migrations.AlterField(
+            model_name='event',
+            name='feedback_aspects',
+            field=models.ManyToManyField(blank=True, related_name='event', to='paweljong.FeedbackAspect', verbose_name='Feedback aspects'),
+        ),
+        migrations.CreateModel(
+            name='Terms',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('extended_data', models.JSONField(default=dict, editable=False)),
+                ('title', models.CharField(max_length=255, verbose_name='Title')),
+                ('term', ckeditor.fields.RichTextField(verbose_name='Term')),
+                ('site', models.ForeignKey(default=1, editable=False, on_delete=django.db.models.deletion.CASCADE, to='sites.site')),
+            ],
+            options={
+                'abstract': False,
+            },
+            managers=[
+                ('objects', django.contrib.sites.managers.CurrentSiteManager()),
+            ],
+        ),
+        migrations.AddField(
+            model_name='event',
+            name='terms',
+            field=models.ManyToManyField(blank=True, related_name='event', to='paweljong.Terms', verbose_name='Terms'),
+        ),
+    ]
diff --git a/aleksis/apps/paweljong/migrations/0008_remove_terms_from_event.py b/aleksis/apps/paweljong/migrations/0008_remove_terms_from_event.py
new file mode 100644
index 0000000..244a64b
--- /dev/null
+++ b/aleksis/apps/paweljong/migrations/0008_remove_terms_from_event.py
@@ -0,0 +1,25 @@
+# Generated by Django 3.2.12 on 2022-02-20 16:53
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('paweljong', '0007_terms'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='eventregistration',
+            name='accept_data',
+        ),
+        migrations.RemoveField(
+            model_name='eventregistration',
+            name='accept_general_terms',
+        ),
+        migrations.RemoveField(
+            model_name='eventregistration',
+            name='accept_terms',
+        ),
+    ]
diff --git a/aleksis/apps/paweljong/model_extensions.py b/aleksis/apps/paweljong/model_extensions.py
deleted file mode 100644
index 09f8924..0000000
--- a/aleksis/apps/paweljong/model_extensions.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from django.utils.translation import gettext_lazy as _
-
-from jsonstore import CharField
-
-from aleksis.core.models import Person
-
-# Additional fields for persons
-Person.field(school=CharField(verbose_name=_("Name of school")))
-Person.field(school_class=CharField(verbose_name=_("School class")))
-Person.field(school_place=CharField(verbose_name=_("Place of the school")))
diff --git a/aleksis/apps/paweljong/models.py b/aleksis/apps/paweljong/models.py
index 2b325aa..7fa1cf9 100644
--- a/aleksis/apps/paweljong/models.py
+++ b/aleksis/apps/paweljong/models.py
@@ -8,8 +8,7 @@ from django_iban.fields import IBANField
 
 from aleksis.core.mixins import ExtensibleModel
 from aleksis.core.models import Group, Person
-
-from .util import generate_code
+from aleksis.core.util.core_helpers import generate_random_code
 
 
 class FeedbackAspect(ExtensibleModel):
@@ -19,6 +18,14 @@ class FeedbackAspect(ExtensibleModel):
         return self.aspect
 
 
+class Terms(ExtensibleModel):
+    title = models.CharField(max_length=255, verbose_name=_("Title"))
+    term = RichTextField(verbose_name=_("Term"))
+
+    def __str__(self) -> str:
+        return self.title
+
+
 class Event(ExtensibleModel):
     # Event details
     display_name = models.CharField(verbose_name=_("Display name"), max_length=255)
@@ -38,13 +45,13 @@ class Event(ExtensibleModel):
     cost = models.IntegerField(verbose_name=_("Cost in €"))
     max_participants = models.PositiveSmallIntegerField(verbose_name=_("Maximum participants"))
     information = RichTextField(verbose_name=_("Information about the event"))
+    terms = models.ManyToManyField(Terms, verbose_name=_("Terms"), related_name="event", blank=True)
 
     # Feedback
     feedback_aspects = models.ManyToManyField(
         FeedbackAspect,
         verbose_name=_("Feedback aspects"),
         related_name="event",
-        null=True,
         blank=True,
     )
 
@@ -167,7 +174,7 @@ class Voucher(ExtensibleModel):
 
     def save(self, *args, **kwargs):
         if not self.code:
-            self.code = generate_code()
+            self.code = generate_random_code(5, 3)
         super().save(*args, **kwargs)
 
 
@@ -177,11 +184,14 @@ class EventRegistration(ExtensibleModel):
     person = models.ForeignKey(Person, on_delete=models.CASCADE, verbose_name=_("Person"))
     date_registred = models.DateTimeField(auto_now_add=True, verbose_name=_("Registration date"))
 
+    school=models.CharField(verbose_name=_("Name of school"), max_length=255)
+    school_class=models.CharField(verbose_name=_("School class"), max_length=255)
+    school_place=models.CharField(verbose_name=_("Place of the school"), max_length=255)
+
     comment = models.TextField(verbose_name=_("Comment / remarks"), blank=True, default="")
     medical_information = models.TextField(
         verbose_name=_("Medical information / intolerances"), blank=True, default=""
     )
-    channel = models.CharField(verbose_name=_("Channel"), max_length=255, blank=True, default="")
     voucher = models.ForeignKey(
         Voucher,
         on_delete=models.CASCADE,
@@ -199,12 +209,6 @@ class EventRegistration(ExtensibleModel):
         blank=True,
     )
 
-    accept_terms = models.BooleanField(verbose_name=_("Delcaration of consent by parents"))
-    accept_data = models.BooleanField(verbose_name=_("Declaration of consent data protection"))
-    accept_general_terms = models.BooleanField(
-        verbose_name=_("Declatation of consent terms and condition")
-    )
-
     def __str__(self) -> str:
         return f"{self.event}, {self.person.first_name} {self.person.last_name}"
 
diff --git a/aleksis/apps/paweljong/tables.py b/aleksis/apps/paweljong/tables.py
index 2194650..fa44166 100644
--- a/aleksis/apps/paweljong/tables.py
+++ b/aleksis/apps/paweljong/tables.py
@@ -4,38 +4,6 @@ import django_tables2 as tables
 from django_tables2.utils import A
 
 
-class EventsTable(tables.Table):
-    class Meta:
-        attrs = {"class": "responsive-table highlight"}
-
-    display_name = tables.Column(verbose_name=_("Event"))
-    date_event = tables.Column(verbose_name=_("Date"))
-    max_participants = tables.Column(verbose_name=_("Max. participants"))
-    date_registration = tables.Column(verbose_name=_("Registration until"))
-
-    register = tables.LinkColumn(
-        "register_event_by_pk",
-        args=[A("id")],
-        verbose_name=_("Register"),
-        text=_("Register"),
-    )
-
-
-class ParticipatedEventsTable(tables.Table):
-    class Meta:
-        attrs = {"class": "responsive-table highlight"}
-
-    display_name = tables.Column(verbose_name=_("Event"))
-    date_event = tables.Column(verbose_name=_("Date"))
-
-    feedback = tables.LinkColumn(
-        "feedback_event_by_pk",
-        args=[A("id")],
-        verbose_name=_("Feedback"),
-        text=_("Feedback"),
-    )
-
-
 class ManageEventsTable(tables.Table):
     class Meta:
         attrs = {"class": "responsive-table highlight"}
@@ -46,7 +14,7 @@ class ManageEventsTable(tables.Table):
     date_registration = tables.Column(verbose_name=_("Registration until"))
 
     edit = tables.LinkColumn(
-        "edit_event_by_pk", args=[A("id")], verbose_name=_("Edit"), text=_("Edit")
+        "edit_event_by_slug", args=[A("linked_group__short_name")], verbose_name=_("Edit"), text=_("Edit")
     )
 
 
@@ -99,3 +67,17 @@ class FeedbackAspectsTable(tables.Table):
         verbose_name=_("Edit"),
         text=_("Edit"),
     )
+
+
+class TermsTable(tables.Table):
+    class Meta:
+        attrs = {"class": "responsive-table highlight"}
+
+    title = tables.Column()
+
+    edit = tables.LinkColumn(
+        "edit_term_by_pk",
+        args=[A("id")],
+        verbose_name=_("Edit"),
+        text=_("Edit"),
+    )
diff --git a/aleksis/apps/paweljong/templates/paweljong/term/create.html b/aleksis/apps/paweljong/templates/paweljong/term/create.html
new file mode 100644
index 0000000..e3db75f
--- /dev/null
+++ b/aleksis/apps/paweljong/templates/paweljong/term/create.html
@@ -0,0 +1,19 @@
+{% extends "core/base.html" %}
+{% load material_form i18n %}
+
+{% block page_title %}{% blocktrans %}Create term{% endblocktrans %}{% endblock %}
+{% block browser_title %}{% blocktrans %}Create term{% endblocktrans %}{% endblock %}
+
+{% block extra_head %}
+    {{ form.media.css }}
+{% endblock %}
+
+{% block content %}
+  <form method="post">
+    {% csrf_token %}
+    {% form form=form %}{% endform %}
+    {% include "core/partials/save_button.html" %}
+  </form>
+
+  {{ form.media.js }}
+{% endblock %}
diff --git a/aleksis/apps/paweljong/templates/paweljong/term/edit.html b/aleksis/apps/paweljong/templates/paweljong/term/edit.html
new file mode 100644
index 0000000..1a467f0
--- /dev/null
+++ b/aleksis/apps/paweljong/templates/paweljong/term/edit.html
@@ -0,0 +1,13 @@
+{% extends "core/base.html" %}
+{% load material_form i18n %}
+
+{% block page_title %}{% blocktrans %}Edit term{% endblocktrans %}{% endblock %}
+{% block browser_title %}{% blocktrans %}Edit term{% endblocktrans %}{% endblock %}
+
+{% block content %}
+  <form method="post">
+    {% csrf_token %}
+    {% form form=form %}{% endform %}
+    {% include "core/partials/save_button.html" %}
+  </form>
+{% endblock %}
diff --git a/aleksis/apps/paweljong/templates/paweljong/term/list.html b/aleksis/apps/paweljong/templates/paweljong/term/list.html
new file mode 100644
index 0000000..9af6321
--- /dev/null
+++ b/aleksis/apps/paweljong/templates/paweljong/term/list.html
@@ -0,0 +1,14 @@
+{% extends "core/base.html" %}
+{% load material_form i18n %}
+
+{% load render_table from django_tables2 %}
+
+{% block page_title %}{% blocktrans %}Terms{% endblocktrans %}{% endblock %}
+{% block browser_title %}{% blocktrans %}Terms{% endblocktrans %}{% endblock %}
+
+{% block content %}
+
+    <a class="btn colour-primary waves-effect waves-light" href="{% url 'create_term' %}">{% trans "Create term" %}</a>
+    {% render_table table %}
+
+{% endblock %}
diff --git a/aleksis/apps/paweljong/urls.py b/aleksis/apps/paweljong/urls.py
index 7b97f61..6b3ac9d 100644
--- a/aleksis/apps/paweljong/urls.py
+++ b/aleksis/apps/paweljong/urls.py
@@ -27,16 +27,16 @@ condition_dict = {
 }
 
 urlpatterns = [
-    path("event/<int:pk>/edit", views.EditEventView.as_view(), name="edit_event_by_pk"),
-    path("event/<int:pk>/feedback", views.feedback_event, name="feedback_event_by_pk"),
+    path("event/<slug:slug>/edit", views.EditEventView.as_view(), name="edit_event_by_slug"),
+    path("event/<slug:slug>/feedback", views.feedback_event, name="feedback_event_by_slug"),
     path(
-        "event/<int:pk>/register",
+        "event/<slug:slug>/register",
         views.RegisterEventWizardView.as_view(register_forms, condition_dict=condition_dict),
-        name="register_event_by_id",
+        name="register_event_by_slug",
     ),
     path("event/<slug:slug>", views.EventFullView.as_view(), name="event_by_name"),
-    path("event/<slug:slug>/register", views.RegisterEventStart.as_view(), name="register_event_by_name"),
-    path("misc/set_email_needed/<int:pk>", views.set_email_needed, name="set_email_needed"),
+    path("event/<slug:slug>/start", views.RegisterEventStart.as_view(), name="register_event_by_slug_start"),
+    path("misc/set_email_needed/<slug:slug>", views.set_email_needed, name="set_email_needed"),
     path("events/create", views.CreateEventView.as_view(), name="create_event"),
     path("events/manage", views.manage_events, name="manage_events"),
     path("vouchers/create", views.VoucherCreateView.as_view(), name="create_vouchers"),
@@ -78,4 +78,20 @@ urlpatterns = [
         views.FeedbackAspectEditView.as_view(),
         name="edit_feedback_aspect_by_pk",
     ),
+    path(
+        "event/terms/list",
+        views.TermListView.as_view(),
+        name="terms",
+    ),
+    path(
+        "event/terms/create",
+        views.TermCreateView.as_view(),
+        name="create_term",
+    ),
+    path(
+        "event/terms/<int:pk>/edit",
+        views.TermEditView.as_view(),
+        name="edit_term_by_pk",
+    ),
+
 ]
-- 
GitLab