From f63c1fe541b7e7e8d4a16b08dabe00b448729136 Mon Sep 17 00:00:00 2001 From: Jonathan Weth <git@jonathanweth.de> Date: Thu, 20 Feb 2020 16:33:57 +0100 Subject: [PATCH] Include graphical announcement form --- aleksis/core/forms.py | 76 ++++++++++++++++++- aleksis/core/menus.py | 9 +++ .../templates/core/announcement/form.html | 33 ++++++++ .../templates/core/announcement/list.html | 48 ++++++++++++ aleksis/core/urls.py | 3 + aleksis/core/views.py | 45 +++++++++++ 6 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 aleksis/core/templates/core/announcement/form.html create mode 100644 aleksis/core/templates/core/announcement/list.html diff --git a/aleksis/core/forms.py b/aleksis/core/forms.py index cef045d86..1ca40c66c 100644 --- a/aleksis/core/forms.py +++ b/aleksis/core/forms.py @@ -1,10 +1,15 @@ +from datetime import time + from django import forms from django.contrib.auth import get_user_model +from django.core.exceptions import ValidationError +from django.utils import timezone from django.utils.translation import ugettext_lazy as _ from django_select2.forms import ModelSelect2MultipleWidget, Select2Widget +from material import Layout, Fieldset, Row -from .models import Group, Person, School, SchoolTerm +from .models import Group, Person, School, SchoolTerm, Announcement class PersonAccountForm(forms.ModelForm): @@ -132,3 +137,72 @@ class EditTermForm(forms.ModelForm): class Meta: model = SchoolTerm fields = ["caption", "date_start", "date_end"] + + +class AnnouncementForm(forms.ModelForm): + valid_from = forms.DateTimeField(required=False) + valid_until = forms.DateTimeField(required=False) + + valid_from_date = forms.DateField(label=_("Date")) + valid_from_time = forms.TimeField(label=_("Time")) + + valid_until_date = forms.DateField(label=_("Date")) + valid_until_time = forms.TimeField(label=_("Time")) + + layout = Layout( + Fieldset( + _("From when until when should the announcement be displayed?"), + Row("valid_from_date", "valid_from_time", "valid_until_date", "valid_until_time"), + ), + Fieldset(_("Write your announcement:"), "title", "description"), + ) + + @classmethod + def get_initial(cls): + return { + "valid_from_date": timezone.datetime.now(), + "valid_from_time": time(0,0), + "valid_until_date": timezone.datetime.now(), + "valid_until_time": time(23, 59) + } + + def clean(self): + data = super().clean() + + from_date = data["valid_from_date"] + from_time = data["valid_from_time"] + until_date = data["valid_until_date"] + until_time = data["valid_until_time"] + + valid_from = timezone.datetime.combine(from_date, from_time) + valid_until = timezone.datetime.combine(until_date, until_time) + + if valid_until < timezone.datetime.now(): + raise ValidationError( + _("You are not allowed to create announcements which are only valid in the past.") + ) + elif valid_from > valid_until: + raise ValidationError( + _("The from date and time must be earlier then the until date and time.") + ) + + data["valid_from"] = valid_from + data["valid_until"] = valid_until + + return data + + def save(self, _ = False): + a = self.instance if self.instance is not None else Announcement() + + a.valid_from = self.cleaned_data["valid_from"] + a.valid_until = self.cleaned_data["valid_until"] + a.title = self.cleaned_data["title"] + a.description = self.cleaned_data["description"] + + a.save() + + return a + + class Meta: + model = Announcement + exclude = [] diff --git a/aleksis/core/menus.py b/aleksis/core/menus.py index 1c41dbb70..0fdada99f 100644 --- a/aleksis/core/menus.py +++ b/aleksis/core/menus.py @@ -57,6 +57,15 @@ MENUS = { "menu_generator.validators.is_superuser", ], "submenu": [ + { + "name": _("Announcements"), + "url": "announcements", + "icon": "announcement", + "validators": [ + "menu_generator.validators.is_authenticated", + "menu_generator.validators.is_superuser", + ], + }, { "name": _("Data management"), "url": "data_management", diff --git a/aleksis/core/templates/core/announcement/form.html b/aleksis/core/templates/core/announcement/form.html new file mode 100644 index 000000000..0cd31cdef --- /dev/null +++ b/aleksis/core/templates/core/announcement/form.html @@ -0,0 +1,33 @@ +{# -*- engine:django -*- #} + +{% extends "core/base.html" %} + +{% load i18n material_form %} + + +{% block browser_title %} + {% if mode == "edit" %} + {% blocktrans %}Edit announcement{% endblocktrans %} + {% else %} + {% blocktrans %}Publish announcement{% endblocktrans %} + {% endif %} +{% endblock %} +{% block page_title %} + {% if mode == "edit" %} + {% blocktrans %}Edit announcement{% endblocktrans %} + {% else %} + {% blocktrans %}Publish new announcement{% endblocktrans %} + {% endif %} +{% endblock %} + +{% block content %} + <form action="" method="post"> + {% csrf_token %} + {% form form=form %}{% endform %} + + <button type="submit" class="btn green waves-effect waves-light"> + <i class="material-icons left">save</i> + {% trans "Save und publish announcement" %} + </button> + </form> +{% endblock %} diff --git a/aleksis/core/templates/core/announcement/list.html b/aleksis/core/templates/core/announcement/list.html new file mode 100644 index 000000000..e3fe5e4a0 --- /dev/null +++ b/aleksis/core/templates/core/announcement/list.html @@ -0,0 +1,48 @@ +{# -*- engine:django -*- #} + +{% extends "core/base.html" %} + +{% load i18n %} + +{% block browser_title %}{% blocktrans %}Announcements{% endblocktrans %}{% endblock %} +{% block page_title %}{% blocktrans %}Announcements{% endblocktrans %}{% endblock %} + +{% block content %} + <a class="btn green waves-effect waves-light" href="{% url "add_announcement" %}"> + <i class="material-icons left">add</i> + {% trans "Publish new announcement" %} + </a> + <table class="highlight"> + <thead> + <tr> + <th>{% trans "Title" %}</th> + <th>{% trans "Valid from" %}</th> + <th>{% trans "Valid until" %}</th> + <th>{% trans "Recipients" %}</th> + <th>{% trans "Actions" %}</th> + </tr> + </thead> + <tbody> + {% for announcement in announcements %} + <tr> + <td>{{ announcement.title }}</td> + <td>{{ announcement.valid_from }}</td> + <td>{{ announcement.valid_until }}</td> + <td>{{ announcement.recipients.all|join:", " }}</td> + <td> + <a class="btn-flat waves-effect waves-orange orange-text" href="{% url "edit_announcement" announcement.id %}"> + <i class="material-icons left">edit</i> + {% trans "Edit" %} + </a> + </td> + </tr> + {% empty %} + <tr> + <td colspan="4"> + <p class="flow-text">{% trans "There are no announcements." %}</p> + </td> + </tr> + {% endfor %} + </tbody> + </table> +{% endblock %} diff --git a/aleksis/core/urls.py b/aleksis/core/urls.py index 6f1d4275e..894ffcfc2 100644 --- a/aleksis/core/urls.py +++ b/aleksis/core/urls.py @@ -35,6 +35,9 @@ urlpatterns = [ path("group/<int:id_>/edit", views.edit_group, name="edit_group_by_id"), path("", views.index, name="index"), path("notifications/mark-read/<int:id_>", views.notification_mark_read, name="notification_mark_read"), + path("announcements/", views.announcements, name="announcements"), + path("announcement/create/", views.announcement_form, name="add_announcement"), + path("announcement/edit/<int:pk>/", views.announcement_form, name="edit_announcement"), path("maintenance-mode/", include("maintenance_mode.urls")), path("impersonate/", include("impersonate.urls")), path("__i18n__/", include("django.conf.urls.i18n")), diff --git a/aleksis/core/views.py b/aleksis/core/views.py index 98db80da4..4008d1896 100644 --- a/aleksis/core/views.py +++ b/aleksis/core/views.py @@ -15,6 +15,7 @@ from .forms import ( EditSchoolForm, EditTermForm, PersonsAccountsFormSet, + AnnouncementForm, ) from .models import Activity, Group, Notification, Person, School, DashboardWidget, Announcement from .tables import GroupsTable, PersonsTable @@ -271,3 +272,47 @@ def notification_mark_read(request: HttpRequest, id_: int) -> HttpResponse: raise PermissionDenied(_("You are not allowed to mark notifications from other users as read!")) return redirect("index") + + +@admin_required +def announcements(request: HttpRequest) -> HttpResponse: + context = {} + + # Get all persons + announcements = Announcement.objects.all() + context["announcements"] = announcements + + return render(request, "core/announcement/list.html", context) + + +@admin_required +def announcement_form(request: HttpRequest, pk: Optional[int] = None) -> HttpResponse: + context = {} + + if pk: + announcement = get_object_or_404(Announcement, pk=pk) + form = AnnouncementForm( + request.POST or None, + instance=announcement, + initial={ + "valid_from_date": announcement.valid_from.date(), + "valid_from_time": announcement.valid_from.time(), + "valid_until_date": announcement.valid_until.date(), + "valid_until_time": announcement.valid_until.time() + } + ) + context["mode"] = "edit" + else: + form = AnnouncementForm(request.POST or None, initial=AnnouncementForm.get_initial()) + context["mode"] = "add" + + if request.method == "POST": + if form.is_valid(): + form.save() + + messages.success(request, _("The announcement has been saved.")) + return redirect("announcements") + + context["form"] = form + + return render(request, "core/announcement/form.html", context) -- GitLab