From c5b0523b074eeb196b8905ff4ca9fb2b74cbb8a8 Mon Sep 17 00:00:00 2001 From: Jakob Libak Rasmussen <31068308+JakobLibak@users.noreply.github.com> Date: Thu, 27 May 2021 09:51:40 +0200 Subject: [PATCH 1/9] Opdatering overskrift i entry_page (#672) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ændrer overskrift, så den giver mening. --- members/templates/members/entry_page.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/members/templates/members/entry_page.html b/members/templates/members/entry_page.html index e79aaae1..e08ae5d2 100644 --- a/members/templates/members/entry_page.html +++ b/members/templates/members/entry_page.html @@ -49,7 +49,7 @@

Bliv medlem af en lokalforening

-

Bliv støttemedlemskab

+

Bliv støttemedlem

Støttemedlemskaber From 0583051ce39f109bb47c1554fb882fa21da08ff4 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Thu, 27 May 2021 22:16:42 +0200 Subject: [PATCH 2/9] Hot fix for return link bug (#673) --- members/views/ActivitySignup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/members/views/ActivitySignup.py b/members/views/ActivitySignup.py index 4a5874ec..fb929cb6 100644 --- a/members/views/ActivitySignup.py +++ b/members/views/ActivitySignup.py @@ -176,7 +176,7 @@ def ActivitySignup(request, activity_id, person_id=None): payment.save() return_link_url = payment.get_quickpaytransaction().get_link_url( - return_url=settings.BASE_URL + return_url=settings.BASE_URL[:-1] # skip trailing slash + reverse("activity_view_person", args=[activity.id, person.id]) ) From 341b740fedd679ad73579b28226db904a472292b Mon Sep 17 00:00:00 2001 From: Jarl Friis Date: Mon, 31 May 2021 10:46:55 +0200 Subject: [PATCH 3/9] Bugs/675 filter sidste tilmelding (#676) * Red: filtering out old membership activities and support membership activities * Green: filtering out old membership activities and support membership activities, fixes #675 --- .../tests/test_functional/test_activities.py | 18 ++++++++++++++++++ members/views/Membership.py | 1 + members/views/SupportMembership.py | 1 + 3 files changed, 20 insertions(+) diff --git a/members/tests/test_functional/test_activities.py b/members/tests/test_functional/test_activities.py index f7512131..bd401e30 100644 --- a/members/tests/test_functional/test_activities.py +++ b/members/tests/test_functional/test_activities.py @@ -81,6 +81,15 @@ def setUp(self): activitytype_id="FORENINGSMEDLEMSKAB", ) self.activity_foreningsmedlemskab.save() + self.activity_foreningsmedlemskab_old = ActivityFactory.create( + open_invite=True, + signup_closing=Faker("past_date", start_date="-10d"), + min_age=5, + max_age=90, + name="Foreningsmedlemskab", + activitytype_id="FORENINGSMEDLEMSKAB", + ) + self.activity_foreningsmedlemskab_old.save() self.activity_foreningsmedlemskab_participate = ActivityFactory.create( name="Foreningsmedlemskab deltagelse", activitytype_id="FORENINGSMEDLEMSKAB" ) @@ -97,6 +106,15 @@ def setUp(self): activitytype_id="STØTTEMEDLEMSKAB", ) self.activity_støttemedlemskab.save() + self.activity_støttemedlemskab_old = ActivityFactory.create( + open_invite=True, + signup_closing=Faker("past_date", start_date="-10d"), + min_age=5, + max_age=90, + name="Støttemedlemskab", + activitytype_id="STØTTEMEDLEMSKAB", + ) + self.activity_støttemedlemskab_old.save() self.activity_støttemedlemskab_participate = ActivityFactory.create( name="Støttemedlemskab deltagelse", activitytype_id="STØTTEMEDLEMSKAB" ) diff --git a/members/views/Membership.py b/members/views/Membership.py index e606c1f4..34fcee65 100644 --- a/members/views/Membership.py +++ b/members/views/Membership.py @@ -14,6 +14,7 @@ def Membership(request): family = user_to_person(request.user).family membership_activities = Activity.objects.filter( + signup_closing__gte=timezone.now(), activitytype__in=["FORENINGSMEDLEMSKAB"], ).order_by("zipcode") participating = ActivityParticipant.objects.filter( diff --git a/members/views/SupportMembership.py b/members/views/SupportMembership.py index fb39559f..4050d3c5 100644 --- a/members/views/SupportMembership.py +++ b/members/views/SupportMembership.py @@ -14,6 +14,7 @@ def SupportMembership(request): family = user_to_person(request.user).family activities = Activity.objects.filter( + signup_closing__gte=timezone.now(), activitytype__in=["STØTTEMEDLEMSKAB"], ).order_by("zipcode") participating = ActivityParticipant.objects.filter( From 5c8a60b6f464ce33c946e6469a58766519e2ec6f Mon Sep 17 00:00:00 2001 From: Jarl Friis Date: Wed, 2 Jun 2021 20:24:10 +0200 Subject: [PATCH 4/9] Fix #678: Assert that BASE_URL does not end with slash (#679) --- forenings_medlemmer/settings.py | 4 +++- members/views/ActivitySignup.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/forenings_medlemmer/settings.py b/forenings_medlemmer/settings.py index 4912ead3..d18893e2 100644 --- a/forenings_medlemmer/settings.py +++ b/forenings_medlemmer/settings.py @@ -73,7 +73,9 @@ ALLOWED_HOSTS = [host.replace(" ", "") for host in env.list("ALLOWED_HOSTS")] BASE_URL = os.environ["BASE_URL"] - +assert not BASE_URL.endswith( + "/" +), f"BASE_URL environment variable must not end with '/'. It is set to '{BASE_URL}'." INSTALLED_APPS = ( "bootstrap4", diff --git a/members/views/ActivitySignup.py b/members/views/ActivitySignup.py index fb929cb6..7f00d561 100644 --- a/members/views/ActivitySignup.py +++ b/members/views/ActivitySignup.py @@ -176,7 +176,7 @@ def ActivitySignup(request, activity_id, person_id=None): payment.save() return_link_url = payment.get_quickpaytransaction().get_link_url( - return_url=settings.BASE_URL[:-1] # skip trailing slash + return_url=settings.BASE_URL # skip trailing slash + reverse("activity_view_person", args=[activity.id, person.id]) ) From b28b7b3764059bdaaa7126048bf77a93211e680e Mon Sep 17 00:00:00 2001 From: Jarl Friis Date: Mon, 7 Jun 2021 12:25:32 +0200 Subject: [PATCH 5/9] Feature/677 membership and support membership (#680) * Red: #677 foreningsnavn i stedet for afdelingsnavn * Green: Fixes #677 foreningsnavn i stedet for afdelingsnavn * Cleanup obsolete comment --- members/templates/members/membership.html | 8 +- .../templates/members/support_membership.html | 8 +- .../tests/test_functional/test_activities.py | 82 +++++++++++-------- members/views/ActivitySignup.py | 2 +- members/views/Membership.py | 2 +- members/views/SupportMembership.py | 2 +- 6 files changed, 59 insertions(+), 45 deletions(-) diff --git a/members/templates/members/membership.html b/members/templates/members/membership.html index 3af170c3..09e5d051 100644 --- a/members/templates/members/membership.html +++ b/members/templates/members/membership.html @@ -37,7 +37,7 @@

Nuværende og tidligere medlemsskaber

Navn Aktivitet - Afdeling + Forening Start Slut @@ -56,7 +56,7 @@

Nuværende og tidligere medlemsskaber

{%endif%} - {{participation.activity.department.name}} + {{participation.activity.union.name}} {{participation.activity.start_date}} {{participation.activity.end_date}} @@ -78,7 +78,7 @@

Tilmeld som medlem her

- + @@ -88,7 +88,7 @@

Tilmeld som medlem her

{% for activity in membership_activities %} - + @@ -54,7 +54,7 @@

Nuværende og tidligere støttemedlemsskaber

{%endif%} - + @@ -76,7 +76,7 @@

Tilmeld som støttemedlem her

AfdelingForening Aktivitet Beskrivelse Handling
- Coding Pirates {{activity.department.name}} + {{activity.union.name}} {{activity.name}} diff --git a/members/templates/members/support_membership.html b/members/templates/members/support_membership.html index a992072c..18d414e5 100644 --- a/members/templates/members/support_membership.html +++ b/members/templates/members/support_membership.html @@ -35,7 +35,7 @@

Nuværende og tidligere støttemedlemsskaber

Navn AktivitetAfdelingForening Start Slut
{{participation.activity.department.name}}{{participation.activity.union.name}} {{participation.activity.start_date}} {{participation.activity.end_date}}
- + @@ -86,7 +86,7 @@

Tilmeld som støttemedlem her

{% for activity in activities %}
AfdelingForening Aktivitet Beskrivelse Handling
- Coding Pirates {{activity.department.name}} + {{activity.union.name}} {{activity.name}} diff --git a/members/tests/test_functional/test_activities.py b/members/tests/test_functional/test_activities.py index bd401e30..bcddd229 100644 --- a/members/tests/test_functional/test_activities.py +++ b/members/tests/test_functional/test_activities.py @@ -181,7 +181,6 @@ def test_entry_page(self): self.assertEqual(links[0], links[1]) def test_activities(self): - # Loads the activities self.browser.find_element_by_link_text("Arrangementer").click() WebDriverWait(self.browser, 10).until( @@ -220,26 +219,32 @@ def test_membership(self): self.browser.save_screenshot("test-screens/membership_list.png") # Check that the page contains all participating activities - activity_names = [ - e.text - for e in self.browser.find_elements_by_xpath( - "//section[@id='participation']/table/tbody/tr/td[@data-label='Aktivitet']" - ) - ] - self.assertEqual(1, len(activity_names)) - self.assertIn( - self.activity_foreningsmedlemskab_participate.name, activity_names + activities = self.browser.find_elements_by_xpath( + "//section[@id='participation']/table/tbody/tr" + ) + self.assertEqual(1, len(activities)) + self.assertEqual( + self.activity_foreningsmedlemskab_participate.name, + activities[0].find_element_by_xpath("td[@data-label='Aktivitet']").text, + ) + self.assertEqual( + self.activity_foreningsmedlemskab_participate.union.name, + activities[0].find_element_by_xpath("td[@data-label='Forening']").text, ) # Check that the page contains all activities - activity_names = [ - e.text - for e in self.browser.find_elements_by_xpath( - "//section[@id='open_activities']/table/tbody/tr/td[@data-label='Aktivitet']" - ) - ] - self.assertEqual(1, len(activity_names)) - self.assertIn(self.activity_foreningsmedlemskab.name, activity_names) + activities = self.browser.find_elements_by_xpath( + "//section[@id='open_activities']/table/tbody/tr" + ) + self.assertEqual(1, len(activities)) + self.assertEqual( + self.activity_foreningsmedlemskab.name, + activities[0].find_element_by_xpath("td[@data-label='Aktivitet']").text, + ) + self.assertEqual( + self.activity_foreningsmedlemskab.union.name, + activities[0].find_element_by_xpath("td[@data-label='Forening']").text, + ) def test_supportmembership(self): # Loads the members @@ -250,21 +255,30 @@ def test_supportmembership(self): self.browser.save_screenshot("test-screens/supportmembership_list.png") # Check that the page contains all participating activities - activity_names = [ - e.text - for e in self.browser.find_elements_by_xpath( - "//section[@id='participation']/table/tbody/tr/td[@data-label='Aktivitet']" - ) - ] - self.assertEqual(1, len(activity_names)) - self.assertIn(self.activity_støttemedlemskab_participate.name, activity_names) + activities = self.browser.find_elements_by_xpath( + "//section[@id='participation']/table/tbody/tr" + ) + self.assertEqual(1, len(activities)) + self.assertEqual( + self.activity_støttemedlemskab_participate.name, + activities[0].find_element_by_xpath("td[@data-label='Aktivitet']").text, + ) + self.assertEqual( + self.activity_støttemedlemskab_participate.union.name, + activities[0].find_element_by_xpath("td[@data-label='Forening']").text, + ) # Check that the page contains all activities - activity_names = [ - e.text - for e in self.browser.find_elements_by_xpath( - "//section[@id='open_activities']/table/tbody/tr/td[@data-label='Aktivitet']" - ) - ] - self.assertEqual(1, len(activity_names)) - self.assertIn(self.activity_støttemedlemskab.name, activity_names) + activities = self.browser.find_elements_by_xpath( + "//section[@id='open_activities']/table/tbody/tr" + ) + self.assertEqual(1, len(activities)) + self.assertEqual(1, len(activities)) + self.assertEqual( + self.activity_støttemedlemskab.name, + activities[0].find_element_by_xpath("td[@data-label='Aktivitet']").text, + ) + self.assertEqual( + self.activity_støttemedlemskab.union.name, + activities[0].find_element_by_xpath("td[@data-label='Forening']").text, + ) diff --git a/members/views/ActivitySignup.py b/members/views/ActivitySignup.py index 7f00d561..4a5874ec 100644 --- a/members/views/ActivitySignup.py +++ b/members/views/ActivitySignup.py @@ -176,7 +176,7 @@ def ActivitySignup(request, activity_id, person_id=None): payment.save() return_link_url = payment.get_quickpaytransaction().get_link_url( - return_url=settings.BASE_URL # skip trailing slash + return_url=settings.BASE_URL + reverse("activity_view_person", args=[activity.id, person.id]) ) diff --git a/members/views/Membership.py b/members/views/Membership.py index 34fcee65..f49c1ba6 100644 --- a/members/views/Membership.py +++ b/members/views/Membership.py @@ -40,7 +40,7 @@ def Membership(request): { "id": curActivity.id, "name": curActivity.name, - "department": curActivity.department, + "union": curActivity.union, "persons": applicablePersons, } ) diff --git a/members/views/SupportMembership.py b/members/views/SupportMembership.py index 4050d3c5..06659dd9 100644 --- a/members/views/SupportMembership.py +++ b/members/views/SupportMembership.py @@ -40,7 +40,7 @@ def SupportMembership(request): { "id": curActivity.id, "name": curActivity.name, - "department": curActivity.department, + "union": curActivity.union, "persons": applicablePersons, } ) From 40b5fb69177400350408f1d88414d0c18713b914 Mon Sep 17 00:00:00 2001 From: Jarl Friis Date: Mon, 7 Jun 2021 12:39:37 +0200 Subject: [PATCH 6/9] Bugs/681 departments skal ikke vise headermenu (#682) * Red: iframe view shall not showheader * Green: Fix #681. iframe view shall not showheader Co-authored-by: Jakob Libak Rasmussen <31068308+JakobLibak@users.noreply.github.com> --- members/templates/members/base.html | 4 +++- members/tests/test_functional/test_department_list.py | 1 + members/views/departmentView.py | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/members/templates/members/base.html b/members/templates/members/base.html index dad86715..c69e80ae 100644 --- a/members/templates/members/base.html +++ b/members/templates/members/base.html @@ -27,15 +27,17 @@ - {% include "members/header.html" %} + {% if not skip_context %} {% include "members/header.html" %} {% endif %}
{% block content %}{% endblock %}
+ {% if not skip_context %}

Coding Pirates Denmark - Sverigesgade 20, 1., 5000 Odense C, E-mail: kontakt@codingpirates.dk Tlf: +45 27 83 65 25, CVR: 35 55 23 16

+ {% endif %} diff --git a/members/tests/test_functional/test_department_list.py b/members/tests/test_functional/test_department_list.py index 1ff65865..2aa31729 100644 --- a/members/tests/test_functional/test_department_list.py +++ b/members/tests/test_functional/test_department_list.py @@ -58,6 +58,7 @@ def test_department_list(self): self.assertEqual("Coding Pirates Medlemssystem", self.browser.title) self.browser.save_screenshot("test-screens/department_list_1.png") + self.assertEquals(0, len(self.browser.find_elements_by_tag_name("nav"))) # check that there's the "Hovedstaden" region tab self.browser.find_element_by_xpath( "//div[@class='tabs']/ul/li[text()[contains(.,'Region Hovedstaden')]]" diff --git a/members/views/departmentView.py b/members/views/departmentView.py index e224a98b..cada5ae3 100644 --- a/members/views/departmentView.py +++ b/members/views/departmentView.py @@ -10,8 +10,9 @@ def departmentView(request, unique=None): request, "members/department_list.html", { + "skip_context": True, "departments": filter( lambda dep: dep.isVisible, Department.get_open_departments() - ) + ), }, ) From 8b8afb908251c022a4bad9be28461d901c1e6f0b Mon Sep 17 00:00:00 2001 From: Kristoffer Rath Hansen Date: Mon, 9 Aug 2021 21:04:39 +0200 Subject: [PATCH 7/9] Quick form that makes it possible to get an email to a department head (#689) * Quick form that sends an email to department head instead of manual * Remove unused models * Remove more * Remove more * Add test * Remove unused variable * also remove unused class * Fix PR --- members/forms/__init__.py | 2 + members/forms/volunteer_email_form.py | 60 +++++++++++++++++++ members/models/department.py | 20 ++++++- .../templates/members/volunteer_email.html | 15 +++++ .../members/volunteer_email_sent.html | 11 ++++ members/tests/test_model_department.py | 41 ++++++++++++- members/urls.py | 4 ++ members/views/__init__.py | 2 + members/views/volunteerEmail.py | 36 +++++++++++ members/views/volunteerEmailSentView.py | 5 ++ 10 files changed, 191 insertions(+), 5 deletions(-) create mode 100644 members/forms/volunteer_email_form.py create mode 100644 members/templates/members/volunteer_email.html create mode 100644 members/templates/members/volunteer_email_sent.html create mode 100644 members/views/volunteerEmail.py create mode 100644 members/views/volunteerEmailSentView.py diff --git a/members/forms/__init__.py b/members/forms/__init__.py index 688ba0a6..517a0fbe 100644 --- a/members/forms/__init__.py +++ b/members/forms/__init__.py @@ -4,6 +4,7 @@ from .admin_signup_form import adminSignupForm from .activity_signup_form import ActivitySignupForm from .activity_invite_decline_form import ActivivtyInviteDeclineForm +from .volunteer_email_form import VolEmailForm __all__ = [ ActivivtyInviteDeclineForm, @@ -12,4 +13,5 @@ signupForm, vol_signupForm, adminSignupForm, + VolEmailForm, ] diff --git a/members/forms/volunteer_email_form.py b/members/forms/volunteer_email_form.py new file mode 100644 index 00000000..2c7d6501 --- /dev/null +++ b/members/forms/volunteer_email_form.py @@ -0,0 +1,60 @@ +from django import forms +from django.conf import settings +from crispy_forms.helper import FormHelper +from crispy_forms.layout import Layout, Fieldset, Submit, Field, Hidden, Div + +from members.models.department import Department + + +class VolEmailForm(forms.Form): + def __init__(self, *args, **kwargs): + super(VolEmailForm, self).__init__(*args, **kwargs) + self.helper = FormHelper() + self.helper.form_method = "post" + self.helper.form_action = "volunteer_email" + self.helper.html5_required = True + self.fields["volunteer_birthday"].widget.format = "%d-%m-%Y" + self.helper.layout = Layout( + Hidden("form_id", "vol_email", id="id_form_id"), + Fieldset( + "Frivilliges oplysninger", + Div( + Div(Field("volunteer_email"), css_class="col-md-5"), + Div(Field("volunteer_name"), css_class="col-md-5"), + Div(Field("volunteer_zipcode"), css_class="col-md-5"), + Div(Field("volunteer_city"), css_class="col-md-5"), + Div(Field("volunteer_phone"), css_class="col-md-5"), + Div(Field("volunteer_birthday"), css_class="col-md-5"), + Div(Field("volunteer_department"), css_class="col-md-5"), + css_class="row", + ), + ), + Submit("submit", "Opret", css_class="btn-success"), + ) + + volunteer_name = forms.CharField( + label="Dit fulde navn", required=True, max_length=200 + ) + volunteer_email = forms.EmailField(label="Email", required=True) + volunteer_phone = forms.CharField(label="Telefon", required=True, max_length=50) + volunteer_birthday = forms.DateField( + label="Fødselsdato (dd-mm-åååå)", + required=True, + input_formats=(settings.DATE_INPUT_FORMATS), + error_messages={"invalid": "Indtast en gyldig dato."}, + widget=forms.DateInput(attrs={"type": "date"}), + ) + volunteer_department = forms.ModelChoiceField( + queryset=Department.objects.filter(closed_dtm__isnull=True).order_by("name"), + required=True, + label="Afdeling", + empty_label="-", + ) + + volunteer_zipcode = forms.CharField( + label="Postnummer", max_length=4.0, required=True + ) + volunteer_city = forms.CharField(label="By", max_length=200, required=True) + form_id = forms.CharField( + label="Form ID", max_length=10, widget=forms.HiddenInput(), initial="signup" + ) diff --git a/members/models/department.py b/members/models/department.py index c2636f56..8dd09187 100644 --- a/members/models/department.py +++ b/members/models/department.py @@ -29,9 +29,7 @@ class Meta: isOpening = models.BooleanField("Er afdelingen under opstart", default=False) website = models.URLField("Hjemmeside", blank=True) union = models.ForeignKey( - "Union", - verbose_name="Lokalforening", - on_delete=models.PROTECT, + "Union", verbose_name="Lokalforening", on_delete=models.PROTECT ) def no_members(self): @@ -50,6 +48,22 @@ def new_volunteer_email(self, volunteer_name): context = {"department": self, "volunteer_name": volunteer_name} new_vol_email.makeEmail(self, context) + def new_volunteer_email_dep_head(self, volunteer_form): + # First fetch department leaders email + new_vol_email = members.models.emailtemplate.EmailTemplate.objects.get( + idname="VOL_NEW_DEP_HEAD" + ) + context = { + "department": self, + "volunteer_name": volunteer_form.cleaned_data["volunteer_name"], + "volunteer_email": volunteer_form.cleaned_data["volunteer_email"], + "volunteer_phone": volunteer_form.cleaned_data["volunteer_phone"], + "volunteer_birthday": volunteer_form.cleaned_data["volunteer_birthday"], + "volunteer_zipcode": volunteer_form.cleaned_data["volunteer_zipcode"], + "volunteer_city": volunteer_form.cleaned_data["volunteer_city"], + } + new_vol_email.makeEmail(self, context) + @staticmethod def get_open_departments(): result = [] diff --git a/members/templates/members/volunteer_email.html b/members/templates/members/volunteer_email.html new file mode 100644 index 00000000..9d8e2c9a --- /dev/null +++ b/members/templates/members/volunteer_email.html @@ -0,0 +1,15 @@ +{% extends 'members/base.html' %} +{% load crispy_forms_tags %} +{% block content %} +
+

Nedenfor kan du udfylde informationer så du kan komme i kontakt med den rette afdeling.

+

Du kan for eksempel vælge en afdeling tæt på hvor du bor eller arbejder.

+

Hvis du er i tvivl og har brug for ekstra hjælp, kan du kontakte os på

+ kontakt@codingpirates.dk +
+
+
+ {% crispy VolEmailForm %} +
+
+{% endblock %} diff --git a/members/templates/members/volunteer_email_sent.html b/members/templates/members/volunteer_email_sent.html new file mode 100644 index 00000000..c1d80061 --- /dev/null +++ b/members/templates/members/volunteer_email_sent.html @@ -0,0 +1,11 @@ +{% extends "members/base.html" %} + +{% block content %} +

Din anmodning er sendt!

+

+ Afdelingslederen vil snart være i kontakt med dig. +

+

+ Hvis du mod forventning ikke bliver kontaktet skal du være meget velkommen til at kontakte kontakt@codingpirates.dk. +

+{% endblock %} diff --git a/members/tests/test_model_department.py b/members/tests/test_model_department.py index 1bdb1cd9..fb363e40 100644 --- a/members/tests/test_model_department.py +++ b/members/tests/test_model_department.py @@ -1,12 +1,27 @@ -from datetime import timedelta +from datetime import timedelta, date from random import randint +from django.core import mail +from django.core.management import call_command from django.test import TestCase from django.utils import timezone -from members.models import Department +from members.forms import VolEmailForm +from members.models import Department, EmailTemplate from .factories import DepartmentFactory, ActivityFactory class TestModelDepartment(TestCase): + def setUp(self): + self.department = DepartmentFactory(closed_dtm=None) + self.template = EmailTemplate( + idname="VOL_NEW_DEP_HEAD", + name="Email til afdelingslederen ved ny frivillig", + description="Ny frivillig notifikation til afdelingskaptajnen", + subject="Ny frivillig", + body_html="Just a test", + body_text="Just a test", + ) + self.template.save() + def test_get_open_departments(self): open_departments = DepartmentFactory.create_batch(10, closed_dtm=None) [ @@ -44,3 +59,25 @@ def test_get_open_departments(self): ] self.assertEqual(expected, set(Department.get_open_departments())) + + def test_send_new_volunteer(self): + email_amount = len(mail.outbox) + department = Department.objects.get(pk=self.department.pk) + form = VolEmailForm( + data={ + "form_id": "vol_email", + "volunteer_email": "test@test.dk", + "volunteer_name": "Åse", + "volunteer_zipcode": 4100, + "volunteer_city": "Ringsted", + "volunteer_phone": 11223344, + "volunteer_birthday": date.today() + - timedelta(days=randint(7305, 12784)), + "volunteer_department": self.department.pk, + } + ) + if form.is_valid(): + department.new_volunteer_email_dep_head(form) + email_amount += 1 + call_command("runcrons") + self.assertEqual(len(mail.outbox), email_amount) diff --git a/members/urls.py b/members/urls.py index 934cea86..590ad29f 100644 --- a/members/urls.py +++ b/members/urls.py @@ -18,6 +18,8 @@ Membership, SupportMembership, AdminSignup, + volunteerEmail, + volunteerEmailSentView, ) from django.contrib.auth import views as auth_views from graphene_django.views import GraphQLView @@ -112,4 +114,6 @@ url(r"^quickpay_callback$", QuickpayCallback, name="quickpay_callback"), url(r"^department_signup$", DepartmentSignView, name="department_signup"), url(r"^departments$", departmentView, name="department_view"), + url(r"^volunteer_signup$", volunteerEmail, name="volunteer_email"), + url(r"^volunteer_email_sent", volunteerEmailSentView, name="vol_email_sent"), ] diff --git a/members/views/__init__.py b/members/views/__init__.py index 1e1443e0..f1383904 100644 --- a/members/views/__init__.py +++ b/members/views/__init__.py @@ -18,3 +18,5 @@ from members.views.Membership import Membership from members.views.SupportMembership import SupportMembership from members.views.AdminSignup import AdminSignup +from members.views.volunteerEmail import volunteerEmail +from members.views.volunteerEmailSentView import volunteerEmailSentView diff --git a/members/views/volunteerEmail.py b/members/views/volunteerEmail.py new file mode 100644 index 00000000..b0f634a2 --- /dev/null +++ b/members/views/volunteerEmail.py @@ -0,0 +1,36 @@ +from django.urls import reverse +from django.http import HttpResponseRedirect +from django.shortcuts import render +from django.views.decorators.clickjacking import xframe_options_exempt + +from members.forms import VolEmailForm +from members.models.department import Department + + +@xframe_options_exempt +def volunteerEmail(request): + # figure out which form was filled out. + if request.method == "POST" and request.POST["form_id"] == "vol_email": + # email form has been filled + vol_email = VolEmailForm(request.POST) + if vol_email.is_valid(): + # email to department leader + department = Department.objects.get( + name=vol_email.cleaned_data["volunteer_department"] + ) + department.new_volunteer_email_dep_head(vol_email) + return HttpResponseRedirect(reverse("vol_email_sent")) + else: + return render( + request, + "members/volunteer_email.html", + {"skip_context": True, "VolEmailForm": vol_email}, + ) + + # initial load (if we did not return above) + vol_email = VolEmailForm() + return render( + request, + "members/volunteer_email.html", + {"skip_context": True, "VolEmailForm": vol_email}, + ) diff --git a/members/views/volunteerEmailSentView.py b/members/views/volunteerEmailSentView.py new file mode 100644 index 00000000..98eac188 --- /dev/null +++ b/members/views/volunteerEmailSentView.py @@ -0,0 +1,5 @@ +from django.shortcuts import render + + +def volunteerEmailSentView(request): + return render(request, "members/volunteer_email_sent.html", {"skip_context": True}) From b55e0c0134bd99a352c3be2a121cff54ac7fee00 Mon Sep 17 00:00:00 2001 From: Jakob Libak Rasmussen <31068308+JakobLibak@users.noreply.github.com> Date: Wed, 1 Sep 2021 23:20:10 +0200 Subject: [PATCH 8/9] Revert "Quick form that makes it possible to get an email to a department head (#689)" (#693) This reverts commit 8b8afb908251c022a4bad9be28461d901c1e6f0b. --- members/forms/__init__.py | 2 - members/forms/volunteer_email_form.py | 60 ------------------- members/models/department.py | 20 +------ .../templates/members/volunteer_email.html | 15 ----- .../members/volunteer_email_sent.html | 11 ---- members/tests/test_model_department.py | 41 +------------ members/urls.py | 4 -- members/views/__init__.py | 2 - members/views/volunteerEmail.py | 36 ----------- members/views/volunteerEmailSentView.py | 5 -- 10 files changed, 5 insertions(+), 191 deletions(-) delete mode 100644 members/forms/volunteer_email_form.py delete mode 100644 members/templates/members/volunteer_email.html delete mode 100644 members/templates/members/volunteer_email_sent.html delete mode 100644 members/views/volunteerEmail.py delete mode 100644 members/views/volunteerEmailSentView.py diff --git a/members/forms/__init__.py b/members/forms/__init__.py index 517a0fbe..688ba0a6 100644 --- a/members/forms/__init__.py +++ b/members/forms/__init__.py @@ -4,7 +4,6 @@ from .admin_signup_form import adminSignupForm from .activity_signup_form import ActivitySignupForm from .activity_invite_decline_form import ActivivtyInviteDeclineForm -from .volunteer_email_form import VolEmailForm __all__ = [ ActivivtyInviteDeclineForm, @@ -13,5 +12,4 @@ signupForm, vol_signupForm, adminSignupForm, - VolEmailForm, ] diff --git a/members/forms/volunteer_email_form.py b/members/forms/volunteer_email_form.py deleted file mode 100644 index 2c7d6501..00000000 --- a/members/forms/volunteer_email_form.py +++ /dev/null @@ -1,60 +0,0 @@ -from django import forms -from django.conf import settings -from crispy_forms.helper import FormHelper -from crispy_forms.layout import Layout, Fieldset, Submit, Field, Hidden, Div - -from members.models.department import Department - - -class VolEmailForm(forms.Form): - def __init__(self, *args, **kwargs): - super(VolEmailForm, self).__init__(*args, **kwargs) - self.helper = FormHelper() - self.helper.form_method = "post" - self.helper.form_action = "volunteer_email" - self.helper.html5_required = True - self.fields["volunteer_birthday"].widget.format = "%d-%m-%Y" - self.helper.layout = Layout( - Hidden("form_id", "vol_email", id="id_form_id"), - Fieldset( - "Frivilliges oplysninger", - Div( - Div(Field("volunteer_email"), css_class="col-md-5"), - Div(Field("volunteer_name"), css_class="col-md-5"), - Div(Field("volunteer_zipcode"), css_class="col-md-5"), - Div(Field("volunteer_city"), css_class="col-md-5"), - Div(Field("volunteer_phone"), css_class="col-md-5"), - Div(Field("volunteer_birthday"), css_class="col-md-5"), - Div(Field("volunteer_department"), css_class="col-md-5"), - css_class="row", - ), - ), - Submit("submit", "Opret", css_class="btn-success"), - ) - - volunteer_name = forms.CharField( - label="Dit fulde navn", required=True, max_length=200 - ) - volunteer_email = forms.EmailField(label="Email", required=True) - volunteer_phone = forms.CharField(label="Telefon", required=True, max_length=50) - volunteer_birthday = forms.DateField( - label="Fødselsdato (dd-mm-åååå)", - required=True, - input_formats=(settings.DATE_INPUT_FORMATS), - error_messages={"invalid": "Indtast en gyldig dato."}, - widget=forms.DateInput(attrs={"type": "date"}), - ) - volunteer_department = forms.ModelChoiceField( - queryset=Department.objects.filter(closed_dtm__isnull=True).order_by("name"), - required=True, - label="Afdeling", - empty_label="-", - ) - - volunteer_zipcode = forms.CharField( - label="Postnummer", max_length=4.0, required=True - ) - volunteer_city = forms.CharField(label="By", max_length=200, required=True) - form_id = forms.CharField( - label="Form ID", max_length=10, widget=forms.HiddenInput(), initial="signup" - ) diff --git a/members/models/department.py b/members/models/department.py index 8dd09187..c2636f56 100644 --- a/members/models/department.py +++ b/members/models/department.py @@ -29,7 +29,9 @@ class Meta: isOpening = models.BooleanField("Er afdelingen under opstart", default=False) website = models.URLField("Hjemmeside", blank=True) union = models.ForeignKey( - "Union", verbose_name="Lokalforening", on_delete=models.PROTECT + "Union", + verbose_name="Lokalforening", + on_delete=models.PROTECT, ) def no_members(self): @@ -48,22 +50,6 @@ def new_volunteer_email(self, volunteer_name): context = {"department": self, "volunteer_name": volunteer_name} new_vol_email.makeEmail(self, context) - def new_volunteer_email_dep_head(self, volunteer_form): - # First fetch department leaders email - new_vol_email = members.models.emailtemplate.EmailTemplate.objects.get( - idname="VOL_NEW_DEP_HEAD" - ) - context = { - "department": self, - "volunteer_name": volunteer_form.cleaned_data["volunteer_name"], - "volunteer_email": volunteer_form.cleaned_data["volunteer_email"], - "volunteer_phone": volunteer_form.cleaned_data["volunteer_phone"], - "volunteer_birthday": volunteer_form.cleaned_data["volunteer_birthday"], - "volunteer_zipcode": volunteer_form.cleaned_data["volunteer_zipcode"], - "volunteer_city": volunteer_form.cleaned_data["volunteer_city"], - } - new_vol_email.makeEmail(self, context) - @staticmethod def get_open_departments(): result = [] diff --git a/members/templates/members/volunteer_email.html b/members/templates/members/volunteer_email.html deleted file mode 100644 index 9d8e2c9a..00000000 --- a/members/templates/members/volunteer_email.html +++ /dev/null @@ -1,15 +0,0 @@ -{% extends 'members/base.html' %} -{% load crispy_forms_tags %} -{% block content %} -
-

Nedenfor kan du udfylde informationer så du kan komme i kontakt med den rette afdeling.

-

Du kan for eksempel vælge en afdeling tæt på hvor du bor eller arbejder.

-

Hvis du er i tvivl og har brug for ekstra hjælp, kan du kontakte os på

- kontakt@codingpirates.dk -
-
-
- {% crispy VolEmailForm %} -
-
-{% endblock %} diff --git a/members/templates/members/volunteer_email_sent.html b/members/templates/members/volunteer_email_sent.html deleted file mode 100644 index c1d80061..00000000 --- a/members/templates/members/volunteer_email_sent.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends "members/base.html" %} - -{% block content %} -

Din anmodning er sendt!

-

- Afdelingslederen vil snart være i kontakt med dig. -

-

- Hvis du mod forventning ikke bliver kontaktet skal du være meget velkommen til at kontakte kontakt@codingpirates.dk. -

-{% endblock %} diff --git a/members/tests/test_model_department.py b/members/tests/test_model_department.py index fb363e40..1bdb1cd9 100644 --- a/members/tests/test_model_department.py +++ b/members/tests/test_model_department.py @@ -1,27 +1,12 @@ -from datetime import timedelta, date +from datetime import timedelta from random import randint -from django.core import mail -from django.core.management import call_command from django.test import TestCase from django.utils import timezone -from members.forms import VolEmailForm -from members.models import Department, EmailTemplate +from members.models import Department from .factories import DepartmentFactory, ActivityFactory class TestModelDepartment(TestCase): - def setUp(self): - self.department = DepartmentFactory(closed_dtm=None) - self.template = EmailTemplate( - idname="VOL_NEW_DEP_HEAD", - name="Email til afdelingslederen ved ny frivillig", - description="Ny frivillig notifikation til afdelingskaptajnen", - subject="Ny frivillig", - body_html="Just a test", - body_text="Just a test", - ) - self.template.save() - def test_get_open_departments(self): open_departments = DepartmentFactory.create_batch(10, closed_dtm=None) [ @@ -59,25 +44,3 @@ def test_get_open_departments(self): ] self.assertEqual(expected, set(Department.get_open_departments())) - - def test_send_new_volunteer(self): - email_amount = len(mail.outbox) - department = Department.objects.get(pk=self.department.pk) - form = VolEmailForm( - data={ - "form_id": "vol_email", - "volunteer_email": "test@test.dk", - "volunteer_name": "Åse", - "volunteer_zipcode": 4100, - "volunteer_city": "Ringsted", - "volunteer_phone": 11223344, - "volunteer_birthday": date.today() - - timedelta(days=randint(7305, 12784)), - "volunteer_department": self.department.pk, - } - ) - if form.is_valid(): - department.new_volunteer_email_dep_head(form) - email_amount += 1 - call_command("runcrons") - self.assertEqual(len(mail.outbox), email_amount) diff --git a/members/urls.py b/members/urls.py index 590ad29f..934cea86 100644 --- a/members/urls.py +++ b/members/urls.py @@ -18,8 +18,6 @@ Membership, SupportMembership, AdminSignup, - volunteerEmail, - volunteerEmailSentView, ) from django.contrib.auth import views as auth_views from graphene_django.views import GraphQLView @@ -114,6 +112,4 @@ url(r"^quickpay_callback$", QuickpayCallback, name="quickpay_callback"), url(r"^department_signup$", DepartmentSignView, name="department_signup"), url(r"^departments$", departmentView, name="department_view"), - url(r"^volunteer_signup$", volunteerEmail, name="volunteer_email"), - url(r"^volunteer_email_sent", volunteerEmailSentView, name="vol_email_sent"), ] diff --git a/members/views/__init__.py b/members/views/__init__.py index f1383904..1e1443e0 100644 --- a/members/views/__init__.py +++ b/members/views/__init__.py @@ -18,5 +18,3 @@ from members.views.Membership import Membership from members.views.SupportMembership import SupportMembership from members.views.AdminSignup import AdminSignup -from members.views.volunteerEmail import volunteerEmail -from members.views.volunteerEmailSentView import volunteerEmailSentView diff --git a/members/views/volunteerEmail.py b/members/views/volunteerEmail.py deleted file mode 100644 index b0f634a2..00000000 --- a/members/views/volunteerEmail.py +++ /dev/null @@ -1,36 +0,0 @@ -from django.urls import reverse -from django.http import HttpResponseRedirect -from django.shortcuts import render -from django.views.decorators.clickjacking import xframe_options_exempt - -from members.forms import VolEmailForm -from members.models.department import Department - - -@xframe_options_exempt -def volunteerEmail(request): - # figure out which form was filled out. - if request.method == "POST" and request.POST["form_id"] == "vol_email": - # email form has been filled - vol_email = VolEmailForm(request.POST) - if vol_email.is_valid(): - # email to department leader - department = Department.objects.get( - name=vol_email.cleaned_data["volunteer_department"] - ) - department.new_volunteer_email_dep_head(vol_email) - return HttpResponseRedirect(reverse("vol_email_sent")) - else: - return render( - request, - "members/volunteer_email.html", - {"skip_context": True, "VolEmailForm": vol_email}, - ) - - # initial load (if we did not return above) - vol_email = VolEmailForm() - return render( - request, - "members/volunteer_email.html", - {"skip_context": True, "VolEmailForm": vol_email}, - ) diff --git a/members/views/volunteerEmailSentView.py b/members/views/volunteerEmailSentView.py deleted file mode 100644 index 98eac188..00000000 --- a/members/views/volunteerEmailSentView.py +++ /dev/null @@ -1,5 +0,0 @@ -from django.shortcuts import render - - -def volunteerEmailSentView(request): - return render(request, "members/volunteer_email_sent.html", {"skip_context": True}) From ef23dc1cd8282dce07aa13aaaa66c10608e02eee Mon Sep 17 00:00:00 2001 From: Jarl Friis Date: Thu, 2 Sep 2021 11:48:38 +0200 Subject: [PATCH 9/9] Feature/483 lav aktiviteter offentlige account login (#690) * issue #663: Refactor: rework on activities test * Refactor: put login into test_functions * Refactor: Taking out account_create from entry_page * Restructuring front page, login page, account creation page * Redesign: Navigation buttons * Update package-lock.json * Added .nvmrc to ensure using identical node version --- .nvmrc | 1 + members/forms/signup_form.py | 2 +- .../static/members/sass/account_create.scss | 10 + members/static/members/sass/entry_page.scss | 7 - members/static/members/sass/header.scss | 8 +- members/templates/members/account_create.html | 12 + members/templates/members/entry_page.html | 23 +- members/templates/members/header.html | 13 +- members/templates/members/login.html | 12 + ...reate_family.py => test_account_create.py} | 13 +- .../test_functional/test_account_login.py | 56 +++++ .../tests/test_functional/test_activities.py | 215 +++++------------- .../test_functional/test_admin_can_load.py | 2 +- .../tests/test_functional/test_entry_page.py | 80 +++++++ .../test_functional/test_payment_status.py | 2 +- members/urls.py | 26 ++- members/views/AccountCreate.py | 105 +++++++++ members/views/EntryPage.py | 100 +------- members/views/__init__.py | 27 +-- package-lock.json | 113 ++------- 20 files changed, 404 insertions(+), 423 deletions(-) create mode 100644 .nvmrc create mode 100644 members/static/members/sass/account_create.scss create mode 100644 members/templates/members/account_create.html rename members/tests/test_functional/{test_create_family.py => test_account_create.py} (90%) create mode 100644 members/tests/test_functional/test_account_login.py create mode 100644 members/tests/test_functional/test_entry_page.py create mode 100644 members/views/AccountCreate.py diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 00000000..9631d5d3 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +v10.19.0 diff --git a/members/forms/signup_form.py b/members/forms/signup_form.py index 4ef761f7..ed86dee4 100644 --- a/members/forms/signup_form.py +++ b/members/forms/signup_form.py @@ -11,7 +11,7 @@ def __init__(self, *args, **kwargs): super(signupForm, self).__init__(*args, **kwargs) self.helper = FormHelper() self.helper.form_method = "post" - self.helper.form_action = "entry_page" + self.helper.form_action = "account_create" self.helper.html5_required = True self.helper.layout = Layout( Hidden("form_id", "signup", id="id_form_id"), diff --git a/members/static/members/sass/account_create.scss b/members/static/members/sass/account_create.scss new file mode 100644 index 00000000..70f79b66 --- /dev/null +++ b/members/static/members/sass/account_create.scss @@ -0,0 +1,10 @@ +@use "definitions"; +@use "buttons"; + + +#signup-form { + border: 1px solid rgba(0, 0, 0, 0.125); + padding: 1.25rem; + background-clip: border-box; + border-radius: 0.25rem; +} diff --git a/members/static/members/sass/entry_page.scss b/members/static/members/sass/entry_page.scss index a3dcc1d7..77ac247d 100644 --- a/members/static/members/sass/entry_page.scss +++ b/members/static/members/sass/entry_page.scss @@ -47,10 +47,3 @@ } } } - -#signup-form { - border: 1px solid rgba(0, 0, 0, 0.125); - padding: 1.25rem; - background-clip: border-box; - border-radius: 0.25rem; -} diff --git a/members/static/members/sass/header.scss b/members/static/members/sass/header.scss index 4d848b69..f8a4ab79 100644 --- a/members/static/members/sass/header.scss +++ b/members/static/members/sass/header.scss @@ -36,10 +36,10 @@ nav { text-align: center !important; } } -} -#login-logout { - text-align: right; - flex-grow: 2; + > .account-sep { + text-align: right; + flex-grow: 2; + } } .inactive { diff --git a/members/templates/members/account_create.html b/members/templates/members/account_create.html new file mode 100644 index 00000000..7a514f26 --- /dev/null +++ b/members/templates/members/account_create.html @@ -0,0 +1,12 @@ +{% extends 'members/base.html' %} {% load crispy_forms_tags %} {% load static %} +{% block content %} + +

Tilmelding til Coding Pirates

+

+ Indtast barnets og forældrenes informationer, for at tilmelde en Coding + Pirates-workshop, eller for at komme med på ventelisten. +

+
+ {% crispy signupform %} +
+{% endblock %} diff --git a/members/templates/members/entry_page.html b/members/templates/members/entry_page.html index e08ae5d2..38a14bae 100644 --- a/members/templates/members/entry_page.html +++ b/members/templates/members/entry_page.html @@ -1,18 +1,7 @@ {% extends 'members/base.html' %} {% load crispy_forms_tags %} {% load static %} {% block content %} -{% if user.is_authenticated %}
-
- -
-

Jeres familie

- - Se familie - -

Se og ret stamdata for din familie

-
-
@@ -21,7 +10,7 @@

Opskriv til afdelinger

Afdelinger

- Her kan du opskrive dig som interesseret til en af vores + Her kan du opskrive dig som interesseret til en af vores afdelinger til næste gang vi starter nye hold op

@@ -65,14 +54,4 @@

Bliv støttemedlem

kontakt@codingpirates.dk.

-{% else %} -

Tilmelding til Coding Pirates

-

- Indtast barnets og forældrenes informationer, for at tilmelde en Coding - Pirates-workshop, eller for at komme med på ventelisten. -

-
- {% crispy signupform %} -
-{% endif %} {% endblock %} diff --git a/members/templates/members/header.html b/members/templates/members/header.html index ca9323d6..b1331ec0 100644 --- a/members/templates/members/header.html +++ b/members/templates/members/header.html @@ -3,16 +3,17 @@ + Oversigt + Afdelinger + Arrangementer + Medlemskaber + Støttemedlemskaber + {% if user.is_authenticated %} - Oversigt Familie - Afdelinger - Arrangementer - Medlemskaber - Støttemedlemskaber Log ud {% else %} - Tilmeld barn + Tilmeld barn Bliv frivillig Log ind {% endif %} diff --git a/members/templates/members/login.html b/members/templates/members/login.html index 6d728d61..1578b822 100644 --- a/members/templates/members/login.html +++ b/members/templates/members/login.html @@ -30,6 +30,18 @@
+ + {% endblock %} diff --git a/members/tests/test_functional/test_create_family.py b/members/tests/test_functional/test_account_create.py similarity index 90% rename from members/tests/test_functional/test_create_family.py rename to members/tests/test_functional/test_account_create.py index adfc5dd0..8d7576f7 100644 --- a/members/tests/test_functional/test_create_family.py +++ b/members/tests/test_functional/test_account_create.py @@ -16,7 +16,7 @@ """ -class SignUpTest(StaticLiveServerTestCase): +class AccountCreateTest(StaticLiveServerTestCase): host = socket.gethostbyname(socket.gethostname()) serialized_rollback = True @@ -32,9 +32,9 @@ def tearDown(self): self.browser.save_screenshot("test-screens/sign_up_screen_final.png") self.browser.quit() - def test_entry_page(self): + def test_account_create(self): # Loads the front page - self.browser.get(self.live_server_url) + self.browser.get(f"{self.live_server_url}/account/create") self.assertEqual("Coding Pirates Medlemssystem", self.browser.title) self.browser.save_screenshot("test-screens/sign_up_screen_1.png") @@ -96,8 +96,5 @@ def test_entry_page(self): self.browser.find_element_by_xpath("//input[@type='submit']").click() - # Check that we were redirectet to overview page - elements = self.browser.find_elements_by_xpath( - "//*[text()[contains(.,'For yderligere hjælp med at bruge denne side')]]" - ) - self.assertGreater(len(elements), 0) + # Check that we were redirectet to front page + self.assertEqual(f"{self.live_server_url}/", self.browser.current_url) diff --git a/members/tests/test_functional/test_account_login.py b/members/tests/test_functional/test_account_login.py new file mode 100644 index 00000000..8ecf44fb --- /dev/null +++ b/members/tests/test_functional/test_account_login.py @@ -0,0 +1,56 @@ +import os +import socket + +from django.contrib.staticfiles.testing import StaticLiveServerTestCase +from selenium import webdriver +from selenium.webdriver.common.desired_capabilities import DesiredCapabilities + +from members.tests.factories import ( + MemberFactory, +) + +""" +This test goes to the account login page +""" + + +class AccountLoginTest(StaticLiveServerTestCase): + host = socket.gethostbyname(socket.gethostname()) + serialized_rollback = True + + def setUp(self): + self.member = MemberFactory.create() + + self.browser = webdriver.Remote( + "http://selenium:4444/wd/hub", DesiredCapabilities.CHROME + ) + + def tearDown(self): + if not os.path.exists("test-screens"): + os.mkdir("test-screens") + self.browser.save_screenshot("test-screens/activities_list_final.png") + self.browser.quit() + + def test_account_login(self): + self.browser.get(f"{self.live_server_url}/account/login") + self.assertIn( + "Log ind", + [ + e.text + for e in self.browser.find_elements_by_xpath( + "//body/descendant-or-self::*" + ) + ], + ) + self.browser.find_element_by_link_text("Log ind") + self.assertIn( + "Opret bruger", + [ + e.text + for e in self.browser.find_elements_by_xpath( + "//body/descendant-or-self::*" + ) + ], + ) + self.browser.find_element_by_link_text("Tilmeld barn") + self.browser.find_element_by_link_text("Bliv frivillig") diff --git a/members/tests/test_functional/test_activities.py b/members/tests/test_functional/test_activities.py index bcddd229..917c3745 100644 --- a/members/tests/test_functional/test_activities.py +++ b/members/tests/test_functional/test_activities.py @@ -1,4 +1,3 @@ -import datetime import os import socket @@ -10,11 +9,10 @@ from members.tests.factories import ( ActivityFactory, - PersonFactory, ActivityParticipantFactory, MemberFactory, ) -from members.tests.factories.person_factory import UserFactory +from members.tests.test_functional.functional_helpers import log_in """ This test goes to the activities list. @@ -26,161 +24,49 @@ class ActivitiesTest(StaticLiveServerTestCase): serialized_rollback = True def setUp(self): - self.email = "some_email@bob.gl" - self.password = "MySecret" - self.member = MemberFactory.create( - person=PersonFactory.create( - birthday=Faker("date_between", start_date="-50y", end_date="-10y"), - user=UserFactory.create( - username=self.email, email=self.email, password=self.password - ), - ) - ) - self.member.person.user.set_password(self.password) - self.member.person.user.save() - self.member.person.birthday = datetime.date(1980, 1, 10) - - self.activity_arrangement = ActivityFactory.create( - open_invite=True, - signup_closing=Faker("future_datetime", end_date="+100d"), - min_age=5, - max_age=90, - name="Arrangement", - activitytype_id="ARRANGEMENT", - ) - self.activity_arrangement.save() - self.activity_arrangement_participate = ActivityFactory.create( - name="Arrangement deltagelse", activitytype_id="ARRANGEMENT" - ) - self.activity_arrangement_participate.save() - ActivityParticipantFactory.create( - activity=self.activity_arrangement_participate, member=self.member - ).save() - self.activity_forløb = ActivityFactory.create( - open_invite=True, - signup_closing=Faker("future_datetime", end_date="+100d"), - min_age=5, - max_age=90, - name="Forløb", - activitytype_id="FORLØB", - ) - self.activity_forløb.save() - self.activity_forløb_participate = ActivityFactory.create( - name="Forløb deltagelse", activitytype_id="FORLØB" - ) - self.activity_forløb_participate.save() - ActivityParticipantFactory.create( - activity=self.activity_forløb_participate, member=self.member - ).save() - self.activity_foreningsmedlemskab = ActivityFactory.create( - open_invite=True, - signup_closing=Faker("future_datetime", end_date="+100d"), - min_age=5, - max_age=90, - name="Foreningsmedlemskab", - activitytype_id="FORENINGSMEDLEMSKAB", - ) - self.activity_foreningsmedlemskab.save() - self.activity_foreningsmedlemskab_old = ActivityFactory.create( - open_invite=True, - signup_closing=Faker("past_date", start_date="-10d"), - min_age=5, - max_age=90, - name="Foreningsmedlemskab", - activitytype_id="FORENINGSMEDLEMSKAB", - ) - self.activity_foreningsmedlemskab_old.save() - self.activity_foreningsmedlemskab_participate = ActivityFactory.create( - name="Foreningsmedlemskab deltagelse", activitytype_id="FORENINGSMEDLEMSKAB" - ) - self.activity_foreningsmedlemskab_participate.save() - ActivityParticipantFactory.create( - activity=self.activity_foreningsmedlemskab_participate, member=self.member - ).save() - self.activity_støttemedlemskab = ActivityFactory.create( - open_invite=True, - signup_closing=Faker("future_datetime", end_date="+100d"), - min_age=5, - max_age=90, - name="Støttemedlemskab", - activitytype_id="STØTTEMEDLEMSKAB", - ) - self.activity_støttemedlemskab.save() - self.activity_støttemedlemskab_old = ActivityFactory.create( - open_invite=True, - signup_closing=Faker("past_date", start_date="-10d"), - min_age=5, - max_age=90, - name="Støttemedlemskab", - activitytype_id="STØTTEMEDLEMSKAB", - ) - self.activity_støttemedlemskab_old.save() - self.activity_støttemedlemskab_participate = ActivityFactory.create( - name="Støttemedlemskab deltagelse", activitytype_id="STØTTEMEDLEMSKAB" - ) - self.activity_støttemedlemskab_participate.save() - ActivityParticipantFactory.create( - activity=self.activity_støttemedlemskab_participate, member=self.member - ).save() + self.member = MemberFactory.create() + + self.activities = {} + for activity_type in [ + "ARRANGEMENT", + "FORLØB", + "FORENINGSMEDLEMSKAB", + "STØTTEMEDLEMSKAB", + ]: + self.activities[activity_type] = {} + for variant in ["participate", "recent", "old"]: + self.activities[activity_type][variant] = ActivityFactory.create( + open_invite=True, + min_age=5, + max_age=90, + signup_closing=( + Faker("past_date", start_date="-10d") + if variant == "old" + else Faker("future_datetime", end_date="+100d") + ), + name=f"-{activity_type}-{variant}", + activitytype_id=activity_type, + ) + if variant == "participate": + ActivityParticipantFactory.create( + activity=self.activities[activity_type][variant], + member=self.member, + ) self.browser = webdriver.Remote( "http://selenium:4444/wd/hub", DesiredCapabilities.CHROME ) - # Login - self.browser.get(f"{self.live_server_url}/account/login") - self.browser.save_screenshot("test-screens/login.png") - field = self.browser.find_element_by_name("username") - field.send_keys(self.email) - field = self.browser.find_element_by_name("password") - field.send_keys(self.password) - - self.browser.save_screenshot("test-screens/activities_login_filled.png") - - self.browser.find_element_by_xpath("//input[@value='Log ind']").click() - self.browser.save_screenshot("test-screens/activities_logged_in.png") - def tearDown(self): if not os.path.exists("test-screens"): os.mkdir("test-screens") self.browser.save_screenshot("test-screens/activities_list_final.png") self.browser.quit() - def test_entry_page(self): - self.assertIn( - "Jeres familie", - [ - e.text - for e in self.browser.find_elements_by_xpath( - "//body/descendant-or-self::*" - ) - ], - ) - self.browser.find_element_by_link_text("Se familie") - self.browser.find_element_by_link_text("Afdelinger") - links = list( - map( - lambda e: e.get_attribute("href"), - self.browser.find_elements_by_link_text("Arrangementer"), - ) - ) - self.assertEqual(links[0], links[1]) - links = list( - map( - lambda e: e.get_attribute("href"), - self.browser.find_elements_by_link_text("Medlemskaber"), - ) - ) - self.assertEqual(links[0], links[1]) - links = list( - map( - lambda e: e.get_attribute("href"), - self.browser.find_elements_by_link_text("Støttemedlemskaber"), - ) - ) - self.assertEqual(links[0], links[1]) + def test_activities_as_member(self): + # Login + log_in(self, self.member.person) - def test_activities(self): # Loads the activities self.browser.find_element_by_link_text("Arrangementer").click() WebDriverWait(self.browser, 10).until( @@ -196,8 +82,10 @@ def test_activities(self): ) ] self.assertEqual(2, len(activity_names)) - self.assertIn(self.activity_arrangement_participate.name, activity_names) - self.assertIn(self.activity_forløb_participate.name, activity_names) + self.assertIn( + self.activities["ARRANGEMENT"]["participate"].name, activity_names + ) + self.assertIn(self.activities["FORLØB"]["participate"].name, activity_names) # Check that the page contains all activities activity_names = [ @@ -207,10 +95,13 @@ def test_activities(self): ) ] self.assertEqual(2, len(activity_names)) - self.assertIn(self.activity_arrangement.name, activity_names) - self.assertIn(self.activity_forløb.name, activity_names) + self.assertIn(self.activities["ARRANGEMENT"]["recent"].name, activity_names) + self.assertIn(self.activities["FORLØB"]["recent"].name, activity_names) + + def test_membership_as_member(self): + # Login + log_in(self, self.member.person) - def test_membership(self): # Loads the members self.browser.find_element_by_link_text("Medlemskaber").click() WebDriverWait(self.browser, 10).until( @@ -224,11 +115,11 @@ def test_membership(self): ) self.assertEqual(1, len(activities)) self.assertEqual( - self.activity_foreningsmedlemskab_participate.name, + self.activities["FORENINGSMEDLEMSKAB"]["participate"].name, activities[0].find_element_by_xpath("td[@data-label='Aktivitet']").text, ) self.assertEqual( - self.activity_foreningsmedlemskab_participate.union.name, + self.activities["FORENINGSMEDLEMSKAB"]["participate"].union.name, activities[0].find_element_by_xpath("td[@data-label='Forening']").text, ) @@ -238,15 +129,18 @@ def test_membership(self): ) self.assertEqual(1, len(activities)) self.assertEqual( - self.activity_foreningsmedlemskab.name, + self.activities["FORENINGSMEDLEMSKAB"]["recent"].name, activities[0].find_element_by_xpath("td[@data-label='Aktivitet']").text, ) self.assertEqual( - self.activity_foreningsmedlemskab.union.name, + self.activities["FORENINGSMEDLEMSKAB"]["recent"].union.name, activities[0].find_element_by_xpath("td[@data-label='Forening']").text, ) - def test_supportmembership(self): + def test_supportmembership_as_member(self): + # Login + log_in(self, self.member.person) + # Loads the members self.browser.find_element_by_link_text("Støttemedlemskaber").click() WebDriverWait(self.browser, 10).until( @@ -260,11 +154,11 @@ def test_supportmembership(self): ) self.assertEqual(1, len(activities)) self.assertEqual( - self.activity_støttemedlemskab_participate.name, + self.activities["STØTTEMEDLEMSKAB"]["participate"].name, activities[0].find_element_by_xpath("td[@data-label='Aktivitet']").text, ) self.assertEqual( - self.activity_støttemedlemskab_participate.union.name, + self.activities["STØTTEMEDLEMSKAB"]["participate"].union.name, activities[0].find_element_by_xpath("td[@data-label='Forening']").text, ) @@ -273,12 +167,11 @@ def test_supportmembership(self): "//section[@id='open_activities']/table/tbody/tr" ) self.assertEqual(1, len(activities)) - self.assertEqual(1, len(activities)) self.assertEqual( - self.activity_støttemedlemskab.name, + self.activities["STØTTEMEDLEMSKAB"]["recent"].name, activities[0].find_element_by_xpath("td[@data-label='Aktivitet']").text, ) self.assertEqual( - self.activity_støttemedlemskab.union.name, + self.activities["STØTTEMEDLEMSKAB"]["recent"].union.name, activities[0].find_element_by_xpath("td[@data-label='Forening']").text, ) diff --git a/members/tests/test_functional/test_admin_can_load.py b/members/tests/test_functional/test_admin_can_load.py index f7f02bab..8c145418 100644 --- a/members/tests/test_functional/test_admin_can_load.py +++ b/members/tests/test_functional/test_admin_can_load.py @@ -34,7 +34,7 @@ def tearDown(self): self.browser.save_screenshot("test-screens/admin_load_test.png") self.browser.quit() - def test_entry_page(self): + def test_admin_page(self): # Loads the admin login page self.browser.get(f"{self.live_server_url}/admin") self.assertIn("admin", self.browser.title) diff --git a/members/tests/test_functional/test_entry_page.py b/members/tests/test_functional/test_entry_page.py new file mode 100644 index 00000000..d85ddc26 --- /dev/null +++ b/members/tests/test_functional/test_entry_page.py @@ -0,0 +1,80 @@ +import os +import socket + +from django.contrib.staticfiles.testing import StaticLiveServerTestCase +from selenium import webdriver +from selenium.webdriver.common.desired_capabilities import DesiredCapabilities + +from members.tests.factories import ( + MemberFactory, +) +from members.tests.test_functional.functional_helpers import log_in + + +""" +This tests the entry page +""" + + +class EntryPageTest(StaticLiveServerTestCase): + host = socket.gethostbyname(socket.gethostname()) + serialized_rollback = True + + def setUp(self): + self.member = MemberFactory.create() + self.browser = webdriver.Remote( + "http://selenium:4444/wd/hub", DesiredCapabilities.CHROME + ) + + def tearDown(self): + if not os.path.exists("test-screens"): + os.mkdir("test-screens") + self.browser.save_screenshot("test-screens/activities_list_final.png") + self.browser.quit() + + def test_entry_page_as_member(self): + # Login + log_in(self, self.member.person) + + self.browser.find_element_by_link_text("Familie") + self.browser.find_element_by_link_text("Log ud") + + def test_entry_page(self): + self.browser.get(f"{self.live_server_url}") + + self.browser.find_element_by_link_text("Log ind") + self.browser.find_element_by_link_text("Tilmeld barn") + self.browser.find_element_by_link_text("Bliv frivillig") + + self.browser.find_element_by_link_text("Afdelinger") + self.browser.find_element_by_link_text("Arrangementer") + self.browser.find_element_by_link_text("Medlemskaber") + self.browser.find_element_by_link_text("Støttemedlemskaber") + links = list( + map( + lambda e: e.get_attribute("href"), + self.browser.find_elements_by_link_text("Arrangementer"), + ) + ) + self.assertEqual(links[0], links[1]) + links = list( + map( + lambda e: e.get_attribute("href"), + self.browser.find_elements_by_link_text("Afdelinger"), + ) + ) + self.assertEqual(links[0], links[1]) + links = list( + map( + lambda e: e.get_attribute("href"), + self.browser.find_elements_by_link_text("Medlemskaber"), + ) + ) + self.assertEqual(links[0], links[1]) + links = list( + map( + lambda e: e.get_attribute("href"), + self.browser.find_elements_by_link_text("Støttemedlemskaber"), + ) + ) + self.assertEqual(links[0], links[1]) diff --git a/members/tests/test_functional/test_payment_status.py b/members/tests/test_functional/test_payment_status.py index fb792058..7eb0c18c 100644 --- a/members/tests/test_functional/test_payment_status.py +++ b/members/tests/test_functional/test_payment_status.py @@ -50,7 +50,7 @@ def tearDown(self): self.browser.quit() @override_settings(DEBUG=True) - def test_entry_page(self): + def test_account_create(self): log_in(self, self.person) # Goes to activity sign up screen for first kid diff --git a/members/urls.py b/members/urls.py index 934cea86..90eb8352 100644 --- a/members/urls.py +++ b/members/urls.py @@ -1,23 +1,24 @@ from django.conf.urls import url from members.views import ( + AccountCreate, + Activities, + ActivitySignup, + AdminSignup, + ConfirmFamily, + DeclineInvitation, + DepartmentSignView, + EntryPage, FamilyDetails, + Membership, PersonCreate, PersonUpdate, - WaitingListSetSubscription, - DeclineInvitation, - EntryPage, - userCreated, - ConfirmFamily, QuickpayCallback, - ActivitySignup, - DepartmentSignView, + SupportMembership, + WaitingListSetSubscription, + departmentView, paymentGatewayErrorView, + userCreated, volunteerSignup, - departmentView, - Activities, - Membership, - SupportMembership, - AdminSignup, ) from django.contrib.auth import views as auth_views from graphene_django.views import GraphQLView @@ -26,6 +27,7 @@ urlpatterns = [ url(r"^$", EntryPage, name="entry_page"), url(r"^graphql", csrf_exempt(GraphQLView.as_view(graphiql=True))), + url(r"^account/create/$", AccountCreate, name="account_create"), url( r"^account/login/$", auth_views.LoginView.as_view(template_name="members/login.html"), diff --git a/members/views/AccountCreate.py b/members/views/AccountCreate.py new file mode 100644 index 00000000..82efea15 --- /dev/null +++ b/members/views/AccountCreate.py @@ -0,0 +1,105 @@ +from django.urls import reverse +from django.views.decorators.clickjacking import xframe_options_exempt +from members.forms import signupForm +from django.shortcuts import render +from django.utils import timezone +from django.http import HttpResponseRedirect +from django.contrib.auth.models import User +from members.models.family import Family +from members.models.person import Person + + +@xframe_options_exempt +def AccountCreate(request): + if request.method == "POST": + # figure out which form was filled out. + if request.POST["form_id"] == "signup": + # signup has been filled + signup = signupForm(request.POST) + if signup.is_valid(): + # check if family already exists + # TODO: rewrite this! >>>> + try: + family = Family.objects.get( + email__iexact=request.POST["parent_email"] + ) + # family was already created - we can't create this family again + signup.add_error( + "parent_email", + "Denne email adresse er allerede oprettet. Du kan tilføje flere børn på samme forælder, når du er kommet videre! - Log ind ovenfor, for at få adgang.", + ) + return render( + request, "members/account_create.html", {"signupform": signup} + ) + except Exception: + # all is fine - we did not expect any + pass + # TODO: rewrite this! <<<< + # create new family. + family = Family.objects.create( + email=signup.cleaned_data["parent_email"] + ) + family.confirmed_dtm = timezone.now() + family.save() + + # create parent as user + user = User.objects.create_user( + username=signup.cleaned_data["parent_email"], + email=signup.cleaned_data["parent_email"], + ) + password = User.objects.make_random_password() + user.set_password(password) + user.save() + + # create parent + parent = Person.objects.create( + membertype=Person.PARENT, + name=signup.cleaned_data["parent_name"], + zipcode=signup.cleaned_data["zipcode"], + city=signup.cleaned_data["city"], + streetname=signup.cleaned_data["streetname"], + housenumber=signup.cleaned_data["housenumber"], + floor=signup.cleaned_data["floor"], + door=signup.cleaned_data["door"], + dawa_id=signup.cleaned_data["dawa_id"], + placename=signup.cleaned_data["placename"], + email=signup.cleaned_data["parent_email"], + phone=signup.cleaned_data["parent_phone"], + birthday=signup.cleaned_data["parent_birthday"], + gender=signup.cleaned_data["parent_gender"], + family=family, + user=user, + ) + parent.save() + + # create child + child = Person.objects.create( + membertype=Person.CHILD, + name=signup.cleaned_data["child_name"], + zipcode=signup.cleaned_data["zipcode"], + city=signup.cleaned_data["city"], + streetname=signup.cleaned_data["streetname"], + housenumber=signup.cleaned_data["housenumber"], + floor=signup.cleaned_data["floor"], + door=signup.cleaned_data["door"], + dawa_id=signup.cleaned_data["dawa_id"], + placename=signup.cleaned_data["placename"], + email=signup.cleaned_data["child_email"], + phone=signup.cleaned_data["child_phone"], + birthday=signup.cleaned_data["child_birthday"], + gender=signup.cleaned_data["child_gender"], + family=family, + ) + child.save() + + # redirect to success + request.session["password"] = password + return HttpResponseRedirect(reverse("user_created")) + else: + return render( + request, "members/account_create.html", {"signupform": signup} + ) + + # initial load (if we did not return above) + signup = signupForm() + return render(request, "members/account_create.html", {"signupform": signup}) diff --git a/members/views/EntryPage.py b/members/views/EntryPage.py index abc18d58..693490c9 100644 --- a/members/views/EntryPage.py +++ b/members/views/EntryPage.py @@ -1,105 +1,7 @@ -from django.urls import reverse from django.views.decorators.clickjacking import xframe_options_exempt -from members.forms import signupForm from django.shortcuts import render -from django.utils import timezone -from django.http import HttpResponseRedirect -from django.contrib.auth.models import User -from members.models.family import Family -from members.models.person import Person @xframe_options_exempt def EntryPage(request): - if request.method == "POST": - # figure out which form was filled out. - if request.POST["form_id"] == "signup": - # signup has been filled - signup = signupForm(request.POST) - if signup.is_valid(): - # check if family already exists - # TODO: rewrite this! >>>> - try: - family = Family.objects.get( - email__iexact=request.POST["parent_email"] - ) - # family was already created - we can't create this family again - signup.add_error( - "parent_email", - "Denne email adresse er allerede oprettet. Du kan tilføje flere børn på samme forælder, når du er kommet videre! - Log ind ovenfor, for at få adgang.", - ) - return render( - request, "members/entry_page.html", {"signupform": signup} - ) - except Exception: - # all is fine - we did not expect any - pass - # TODO: rewrite this! <<<< - # create new family. - family = Family.objects.create( - email=signup.cleaned_data["parent_email"] - ) - family.confirmed_dtm = timezone.now() - family.save() - - # create parent as user - user = User.objects.create_user( - username=signup.cleaned_data["parent_email"], - email=signup.cleaned_data["parent_email"], - ) - password = User.objects.make_random_password() - user.set_password(password) - user.save() - - # create parent - parent = Person.objects.create( - membertype=Person.PARENT, - name=signup.cleaned_data["parent_name"], - zipcode=signup.cleaned_data["zipcode"], - city=signup.cleaned_data["city"], - streetname=signup.cleaned_data["streetname"], - housenumber=signup.cleaned_data["housenumber"], - floor=signup.cleaned_data["floor"], - door=signup.cleaned_data["door"], - dawa_id=signup.cleaned_data["dawa_id"], - placename=signup.cleaned_data["placename"], - email=signup.cleaned_data["parent_email"], - phone=signup.cleaned_data["parent_phone"], - birthday=signup.cleaned_data["parent_birthday"], - gender=signup.cleaned_data["parent_gender"], - family=family, - user=user, - ) - parent.save() - - # create child - child = Person.objects.create( - membertype=Person.CHILD, - name=signup.cleaned_data["child_name"], - zipcode=signup.cleaned_data["zipcode"], - city=signup.cleaned_data["city"], - streetname=signup.cleaned_data["streetname"], - housenumber=signup.cleaned_data["housenumber"], - floor=signup.cleaned_data["floor"], - door=signup.cleaned_data["door"], - dawa_id=signup.cleaned_data["dawa_id"], - placename=signup.cleaned_data["placename"], - email=signup.cleaned_data["child_email"], - phone=signup.cleaned_data["child_phone"], - birthday=signup.cleaned_data["child_birthday"], - gender=signup.cleaned_data["child_gender"], - family=family, - ) - child.save() - - # redirect to success - request.session["password"] = password - return HttpResponseRedirect(reverse("user_created")) - else: - return render( - request, "members/entry_page.html", {"signupform": signup} - ) - - # initial load (if we did not return above) - signup = signupForm() - return render(request, "members/entry_page.html", {"signupform": signup}) + return render(request, "members/entry_page.html") diff --git a/members/views/__init__.py b/members/views/__init__.py index 1e1443e0..33d1cd11 100644 --- a/members/views/__init__.py +++ b/members/views/__init__.py @@ -1,20 +1,21 @@ # flake8: noqa # ignored since it is being used in the files -from members.views.FamilyDetails import FamilyDetails +from members.views.AccountCreate import AccountCreate +from members.views.Activities import Activities +from members.views.ActivitySignup import ActivitySignup +from members.views.AdminSignup import AdminSignup from members.views.ConfirmFamily import ConfirmFamily +from members.views.DeclineInvitation import DeclineInvitation +from members.views.DepartmentSignView import DepartmentSignView +from members.views.EntryPage import EntryPage +from members.views.FamilyDetails import FamilyDetails +from members.views.Membership import Membership from members.views.PersonCreate import PersonCreate from members.views.PersonUpdate import PersonUpdate -from members.views.WaitingListSetSubscription import WaitingListSetSubscription -from members.views.DeclineInvitation import DeclineInvitation -from members.views.ActivitySignup import ActivitySignup +from members.views.QuickpayCallback import QuickpayCallback +from members.views.SupportMembership import SupportMembership from members.views.UpdatePersonFromForm import UpdatePersonFromForm -from members.views.EntryPage import EntryPage +from members.views.WaitingListSetSubscription import WaitingListSetSubscription +from members.views.departmentView import departmentView +from members.views.paymentGatewayErrorView import paymentGatewayErrorView from members.views.userCreated import userCreated from members.views.volunteerSignup import volunteerSignup -from members.views.QuickpayCallback import QuickpayCallback -from members.views.DepartmentSignView import DepartmentSignView -from members.views.paymentGatewayErrorView import paymentGatewayErrorView -from members.views.departmentView import departmentView -from members.views.Activities import Activities -from members.views.Membership import Membership -from members.views.SupportMembership import SupportMembership -from members.views.AdminSignup import AdminSignup diff --git a/package-lock.json b/package-lock.json index 4ab8f8ef..2a61bc8d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,6 +5,7 @@ "requires": true, "packages": { "": { + "name": "forenings_medlemmer", "version": "1.0.0", "license": "ISC", "dependencies": { @@ -13,7 +14,6 @@ }, "node_modules/anymatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dependencies": { "normalize-path": "^3.0.0", @@ -25,7 +25,6 @@ }, "node_modules/binary-extensions": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "engines": { "node": ">=8" @@ -33,7 +32,6 @@ }, "node_modules/braces": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dependencies": { "fill-range": "^7.0.1" @@ -43,28 +41,26 @@ } }, "node_modules/chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "version": "3.5.2", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", "dependencies": { - "anymatch": "~3.1.1", + "anymatch": "~3.1.2", "braces": "~3.0.2", - "glob-parent": "~5.1.0", + "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" + "readdirp": "~3.6.0" }, "engines": { "node": ">= 8.10.0" }, "optionalDependencies": { - "fsevents": "~2.3.1" + "fsevents": "~2.3.2" } }, "node_modules/fill-range": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dependencies": { "to-regex-range": "^5.0.1" @@ -73,22 +69,8 @@ "node": ">=8" } }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/glob-parent": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dependencies": { "is-glob": "^4.0.1" @@ -99,7 +81,6 @@ }, "node_modules/is-binary-path": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dependencies": { "binary-extensions": "^2.0.0" @@ -110,7 +91,6 @@ }, "node_modules/is-extglob": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "engines": { "node": ">=0.10.0" @@ -118,7 +98,6 @@ }, "node_modules/is-glob": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", "dependencies": { "is-extglob": "^2.1.1" @@ -129,7 +108,6 @@ }, "node_modules/is-number": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "engines": { "node": ">=0.12.0" @@ -137,16 +115,14 @@ }, "node_modules/normalize-path": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "engines": { "node": ">=0.10.0" } }, "node_modules/picomatch": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", - "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==", + "version": "2.3.0", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", "engines": { "node": ">=8.6" }, @@ -155,9 +131,8 @@ } }, "node_modules/readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "version": "3.6.0", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dependencies": { "picomatch": "^2.2.1" }, @@ -166,9 +141,8 @@ } }, "node_modules/sass": { - "version": "1.32.12", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.32.12.tgz", - "integrity": "sha512-zmXn03k3hN0KaiVTjohgkg98C3UowhL1/VSGdj4/VAAiMKGQOE80PFPxFP2Kyq0OUskPKcY5lImkhBKEHlypJA==", + "version": "1.38.2", + "integrity": "sha512-Bz1fG6qiyF0FX6m/I+VxtdVKz1Dfmg/e9kfDy2PhWOkq3T384q2KxwIfP0fXpeI+EyyETdOauH+cRHQDFASllA==", "dependencies": { "chokidar": ">=3.0.0 <4.0.0" }, @@ -181,7 +155,6 @@ }, "node_modules/to-regex-range": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dependencies": { "is-number": "^7.0.0" @@ -194,119 +167,83 @@ "dependencies": { "anymatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "requires": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" + "version": "2.2.0" }, "braces": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "requires": { "fill-range": "^7.0.1" } }, "chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "version": "3.5.2", "requires": { - "anymatch": "~3.1.1", + "anymatch": "~3.1.2", "braces": "~3.0.2", - "fsevents": "~2.3.1", - "glob-parent": "~5.1.0", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" + "readdirp": "~3.6.0" } }, "fill-range": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "requires": { "to-regex-range": "^5.0.1" } }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "optional": true - }, "glob-parent": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "requires": { "is-glob": "^4.0.1" } }, "is-binary-path": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "requires": { "binary-extensions": "^2.0.0" } }, "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + "version": "2.1.1" }, "is-glob": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", "requires": { "is-extglob": "^2.1.1" } }, "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + "version": "7.0.0" }, "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + "version": "3.0.0" }, "picomatch": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", - "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==" + "version": "2.3.0" }, "readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "version": "3.6.0", "requires": { "picomatch": "^2.2.1" } }, "sass": { - "version": "1.32.12", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.32.12.tgz", - "integrity": "sha512-zmXn03k3hN0KaiVTjohgkg98C3UowhL1/VSGdj4/VAAiMKGQOE80PFPxFP2Kyq0OUskPKcY5lImkhBKEHlypJA==", + "version": "1.38.2", "requires": { "chokidar": ">=3.0.0 <4.0.0" } }, "to-regex-range": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "requires": { "is-number": "^7.0.0" }