diff --git a/cspell.json b/cspell.json
index 3987eac..4b33dee 100644
--- a/cspell.json
+++ b/cspell.json
@@ -18,7 +18,10 @@
"Keycloak",
"tabindex",
"onetimelogin",
- "Ammeldelink"
+ "Ammeldelink",
+ "orga",
+ "repa",
+ "organisators"
],
"ignoreWords": [],
"import": []
diff --git a/rc_hip/rc_hip/settings.py b/rc_hip/rc_hip/settings.py
index 81459fe..78d64cf 100644
--- a/rc_hip/rc_hip/settings.py
+++ b/rc_hip/rc_hip/settings.py
@@ -179,10 +179,10 @@ OIDC_OP_TOKEN_ENDPOINT = "https://sso.makes-hacks-hip.de/realms/Makes-Hacks-Hip/
OIDC_OP_USER_ENDPOINT = "https://sso.makes-hacks-hip.de/realms/Makes-Hacks-Hip/protocol/openid-connect/userinfo"
if DEBUG:
- LOGIN_REDIRECT_URL = "http://127.0.0.1:8000/"
+ LOGIN_REDIRECT_URL = "http://127.0.0.1:8000/member"
LOGOUT_REDIRECT_URL = "http://127.0.0.1:8000/"
else:
- LOGIN_REDIRECT_URL = "https://anmeldung.repaircafe-hilpoltstein.de/"
+ LOGIN_REDIRECT_URL = "https://anmeldung.repaircafe-hilpoltstein.de/member"
LOGOUT_REDIRECT_URL = "https://anmeldung.repaircafe-hilpoltstein.de/"
diff --git a/rc_hip/repapp/fixtures/cafe-data.json b/rc_hip/repapp/fixtures/cafe-data.json
index da8e763..59ff18a 100644
--- a/rc_hip/repapp/fixtures/cafe-data.json
+++ b/rc_hip/repapp/fixtures/cafe-data.json
@@ -5,7 +5,7 @@
"fields": {
"location": "Haus Einstein' (beim Gymnasium)",
"address": "Patersholzer Weg 19, Hilpoltstein",
- "event_date": "2023-05-13"
+ "event_date": "2024-05-13"
}
},
{
diff --git a/rc_hip/repapp/forms.py b/rc_hip/repapp/forms.py
index 6c01cfa..cc8650a 100644
--- a/rc_hip/repapp/forms.py
+++ b/rc_hip/repapp/forms.py
@@ -19,13 +19,13 @@ class HoneypotWidget(forms.TextInput):
def __init__(self, attrs=None, html_comment=False, *args, **kwargs):
self.html_comment = html_comment
super(HoneypotWidget, self).__init__(attrs, *args, **kwargs)
- if not 'class' in self.attrs:
+ if 'class' not in self.attrs:
self.attrs['style'] = 'display:none'
def render(self, *args, **kwargs):
value = super(HoneypotWidget, self).render(*args, **kwargs)
if self.html_comment:
- value = '' % value
+ value = f''
return value
diff --git a/rc_hip/repapp/live_tests.py b/rc_hip/repapp/live_tests.py
index 4ed713f..f030013 100644
--- a/rc_hip/repapp/live_tests.py
+++ b/rc_hip/repapp/live_tests.py
@@ -5,7 +5,6 @@ import random
from pathlib import Path
from hashlib import sha256
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
-from django.contrib.auth import get_user_model
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.webdriver import WebDriver
from selenium.webdriver import ActionChains, Keys
@@ -202,11 +201,3 @@ class WorkflowTests(StaticLiveServerTestCase):
self.assertTrue("Test Hersteller" in self.selenium.page_source)
self.assertTrue("/device/" in self.selenium.current_url)
time.sleep(1)
-
- def test_member_login(self):
- self.selenium.get(
- f"{self.live_server_url}/member/login/")
- time.sleep(1)
- self.selenium.find_element(By.CLASS_NAME, 'login_link').click()
- time.sleep(1)
- self.assertTrue('sso.makes-hacks-hip.de' in self.selenium.current_url)
diff --git a/rc_hip/repapp/templates/repapp/base.html b/rc_hip/repapp/templates/repapp/base.html
index b9df74a..91436ab 100644
--- a/rc_hip/repapp/templates/repapp/base.html
+++ b/rc_hip/repapp/templates/repapp/base.html
@@ -33,7 +33,7 @@
Repair-Café
- Anmeldung
+ Geräteanmeldung
{% if user.is_authenticated %}
@@ -41,12 +41,12 @@
{% else %}
- Gast Anmeldung
-
-
- Mitglieder Anmeldung
+ Gast Login
{% endif %}
+
+ Mitglieder
+
diff --git a/rc_hip/repapp/templates/repapp/bootstrap.html b/rc_hip/repapp/templates/repapp/bootstrap.html
index 1777fbc..192d367 100644
--- a/rc_hip/repapp/templates/repapp/bootstrap.html
+++ b/rc_hip/repapp/templates/repapp/bootstrap.html
@@ -45,7 +45,7 @@
Gast Anmeldung
- Mitglieder Anmeldung
+ Mitglieder
{% endif %}
diff --git a/rc_hip/repapp/templates/repapp/index.html b/rc_hip/repapp/templates/repapp/index.html
index 8421850..b1bfb2c 100644
--- a/rc_hip/repapp/templates/repapp/index.html
+++ b/rc_hip/repapp/templates/repapp/index.html
@@ -2,326 +2,35 @@
{% block title %}
- Repair-Cafés
{% endblock title %}
-{% block head %}
-
-{% endblock head %}
{% block page_title %}
Anmeldung
{% endblock page_title %}
{% block content %}
- Repair-Café auswählen
-
-
- {% for cafe in object_list %}
- -
-
-
-
-
-
-
-
- {{ cafe.location }}
-
- {{ cafe.address }}
-
-
-
-
-
-
- {% empty %}
- - Es gibt keine geplanten Repair-Cafés.
- {% endfor %}
-
+
+
+
+ | Datum |
+ Ort |
+ Adresse |
+ Gerät anmelden |
+
+
+
+ {% for cafe in object_list %}
+
+ | {{ cafe.event_date|date:"l" }}, {{ cafe.event_date|date }} |
+ {{ cafe.location }} |
+ {{ cafe.address }} |
+
+ Gerät anmelden
+ |
+
+ {% empty %}
+
+ | Es sind keine Repair-Cafés angelegt. |
+
+ {% endfor %}
+
+
{% endblock content %}
diff --git a/rc_hip/repapp/templates/repapp/member_login.html b/rc_hip/repapp/templates/repapp/member_login.html
deleted file mode 100644
index a3a4585..0000000
--- a/rc_hip/repapp/templates/repapp/member_login.html
+++ /dev/null
@@ -1,25 +0,0 @@
-{% extends "repapp/base.html" %}
-{% block title %}
- Mitarbeiter Anmeldung
-{% endblock title %}
-{% block page_title %}
- Mitarbeiter
- {% if user.is_authenticated %}
- Anmelden
- {% else %}
- Abmelden
- {% endif %}
-{% endblock page_title %}
-{% block content %}
-
-{% endblock content %}
diff --git a/rc_hip/repapp/templates/repapp/orga/cafe.html b/rc_hip/repapp/templates/repapp/orga/cafe.html
new file mode 100644
index 0000000..0db2d30
--- /dev/null
+++ b/rc_hip/repapp/templates/repapp/orga/cafe.html
@@ -0,0 +1,45 @@
+{% extends "repapp/base.html" %}
+{% block title %}
+ - Repair-Cafés
+{% endblock title %}
+{% block page_title %}
+ Repair-Cafés
+{% endblock page_title %}
+{% block content %}
+
+ Neues Repair-Cafés anlegen
+ Zurück
+
+
+
+
+ | ID |
+ Datum |
+ Ort |
+ Adresse |
+ Bearbeiten |
+ Löschen |
+
+
+
+ {% for cafe in object_list %}
+
+ | {{ cafe.pk }} |
+ {{ cafe.event_date|date:"l" }}, {{ cafe.event_date|date }} |
+ {{ cafe.location }} |
+ {{ cafe.address }} |
+
+ Bearbeiten
+ |
+
+ Löschen
+ |
+
+ {% empty %}
+
+ | Es sind keine Repair-Cafés angelegt. |
+
+ {% endfor %}
+
+
+{% endblock content %}
diff --git a/rc_hip/repapp/templates/repapp/orga/create_cafe.html b/rc_hip/repapp/templates/repapp/orga/create_cafe.html
new file mode 100644
index 0000000..738637c
--- /dev/null
+++ b/rc_hip/repapp/templates/repapp/orga/create_cafe.html
@@ -0,0 +1,15 @@
+{% extends "repapp/base.html" %}
+{% load crispy_forms_tags %}
+{% block title %}
+ - Repair-Café anlegen
+{% endblock title %}
+{% block page_title %}
+ Repair-Café anlegen
+{% endblock page_title %}
+{% block content %}
+
+{% endblock content %}
diff --git a/rc_hip/repapp/templates/repapp/orga/delete_cafe.html b/rc_hip/repapp/templates/repapp/orga/delete_cafe.html
new file mode 100644
index 0000000..3617efd
--- /dev/null
+++ b/rc_hip/repapp/templates/repapp/orga/delete_cafe.html
@@ -0,0 +1,16 @@
+{% extends "repapp/base.html" %}
+{% load crispy_forms_tags %}
+{% block title %}
+ - Repair-Café anlegen
+{% endblock title %}
+{% block page_title %}
+ Repair-Café anlegen
+{% endblock page_title %}
+{% block content %}
+
+{% endblock content %}
diff --git a/rc_hip/repapp/templates/repapp/orga/edit_cafe.html b/rc_hip/repapp/templates/repapp/orga/edit_cafe.html
new file mode 100644
index 0000000..b7da85f
--- /dev/null
+++ b/rc_hip/repapp/templates/repapp/orga/edit_cafe.html
@@ -0,0 +1,15 @@
+{% extends "repapp/base.html" %}
+{% load crispy_forms_tags %}
+{% block title %}
+ - Repair-Café anlegen
+{% endblock title %}
+{% block page_title %}
+ Repair-Café anlegen
+{% endblock page_title %}
+{% block content %}
+
+{% endblock content %}
diff --git a/rc_hip/repapp/templates/repapp/orga/edit_guest.html b/rc_hip/repapp/templates/repapp/orga/edit_guest.html
new file mode 100644
index 0000000..3b3edc1
--- /dev/null
+++ b/rc_hip/repapp/templates/repapp/orga/edit_guest.html
@@ -0,0 +1,15 @@
+{% extends "repapp/base.html" %}
+{% load crispy_forms_tags %}
+{% block title %}
+ - Repair-Café anlegen
+{% endblock title %}
+{% block page_title %}
+ Repair-Café anlegen
+{% endblock page_title %}
+{% block content %}
+
+{% endblock content %}
diff --git a/rc_hip/repapp/templates/repapp/orga/guest.html b/rc_hip/repapp/templates/repapp/orga/guest.html
new file mode 100644
index 0000000..a587a6b
--- /dev/null
+++ b/rc_hip/repapp/templates/repapp/orga/guest.html
@@ -0,0 +1,44 @@
+{% extends "repapp/base.html" %}
+{% block title %}
+ - Gäste
+{% endblock title %}
+{% block page_title %}
+ Gäste
+{% endblock page_title %}
+{% block content %}
+
+ Neuen Gast anlegen
+ Zurück
+
+
+
+
+ | ID |
+ Name |
+ Telefon |
+ Wohnort |
+ eMail Adresse |
+ Bearbeiten |
+ Löschen |
+
+
+
+ {% for guest in object_list %}
+
+ | {{ guest.pk }} |
+ {{ guest.name }} |
+ {{ guest.phone }} |
+ {{ guest.residence }} |
+ {{ guest.mail }} |
+
+ Bearbeiten
+ |
+
+ {% empty %}
+
+ | Es sind keine Gäste verfügbar. |
+
+ {% endfor %}
+
+
+{% endblock content %}
diff --git a/rc_hip/repapp/templates/repapp/orga/main.html b/rc_hip/repapp/templates/repapp/orga/main.html
new file mode 100644
index 0000000..788764c
--- /dev/null
+++ b/rc_hip/repapp/templates/repapp/orga/main.html
@@ -0,0 +1,27 @@
+{% extends "repapp/base.html" %}
+{% load crispy_forms_tags %}
+{% block title %}
+ - Organisation
+{% endblock title %}
+{% block page_title %}
+ Organisation
+{% endblock page_title %}
+{% block content %}
+
+ Repair-Cafés
+ Gäste
+
+
+ Repair-Cafés
+ Neues Repair-Café anlegen
+ // TODO: Button nächstes Repair-Café
+
+ Geräte
+ // TODO: Liste mit neuen Geräten
+
+ Rückfragen
+ // TODO: Liste mit Antworten auf Rückfragen
+
+ Nachrichten
+ // TODO: Liste mit neuen Nachrichten
+{% endblock content %}
diff --git a/rc_hip/repapp/templates/repapp/orga/select_role.html b/rc_hip/repapp/templates/repapp/orga/select_role.html
new file mode 100644
index 0000000..2933f68
--- /dev/null
+++ b/rc_hip/repapp/templates/repapp/orga/select_role.html
@@ -0,0 +1,16 @@
+{% extends "repapp/base.html" %}
+{% load crispy_forms_tags %}
+{% block title %}
+ - Organisation
+{% endblock title %}
+{% block page_title %}
+ Organisation
+{% endblock page_title %}
+{% block content %}
+
+ Weiter als Organisator
+
+
+ Weiter als Reparateur
+
+{% endblock content %}
diff --git a/rc_hip/repapp/templates/repapp/repa/main.html b/rc_hip/repapp/templates/repapp/repa/main.html
new file mode 100644
index 0000000..179e8d5
--- /dev/null
+++ b/rc_hip/repapp/templates/repapp/repa/main.html
@@ -0,0 +1,10 @@
+{% extends "repapp/base.html" %}
+{% load crispy_forms_tags %}
+{% block title %}
+ - Reparateur
+{% endblock title %}
+{% block page_title %}
+ Reparateur
+{% endblock page_title %}
+{% block content %}
+{% endblock content %}
diff --git a/rc_hip/repapp/tests.py b/rc_hip/repapp/tests.py
index 3d05dde..7312652 100644
--- a/rc_hip/repapp/tests.py
+++ b/rc_hip/repapp/tests.py
@@ -271,27 +271,6 @@ class ViewsTest(TestCase):
response, 'type="text" name="accept_agb" style="display:none"')
self.assertContains(response, 'csrfmiddlewaretoken')
- def test_member_login(self):
- """
- Test member login view
- """
- client = Client(enforce_csrf_checks=True)
- response = client.get(reverse('member_login'))
-
- self.assertEqual(response.status_code, 200)
-
- # ensure OIDC link is displayed
- self.assertContains(response, "Anmelden mit Makes-Hacks-Hip")
-
- client.login(username="testuser@example.com", password="aTestPassword")
- response = client.get(reverse('member_login'))
- self.assertEqual(response.status_code, 200)
-
- # ensure logout button is displayed
- self.assertContains(response, "Abmelden")
- # ensure user is displayed
- self.assertContains(response, "testuser@example.com")
-
def test_send_one_time_login_mail(self):
"""
Test that a mail is sent with the right link included.
diff --git a/rc_hip/repapp/urls.py b/rc_hip/repapp/urls.py
index b2ee999..ea75c77 100644
--- a/rc_hip/repapp/urls.py
+++ b/rc_hip/repapp/urls.py
@@ -24,8 +24,17 @@ urlpatterns = [
views.profile, name="guest_profile"),
path("onetimelogin//",
views.one_time_login, name="one_time_login"),
- path("member/login/",
- views.member_login, name="member_login"),
+ path("member/", views.member, name="member"),
+ path("orga/select/", views.select_role, name="select_role"),
+ path("orga/", views.orga, name="orga"),
+ path("repa/", views.repa, name="repa"),
+ path("cafe/", views.CafeView.as_view(), name="cafe"),
+ path("cafe/create", views.CafeCreateView.as_view(), name="create_cafe"),
+ path("cafe//edit", views.CafeUpdateView.as_view(), name="edit_cafe"),
+ path("cafe//delete",
+ views.CafeDeleteView.as_view(), name="delete_cafe"),
+ path("guest/", views.GuestView.as_view(), name="guest"),
+ path("guest//edit", views.GuestUpdateView.as_view(), name="edit_guest"),
path("cron", views.cron, name="cron"),
path("bootstrap", views.bootstrap, name="bootstrap"),
path("process_mails", views.process_mails, name="process_mails"),
diff --git a/rc_hip/repapp/views.py b/rc_hip/repapp/views.py
index e3cfcd1..692dcf4 100644
--- a/rc_hip/repapp/views.py
+++ b/rc_hip/repapp/views.py
@@ -6,6 +6,7 @@ import random
import string
import time
import os
+import logging
from hashlib import sha256
from django.template.loader import render_to_string
from django.urls import reverse
@@ -17,6 +18,7 @@ from django.shortcuts import get_object_or_404, render
from django.http import HttpResponseRedirect, HttpResponse
from django.core.exceptions import PermissionDenied
from django.contrib.auth.decorators import login_required
+from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.contrib.auth import authenticate, login
from django.utils.timezone import now
from .forms import RegisterDevice, RegisterGuest
@@ -25,6 +27,9 @@ from .models import (Cafe, Device, Guest, OneTimeLogin,
from . import mail_interface
+logger = logging.getLogger(__name__)
+
+
def send_one_time_login_mail(secret, mail, request):
"""
Send a mail with a one time login link.
@@ -40,7 +45,7 @@ def send_one_time_login_mail(secret, mail, request):
'link': url,
})
- ok = send_mail(
+ send_ok = send_mail(
subject=subject,
message=text,
from_email=os.getenv("DJANGO_SENDER_ADDRESS", ""),
@@ -49,7 +54,7 @@ def send_one_time_login_mail(secret, mail, request):
html_message=html
)
- if ok > 0:
+ if send_ok > 0:
messages.add_message(request, messages.INFO,
'Sie haben einen neuen Login per eMail erhalten.')
@@ -133,7 +138,7 @@ def send_guest_account_mail(guest, password, request):
'password': password,
})
- ok = send_mail(
+ send_ok = send_mail(
subject=subject,
message=text,
from_email=os.getenv("DJANGO_SENDER_ADDRESS", ""),
@@ -142,7 +147,7 @@ def send_guest_account_mail(guest, password, request):
html_message=html
)
- if ok > 0:
+ if send_ok > 0:
messages.add_message(request, messages.INFO,
'Sie haben ihre Benutzerdaten per eMail erhalten.')
@@ -156,6 +161,22 @@ def is_member(user):
return organisator or reparateur
+def is_organisator(user):
+ """
+ Test is a user is a Repair-Café organisator.
+ """
+ organisator = Organisator.objects.filter(mail=user.email).first()
+ return organisator is not None
+
+
+def is_reparateur(user):
+ """
+ Test is a user is a Repair-Café reparateur.
+ """
+ reparateur = Reparateur.objects.filter(mail=user.email).first()
+ return reparateur is not None
+
+
def create_one_time_login(user, url) -> str:
"""
create_one_time_login creates a one time login object for guest user logins.
@@ -397,16 +418,92 @@ def profile(request):
)
-def member_login(request):
+def member(request):
"""
Login page for repair cafe members, using OIDC.
"""
+ # TODO: test
+ logger.debug('Member-View: User: %s', str(request.user))
+ if request.user.is_anonymous or not is_member(request.user):
+ return HttpResponseRedirect(reverse_lazy('oidc_authentication_init'))
+
+ if is_organisator(request.user) and is_reparateur(request.user):
+ return HttpResponseRedirect(reverse_lazy('select_role'))
+
+ if is_organisator(request.user):
+ return HttpResponseRedirect(reverse_lazy('orga'))
+
+ if is_reparateur(request.user):
+ return HttpResponseRedirect(reverse_lazy('repa'))
+
+ logger.warning('User %s is no member!', str(request.user))
+ raise PermissionDenied('No member!')
+
+
+@login_required(login_url=reverse_lazy('member'))
+def orga(request):
+ """
+ Landing page for organisators.
+ """
+ # TODO: test
+ if not is_organisator(request.user):
+ logger.warning('The user %s is no organisator!', str(request.user))
+ raise PermissionDenied('Not organisator!')
+
return render(
request,
- "repapp/member_login.html"
+ "repapp/orga/main.html"
)
+@login_required(login_url=reverse_lazy('member'))
+def select_role(request):
+ """
+ Landing page for organisators.
+ """
+ # TODO: test
+ if not is_organisator(request.user):
+ logger.warning('The user %s is no organisator!', str(request.user))
+ raise PermissionDenied('Not organisator!')
+
+ if not is_reparateur(request.user):
+ logger.warning('The user %s is no reparateur!', str(request.user))
+ raise PermissionDenied('Not reparateur!')
+
+ return render(
+ request,
+ "repapp/orga/select_role.html"
+ )
+
+
+@login_required(login_url=reverse_lazy('member'))
+def repa(request):
+ """
+ Organisator main menu.
+ """
+ # TODO: test
+ if not is_reparateur(request.user):
+ logger.warning('The user %s is no reparateur!', str(request.user))
+ raise PermissionDenied('Not reparateur!')
+
+ return render(
+ request,
+ "repapp/repa/main.html"
+ )
+
+
+class CafeView(LoginRequiredMixin, generic.ListView):
+ """
+ List of all Repair-Cafés.
+ """
+ # TODO: test
+ login_url = reverse_lazy('member')
+ template_name = "repapp/orga/cafe.html"
+
+ def get_queryset(self):
+ return Cafe.objects.all()
+
+
def cron(request):
"""
View to trigger automated regular tasks.
@@ -462,3 +559,77 @@ def bootstrap(request):
request,
"repapp/bootstrap.html"
)
+
+
+class CafeCreateView(LoginRequiredMixin, UserPassesTestMixin, generic.edit.CreateView):
+ """
+ Organisator view for creating a new Repair-Café.
+ """
+ # TODO: create test
+ login_url = reverse_lazy('member')
+ template_name = "repapp/orga/create_cafe.html"
+ model = Cafe
+ fields = ['event_date', 'location', 'address']
+ success_url = reverse_lazy('cafe')
+
+ def test_func(self):
+ return is_organisator(self.request.user)
+
+
+class CafeUpdateView(LoginRequiredMixin, UserPassesTestMixin, generic.edit.UpdateView):
+ """
+ Organisator view for edit a Repair-Café.
+ """
+ # TODO: create test
+ login_url = reverse_lazy('member')
+ template_name = "repapp/orga/edit_cafe.html"
+ model = Cafe
+ fields = ['event_date', 'location', 'address']
+ success_url = reverse_lazy('cafe')
+
+ def test_func(self):
+ logger.debug('User passes test: user: %s, result: %s.',
+ self.request.user, is_organisator(self.request.user))
+ return is_organisator(self.request.user)
+
+
+class CafeDeleteView(LoginRequiredMixin, UserPassesTestMixin, generic.edit.DeleteView):
+ """
+ Organisator view for edit a Repair-Café.
+ """
+ # TODO: create test
+ login_url = reverse_lazy('member')
+ template_name = "repapp/orga/delete_cafe.html"
+ model = Cafe
+ success_url = reverse_lazy('cafe')
+
+ def test_func(self):
+ logger.debug('User passes test: user: %s, result: %s.',
+ self.request.user, is_organisator(self.request.user))
+ return is_organisator(self.request.user)
+
+
+class GuestView(LoginRequiredMixin, generic.ListView):
+ """
+ List of all Repair-Cafés.
+ """
+ # TODO: test
+ login_url = reverse_lazy('member')
+ template_name = "repapp/orga/guest.html"
+
+ def get_queryset(self):
+ return Guest.objects.all()
+
+
+class GuestUpdateView(LoginRequiredMixin, UserPassesTestMixin, generic.edit.UpdateView):
+ """
+ Organisator view for edit a guest.
+ """
+ # TODO: create test
+ login_url = reverse_lazy('member')
+ template_name = "repapp/orga/edit_guest.html"
+ model = Guest
+ success_url = reverse_lazy('guest')
+
+ def test_func(self):
+ return is_organisator(self.request.user)