Merge pull request #44 from makes-hacks-hip/26-orga-create-cafe

26 orga create cafe
This commit is contained in:
Thomas Irgang 2023-05-18 09:19:34 +02:00 committed by GitHub
commit 068c626a87
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 433 additions and 393 deletions

View file

@ -18,7 +18,10 @@
"Keycloak",
"tabindex",
"onetimelogin",
"Ammeldelink"
"Ammeldelink",
"orga",
"repa",
"organisators"
],
"ignoreWords": [],
"import": []

View file

@ -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/"

View file

@ -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"
}
},
{

View file

@ -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 = '<!-- %s -->' % value
value = f'<!-- {value} -->'
return value

View file

@ -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)

View file

@ -33,7 +33,7 @@
<a href="https://www.repaircafe-hilpoltstein.de/" class="nav-link">Repair-Café</a>
</li>
<li class="nav-item">
<a href="{% url 'index' %}" class="nav-link">Anmeldung</a>
<a href="{% url 'index' %}" class="nav-link">Geräteanmeldung</a>
</li>
{% if user.is_authenticated %}
<li class="nav-item">
@ -41,12 +41,12 @@
</li>
{% else %}
<li class="nav-item">
<a href="{% url 'login' %}" class="nav-link">Gast Anmeldung</a>
</li>
<li class="nav-item">
<a href="{% url 'member_login' %}" class="nav-link">Mitglieder Anmeldung</a>
<a href="{% url 'login' %}" class="nav-link">Gast Login</a>
</li>
{% endif %}
<li class="nav-item">
<a href="{% url 'member' %}" class="nav-link">Mitglieder</a>
</li>
</ul>
</header>
</div>

View file

@ -45,7 +45,7 @@
<a href="{% url 'login' %}" class="nav-link">Gast Anmeldung</a>
</li>
<li class="nav-item">
<a href="{% url 'member_login' %}" class="nav-link">Mitglieder Anmeldung</a>
<a href="{% url 'member' %}" class="nav-link">Mitglieder</a>
</li>
{% endif %}
</ul>

View file

@ -2,326 +2,35 @@
{% block title %}
- Repair-Cafés
{% endblock title %}
{% block head %}
<style>
@font-face {
font-family:'Source Sans Pro';
font-style:normal;
font-weight:300;
src:url('https://www.repaircafe-hilpoltstein.de/fileadmin/repaircafe-hilpoltstein/default/templates/fonts/source-sans-pro-v11-latin-300.eot');
/* IE9 Compat Modes */
src:local('Source Sans Pro Light'), local('SourceSansPro-Light'),
url('https://www.repaircafe-hilpoltstein.de/fileadmin/repaircafe-hilpoltstein/default/templates/fonts/source-sans-pro-v11-latin-300.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('https://www.repaircafe-hilpoltstein.de/fileadmin/repaircafe-hilpoltstein/default/templates/fonts/source-sans-pro-v11-latin-300.woff2') format('woff2'), /* Super Modern Browsers */
url('https://www.repaircafe-hilpoltstein.de/fileadmin/repaircafe-hilpoltstein/default/templates/fonts/source-sans-pro-v11-latin-300.woff') format('woff'), /* Modern Browsers */
url('https://www.repaircafe-hilpoltstein.de/fileadmin/repaircafe-hilpoltstein/default/templates/fonts/source-sans-pro-v11-latin-300.ttf') format('truetype'), /* Safari, Android, iOS */
url('https://www.repaircafe-hilpoltstein.de/fileadmin/repaircafe-hilpoltstein/default/templates/fonts/source-sans-pro-v11-latin-300.svg#SourceSansPro') format('svg');/* Legacy iOS */;
}
@font-face {
font-family:'Source Sans Pro';
font-style:normal;
font-weight:400;
src:url('https://www.repaircafe-hilpoltstein.de/fileadmin/repaircafe-hilpoltstein/default/templates/fonts/source-sans-pro-v11-latin-regular.eot');
/* IE9 Compat Modes */
src:local('Source Sans Pro Regular'), local('SourceSansPro-Regular'),
url('https://www.repaircafe-hilpoltstein.de/fileadmin/repaircafe-hilpoltstein/default/templates/fonts/source-sans-pro-v11-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('https://www.repaircafe-hilpoltstein.de/fileadmin/repaircafe-hilpoltstein/default/templates/fonts/source-sans-pro-v11-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */
url('https://www.repaircafe-hilpoltstein.de/fileadmin/repaircafe-hilpoltstein/default/templates/fonts/source-sans-pro-v11-latin-regular.woff') format('woff'), /* Modern Browsers */
url('https://www.repaircafe-hilpoltstein.de/fileadmin/repaircafe-hilpoltstein/default/templates/fonts/source-sans-pro-v11-latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */
url('https://www.repaircafe-hilpoltstein.de/fileadmin/repaircafe-hilpoltstein/default/templates/fonts/source-sans-pro-v11-latin-regular.svg#SourceSansPro') format('svg');/* Legacy iOS */;
}
@font-face {
font-family:'Source Sans Pro';
font-style:normal;
font-weight:700;
src:url('https://www.repaircafe-hilpoltstein.de/fileadmin/repaircafe-hilpoltstein/default/templates/fonts/source-sans-pro-v11-latin-700.eot');
/* IE9 Compat Modes */
src:local('Source Sans Pro Bold'), local('SourceSansPro-Bold'),
url('https://www.repaircafe-hilpoltstein.de/fileadmin/repaircafe-hilpoltstein/default/templates/fonts/source-sans-pro-v11-latin-700.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('https://www.repaircafe-hilpoltstein.de/fileadmin/repaircafe-hilpoltstein/default/templates/fonts/source-sans-pro-v11-latin-700.woff2') format('woff2'), /* Super Modern Browsers */
url('https://www.repaircafe-hilpoltstein.de/fileadmin/repaircafe-hilpoltstein/default/templates/fonts/source-sans-pro-v11-latin-700.woff') format('woff'), /* Modern Browsers */
url('https://www.repaircafe-hilpoltstein.de/fileadmin/repaircafe-hilpoltstein/default/templates/fonts/source-sans-pro-v11-latin-700.ttf') format('truetype'), /* Safari, Android, iOS */
url('https://www.repaircafe-hilpoltstein.de/fileadmin/repaircafe-hilpoltstein/default/templates/fonts/source-sans-pro-v11-latin-700.svg#SourceSansPro') format('svg');/* Legacy iOS */;
}
@media all{
* {
box-sizing: border-box;
}
.frame > :last-child {
margin-bottom: 0;
}
.frame {
position: relative;
padding-top: 2rem;
padding-bottom: 2rem;
}
}
@media (min-width: 576px){
.frame {
padding-top: 2.5rem;
padding-bottom: 2.5rem;
}
}
@media (min-width: 768px){
.frame {
padding-top: 3rem;
padding-bottom: 3rem;
}
}
@media (min-width: 992px){
.frame {
padding-top: 3.5rem;
padding-bottom: 3.5rem;
}
}
@media (min-width: 1200px){
.frame {
padding-top: 3.5rem;
padding-bottom: 3.5rem;
}
}
@media all{
.frame-space-before-none {
margin-top: 0rem;
}
.frame-space-after-none {
margin-bottom: 0rem;
}
}
@media screen and (min-width: 1200px){
.frame {
padding-top: 2.5rem;
padding-bottom: 2.5rem;
}
}
@media all{
.col-md-8 {
position: relative;
width: 100%;
padding-right: 20px;
padding-left: 20px;
}
}
@media (min-width: 768px){
.col-md-8 {
flex: 0 0 66.6666666667%;
max-width: 66.6666666667%;
}
}
@media screen and (min-width: 992px){
.col-md-8 {
padding-right: 40px;
}
}
@media all{
.row {
display: flex;
flex-wrap: wrap;
margin-right: -20px;
margin-left: -20px;
}
.container {
width: 100%;
padding-right: 20px;
padding-left: 20px;
margin-right: auto;
margin-left: auto;
}
}
@media (min-width: 576px){
.container {
max-width: 540px;
}
}
@media (min-width: 768px){
.container {
max-width: 720px;
}
}
@media (min-width: 992px){
.container {
max-width: 960px;
}
}
@media (min-width: 1200px){
.container {
max-width: 1140px;
}
}
@media all{
.section {
overflow: hidden;
}
}
@media screen and (min-width: 1200px){
.section {
padding-top: 30px;
}
}
@media all{
body {
margin: 0;
font-family: "Source Sans Pro", sans-serif;
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #2b2b2b;
text-align: left;
background-color: #fff;
}
html {
font-family: sans-serif;
line-height: 1.15;
-webkit-text-size-adjust: 100%;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
.news-list-view {
padding-right: 20px;
padding-top: 10px;
}
*,:before,:after {
box-sizing: border-box;
}
:selection {
background-color: darkred;
}
.article {
padding: 30px 0;
border-bottom: 2px solid #cccccc;
}
.article:last-child {
border-bottom: 2px solid transparent;
}
.article.event {
padding: 0 0 30px 0;
}
.dtdate {
font-size: 21px;
color: #FFFFFF;
background: darkred;
padding: 0 5px;
display: inline-block;
}
.article.event .ort {
margin-top: 5px;
font-size: 19px;
}
.teaser-text {
position: relative;
padding-bottom: 30px;
width: 100%;
}
.article.event .teaser-text {
padding-top: 20px;
padding-bottom: 10px;
}
p {
margin-top: 0;
margin-bottom: 1rem;
}
strong {
font-weight: bolder;
}
a {
color: darkred;
text-decoration: none;
background-color: transparent;
}
a {
color: darkblue;
border-bottom: 1px solid;
font-style: normal;
}
a:hover {
color: #FFFFFF;
text-decoration: underline;
background: darkred;
}
a:hover {
background: darkblue;
color: #ffffff!important;
}
}
</style>
{% endblock head %}
{% block page_title %}
Anmeldung
{% endblock page_title %}
{% block content %}
<h1 class="mt-2">Repair-Café auswählen</h1>
<hr class="mt-0 mb-4">
<ul class="list-group">
{% for cafe in object_list %}
<li class="list-group-item">
<div class="news-list-view eventhome snipcss-aSNzE" id="news-container-82">
<div class="rc-date">
<div class="article event rc-date "
itemscope="itemscope"
itemtype="http://schema.org/Article">
<div class="dtdate">
<time itemprop="datePublished" datetime="13.05.2023">
{{ cafe.event_date|date:"l" }}, {{ cafe.event_date|date }}, 14.00 - 17.00 Uhr
</time>
</div>
<div class="ort">
{{ cafe.location }}
<br>
{{ cafe.address }}
</div>
<div class="teaser-text">
<div itemprop="description">
<p>
<strong>
Nur per Voranmeldung.
<a class="register_link" href="{% url 'register_device' cafe.id %}">Zum Anmeldeformular</a>
</strong>
</p>
</div>
</div>
</div>
</div>
</div>
</li>
{% empty %}
<li class="list-group-item">Es gibt keine geplanten Repair-Cafés.</li>
{% endfor %}
</ul>
<table class="table">
<thead>
<tr>
<th scope="col">Datum</th>
<th scope="col">Ort</th>
<th scope="col">Adresse</th>
<th scope="col">Gerät anmelden</th>
</tr>
</thead>
<tbody>
{% for cafe in object_list %}
<tr>
<td>{{ cafe.event_date|date:"l" }}, {{ cafe.event_date|date }}</td>
<td>{{ cafe.location }}</td>
<td>{{ cafe.address }}</td>
<td>
<a href="{% url 'register_device' cafe.pk %}"
class="btn btn-primary register_link">Gerät anmelden</a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="4">Es sind keine Repair-Cafés angelegt.</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock content %}

View file

@ -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 %}
<div>
{% if user.is_authenticated %}
<p>Angemeldeter Benutzer: {{ user.email }}</p>
<form action="{% url 'oidc_logout' %}" method="post">
{% csrf_token %}
<button type="submit" class="btn btn-primary">Abmelden</button>
</form>
{% else %}
<a class="login_link" href="{% url 'oidc_authentication_init' %}">Anmelden mit Makes-Hacks-Hip</a>
{% endif %}
</div>
{% endblock content %}

View file

@ -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 %}
<p>
<a href="{% url 'create_cafe' %}" class="btn btn-primary">Neues Repair-Cafés anlegen</a>
<a href="javascript:history.back()" class="btn btn-primary">Zurück</a>
</p>
<table class="table">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Datum</th>
<th scope="col">Ort</th>
<th scope="col">Adresse</th>
<th scope="col">Bearbeiten</th>
<th scope="col">Löschen</th>
</tr>
</thead>
<tbody>
{% for cafe in object_list %}
<tr>
<th scope="row">{{ cafe.pk }}</th>
<td>{{ cafe.event_date|date:"l" }}, {{ cafe.event_date|date }}</td>
<td>{{ cafe.location }}</td>
<td>{{ cafe.address }}</td>
<td>
<a href="{% url 'edit_cafe' cafe.pk %}" class="btn btn-primary">Bearbeiten</a>
</td>
<td>
<a href="{% url 'delete_cafe' cafe.pk %}" class="btn btn-danger">Löschen</a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="4">Es sind keine Repair-Cafés angelegt.</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock content %}

View file

@ -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 %}
<form action="{% url 'create_cafe' %}" method="post">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-primary submit_button">Absenden</button>
</form>
{% endblock content %}

View file

@ -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 %}
<form action="{% url 'delete_cafe' cafe.id %}" method="post">
{% csrf_token %}
<p>Soll das Repair-Café am {{ cafe.event_date|date:"l" }}, {{ cafe.event_date|date }} wirklich gelöscht werden?</p>
{{ form|crispy }}
<button type="submit" class="btn btn-primary submit_button">Löschen</button>
</form>
{% endblock content %}

View file

@ -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 %}
<form action="{% url 'edit_cafe' cafe.id %}" method="post">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-primary submit_button">Aktualisieren</button>
</form>
{% endblock content %}

View file

@ -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 %}
<form action="{% url 'edit_guest' guest.id %}" method="post">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-primary submit_button">Aktualisieren</button>
</form>
{% endblock content %}

View file

@ -0,0 +1,44 @@
{% extends "repapp/base.html" %}
{% block title %}
- Gäste
{% endblock title %}
{% block page_title %}
Gäste
{% endblock page_title %}
{% block content %}
<p>
<a href="{% url 'create_guest' %}" class="btn btn-primary">Neuen Gast anlegen</a>
<a href="javascript:history.back()" class="btn btn-primary">Zurück</a>
</p>
<table class="table">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Name</th>
<th scope="col">Telefon</th>
<th scope="col">Wohnort</th>
<th scope="col">eMail Adresse</th>
<th scope="col">Bearbeiten</th>
<th scope="col">Löschen</th>
</tr>
</thead>
<tbody>
{% for guest in object_list %}
<tr>
<th scope="row">{{ guest.pk }}</th>
<td>{{ guest.name }}</td>
<td>{{ guest.phone }}</td>
<td>{{ guest.residence }}</td>
<td>{{ guest.mail }}</td>
<td>
<a href="{% url 'edit_guest' guest.pk %}" class="btn btn-primary">Bearbeiten</a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="4">Es sind keine Gäste verfügbar.</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock content %}

View file

@ -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 %}
<p>
<a href="{% url 'cafe' %}" class="btn btn-primary">Repair-Cafés</a>
<a href="{% url 'guest' %}" class="btn btn-primary">Gäste</a>
</p>
<hr class="mt-0 mb-4">
<h1 class="mt-2">Repair-Cafés</h1>
<a href="{% url 'create_cafe' %}" class="btn btn-primary">Neues Repair-Café anlegen</a>
// TODO: Button nächstes Repair-Café
<hr class="mt-0 mb-4">
<h1 class="mt-2">Geräte</h1>
// TODO: Liste mit neuen Geräten
<hr class="mt-0 mb-4">
<h1 class="mt-2">Rückfragen</h1>
// TODO: Liste mit Antworten auf Rückfragen
<hr class="mt-0 mb-4">
<h1 class="mt-2">Nachrichten</h1>
// TODO: Liste mit neuen Nachrichten
{% endblock content %}

View file

@ -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 %}
<p>
<a href="{% url 'orga' %}" class="btn btn-primary">Weiter als Organisator</a>
</p>
<p>
<a href="{% url 'repa' %}" class="btn btn-primary">Weiter als Reparateur</a>
</p>
{% endblock content %}

View file

@ -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 %}

View file

@ -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.

View file

@ -24,8 +24,17 @@ urlpatterns = [
views.profile, name="guest_profile"),
path("onetimelogin/<str:secret>/",
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/<int:pk>/edit", views.CafeUpdateView.as_view(), name="edit_cafe"),
path("cafe/<int:pk>/delete",
views.CafeDeleteView.as_view(), name="delete_cafe"),
path("guest/", views.GuestView.as_view(), name="guest"),
path("guest/<int:pk>/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"),

View file

@ -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)