From 56a672cef605487426bb76cdb045793d8b829d83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Kl=C3=BCnder?= Date: Sun, 10 Dec 2017 15:23:53 +0100 Subject: [PATCH] manage announcements --- src/c3nav/control/forms.py | 12 +++++ .../templates/control/announcement.html | 12 +++++ .../templates/control/announcements.html | 34 +++++++++++++ src/c3nav/control/templates/control/base.html | 3 ++ src/c3nav/control/urls.py | 5 +- src/c3nav/control/views.py | 49 ++++++++++++++++++- .../migrations/0002_announcement_tweaks.py | 33 +++++++++++++ src/c3nav/site/models.py | 6 ++- 8 files changed, 150 insertions(+), 4 deletions(-) create mode 100644 src/c3nav/control/templates/control/announcement.html create mode 100644 src/c3nav/control/templates/control/announcements.html create mode 100644 src/c3nav/site/migrations/0002_announcement_tweaks.py diff --git a/src/c3nav/control/forms.py b/src/c3nav/control/forms.py index 39fad030..98da4143 100644 --- a/src/c3nav/control/forms.py +++ b/src/c3nav/control/forms.py @@ -8,7 +8,9 @@ from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ungettext_lazy from c3nav.control.models import UserPermissions +from c3nav.mapdata.forms import I18nModelFormMixin from c3nav.mapdata.models.access import AccessPermissionToken, AccessPermissionTokenItem, AccessRestriction +from c3nav.site.models import Announcement class UserPermissionsForm(ModelForm): @@ -108,3 +110,13 @@ class AccessPermissionForm(Form): return AccessPermissionToken(author=self.author, can_grant=self.cleaned_data.get('can_grant', '0') == '1', restrictions=tuple(restrictions)) + + +class AnnouncementForm(I18nModelFormMixin, ModelForm): + class Meta: + model = Announcement + fields = ('text', 'active', 'active_until') + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields['active_until'].initial = timezone.now() diff --git a/src/c3nav/control/templates/control/announcement.html b/src/c3nav/control/templates/control/announcement.html new file mode 100644 index 00000000..1ecd2edf --- /dev/null +++ b/src/c3nav/control/templates/control/announcement.html @@ -0,0 +1,12 @@ +{% extends 'control/base.html' %} +{% load i18n %} + +{% block heading %}{% trans 'Edit announcement' %}{% endblock %} + +{% block subcontent %} +
+ {% csrf_token %} + {{ form }} + +
+{% endblock %} diff --git a/src/c3nav/control/templates/control/announcements.html b/src/c3nav/control/templates/control/announcements.html new file mode 100644 index 00000000..9d36a99c --- /dev/null +++ b/src/c3nav/control/templates/control/announcements.html @@ -0,0 +1,34 @@ +{% extends 'control/base.html' %} +{% load i18n %} + +{% block heading %}{% trans 'Users' %}{% endblock %} + +{% block subcontent %} + + + + + + + + + + {% for announcement in announcements %} + + + + + + + + + {% endfor %} +
{% trans 'ID' %}{% trans 'Text' %}{% trans 'Author' %}{% trans 'Active' %}{% trans 'Active until' %}
{{ announcement.id }}{{ announcement.text }}{{ announcement.author }}{{ announcement.active }}{{ announcement.active_until }}{% trans 'Edit' %}
+ +

{% trans 'Create new announcement' %}

+
+ {% csrf_token %} + {{ form }} + +
+{% endblock %} diff --git a/src/c3nav/control/templates/control/base.html b/src/c3nav/control/templates/control/base.html index d9a0316b..038298f8 100644 --- a/src/c3nav/control/templates/control/base.html +++ b/src/c3nav/control/templates/control/base.html @@ -15,6 +15,9 @@ {% trans 'Overview' %} · {% trans 'Users' %} · {% trans 'Access' %} + {% if request.user_permissions.manage_announcements %} + · {% trans 'Announcements' %} + {% endif %}

{% endblock %} diff --git a/src/c3nav/control/urls.py b/src/c3nav/control/urls.py index 97a12d96..f6a220d9 100644 --- a/src/c3nav/control/urls.py +++ b/src/c3nav/control/urls.py @@ -1,11 +1,14 @@ from django.conf.urls import url -from c3nav.control.views import grant_access, grant_access_qr, main_index, user_detail, user_list +from c3nav.control.views import (announcement_detail, announcement_list, grant_access, grant_access_qr, main_index, + user_detail, user_list) urlpatterns = [ url(r'^users/$', user_list, name='control.users'), url(r'^users/(?P\d+)/$', user_detail, name='control.users.detail'), url(r'^access/$', grant_access, name='control.access'), url(r'^access/(?P[^/]+)$', grant_access_qr, name='control.access.qr'), + url(r'^announcements/$', announcement_list, name='control.announcements'), + url(r'^announcements/(?P\d+)/$', announcement_detail, name='control.announcements.detail'), url(r'^$', main_index, name='control.index'), ] diff --git a/src/c3nav/control/views.py b/src/c3nav/control/views.py index e2b29ca4..200b388f 100644 --- a/src/c3nav/control/views.py +++ b/src/c3nav/control/views.py @@ -11,9 +11,10 @@ from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse from django.utils.translation import ugettext_lazy as _ -from c3nav.control.forms import AccessPermissionForm, UserPermissionsForm +from c3nav.control.forms import AccessPermissionForm, AnnouncementForm, UserPermissionsForm from c3nav.control.models import UserPermissions from c3nav.mapdata.models.access import AccessPermission, AccessPermissionToken +from c3nav.site.models import Announcement def control_panel_view(func): @@ -170,3 +171,49 @@ def grant_access_qr(request, token): 'url_qr': reverse('site.qr', kwargs={'path': url}), 'url_absolute': request.build_absolute_uri(url), }) + + +@login_required +@control_panel_view +def announcement_list(request): + if not request.user_permissions.manage_announcements: + raise PermissionDenied + + announcements = Announcement.objects.order_by('id') + + if request.method == 'POST': + form = AnnouncementForm(data=request.POST) + if form.is_valid(): + announcement = form.instance + announcement = request.user + announcement.save() + return redirect('control.announcements') + else: + form = AnnouncementForm() + + return render(request, 'control/announcements.html', { + 'form': form, + 'announcements': announcements, + }) + + +@login_required +@control_panel_view +def announcement_detail(request, announcement): + if not request.user_permissions.manage_announcements: + raise PermissionDenied + + announcement = get_object_or_404(Announcement, pk=announcement) + + if request.method == 'POST': + form = AnnouncementForm(instance=announcement, data=request.POST) + if form.is_valid(): + form.save() + return redirect('control.announcements') + else: + form = AnnouncementForm(instance=announcement) + + return render(request, 'control/announcement.html', { + 'form': form, + 'announcement': announcement, + }) diff --git a/src/c3nav/site/migrations/0002_announcement_tweaks.py b/src/c3nav/site/migrations/0002_announcement_tweaks.py new file mode 100644 index 00000000..b92dcfdc --- /dev/null +++ b/src/c3nav/site/migrations/0002_announcement_tweaks.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2017-12-10 14:14 +from __future__ import unicode_literals + +import c3nav.mapdata.fields +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('site', '0001_announcement'), + ] + + operations = [ + migrations.RemoveField( + model_name='announcement', + name='message', + ), + migrations.AddField( + model_name='announcement', + name='author', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='announcements', to=settings.AUTH_USER_MODEL, verbose_name='author'), + ), + migrations.AddField( + model_name='announcement', + name='text', + field=c3nav.mapdata.fields.I18nField(fallback_any=True, verbose_name='Text'), + ), + ] diff --git a/src/c3nav/site/models.py b/src/c3nav/site/models.py index 841b4321..fb6fd608 100644 --- a/src/c3nav/site/models.py +++ b/src/c3nav/site/models.py @@ -1,3 +1,4 @@ +from django.conf import settings from django.db import models from django.db.models import Q from django.utils import timezone @@ -10,7 +11,8 @@ class Announcement(models.Model): created = models.DateTimeField(auto_now_add=True, verbose_name=_('created')) active_until = models.DateTimeField(null=True, verbose_name=_('active until')) active = models.BooleanField(default=True, verbose_name=_('active')) - message = I18nField(_('Message')) + author = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.PROTECT, verbose_name=_('author')) + text = I18nField(_('Text'), fallback_any=True) class Meta: verbose_name = _('Announcement') @@ -22,6 +24,6 @@ class Announcement(models.Model): def get_current(cls): try: return cls.objects.filter(Q(active=True) & (Q(active_until__isnull=True) | - Q(active_until__isnull=timezone.now()))).latest() + Q(active_until__gt=timezone.now()))).latest() except cls.DoesNotExist: return None