Notification emails #80
@ -39,6 +39,7 @@ urlpatterns = [
|
||||
path('', include('users.urls')),
|
||||
path('', include('teams.urls')),
|
||||
path('', include('reviewers.urls')),
|
||||
path('', include('notifications.urls')),
|
||||
path('api/swagger/', RedirectView.as_view(url='/api/v1/swagger/')),
|
||||
path('api/v1/', SpectacularAPIView.as_view(), name='schema_v1'),
|
||||
path('api/v1/swagger/', SpectacularSwaggerView.as_view(url_name='schema_v1'), name='swagger'),
|
||||
|
22
notifications/templates/notifications/notification_list.html
Normal file
22
notifications/templates/notifications/notification_list.html
Normal file
@ -0,0 +1,22 @@
|
||||
{% extends "common/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block page_title %}{% blocktranslate %}Notifications{% endblocktranslate %}{% endblock page_title %}
|
||||
|
||||
{% block content %}
|
||||
{% if notification_list %}
|
||||
{% for notification in notification_list %}
|
||||
<div class="row">
|
||||
{{ notification.action }}
|
||||
{% if notification.read_at %}
|
||||
{% else %}
|
||||
{% blocktranslate %}Mark as read{% endblocktranslate %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<p>
|
||||
{% blocktranslate %}You have no notifications{% endblocktranslate %}
|
||||
</p>
|
||||
{% endif %}
|
||||
{% endblock content %}
|
25
notifications/urls.py
Normal file
25
notifications/urls.py
Normal file
@ -0,0 +1,25 @@
|
||||
from django.urls import path, include
|
||||
|
||||
import notifications.views as views
|
||||
|
||||
app_name = 'notifications'
|
||||
urlpatterns = [
|
||||
path(
|
||||
'notifications/',
|
||||
include(
|
||||
[
|
||||
path('', views.NotificationsView.as_view(), name='notifications'),
|
||||
path(
|
||||
'mark-read-all/',
|
||||
views.MarkReadAllView.as_view(),
|
||||
name='notifications-mark-read-all',
|
||||
),
|
||||
path(
|
||||
'<int:pk>/mark-read/',
|
||||
views.MarkReadView.as_view(),
|
||||
name='notifications-mark-read',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
]
|
49
notifications/views.py
Normal file
49
notifications/views.py
Normal file
@ -0,0 +1,49 @@
|
||||
"""Notifications pages."""
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.http import HttpResponseForbidden
|
||||
from django.http.response import JsonResponse
|
||||
from django.utils import timezone
|
||||
from django.views.generic import ListView
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
from django.views.generic.edit import FormView
|
||||
from django.views import View
|
||||
|
||||
from notifications.models import Notification
|
||||
|
||||
|
||||
class NotificationsView(LoginRequiredMixin, ListView):
|
||||
model = Notification
|
||||
ordering = None # FIXME
|
||||
paginate_by = 10
|
||||
|
||||
def get_queryset(self):
|
||||
return Notification.objects.filter(recipient=self.request.user)
|
||||
|
||||
|
||||
class MarkReadAllView(LoginRequiredMixin, FormView):
|
||||
model = Notification
|
||||
raise_exception = True
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""Mark all previously unread notifications as read."""
|
||||
unread = self.model.objects.filter(recipient=request.user, read_at__isnull=True)
|
||||
now = timezone.now()
|
||||
for notification in unread:
|
||||
notification.read_at = now
|
||||
|
||||
Notification.objects.bulk_update(unread, ['read_at'])
|
||||
|
||||
return JsonResponse({})
|
||||
|
||||
|
||||
class MarkReadView(LoginRequiredMixin, SingleObjectMixin, View):
|
||||
model = Notification
|
||||
raise_exception = True
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
notification = self.get_object()
|
||||
if notification.recipient != request.user:
|
||||
return HttpResponseForbidden()
|
||||
notification.read_at = timezone.now()
|
||||
notification.save(update_fields=['read_at'])
|
||||
return JsonResponse({})
|
Loading…
Reference in New Issue
Block a user