From 470680e760012b50c6ec23327c7d8038193a4453 Mon Sep 17 00:00:00 2001 From: klmp200 Date: Mon, 21 Aug 2017 19:53:17 +0200 Subject: [PATCH] Add moderation for mailing lists --- api/views/club.py | 2 +- club/migrations/0011_auto_20170821_1702.py | 26 ++++++++++++++++ club/models.py | 35 ++++++++++++++++------ club/templates/club/mailing.jinja | 4 ++- club/views.py | 20 ++++++++++--- com/templates/com/mailing_admin.jinja | 25 ++++++++++++---- com/urls.py | 3 ++ com/views.py | 22 ++++++++++++-- migrate.py | 5 +++- sith/settings.py | 1 + 10 files changed, 119 insertions(+), 24 deletions(-) create mode 100644 club/migrations/0011_auto_20170821_1702.py diff --git a/api/views/club.py b/api/views/club.py index ece79602..03b7f36c 100644 --- a/api/views/club.py +++ b/api/views/club.py @@ -58,6 +58,6 @@ def FetchMailingLists(request): if key != settings.SITH_MAILING_FETCH_KEY: raise PermissionDenied data = '' - for mailing in Mailing.objects.all(): + for mailing in Mailing.objects.filter(is_moderated=True).all(): data += mailing.fetch_format() return Response(data) diff --git a/club/migrations/0011_auto_20170821_1702.py b/club/migrations/0011_auto_20170821_1702.py new file mode 100644 index 00000000..04588b8b --- /dev/null +++ b/club/migrations/0011_auto_20170821_1702.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +from django.conf import settings + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('club', '0010_auto_20170817_1537'), + ] + + operations = [ + migrations.AddField( + model_name='mailing', + name='is_moderated', + field=models.BooleanField(default=False, verbose_name='is moderated'), + ), + migrations.AddField( + model_name='mailing', + name='moderator', + field=models.ForeignKey(related_name='moderated_mailings', to=settings.AUTH_USER_MODEL, null=True, verbose_name='moderator'), + ), + ] diff --git a/club/models.py b/club/models.py index 7c05faa5..5bf85d86 100644 --- a/club/models.py +++ b/club/models.py @@ -27,12 +27,12 @@ from django.db import models from django.core import validators from django.conf import settings from django.utils.translation import ugettext_lazy as _ -from django.core.exceptions import ValidationError +from django.core.exceptions import ValidationError, ObjectDoesNotExist from django.db import transaction from django.core.urlresolvers import reverse from django.utils import timezone -from core.models import User, MetaGroup, Group, SithFile +from core.models import User, MetaGroup, Group, SithFile, RealGroup, Notification # Create your models here. @@ -230,11 +230,18 @@ class Mailing(models.Model): """ club = models.ForeignKey(Club, verbose_name=_('Club'), related_name="mailings", null=False, blank=False) email = models.EmailField(_('Email address'), unique=True) + is_moderated = models.BooleanField(_('is moderated'), default=False) + moderator = models.ForeignKey(User, related_name="moderated_mailings", verbose_name=_("moderator"), null=True) def clean(self): - if '@' + settings.SITH_MAILING_DOMAIN not in self.email: - raise ValidationError(_('Unothorized mailing domain')) - super(Mailing, self).clean() + if self.can_moderate(self.moderator): + self.is_moderated = True + else: + self.moderator = None + super(Mailing, self).clean() + + def can_moderate(self, user): + return user.is_root or user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID) def is_owned_by(self, user): return user.is_in_group(self) or user.is_root or user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID) @@ -256,6 +263,13 @@ class Mailing(models.Model): resp += sub.fetch_format() return resp + def save(self): + if not self.is_moderated: + for user in RealGroup.objects.filter(id=settings.SITH_GROUP_COM_ADMIN_ID).first().users.all(): + if not user.notifications.filter(type="MAILING_MODERATION", viewed=False).exists(): + Notification(user=user, url=reverse('com:mailing_admin'), type="MAILING_MODERATION").save() + super(Mailing, self).save() + def __str__(self): return "%s - %s" % (self.club, self.email) @@ -274,10 +288,13 @@ class MailingSubscription(models.Model): def clean(self): if not self.user and not self.email: raise ValidationError(_("At least user or email is required")) - if self.user and not self.email: - self.email = self.user.email - if MailingSubscription.objects.filter(mailing=self.mailing, email=self.email).exists(): - raise ValidationError(_("This email is already suscribed in this mailing")) + try: + if self.user and not self.email: + self.email = self.user.email + if MailingSubscription.objects.filter(mailing=self.mailing, email=self.email).exists(): + raise ValidationError(_("This email is already suscribed in this mailing")) + except ObjectDoesNotExist: + pass super(MailingSubscription, self).clean() def is_owned_by(self, user): diff --git a/club/templates/club/mailing.jinja b/club/templates/club/mailing.jinja index 6e51cf15..797abb12 100644 --- a/club/templates/club/mailing.jinja +++ b/club/templates/club/mailing.jinja @@ -7,9 +7,10 @@ {% block content %} {% if has_objects %} - {% trans %}Remember : mailing lists need to be validated by the school to work, please inform us about any new mailing list created{% endtrans %} + {% trans %}Remember : mailing lists need to be moderated, if your new created list is not shown wait until moderation takes action{% endtrans %} {% for mailing in object_list %} + {% if mailing.is_moderated %}

{% trans %}Mailing{% endtrans %} {{ mailing.email }} {%- if user.is_owner(mailing) -%} - {% trans %}Delete{% endtrans %} @@ -33,6 +34,7 @@ {% endfor %} + {% endif %} {% endfor %} {% else %} diff --git a/club/views.py b/club/views.py index f0f967ff..77a1979d 100644 --- a/club/views.py +++ b/club/views.py @@ -43,6 +43,7 @@ from core.views.forms import SelectDate, SelectDateTime from club.models import Club, Membership, Mailing, MailingSubscription from sith.settings import SITH_MAXIMUM_FREE_ROLE from counter.models import Selling, Counter +from core.models import User from django.conf import settings @@ -52,7 +53,7 @@ from django.conf import settings class MailingForm(forms.ModelForm): class Meta: model = Mailing - fields = ('email', 'club') + fields = ('email', 'club', 'moderator') email = forms.CharField( label=_('Email address'), @@ -66,11 +67,16 @@ class MailingForm(forms.ModelForm): def __init__(self, *args, **kwargs): club_id = kwargs.pop('club_id', None) + user_id = kwargs.pop('user_id', -1) # Remember 0 is treated as None super(MailingForm, self).__init__(*args, **kwargs) if club_id: self.fields['club'].queryset = Club.objects.filter(id=club_id) self.fields['club'].initial = club_id self.fields['club'].widget = forms.HiddenInput() + if user_id >= 0: + self.fields['moderator'].queryset = User.objects.filter(id=user_id) + self.fields['moderator'].initial = user_id + self.fields['moderator'].widget = forms.HiddenInput() def clean(self): cleaned_data = super(MailingForm, self).clean() @@ -85,11 +91,12 @@ class MailingSubscriptionForm(forms.ModelForm): fields = ('mailing', 'user', 'email') def __init__(self, *args, **kwargs): + kwargs.pop('user_id', None) # For standart interface club_id = kwargs.pop('club_id', None) super(MailingSubscriptionForm, self).__init__(*args, **kwargs) self.fields['email'].required = False if club_id: - self.fields['mailing'].queryset = Mailing.objects.filter(club__id=club_id) + self.fields['mailing'].queryset = Mailing.objects.filter(club__id=club_id, is_moderated=True) user = AutoCompleteSelectField('users', label=_('User'), help_text=None, required=False) @@ -419,7 +426,7 @@ class ClubMailingView(ClubTabsMixin, ListView): if not self.authorized(): raise PermissionDenied self.member_form = MailingSubscriptionForm(club_id=self.club.id) - self.mailing_form = MailingForm(club_id=self.club.id) + self.mailing_form = MailingForm(club_id=self.club.id, user_id=self.user.id) return super(ClubMailingView, self).dispatch(request, *args, **kwargs) def post(self, request, *args, **kwargs): @@ -464,6 +471,7 @@ class MailingGenericCreateView(CreateView, SingleObjectMixin): def get_form_kwargs(self): kwargs = super(MailingGenericCreateView, self).get_form_kwargs() kwargs['club_id'] = self.list_view.club.id + kwargs['user_id'] = self.list_view.user.id return kwargs def dispatch(self, request, *args, **kwargs): @@ -481,13 +489,17 @@ class MailingDeleteView(CanEditMixin, DeleteView): model = Mailing template_name = 'core/delete_confirm.jinja' pk_url_kwarg = "mailing_id" + redirect_page = None def dispatch(self, request, *args, **kwargs): self.club_id = self.get_object().club.id return super(MailingDeleteView, self).dispatch(request, *args, **kwargs) def get_success_url(self, **kwargs): - return reverse_lazy('club:mailing', kwargs={'club_id': self.club_id}) + if self.redirect_page: + return reverse_lazy(self.redirect_page) + else: + return reverse_lazy('club:mailing', kwargs={'club_id': self.club_id}) class MailingSubscriptionDeleteView(CanEditMixin, DeleteView): diff --git a/com/templates/com/mailing_admin.jinja b/com/templates/com/mailing_admin.jinja index b0c7be95..15784fc1 100644 --- a/com/templates/com/mailing_admin.jinja +++ b/com/templates/com/mailing_admin.jinja @@ -4,22 +4,37 @@ {% trans %}Mailing lists administration{% endtrans %} {% endblock %} -{% block content %} -

{% trans %}This page lists all existing mailing lists{% endtrans %}

- {% if has_objects %} +{% macro display_mailings(list) %} + - {% for mailing in object_list %} + {% for mailing in list %} - + + {% endfor %}
{% trans %}Email{% endtrans %} {% trans %}Club{%endtrans%}{% trans %}Actions{% endtrans %}
{{ mailing.email }}{{ mailing.club }} {% trans %}Delete{% endtrans %}{{ mailing.club }} + {% trans %}Delete{% endtrans %} - {% if not mailing.is_moderated %}{% trans %}Moderate{% endtrans %}{% else %}{% trans user=mailing.moderator %}Moderated by {{ user }}{% endtrans %}{% endif %} +
+ +{% endmacro %} +{% block content %} +

{% trans %}This page lists all mailing lists{% endtrans %}

+ + {% if has_unmoderated %} +

{% trans %}Not moderated mailing lists{% endtrans %}

+ {{ display_mailings(unmoderated) }} + {% endif %} + +

{% trans %}Moderated mailing lists{% endtrans %}

+ {% if has_moderated %} + {{ display_mailings(moderated) }} {% else %}

{% trans %}No mailing list existing{% endtrans %}

{% endif %} diff --git a/com/urls.py b/com/urls.py index 89f0d92c..f5421758 100644 --- a/com/urls.py +++ b/com/urls.py @@ -25,6 +25,7 @@ from django.conf.urls import url from com.views import * +from club.views import MailingDeleteView urlpatterns = [ url(r'^sith/edit/alert$', AlertMsgEditView.as_view(), name='alert_edit'), @@ -43,5 +44,7 @@ urlpatterns = [ url(r'^news/(?P[0-9]+)/edit$', NewsEditView.as_view(), name='news_edit'), url(r'^news/(?P[0-9]+)$', NewsDetailView.as_view(), name='news_detail'), url(r'^mailings$', MailingListAdminView.as_view(), name='mailing_admin'), + url(r'^mailings/(?P[0-9]+)/moderate$', MailingModerateView.as_view(), name='mailing_moderate'), + url(r'^mailings/(?P[0-9]+)/delete$', MailingDeleteView.as_view(redirect_page='com:mailing_admin'), name='mailing_delete'), ] diff --git a/com/views.py b/com/views.py index a005ab8f..942ed335 100644 --- a/com/views.py +++ b/com/views.py @@ -25,7 +25,7 @@ from django.shortcuts import redirect, get_object_or_404 from django.http import HttpResponseRedirect -from django.views.generic import ListView, DetailView +from django.views.generic import ListView, DetailView, View from django.views.generic.edit import UpdateView, CreateView, DeleteView from django.views.generic.detail import SingleObjectMixin from django.utils.translation import ugettext_lazy as _ @@ -429,11 +429,27 @@ class MailingListAdminView(ComTabsMixin, ListView): current_tab = "mailings" def dispatch(self, request, *args, **kwargs): - if not request.user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID) or request.user.is_root: + if not (request.user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID) or request.user.is_root): raise PermissionDenied return super(MailingListAdminView, self).dispatch(request, *args, **kwargs) def get_context_data(self, **kwargs): kwargs = super(MailingListAdminView, self).get_context_data(**kwargs) - kwargs['has_objects'] = len(kwargs['object_list']) > 0 + kwargs['moderated'] = self.get_queryset().filter(is_moderated=True).all() + kwargs['unmoderated'] = self.get_queryset().filter(is_moderated=False).all() + kwargs['has_moderated'] = len(kwargs['moderated']) > 0 + kwargs['has_unmoderated'] = len(kwargs['unmoderated']) > 0 return kwargs + + +class MailingModerateView(View): + + def get(self, request, *args, **kwargs): + mailing = get_object_or_404(Mailing, pk=kwargs['mailing_id']) + if mailing.can_moderate(request.user): + mailing.is_moderated = True + mailing.moderator = request.user + mailing.save() + return redirect('com:mailing_admin') + + raise PermissionDenied diff --git a/migrate.py b/migrate.py index 475d0633..5858af46 100644 --- a/migrate.py +++ b/migrate.py @@ -1346,6 +1346,8 @@ def migrate_mailings(): SELECT * FROM mailing """) + moderator = User.objects.get(id=0) + for mailing in cur: club = Club.objects.filter(id=mailing['id_asso_parent']) if club.exists(): @@ -1353,7 +1355,8 @@ def migrate_mailings(): club = club.first() if mailing['nom']: mailing['nom'] = '.' + mailing['nom'] - Mailing(id=mailing['id_mailing'], club=club, email=to_unicode(club.unix_name + mailing['nom'] + '@utbm.fr')).save() + Mailing(id=mailing['id_mailing'], club=club, email=to_unicode(club.unix_name + mailing['nom'] + '@utbm.fr'), + moderator=moderator, is_moderated=(mailing['is_valid'] > 0)).save() print("-------------------") cur.execute(""" diff --git a/sith/settings.py b/sith/settings.py index 574b1492..a448a394 100644 --- a/sith/settings.py +++ b/sith/settings.py @@ -543,6 +543,7 @@ SITH_LAUNDERETTE_PRICES = { } SITH_NOTIFICATIONS = [ + ('MAILING_MODERATION', _("A new mailing list neet to be moderated")), ('NEWS_MODERATION', _("A fresh new to be moderated")), ('FILE_MODERATION', _("New files to be moderated")), ('SAS_MODERATION', _("New pictures/album to be moderated in the SAS")),