accept, request or question new device
This commit is contained in:
parent
3aebeed113
commit
e85cee2265
30 changed files with 755 additions and 53 deletions
|
|
@ -53,10 +53,13 @@ INSTALLED_APPS = [
|
||||||
"crispy_forms",
|
"crispy_forms",
|
||||||
"crispy_bootstrap5",
|
"crispy_bootstrap5",
|
||||||
"import_export",
|
"import_export",
|
||||||
|
"ckeditor",
|
||||||
|
"ckeditor_uploader",
|
||||||
]
|
]
|
||||||
|
|
||||||
CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5"
|
CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5"
|
||||||
CRISPY_TEMPLATE_PACK = "bootstrap5"
|
CRISPY_TEMPLATE_PACK = "bootstrap5"
|
||||||
|
CKEDITOR_UPLOAD_PATH = "uploads/"
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
"django.middleware.security.SecurityMiddleware",
|
"django.middleware.security.SecurityMiddleware",
|
||||||
|
|
@ -166,7 +169,7 @@ AUTHENTICATION_BACKENDS = [
|
||||||
"repapp.backends.OneTimeLoginBackend",
|
"repapp.backends.OneTimeLoginBackend",
|
||||||
]
|
]
|
||||||
|
|
||||||
LOGIN_REDIRECT_URL = "/guest/profile/"
|
LOGIN_REDIRECT_URL = "/guest/"
|
||||||
|
|
||||||
OIDC_RP_CLIENT_ID = "repapp"
|
OIDC_RP_CLIENT_ID = "repapp"
|
||||||
OIDC_RP_CLIENT_SECRET = os.getenv("DJANGO_OIDC_RP_CLIENT_SECRET", None)
|
OIDC_RP_CLIENT_SECRET = os.getenv("DJANGO_OIDC_RP_CLIENT_SECRET", None)
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ from django.conf.urls.static import static
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("", include("repapp.urls")),
|
path("", include("repapp.urls")),
|
||||||
path('oidc/', include('mozilla_django_oidc.urls')),
|
path('oidc/', include('mozilla_django_oidc.urls')),
|
||||||
path('accounts/', include('django.contrib.auth.urls')),
|
# user management for guests not needed, use one-time-login only
|
||||||
|
# path('accounts/', include('django.contrib.auth.urls')),
|
||||||
|
path('ckeditor/', include('ckeditor_uploader.urls')),
|
||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@ Django forms for RepApp.
|
||||||
"""
|
"""
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from crispy_forms.layout import Field
|
from ckeditor.widgets import CKEditorWidget
|
||||||
|
from .models import Question, Device
|
||||||
|
|
||||||
|
|
||||||
EMPTY_VALUES = (None, '')
|
EMPTY_VALUES = (None, '')
|
||||||
|
|
@ -16,7 +17,7 @@ class HoneypotWidget(forms.TextInput):
|
||||||
"""
|
"""
|
||||||
is_hidden = True
|
is_hidden = True
|
||||||
|
|
||||||
def __init__(self, attrs=None, html_comment=False, *args, **kwargs):
|
def __init__(self, *args, attrs=None, html_comment=False, **kwargs):
|
||||||
self.html_comment = html_comment
|
self.html_comment = html_comment
|
||||||
super(HoneypotWidget, self).__init__(attrs, *args, **kwargs)
|
super(HoneypotWidget, self).__init__(attrs, *args, **kwargs)
|
||||||
if 'class' not in self.attrs:
|
if 'class' not in self.attrs:
|
||||||
|
|
@ -113,3 +114,18 @@ class RegisterGuest(forms.Form):
|
||||||
max_length=200
|
max_length=200
|
||||||
)
|
)
|
||||||
accept_agb = HoneypotField(label="")
|
accept_agb = HoneypotField(label="")
|
||||||
|
|
||||||
|
|
||||||
|
class Mail(forms.Form):
|
||||||
|
"""
|
||||||
|
Form for rejecting a device.
|
||||||
|
"""
|
||||||
|
message = forms.CharField(widget=CKEditorWidget(), label="")
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateDeviceForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Question
|
||||||
|
fields = ['question']
|
||||||
|
question = forms.CharField(widget=CKEditorWidget(
|
||||||
|
attrs={'style': 'width:100%;'}), label="Frage")
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ def process_message(message: MailMessage, guest: Guest):
|
||||||
if question:
|
if question:
|
||||||
if question.device.guest.mail == message.from_:
|
if question.device.guest.mail == message.from_:
|
||||||
question.answer = content
|
question.answer = content
|
||||||
|
question.answered = True
|
||||||
question.save()
|
question.save()
|
||||||
# TODO: save attachments and add to message
|
# TODO: save attachments and add to message
|
||||||
logger.info(
|
logger.info(
|
||||||
|
|
|
||||||
18
rc_hip/repapp/migrations/0005_device_status.py
Normal file
18
rc_hip/repapp/migrations/0005_device_status.py
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 4.2 on 2023-05-29 10:39
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('repapp', '0004_alter_onetimelogin_secret'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='device',
|
||||||
|
name='status',
|
||||||
|
field=models.IntegerField(default=0, verbose_name='Status'),
|
||||||
|
),
|
||||||
|
]
|
||||||
18
rc_hip/repapp/migrations/0006_alter_question_answer.py
Normal file
18
rc_hip/repapp/migrations/0006_alter_question_answer.py
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 4.2 on 2023-05-29 12:45
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('repapp', '0005_device_status'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='question',
|
||||||
|
name='answer',
|
||||||
|
field=models.TextField(null=True, verbose_name='Antwort'),
|
||||||
|
),
|
||||||
|
]
|
||||||
18
rc_hip/repapp/migrations/0007_question_open.py
Normal file
18
rc_hip/repapp/migrations/0007_question_open.py
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 4.2 on 2023-05-29 12:55
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('repapp', '0006_alter_question_answer'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='question',
|
||||||
|
name='open',
|
||||||
|
field=models.BooleanField(default=True, verbose_name='offen'),
|
||||||
|
),
|
||||||
|
]
|
||||||
18
rc_hip/repapp/migrations/0008_question_sent.py
Normal file
18
rc_hip/repapp/migrations/0008_question_sent.py
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 4.2 on 2023-05-29 14:08
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('repapp', '0007_question_open'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='question',
|
||||||
|
name='sent',
|
||||||
|
field=models.BooleanField(default=False, verbose_name='gesendet'),
|
||||||
|
),
|
||||||
|
]
|
||||||
18
rc_hip/repapp/migrations/0009_question_answered.py
Normal file
18
rc_hip/repapp/migrations/0009_question_answered.py
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 4.2 on 2023-05-29 14:45
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('repapp', '0008_question_sent'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='question',
|
||||||
|
name='answered',
|
||||||
|
field=models.BooleanField(default=False, verbose_name='beantwortet'),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -113,6 +113,7 @@ class Device(models.Model):
|
||||||
type_plate_picture = models.FileField(
|
type_plate_picture = models.FileField(
|
||||||
upload_to=device_directory_path, null=True, verbose_name=_("Foto vom Typenschild"))
|
upload_to=device_directory_path, null=True, verbose_name=_("Foto vom Typenschild"))
|
||||||
confirmed = models.BooleanField(verbose_name=_("Bestätigung gesendet?"))
|
confirmed = models.BooleanField(verbose_name=_("Bestätigung gesendet?"))
|
||||||
|
status = models.IntegerField(verbose_name=_("Status"), default=0)
|
||||||
guest = models.ForeignKey(
|
guest = models.ForeignKey(
|
||||||
Guest, on_delete=models.CASCADE, null=True, verbose_name=_("Gast"))
|
Guest, on_delete=models.CASCADE, null=True, verbose_name=_("Gast"))
|
||||||
cafe = models.ForeignKey(
|
cafe = models.ForeignKey(
|
||||||
|
|
@ -123,7 +124,7 @@ class Device(models.Model):
|
||||||
verbose_name_plural = _('Geräte')
|
verbose_name_plural = _('Geräte')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'Gerät {self.device}'
|
return f'Gerät {self.device} - {self.manufacturer}'
|
||||||
|
|
||||||
|
|
||||||
class Appointment(models.Model):
|
class Appointment(models.Model):
|
||||||
|
|
@ -153,7 +154,7 @@ class Question(models.Model):
|
||||||
A Question is a request for information form a Organisator or a Reparateur for a Device.
|
A Question is a request for information form a Organisator or a Reparateur for a Device.
|
||||||
"""
|
"""
|
||||||
question = models.TextField(verbose_name=_("Frage"))
|
question = models.TextField(verbose_name=_("Frage"))
|
||||||
answer = models.TextField(verbose_name=_("Antwort"))
|
answer = models.TextField(verbose_name=_("Antwort"), null=True)
|
||||||
date = models.DateField(verbose_name=_(
|
date = models.DateField(verbose_name=_(
|
||||||
"Erstellungsdatum"), default=django.utils.timezone.now)
|
"Erstellungsdatum"), default=django.utils.timezone.now)
|
||||||
organisator = models.ForeignKey(
|
organisator = models.ForeignKey(
|
||||||
|
|
@ -162,6 +163,10 @@ class Question(models.Model):
|
||||||
Reparateur, on_delete=models.CASCADE, null=True, verbose_name=_("Reparateur"))
|
Reparateur, on_delete=models.CASCADE, null=True, verbose_name=_("Reparateur"))
|
||||||
device = models.ForeignKey(
|
device = models.ForeignKey(
|
||||||
Device, on_delete=models.CASCADE, verbose_name=_("Gerät"))
|
Device, on_delete=models.CASCADE, verbose_name=_("Gerät"))
|
||||||
|
open = models.BooleanField(verbose_name=_("offen"), default=True)
|
||||||
|
sent = models.BooleanField(verbose_name=_("gesendet"), default=False)
|
||||||
|
answered = models.BooleanField(
|
||||||
|
verbose_name=_("beantwortet"), default=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _('Frage')
|
verbose_name = _('Frage')
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,11 @@
|
||||||
{% endblock title %}
|
{% endblock title %}
|
||||||
</title>
|
</title>
|
||||||
<link rel="stylesheet" href="{% static 'repapp/css/bootstrap.min.css' %}" />
|
<link rel="stylesheet" href="{% static 'repapp/css/bootstrap.min.css' %}" />
|
||||||
|
<style>
|
||||||
|
.django-ckeditor-widget{
|
||||||
|
display: block !important
|
||||||
|
}
|
||||||
|
</style>
|
||||||
{% block head %}
|
{% block head %}
|
||||||
{% endblock head %}
|
{% endblock head %}
|
||||||
</head>
|
</head>
|
||||||
|
|
@ -39,10 +44,6 @@
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a href="{% url 'logout' %}" class="nav-link">Abmelden</a>
|
<a href="{% url 'logout' %}" class="nav-link">Abmelden</a>
|
||||||
</li>
|
</li>
|
||||||
{% else %}
|
|
||||||
<li class="nav-item">
|
|
||||||
<a href="{% url 'login' %}" class="nav-link">Gast Login</a>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a href="{% url 'member' %}" class="nav-link">Mitglieder</a>
|
<a href="{% url 'member' %}" class="nav-link">Mitglieder</a>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
Guten Tag,
|
|
||||||
wir haben ein Benutzerkonto für Sie erstellt. Sie können sich im Web-Broswer unter {{ link }} um ihre Daten einzusehen und zu ändern.
|
|
||||||
Ihr Anmeldedaten sind:
|
|
||||||
Benutzername: {{ username }}
|
|
||||||
Passwort: {{ password }}
|
|
||||||
Herzliche Grüße sendet dir das Organisationsteam vom Repair-Café Hilpoltstein!
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
Guten Tag,
|
|
||||||
wir haben für Sie einen Einmal-Ammeldelink erstellt. Wenn sie {{ link }} im Web-Broswer öffnen können sie auf ihre Informationen zugreifen.
|
|
||||||
Herzliche Grüße sendet dir das Organisationsteam vom Repair-Café Hilpoltstein!
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
<p>Guten Tag,</p>
|
||||||
|
<p>Wir benötigen mehr Informationen zu ihrem Gerät {{ device.device }} - {{ device.manufacturer }}.</p>
|
||||||
|
<p>HIER FRAGE EINFÜGEN</p>
|
||||||
|
<p>Herzliche Grüße sendet dir das Organisationsteam vom Repair-Café Hilpoltstein!</p>
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
Repair-Café Hilpoltstein - Q#{{ question.pk }}: Rückfrage zu {{ device.device }}
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
Guten Tag,
|
|
||||||
dies ist eine automatische Nachricht, die du erhältst, nachdem du unser Anmeldeformular abgesendet hast und deine Angaben in unserer Datenbank gespeichert wurden.
|
|
||||||
Vielen Dank für die Anmeldung des Geräts {{ device.device }} - {{ device.manufacturer }} zur Reparatur im Repair-Café am {{ cafe.event_date|date }}.
|
|
||||||
Sie können die Daten zu ihrer Geräteanmeldung <a href="{{ login_url }}">hier</a> einsehen.
|
|
||||||
Die Organisation unserer Repair-Cafés beginnt i.a. einige Tage vor dem jeweiligen Termin. Dann erstellen wir anhand der angemeldeten Geräte und der Spezialgebiete der anwesenden Reparateure einen Zeitplan.
|
|
||||||
Sobald dieser Zeitplan fertig gestellt ist, erhältst du von einem Mitglied unseres Organisationsteams eine Mail. Diese beinhaltet im Normalfall den Reparaturtermin inkl. der Uhrzeit, für die die Reparatur vorgesehen ist. In Ausnahmefällen (z.B. wenn kein Spezialist für die Gerätegruppe anwesend sein wird oder überbucht ist) ist es aber auch möglich, dass wir deinen gewünschten Termin absagen und dir einen Alternativtermin vorschlagen müssen.
|
|
||||||
Falls du in der Zwischenzeit Rückfragen hast oder deinen Wunschtermin doch nicht wahrnehmen kannst, antworte bitte einfach auf diese Nachricht.
|
|
||||||
Herzliche Grüße sendet dir das Organisationsteam vom Repair-Café Hilpoltstein!
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<p>Guten Tag,</p>
|
||||||
|
<p>Leider können wir das Gerät {{ device.device }} - {{ device.manufacturer }} nicht annehmen.</p>
|
||||||
|
<p>
|
||||||
|
In unserem Repair-Café gibt es leider keinen passenden Reparateur mit den notwenidgen Fähigkeiten um das Gerät zu reparieren.
|
||||||
|
</p>
|
||||||
|
<p>Herzliche Grüße sendet dir das Organisationsteam vom Repair-Café Hilpoltstein!</p>
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
Repair-Café Hilpoltstein - Geräteablehnung {{ device.device }}
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<p>
|
<p>
|
||||||
<a href="{% url 'create_cafe' %}" class="btn btn-primary">Neues Repair-Cafés anlegen</a>
|
<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>
|
<a onclick="history.back()" class="btn btn-primary">Zurück</a>
|
||||||
</p>
|
</p>
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
|
|
|
||||||
23
rc_hip/repapp/templates/repapp/orga/device_question.html
Normal file
23
rc_hip/repapp/templates/repapp/orga/device_question.html
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
{% extends "repapp/base.html" %}
|
||||||
|
{% load static %}
|
||||||
|
{% load crispy_forms_tags %}
|
||||||
|
{% block title %}
|
||||||
|
- Rückfrage zum Gerät
|
||||||
|
{% endblock title %}
|
||||||
|
{% block page_title %}
|
||||||
|
Rückfrage zum Gerät
|
||||||
|
{% endblock page_title %}
|
||||||
|
{% block content %}
|
||||||
|
<p>
|
||||||
|
<a onclick="history.back()" class="btn btn-primary">Zurück</a>
|
||||||
|
</p>
|
||||||
|
<hr class="mt-0 mb-4">
|
||||||
|
<h1 class="mt-2">Rückfrage zum Gerät</h1>
|
||||||
|
<form action="{% url 'device_question' device.pk %}" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form|crispy }}
|
||||||
|
<button type="submit" class="btn btn-primary submit_button">Senden</button>
|
||||||
|
</form>
|
||||||
|
<script type="text/javascript" src="{% static "ckeditor/ckeditor-init.js" %}"></script>
|
||||||
|
<script type="text/javascript" src="{% static "ckeditor/ckeditor/ckeditor.js" %}"></script>
|
||||||
|
{% endblock content %}
|
||||||
28
rc_hip/repapp/templates/repapp/orga/edit_question.html
Normal file
28
rc_hip/repapp/templates/repapp/orga/edit_question.html
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
{% extends "repapp/base.html" %}
|
||||||
|
{% load static %}
|
||||||
|
{% load crispy_forms_tags %}
|
||||||
|
{% block title %}
|
||||||
|
- Rückfrage zum Gerät
|
||||||
|
{% endblock title %}
|
||||||
|
{% block page_title %}
|
||||||
|
Rückfrage zum Gerät
|
||||||
|
{% endblock page_title %}
|
||||||
|
{% block content %}
|
||||||
|
<p>
|
||||||
|
<a onclick="history.back()" class="btn btn-primary">Zurück</a>
|
||||||
|
</p>
|
||||||
|
<hr class="mt-0 mb-4">
|
||||||
|
<h1 class="mt-2">Rückfrage zum Gerät</h1>
|
||||||
|
<h2>{{ question.device.device }} - {{ question.device.manufacturer }} vom {{ question.date|date }}</h2>
|
||||||
|
{% if show_guest %}<p>Gast: {{ question.device.guest.name }} ({{ question.device.guest.mail }})</p>{% endif %}
|
||||||
|
{% if question.device.cafe %}<p>Repair-Café: {{ question.device.cafe.event_date|date }}</p>{% endif %}
|
||||||
|
{% if question.organisator %}<p>Organisator: {{ question.organisator.name }}</p>{% endif %}
|
||||||
|
{% if question.reparateur %}<p>Reparateur: {{ question.reparateur.name }}</p>{% endif %}
|
||||||
|
<form action="{% url 'edit_device_question' question.pk %}" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form|crispy }}
|
||||||
|
<button type="submit" class="btn btn-primary submit_button">Als eMail senden</button>
|
||||||
|
</form>
|
||||||
|
<script type="text/javascript" src="{% static "ckeditor/ckeditor-init.js" %}"></script>
|
||||||
|
<script type="text/javascript" src="{% static "ckeditor/ckeditor/ckeditor.js" %}"></script>
|
||||||
|
{% endblock content %}
|
||||||
|
|
@ -7,8 +7,7 @@
|
||||||
{% endblock page_title %}
|
{% endblock page_title %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<p>
|
<p>
|
||||||
<a href="{% url 'create_guest' %}" class="btn btn-primary">Neuen Gast anlegen</a>
|
<a onclick="history.back()" class="btn btn-primary">Zurück</a>
|
||||||
<a href="javascript:history.back()" class="btn btn-primary">Zurück</a>
|
|
||||||
</p>
|
</p>
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
|
|
@ -19,7 +18,6 @@
|
||||||
<th scope="col">Wohnort</th>
|
<th scope="col">Wohnort</th>
|
||||||
<th scope="col">eMail Adresse</th>
|
<th scope="col">eMail Adresse</th>
|
||||||
<th scope="col">Bearbeiten</th>
|
<th scope="col">Bearbeiten</th>
|
||||||
<th scope="col">Löschen</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,119 @@
|
||||||
// TODO: Button nächstes Repair-Café
|
// TODO: Button nächstes Repair-Café
|
||||||
<hr class="mt-0 mb-4">
|
<hr class="mt-0 mb-4">
|
||||||
<h1 class="mt-2">Geräte</h1>
|
<h1 class="mt-2">Geräte</h1>
|
||||||
// TODO: Liste mit neuen Geräten
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">Art des Geräts</th>
|
||||||
|
<th scope="col">Hersteller & Modell/Typ</th>
|
||||||
|
<th scope="col">Name des Gastes</th>
|
||||||
|
<th scope="col">eMail des Gastes</th>
|
||||||
|
<th scope="col"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for device in devices %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ device.device }}</td>
|
||||||
|
<td>{{ device.manufacturer }}</td>
|
||||||
|
<td>{{ device.guest.name }}</td>
|
||||||
|
<td>{{ device.guest.mail }}</td>
|
||||||
|
<td>
|
||||||
|
<a href="{% url 'review_device' device.pk %}" class="btn btn-primary">überpüfen</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% empty %}
|
||||||
|
<tr>
|
||||||
|
<td colspan="4">Es gibt keine neuen Geräte.</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
<hr class="mt-0 mb-4">
|
<hr class="mt-0 mb-4">
|
||||||
<h1 class="mt-2">Rückfragen</h1>
|
<h1 class="mt-2">Rückfragen</h1>
|
||||||
// TODO: Liste mit Antworten auf Rückfragen
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">Geräts</th>
|
||||||
|
<th scope="col">Hersteller</th>
|
||||||
|
<th scope="col">Gast</th>
|
||||||
|
<th scope="col">Organisator</th>
|
||||||
|
<th scope="col">Reparateur</th>
|
||||||
|
<th scope="col">gesendet</th>
|
||||||
|
<th scope="col">beantwortet</th>
|
||||||
|
<th scope="col"></th>
|
||||||
|
<th scope="col"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for question in questions_not_sent %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ question.device.device }}</td>
|
||||||
|
<td>{{ question.device.manufacturer }}</td>
|
||||||
|
<td>{{ question.device.guest.name }}</td>
|
||||||
|
<td>{{ question.organisator.mail }}</td>
|
||||||
|
<td>{{ question.reparateur.mail }}</td>
|
||||||
|
<td>
|
||||||
|
{% if question.sent %}
|
||||||
|
Ja
|
||||||
|
{% else %}
|
||||||
|
Nein
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if question.answered %}
|
||||||
|
Ja
|
||||||
|
{% else %}
|
||||||
|
Nein
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="{% url 'view_device_question' question.pk %}"
|
||||||
|
class="btn btn-primary">ansehen</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if not question.sent %}
|
||||||
|
<a href="{% url 'edit_device_question' question.pk %}"
|
||||||
|
class="btn btn-primary">senden</a>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
{% for question in questions_open_and_answered %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ question.device.device }}</td>
|
||||||
|
<td>{{ question.device.manufacturer }}</td>
|
||||||
|
<td>{{ question.device.guest.name }}</td>
|
||||||
|
<td>{{ question.organisator.mail }}</td>
|
||||||
|
<td>{{ question.reparateur.mail }}</td>
|
||||||
|
<td>
|
||||||
|
{% if question.sent %}
|
||||||
|
Ja
|
||||||
|
{% else %}
|
||||||
|
Nein
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if question.answered %}
|
||||||
|
Ja
|
||||||
|
{% else %}
|
||||||
|
Nein
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="{% url 'view_device_question' question.pk %}"
|
||||||
|
class="btn btn-primary">ansehen</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if not question.sent %}
|
||||||
|
<a href="{% url 'edit_device_question' question.pk %}"
|
||||||
|
class="btn btn-primary">senden</a>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
<hr class="mt-0 mb-4">
|
<hr class="mt-0 mb-4">
|
||||||
<h1 class="mt-2">Nachrichten</h1>
|
<h1 class="mt-2">Nachrichten</h1>
|
||||||
// TODO: Liste mit neuen Nachrichten
|
// TODO: Liste mit neuen Nachrichten
|
||||||
|
|
|
||||||
41
rc_hip/repapp/templates/repapp/orga/question.html
Normal file
41
rc_hip/repapp/templates/repapp/orga/question.html
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
{% extends "repapp/base.html" %}
|
||||||
|
{% load static %}
|
||||||
|
{% load crispy_forms_tags %}
|
||||||
|
{% block title %}
|
||||||
|
- Rückfrage zum Gerät
|
||||||
|
{% endblock title %}
|
||||||
|
{% block page_title %}
|
||||||
|
Rückfrage zum Gerät
|
||||||
|
{% endblock page_title %}
|
||||||
|
{% block content %}
|
||||||
|
<p>
|
||||||
|
<a onclick="history.back()" class="btn btn-primary">Zurück</a>
|
||||||
|
</p>
|
||||||
|
<hr class="mt-0 mb-4">
|
||||||
|
<h1 class="mt-2">Rückfrage zum Gerät</h1>
|
||||||
|
<h2>{{ question.device.device }} - {{ question.device.manufacturer }} vom {{ question.date|date }}</h2>
|
||||||
|
{% if show_guest %}<p>Gast: {{ question.device.guest.name }} ({{ question.device.guest.mail }})</p>{% endif %}
|
||||||
|
{% if question.device.cafe %}<p>Repair-Café: {{ question.device.cafe.event_date|date }}</p>{% endif %}
|
||||||
|
{% if question.organisator %}<p>Organisator: {{ question.organisator.name }}</p>{% endif %}
|
||||||
|
{% if question.reparateur %}<p>Reparateur: {{ question.reparateur.name }}</p>{% endif %}
|
||||||
|
<p>
|
||||||
|
Offen:
|
||||||
|
{% if question.open %}
|
||||||
|
Ja
|
||||||
|
{% else %}
|
||||||
|
Nein
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
<hr class="mt-0 mb-4">
|
||||||
|
<h3>Frage</h3>
|
||||||
|
<div>{{ question.question|safe }}</div>
|
||||||
|
<hr class="mt-0 mb-4">
|
||||||
|
<h3>Antwort</h3>
|
||||||
|
{% if question.answer %}
|
||||||
|
<div>
|
||||||
|
<pre>{{ question.answer|safe }}</pre>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
Der Gast hat noch nicht geantwortet.
|
||||||
|
{% endif %}
|
||||||
|
{% endblock content %}
|
||||||
72
rc_hip/repapp/templates/repapp/orga/review_device.html
Normal file
72
rc_hip/repapp/templates/repapp/orga/review_device.html
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
{% extends "repapp/base.html" %}
|
||||||
|
{% load crispy_forms_tags %}
|
||||||
|
{% block title %}
|
||||||
|
- Gerät überprüfen
|
||||||
|
{% endblock title %}
|
||||||
|
{% block page_title %}
|
||||||
|
Gerät überprüfen
|
||||||
|
{% endblock page_title %}
|
||||||
|
{% block content %}
|
||||||
|
<p>
|
||||||
|
<a onclick="history.back()" class="btn btn-primary">Zurück</a>
|
||||||
|
<a href="{% url 'review_device_accept' device.pk %}"
|
||||||
|
class="btn btn-primary">Annehmen</a>
|
||||||
|
<a href="{% url 'review_device_reject' device.pk %}"
|
||||||
|
class="btn btn-primary">Ablehnen</a>
|
||||||
|
<a href="{% url 'device_question' device.pk %}" class="btn btn-primary">Rückfrage</a>
|
||||||
|
</p>
|
||||||
|
<hr class="mt-0 mb-4">
|
||||||
|
<h1 class="mt-2">Gerät</h1>
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Art des Geräts</th>
|
||||||
|
<td>{{ device.device }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Hersteller & Modell/Typ</th>
|
||||||
|
<td>{{ device.manufacturer }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Fehlerbeschreibung</th>
|
||||||
|
<td>{{ device.error }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Folgetermin</th>
|
||||||
|
<td>
|
||||||
|
{% if device.follow_up %}
|
||||||
|
Ja
|
||||||
|
{% else %}
|
||||||
|
Nein
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Foto vom Gerät</th>
|
||||||
|
<td>
|
||||||
|
{% if device.device_picture %}
|
||||||
|
<img alt="Foto vom Gerät"
|
||||||
|
height="100%"
|
||||||
|
width="50%"
|
||||||
|
src="/media/{{ device.device_picture }}" />
|
||||||
|
{% else %}
|
||||||
|
kein Bild
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Foto vom Typenschild</th>
|
||||||
|
<td>
|
||||||
|
{% if device.device_picture %}
|
||||||
|
<img alt="Foto vom Typenschild"
|
||||||
|
height="100%"
|
||||||
|
width="50%"
|
||||||
|
src="/media/{{ device.type_plate_picture }}" />
|
||||||
|
{% else %}
|
||||||
|
kein Bild
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endblock content %}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
{% extends "repapp/base.html" %}
|
||||||
|
{% load static %}
|
||||||
|
{% load crispy_forms_tags %}
|
||||||
|
{% block title %}
|
||||||
|
- Gerät ablehnen
|
||||||
|
{% endblock title %}
|
||||||
|
{% block page_title %}
|
||||||
|
Gerät ablehnen
|
||||||
|
{% endblock page_title %}
|
||||||
|
{% block content %}
|
||||||
|
<p>
|
||||||
|
<a onclick="history.back()" class="btn btn-primary">Zurück</a>
|
||||||
|
</p>
|
||||||
|
<hr class="mt-0 mb-4">
|
||||||
|
<h1 class="mt-2">Gerät ablehnen</h1>
|
||||||
|
<form action="{% url 'review_device_reject' device.pk %}" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form|crispy }}
|
||||||
|
<button type="submit" class="btn btn-primary submit_button">Senden</button>
|
||||||
|
</form>
|
||||||
|
<script type="text/javascript" src="{% static "ckeditor/ckeditor-init.js" %}"></script>
|
||||||
|
<script type="text/javascript" src="{% static "ckeditor/ckeditor/ckeditor.js" %}"></script>
|
||||||
|
{% endblock content %}
|
||||||
|
|
@ -25,8 +25,20 @@ urlpatterns = [
|
||||||
path("onetimelogin/<str:secret>/",
|
path("onetimelogin/<str:secret>/",
|
||||||
views.one_time_login, name="one_time_login"),
|
views.one_time_login, name="one_time_login"),
|
||||||
path("member/", views.member, name="member"),
|
path("member/", views.member, name="member"),
|
||||||
|
path("logout/", views.user_logout, name="logout"),
|
||||||
path("orga/select/", views.select_role, name="select_role"),
|
path("orga/select/", views.select_role, name="select_role"),
|
||||||
path("orga/", views.orga, name="orga"),
|
path("orga/", views.orga, name="orga"),
|
||||||
|
path("orga/review/<int:device>/", views.review_device, name="review_device"),
|
||||||
|
path("orga/review/<int:device>/accept/",
|
||||||
|
views.review_device_accept, name="review_device_accept"),
|
||||||
|
path("orga/review/<int:device>/reject/",
|
||||||
|
views.RejectDeviceFormView.as_view(), name="review_device_reject"),
|
||||||
|
path("member/device/<int:device>/question/",
|
||||||
|
views.QuestionDeviceFormView.as_view(), name="device_question"),
|
||||||
|
path("member/device/<int:pk>/question/edit",
|
||||||
|
views.QuestionUpdateView.as_view(), name="edit_device_question"),
|
||||||
|
path("member/question/<int:question>/",
|
||||||
|
views.question, name="view_device_question"),
|
||||||
path("repa/", views.repa, name="repa"),
|
path("repa/", views.repa, name="repa"),
|
||||||
path("cafe/", views.CafeView.as_view(), name="cafe"),
|
path("cafe/", views.CafeView.as_view(), name="cafe"),
|
||||||
path("cafe/create", views.CafeCreateView.as_view(), name="create_cafe"),
|
path("cafe/create", views.CafeCreateView.as_view(), name="create_cafe"),
|
||||||
|
|
@ -38,5 +50,4 @@ urlpatterns = [
|
||||||
path("cron", views.cron, name="cron"),
|
path("cron", views.cron, name="cron"),
|
||||||
path("bootstrap", views.bootstrap, name="bootstrap"),
|
path("bootstrap", views.bootstrap, name="bootstrap"),
|
||||||
path("process_mails", views.process_mails, name="process_mails"),
|
path("process_mails", views.process_mails, name="process_mails"),
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import time
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.core.mail import send_mail
|
from django.core.mail import send_mail
|
||||||
|
|
@ -19,10 +20,10 @@ from django.http import HttpResponseRedirect, HttpResponse
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
|
||||||
from django.contrib.auth import authenticate, login
|
from django.contrib.auth import authenticate, login, logout
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from .forms import RegisterDevice, RegisterGuest
|
from .forms import RegisterDevice, RegisterGuest, Mail, UpdateDeviceForm
|
||||||
from .models import (Cafe, Device, Guest, OneTimeLogin,
|
from .models import (Cafe, Device, Guest, OneTimeLogin, Question,
|
||||||
CustomUser, Organisator, Reparateur)
|
CustomUser, Organisator, Reparateur)
|
||||||
from . import mail_interface
|
from . import mail_interface
|
||||||
|
|
||||||
|
|
@ -38,12 +39,11 @@ def send_one_time_login_mail(secret, mail, request):
|
||||||
f'/onetimelogin/{secret}/')
|
f'/onetimelogin/{secret}/')
|
||||||
subject = render_to_string(
|
subject = render_to_string(
|
||||||
'repapp/mail/mail_one_time_login_subject.html').replace('\n', '')
|
'repapp/mail/mail_one_time_login_subject.html').replace('\n', '')
|
||||||
text = render_to_string('repapp/mail/mail_one_time_login_text.html', {
|
|
||||||
'link': url,
|
|
||||||
})
|
|
||||||
html = render_to_string('repapp/mail/mail_one_time_login_html.html', {
|
html = render_to_string('repapp/mail/mail_one_time_login_html.html', {
|
||||||
'link': url,
|
'link': url,
|
||||||
})
|
})
|
||||||
|
soup = BeautifulSoup(html)
|
||||||
|
text = soup.get_text('\n')
|
||||||
|
|
||||||
send_ok = send_mail(
|
send_ok = send_mail(
|
||||||
subject=subject,
|
subject=subject,
|
||||||
|
|
@ -90,18 +90,14 @@ def send_confirmation_mails(device, guest, cafe, request):
|
||||||
subject = render_to_string('repapp/mail/mail_register_device_subject.html', {
|
subject = render_to_string('repapp/mail/mail_register_device_subject.html', {
|
||||||
'cafe': cafe,
|
'cafe': cafe,
|
||||||
}).replace('\n', '')
|
}).replace('\n', '')
|
||||||
text = render_to_string('repapp/mail/mail_register_device_text.html', {
|
|
||||||
'guest': guest,
|
|
||||||
'device': device,
|
|
||||||
'cafe': cafe,
|
|
||||||
'login_url': login_url,
|
|
||||||
})
|
|
||||||
html = render_to_string('repapp/mail/mail_register_device_html.html', {
|
html = render_to_string('repapp/mail/mail_register_device_html.html', {
|
||||||
'guest': guest,
|
'guest': guest,
|
||||||
'device': device,
|
'device': device,
|
||||||
'cafe': cafe,
|
'cafe': cafe,
|
||||||
'login_url': login_url,
|
'login_url': login_url,
|
||||||
})
|
})
|
||||||
|
soup = BeautifulSoup(html)
|
||||||
|
text = soup.get_text('\n')
|
||||||
|
|
||||||
mail_count = send_mail(
|
mail_count = send_mail(
|
||||||
subject=subject,
|
subject=subject,
|
||||||
|
|
@ -127,16 +123,13 @@ def send_guest_account_mail(guest, password, request):
|
||||||
url = request.build_absolute_uri('/guest/profile/')
|
url = request.build_absolute_uri('/guest/profile/')
|
||||||
subject = render_to_string(
|
subject = render_to_string(
|
||||||
'repapp/mail/mail_new_guest_subject.html').replace('\n', '')
|
'repapp/mail/mail_new_guest_subject.html').replace('\n', '')
|
||||||
text = render_to_string('repapp/mail/mail_new_guest_text.html', {
|
|
||||||
'link': url,
|
|
||||||
'username': guest.user.email,
|
|
||||||
'password': password,
|
|
||||||
})
|
|
||||||
html = render_to_string('repapp/mail/mail_new_guest_html.html', {
|
html = render_to_string('repapp/mail/mail_new_guest_html.html', {
|
||||||
'link': url,
|
'link': url,
|
||||||
'username': guest.user.email,
|
'username': guest.user.email,
|
||||||
'password': password,
|
'password': password,
|
||||||
})
|
})
|
||||||
|
soup = BeautifulSoup(html)
|
||||||
|
text = soup.get_text('\n')
|
||||||
|
|
||||||
send_ok = send_mail(
|
send_ok = send_mail(
|
||||||
subject=subject,
|
subject=subject,
|
||||||
|
|
@ -152,6 +145,62 @@ def send_guest_account_mail(guest, password, request):
|
||||||
'Sie haben ihre Benutzerdaten per eMail erhalten.')
|
'Sie haben ihre Benutzerdaten per eMail erhalten.')
|
||||||
|
|
||||||
|
|
||||||
|
def send_device_reject_mail(device, reply, request):
|
||||||
|
"""
|
||||||
|
Send a mail to reject the given device.
|
||||||
|
"""
|
||||||
|
subject = render_to_string(
|
||||||
|
'repapp/mail/mail_reject_device_subject.html', {
|
||||||
|
'device': device
|
||||||
|
}).replace('\n', '')
|
||||||
|
|
||||||
|
soup = BeautifulSoup(reply)
|
||||||
|
text = soup.get_text('\n')
|
||||||
|
|
||||||
|
send_ok = send_mail(
|
||||||
|
subject=subject,
|
||||||
|
message=text,
|
||||||
|
from_email=os.getenv("DJANGO_SENDER_ADDRESS", ""),
|
||||||
|
recipient_list=[device.guest.mail],
|
||||||
|
fail_silently=True,
|
||||||
|
html_message=reply
|
||||||
|
)
|
||||||
|
|
||||||
|
if send_ok > 0:
|
||||||
|
messages.add_message(request, messages.INFO,
|
||||||
|
f'Das Gerät {device.device} wurde abgelehnt.')
|
||||||
|
|
||||||
|
|
||||||
|
def send_device_question_mail(question, request):
|
||||||
|
"""
|
||||||
|
Send a mail to ask more details about an device.
|
||||||
|
"""
|
||||||
|
subject = render_to_string(
|
||||||
|
'repapp/mail/mail_question_device_subject.html', {
|
||||||
|
'device': question.device,
|
||||||
|
'question': question,
|
||||||
|
}).replace('\n', '')
|
||||||
|
|
||||||
|
soup = BeautifulSoup(question.question)
|
||||||
|
text = soup.get_text('\n')
|
||||||
|
|
||||||
|
send_ok = send_mail(
|
||||||
|
subject=subject,
|
||||||
|
message=text,
|
||||||
|
from_email=os.getenv("DJANGO_SENDER_ADDRESS", ""),
|
||||||
|
recipient_list=[question.device.guest.mail],
|
||||||
|
fail_silently=True,
|
||||||
|
html_message=question.question
|
||||||
|
)
|
||||||
|
|
||||||
|
if send_ok > 0:
|
||||||
|
question.sent = True
|
||||||
|
question.save()
|
||||||
|
|
||||||
|
messages.add_message(request, messages.INFO,
|
||||||
|
f'Die Rückfrage zum Gerät {question.device.device} wurde gesendet.')
|
||||||
|
|
||||||
|
|
||||||
def is_member(user):
|
def is_member(user):
|
||||||
"""
|
"""
|
||||||
Test is a user is a Repair-Café member.
|
Test is a user is a Repair-Café member.
|
||||||
|
|
@ -325,7 +374,8 @@ class RegisterGuestFormView(generic.edit.FormView):
|
||||||
guest.user = user
|
guest.user = user
|
||||||
guest.save()
|
guest.save()
|
||||||
|
|
||||||
send_guest_account_mail(guest, password, self.request)
|
# Simplify flow for guests, hide account and use only one-time-login
|
||||||
|
# send_guest_account_mail(guest, password, self.request)
|
||||||
|
|
||||||
send_confirmation_mails(device, guest, cafe, self.request)
|
send_confirmation_mails(device, guest, cafe, self.request)
|
||||||
|
|
||||||
|
|
@ -450,9 +500,231 @@ def orga(request):
|
||||||
logger.warning('The user %s is no organisator!', str(request.user))
|
logger.warning('The user %s is no organisator!', str(request.user))
|
||||||
raise PermissionDenied('Not organisator!')
|
raise PermissionDenied('Not organisator!')
|
||||||
|
|
||||||
|
devices = Device.objects.filter(status=0).order_by('-date')
|
||||||
|
questions_not_sent = Question.objects.filter(sent=False).order_by('-date')
|
||||||
|
questions_open_and_answered = Question.objects.filter(
|
||||||
|
open=True, answered=True).order_by('-date')
|
||||||
|
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
"repapp/orga/main.html"
|
"repapp/orga/main.html",
|
||||||
|
{
|
||||||
|
'devices': devices,
|
||||||
|
'questions_not_sent': questions_not_sent,
|
||||||
|
'questions_open_and_answered': questions_open_and_answered,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required(login_url=reverse_lazy('member'))
|
||||||
|
def review_device(request, device):
|
||||||
|
"""
|
||||||
|
Review new device registration.
|
||||||
|
"""
|
||||||
|
# TODO: test
|
||||||
|
if not is_organisator(request.user):
|
||||||
|
logger.warning('The user %s is no organisator!', str(request.user))
|
||||||
|
raise PermissionDenied('Not organisator!')
|
||||||
|
|
||||||
|
device = get_object_or_404(Device, pk=device)
|
||||||
|
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
"repapp/orga/review_device.html",
|
||||||
|
{
|
||||||
|
'device': device
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required(login_url=reverse_lazy('member'))
|
||||||
|
def review_device_accept(request, device):
|
||||||
|
"""
|
||||||
|
Review new device registration.
|
||||||
|
"""
|
||||||
|
# TODO: test
|
||||||
|
if not is_organisator(request.user):
|
||||||
|
logger.warning('The user %s is no organisator!', str(request.user))
|
||||||
|
raise PermissionDenied('Not organisator!')
|
||||||
|
|
||||||
|
device = get_object_or_404(Device, pk=device)
|
||||||
|
device.status = 3
|
||||||
|
device.save()
|
||||||
|
|
||||||
|
return HttpResponseRedirect(reverse_lazy('orga'))
|
||||||
|
|
||||||
|
|
||||||
|
class RejectDeviceFormView(LoginRequiredMixin, UserPassesTestMixin, generic.edit.FormView):
|
||||||
|
"""
|
||||||
|
View for rejecting a device.
|
||||||
|
"""
|
||||||
|
template_name = "repapp/orga/review_device_reject.html"
|
||||||
|
form_class = Mail
|
||||||
|
login_url = reverse_lazy('member')
|
||||||
|
|
||||||
|
def test_func(self):
|
||||||
|
return is_organisator(self.request.user)
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
device = self.kwargs['device']
|
||||||
|
device = get_object_or_404(Device, pk=device)
|
||||||
|
|
||||||
|
message = form.cleaned_data['message']
|
||||||
|
|
||||||
|
send_device_reject_mail(device, message, self.request)
|
||||||
|
|
||||||
|
device.status = -1
|
||||||
|
device.save()
|
||||||
|
|
||||||
|
return HttpResponseRedirect(
|
||||||
|
reverse_lazy('orga')
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_initial(self, **kwargs):
|
||||||
|
device = self.kwargs['device']
|
||||||
|
device = get_object_or_404(Device, pk=device)
|
||||||
|
|
||||||
|
text = render_to_string('repapp/mail/mail_reject_device_html.html', {
|
||||||
|
'device': device,
|
||||||
|
})
|
||||||
|
|
||||||
|
initial = super(RejectDeviceFormView, self).get_initial()
|
||||||
|
initial['message'] = text
|
||||||
|
|
||||||
|
return initial
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
device = self.kwargs['device']
|
||||||
|
device = get_object_or_404(Device, pk=device)
|
||||||
|
|
||||||
|
context = super(RejectDeviceFormView, self).get_context_data(
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
context["device"] = device
|
||||||
|
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class QuestionDeviceFormView(LoginRequiredMixin, UserPassesTestMixin, generic.edit.FormView):
|
||||||
|
"""
|
||||||
|
View for rejecting a device.
|
||||||
|
"""
|
||||||
|
template_name = "repapp/orga/device_question.html"
|
||||||
|
form_class = Mail
|
||||||
|
login_url = reverse_lazy('member')
|
||||||
|
|
||||||
|
def test_func(self):
|
||||||
|
return is_organisator(self.request.user)
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
device = self.kwargs['device']
|
||||||
|
device = get_object_or_404(Device, pk=device)
|
||||||
|
|
||||||
|
question = None
|
||||||
|
message = form.cleaned_data['message']
|
||||||
|
mail = self.request.user.email
|
||||||
|
organisator = Organisator.objects.filter(mail=mail).first()
|
||||||
|
if organisator:
|
||||||
|
question = Question(
|
||||||
|
question=message,
|
||||||
|
organisator=organisator,
|
||||||
|
device=device
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise PermissionDenied('Not valid member found!')
|
||||||
|
question.save()
|
||||||
|
|
||||||
|
send_device_question_mail(question, self.request)
|
||||||
|
|
||||||
|
device.status = 1
|
||||||
|
device.save()
|
||||||
|
|
||||||
|
return HttpResponseRedirect(
|
||||||
|
reverse_lazy('orga')
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_initial(self, **kwargs):
|
||||||
|
device = self.kwargs['device']
|
||||||
|
device = get_object_or_404(Device, pk=device)
|
||||||
|
|
||||||
|
text = render_to_string('repapp/mail/mail_question_device_html.html', {
|
||||||
|
'device': device,
|
||||||
|
})
|
||||||
|
|
||||||
|
initial = super(QuestionDeviceFormView, self).get_initial()
|
||||||
|
initial['message'] = text
|
||||||
|
|
||||||
|
return initial
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
device = self.kwargs['device']
|
||||||
|
device = get_object_or_404(Device, pk=device)
|
||||||
|
|
||||||
|
context = super(QuestionDeviceFormView, self).get_context_data(
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
context["device"] = device
|
||||||
|
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class QuestionUpdateView(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_question.html"
|
||||||
|
model = Question
|
||||||
|
form_class = UpdateDeviceForm
|
||||||
|
success_url = reverse_lazy('orga')
|
||||||
|
|
||||||
|
def test_func(self):
|
||||||
|
return is_organisator(self.request.user)
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
response = super(QuestionUpdateView, self).form_valid(form)
|
||||||
|
|
||||||
|
question = self.kwargs['pk']
|
||||||
|
question = get_object_or_404(Question, pk=question)
|
||||||
|
send_device_question_mail(question, self.request)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
question = self.kwargs['pk']
|
||||||
|
question = get_object_or_404(Question, pk=question)
|
||||||
|
|
||||||
|
context = super(QuestionUpdateView, self).get_context_data(
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
context["question"] = question
|
||||||
|
context["show_guest"] = True
|
||||||
|
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
@login_required(login_url=reverse_lazy('member'))
|
||||||
|
def question(request, question):
|
||||||
|
"""
|
||||||
|
Review new device registration.
|
||||||
|
"""
|
||||||
|
# TODO: test
|
||||||
|
if not is_member(request.user):
|
||||||
|
raise PermissionDenied('Not member!')
|
||||||
|
|
||||||
|
question = get_object_or_404(Question, pk=question)
|
||||||
|
show_guest = False
|
||||||
|
if is_organisator(request.user):
|
||||||
|
show_guest = True
|
||||||
|
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
"repapp/orga/question.html",
|
||||||
|
{
|
||||||
|
'question': question,
|
||||||
|
'show_guest': show_guest
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -633,3 +905,11 @@ class GuestUpdateView(LoginRequiredMixin, UserPassesTestMixin, generic.edit.Upda
|
||||||
|
|
||||||
def test_func(self):
|
def test_func(self):
|
||||||
return is_organisator(self.request.user)
|
return is_organisator(self.request.user)
|
||||||
|
|
||||||
|
|
||||||
|
def user_logout(request):
|
||||||
|
"""
|
||||||
|
Log current user out.
|
||||||
|
"""
|
||||||
|
logout(request)
|
||||||
|
return HttpResponseRedirect(reverse_lazy('index'))
|
||||||
|
|
|
||||||
|
|
@ -12,3 +12,5 @@ djlint==1.23.3
|
||||||
django-import-export==3.2.0
|
django-import-export==3.2.0
|
||||||
selenium==4.9.0
|
selenium==4.9.0
|
||||||
imap-tools==1.0.0
|
imap-tools==1.0.0
|
||||||
|
django-ckeditor==6.5.1
|
||||||
|
beautifulsoup4==4.12.2
|
||||||
|
|
|
||||||
|
|
@ -6,3 +6,5 @@ crispy-bootstrap5==0.7
|
||||||
mozilla-django-oidc==3.0.0
|
mozilla-django-oidc==3.0.0
|
||||||
django-import-export==3.2.0
|
django-import-export==3.2.0
|
||||||
imap-tools==1.0.0
|
imap-tools==1.0.0
|
||||||
|
django-ckeditor==6.5.1
|
||||||
|
beautifulsoup4==4.12.2
|
||||||
|
|
|
||||||
Reference in a new issue