Even better mailing

This commit is contained in:
Antoine Bartuccio 2017-08-17 20:55:20 +02:00
parent feaf6b73b7
commit 9cb88a878d
11 changed files with 202 additions and 23 deletions

View File

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('club', '0009_mailing_mailingsubscription'),
]
operations = [
migrations.AlterField(
model_name='mailingsubscription',
name='email',
field=models.EmailField(verbose_name='Email address', max_length=254),
),
migrations.AlterUniqueTogether(
name='mailingsubscription',
unique_together=set([('user', 'email', 'mailing')]),
),
]

View File

@ -226,16 +226,21 @@ class Membership(models.Model):
class Mailing(models.Model): class Mailing(models.Model):
""" """
This class correspond to a mailing list This class correspond to a mailing list
Remember that mailing lists should be validated by UTBM
""" """
club = models.ForeignKey(Club, verbose_name=_('Club'), related_name="mailings", null=False, blank=False) club = models.ForeignKey(Club, verbose_name=_('Club'), related_name="mailings", null=False, blank=False)
email = models.EmailField(_('Email address'), unique=True) email = models.EmailField(_('Email address'), unique=True)
def is_owned_by(self, user): def is_owned_by(self, user):
return self.club.has_rights_in_club(user) or user.is_root return user.is_in_group(self) or user.is_root or user.is_board_member
def can_be_edited_by(self, user): def can_view(self, user):
return self.is_owned_by(user) return self.club.has_rights_in_club(user)
def delete(self):
for sub in self.subscriptions.all():
sub.delete()
super(Mailing, self).delete()
def __str__(self): def __str__(self):
return "%s - %s" % (self.club, self.email) return "%s - %s" % (self.club, self.email)
@ -243,14 +248,31 @@ class Mailing(models.Model):
class MailingSubscription(models.Model): class MailingSubscription(models.Model):
""" """
This class make the link between user and mailing list This class makes the link between user and mailing list
""" """
mailing = models.ForeignKey(Mailing, verbose_name=_('Mailing'), related_name="subscriptions", null=False, blank=False) mailing = models.ForeignKey(Mailing, verbose_name=_('Mailing'), related_name="subscriptions", null=False, blank=False)
user = models.ForeignKey(User, verbose_name=_('User'), related_name="mailing_subscriptions", null=True, blank=True) user = models.ForeignKey(User, verbose_name=_('User'), related_name="mailing_subscriptions", null=True, blank=True)
email = models.EmailField(_('Email address'), unique=True) email = models.EmailField(_('Email address'), blank=False, null=False)
class Meta:
unique_together = (('user', 'email', 'mailing'),)
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
super(MailingSubscription, self).clean()
def is_owned_by(self, user): def is_owned_by(self, user):
return self.mailing.club.has_rights_in_club(user) or user.is_root return self.mailing.club.has_rights_in_club(user) or user.is_root
def can_be_edited_by(self, user): def can_be_edited_by(self, user):
return self.is_owned_by(user) or (user is not None and user.id == self.user.id) return self.is_owned_by(user) or (user is not None and user.id == self.user.id)
def __str__(self):
if self.user:
user = str(self.user)
else:
user = _("Unregistered user")
return "(%s) - %s : %s" % (self.mailing, user, self.email)

View File

@ -8,11 +8,16 @@
{% if has_objects %} {% if has_objects %}
{% for mailing in object_list %} {% for mailing in object_list %}
<h2>{% trans %}Mailing{% endtrans %} {{ mailing.email }}</h2> <h2>{% trans %}Mailing{% endtrans %} {{ mailing.email }}
{%- if user.is_owner(mailing) -%}
<a href="{{ url('club:mailing_delete', mailing_id=mailing.id) }}">{% trans %}Delete{% endtrans %}</a>
{%- endif -%}
</h2>
<hr>
<table> <table>
<tr> <tr>
<th>{% trans %}User{% endtrans %}</th> <th>{% trans %}User{% endtrans %}</th>
<th>{% trans %}Mail{%endtrans%}</th> <th colspan="2">{% trans %}Email{%endtrans%}</th>
</tr> </tr>
{% for subscriber in mailing.subscriptions.all() %} {% for subscriber in mailing.subscriptions.all() %}
<tr> <tr>
@ -22,6 +27,7 @@
<td>{% trans %}Unregistered user{% endtrans %}</td> <td>{% trans %}Unregistered user{% endtrans %}</td>
{% endif %} {% endif %}
<td>{{ subscriber.email }}</td> <td>{{ subscriber.email }}</td>
<td><a href="{{ url('club:mailing_subscription_delete', mailing_subscription_id=subscriber.id) }}">{% trans %}Delete{% endtrans %}</a></td>
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>
@ -31,8 +37,6 @@
<p>{% trans %}No mailing list existing for this club{% endtrans %}</p> <p>{% trans %}No mailing list existing for this club{% endtrans %}</p>
{% endif %} {% endif %}
{% if club.has_rights_in_club(user) %}
{% if has_objects %} {% if has_objects %}
<h2>{% trans %}New member{% endtrans %}</h2> <h2>{% trans %}New member{% endtrans %}</h2>
<form action="{{ url('club:mailing_subscription_create', club_id=club.id) }}" method="post" enctype="multipart/form-data"> <form action="{{ url('club:mailing_subscription_create', club_id=club.id) }}" method="post" enctype="multipart/form-data">
@ -48,9 +52,5 @@
{{ new_mailing.as_p() }} {{ new_mailing.as_p() }}
<p><input type="submit" value="{% trans %}Create mailing list{% endtrans %}" /></p> <p><input type="submit" value="{% trans %}Create mailing list{% endtrans %}" /></p>
</form> </form>
{% endif %}
{% endblock %} {% endblock %}

View File

@ -42,5 +42,7 @@ urlpatterns = [
url(r'^(?P<club_id>[0-9]+)/mailing$', ClubMailingView.as_view(action=MailingFormType.DISPLAY), name='mailing'), url(r'^(?P<club_id>[0-9]+)/mailing$', ClubMailingView.as_view(action=MailingFormType.DISPLAY), name='mailing'),
url(r'^(?P<club_id>[0-9]+)/mailing/new/mailing$', ClubMailingView.as_view(action=MailingFormType.MAILING), name='mailing_create'), url(r'^(?P<club_id>[0-9]+)/mailing/new/mailing$', ClubMailingView.as_view(action=MailingFormType.MAILING), name='mailing_create'),
url(r'^(?P<club_id>[0-9]+)/mailing/new/subscription$', ClubMailingView.as_view(action=MailingFormType.MEMBER), name='mailing_subscription_create'), url(r'^(?P<club_id>[0-9]+)/mailing/new/subscription$', ClubMailingView.as_view(action=MailingFormType.MEMBER), name='mailing_subscription_create'),
url(r'^(?P<mailing_id>[0-9]+)/mailing/delete$', MailingDeleteView.as_view(), name='mailing_delete'),
url(r'^(?P<mailing_subscription_id>[0-9]+)/mailing/delete/subscription$', MailingSubscriptionDeleteView.as_view(), name='mailing_subscription_delete'),
url(r'^membership/(?P<membership_id>[0-9]+)/set_old$', MembershipSetOldView.as_view(), name='membership_set_old'), url(r'^membership/(?P<membership_id>[0-9]+)/set_old$', MembershipSetOldView.as_view(), name='membership_set_old'),
] ]

View File

@ -35,6 +35,7 @@ from django.utils import timezone
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ugettext as _t from django.utils.translation import ugettext as _t
from ajax_select.fields import AutoCompleteSelectField from ajax_select.fields import AutoCompleteSelectField
from django.core.exceptions import PermissionDenied
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin, CanCreateMixin from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin, CanCreateMixin
@ -56,6 +57,8 @@ class MailingForm(forms.ModelForm):
super(MailingForm, self).__init__(*args, **kwargs) super(MailingForm, self).__init__(*args, **kwargs)
if club_id: if club_id:
self.fields['club'].queryset = Club.objects.filter(id=club_id) self.fields['club'].queryset = Club.objects.filter(id=club_id)
self.fields['club'].initial = club_id
self.fields['club'].widget = forms.HiddenInput()
class MailingSubscriptionForm(forms.ModelForm): class MailingSubscriptionForm(forms.ModelForm):
@ -66,9 +69,22 @@ class MailingSubscriptionForm(forms.ModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
club_id = kwargs.pop('club_id', None) club_id = kwargs.pop('club_id', None)
super(MailingSubscriptionForm, self).__init__(*args, **kwargs) super(MailingSubscriptionForm, self).__init__(*args, **kwargs)
self.fields['email'].required = False
if club_id: 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)
def clean(self):
cleaned_data = super(MailingSubscriptionForm, self).clean()
user = cleaned_data.get('user', None)
email = cleaned_data.get('email', None)
mailing = cleaned_data.get('mailing')
if not user and not email:
raise forms.ValidationError(_("At least user or email should be filled"))
if user and not email:
email = user.email
if user and MailingSubscription.objects.filter(mailing=mailing, email=email).exists():
raise forms.ValidationError(_("This email is already suscribed in this mailing"))
user = AutoCompleteSelectField('users', label=_('User'), help_text=None, required=False) user = AutoCompleteSelectField('users', label=_('User'), help_text=None, required=False)
@ -94,11 +110,6 @@ class ClubTabsMixin(TabedViewMixin):
'slug': 'elderlies', 'slug': 'elderlies',
'name': _("Old members"), 'name': _("Old members"),
}) })
tab_list.append({
'url': reverse('club:mailing', kwargs={'club_id': self.object.id}),
'slug': 'mailing',
'name': _("Mailing list"),
})
if self.request.user.can_edit(self.object): if self.request.user.can_edit(self.object):
tab_list.append({ tab_list.append({
'url': reverse('club:tools', kwargs={'club_id': self.object.id}), 'url': reverse('club:tools', kwargs={'club_id': self.object.id}),
@ -115,6 +126,11 @@ class ClubTabsMixin(TabedViewMixin):
'slug': 'sellings', 'slug': 'sellings',
'name': _("Sellings"), 'name': _("Sellings"),
}) })
tab_list.append({
'url': reverse('club:mailing', kwargs={'club_id': self.object.id}),
'slug': 'mailing',
'name': _("Mailing list"),
})
if self.request.user.is_owner(self.object): if self.request.user.is_owner(self.object):
tab_list.append({ tab_list.append({
'url': reverse('club:club_prop', kwargs={'club_id': self.object.id}), 'url': reverse('club:club_prop', kwargs={'club_id': self.object.id}),
@ -384,17 +400,24 @@ class MailingFormType(Enum):
MAILING = 2 MAILING = 2
class ClubMailingView(CanViewMixin, ListView): class ClubMailingView(ClubTabsMixin, ListView):
""" """
A list of mailing for a given club A list of mailing for a given club
""" """
action = None action = None
model = Mailing model = Mailing
template_name = "club/mailing.jinja" template_name = "club/mailing.jinja"
current_tab = 'mailing'
def authorized(self):
return self.club.has_rights_in_club(self.user) or self.user.is_root or self.user.is_board_member
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
self.club = get_object_or_404(Club, pk=kwargs['club_id']) self.club = get_object_or_404(Club, pk=kwargs['club_id'])
self.user = request.user self.user = request.user
self.object = self.club
if not self.authorized():
raise PermissionDenied
self.member_form = MailingSubscriptionForm(club_id=self.club.id) 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)
return super(ClubMailingView, self).dispatch(request, *args, **kwargs) return super(ClubMailingView, self).dispatch(request, *args, **kwargs)
@ -405,10 +428,12 @@ class ClubMailingView(CanViewMixin, ListView):
if self.action == MailingFormType.MAILING: if self.action == MailingFormType.MAILING:
form = MailingForm form = MailingForm
string = 'new_mailing' string = 'new_mailing'
model = Mailing
elif self.action == MailingFormType.MEMBER: elif self.action == MailingFormType.MEMBER:
form = MailingSubscriptionForm form = MailingSubscriptionForm
string = 'new_member' string = 'new_member'
return MailingGenericCreateView.as_view(list_view=self, form_class=form, form_kwarg_string=string)(request, *args, **kwargs) model = MailingSubscription
return MailingGenericCreateView.as_view(model=model, list_view=self, form_class=form, form_kwarg_string=string)(request, *args, **kwargs)
return res return res
def get_queryset(self): def get_queryset(self):
@ -424,11 +449,10 @@ class ClubMailingView(CanViewMixin, ListView):
return kwargs return kwargs
class MailingGenericCreateView(CanCreateMixin, CreateView, SingleObjectMixin): class MailingGenericCreateView(CreateView, SingleObjectMixin):
""" """
Create a new mailing list Create a new mailing list
""" """
model = Mailing
list_view = None list_view = None
form_class = None form_class = None
form_kwarg_string = None form_kwarg_string = None
@ -446,8 +470,38 @@ class MailingGenericCreateView(CanCreateMixin, CreateView, SingleObjectMixin):
return kwargs return kwargs
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
if not self.list_view.authorized():
raise PermissionDenied
self.template_name = self.list_view.template_name self.template_name = self.list_view.template_name
return super(MailingGenericCreateView, self).dispatch(request, *args, **kwargs) return super(MailingGenericCreateView, self).dispatch(request, *args, **kwargs)
def get_success_url(self, **kwargs): def get_success_url(self, **kwargs):
return reverse_lazy('club:mailing', kwargs={'club_id': self.list_view.club.id}) return reverse_lazy('club:mailing', kwargs={'club_id': self.list_view.club.id})
class MailingDeleteView(CanEditMixin, DeleteView):
model = Mailing
template_name = 'core/delete_confirm.jinja'
pk_url_kwarg = "mailing_id"
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})
class MailingSubscriptionDeleteView(CanEditMixin, DeleteView):
model = MailingSubscription
template_name = 'core/delete_confirm.jinja'
pk_url_kwarg = "mailing_subscription_id"
def dispatch(self, request, *args, **kwargs):
self.club_id = self.get_object().mailing.club.id
return super(MailingSubscriptionDeleteView, self).dispatch(request, *args, **kwargs)
def get_success_url(self, **kwargs):
return reverse_lazy('club:mailing', kwargs={'club_id': self.club_id})

View File

@ -58,5 +58,12 @@
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% if user.mailing_subscriptions.exists() %}
<h4>{% trans %}Subscribed mailing lists{% endtrans %}</h4>
{% for sub in user.mailing_subscriptions.all() %}
<p>{{ sub }} <a href="{{ url('club:mailing_subscription_delete', mailing_subscription_id=sub.id) }}">{% trans %}Unsuscribe{% endtrans %}</a></p>
{% endfor %}
{% endif %}
{% endblock %} {% endblock %}

View File

@ -13,6 +13,7 @@
{% if user.is_root %} {% if user.is_root %}
<li><a href="{{ url('core:group_list') }}">{% trans %}Groups{% endtrans %}</a></li> <li><a href="{{ url('core:group_list') }}">{% trans %}Groups{% endtrans %}</a></li>
<li><a href="{{ url('rootplace:merge') }}">{% trans %}Merge users{% endtrans %}</a></li> <li><a href="{{ url('rootplace:merge') }}">{% trans %}Merge users{% endtrans %}</a></li>
<li><a href="{{ url('rootplace:mailings') }}">{% trans %}Mailing lists administration{% endtrans %}</a></li>
{% endif %} {% endif %}
{% if user.can_create_subscription or user.is_root %} {% if user.can_create_subscription or user.is_root %}
<li><a href="{{ url('subscription:subscription') }}">{% trans %}Subscriptions{% endtrans %}</a></li> <li><a href="{{ url('subscription:subscription') }}">{% trans %}Subscriptions{% endtrans %}</a></li>

View File

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('counter', '0013_customer_recorded_products'),
]
operations = [
migrations.AlterField(
model_name='customer',
name='recorded_products',
field=models.IntegerField(verbose_name='recorded product', default=0),
),
]

View File

@ -0,0 +1,30 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Mailing lists administration{% endtrans %}
{% endblock %}
{% block content %}
<h1>{% trans %}This page list all existing mailing lists{% endtrans %}</h1>
{% if has_objects %}
<table>
<tr>
<th>{% trans %}Email{% endtrans %}</th>
<th>{% trans %}Club{%endtrans%}</th>
</tr>
{% for mailing in object_list %}
<tr>
<td>{{ mailing.email }}</td>
<td>{{ mailing.club }} <a href="{{ url('club:mailing_delete', mailing_id=mailing.id) }}">{% trans %}Delete{% endtrans %}</a></td>
</tr>
{% endfor %}
</table>
{% else %}
<p>{% trans %}No mailing existing{% endtrans %}</p>
{% endif %}
{% endblock %}

View File

@ -28,4 +28,5 @@ from rootplace.views import *
urlpatterns = [ urlpatterns = [
url(r'^merge$', MergeUsersView.as_view(), name='merge'), url(r'^merge$', MergeUsersView.as_view(), name='merge'),
url(r'^mailings$', MailingListAdminView.as_view(), name='mailings')
] ]

View File

@ -2,6 +2,7 @@
# #
# Copyright 2016,2017 # Copyright 2016,2017
# - Skia <skia@libskia.so> # - Skia <skia@libskia.so>
# - Sli <antoine@bartuccio.fr>
# #
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM, # Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
# http://ae.utbm.fr. # http://ae.utbm.fr.
@ -27,6 +28,7 @@ from django.utils.translation import ugettext as _
from django.views.generic.edit import FormView from django.views.generic.edit import FormView
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django import forms from django import forms
from django.views.generic import ListView
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from ajax_select.fields import AutoCompleteSelectField from ajax_select.fields import AutoCompleteSelectField
@ -34,6 +36,8 @@ from ajax_select.fields import AutoCompleteSelectField
from core.views import CanViewMixin from core.views import CanViewMixin
from core.models import User from core.models import User
from counter.models import Customer from counter.models import Customer
from club.models import Mailing
def merge_users(u1, u2): def merge_users(u1, u2):
u1.nick_name = u1.nick_name or u2.nick_name u1.nick_name = u1.nick_name or u2.nick_name
@ -86,10 +90,12 @@ def merge_users(u1, u2):
u2.delete() u2.delete()
return u1 return u1
class MergeForm(forms.Form): class MergeForm(forms.Form):
user1 = AutoCompleteSelectField('users', label=_("User that will be kept"), help_text=None, required=True) user1 = AutoCompleteSelectField('users', label=_("User that will be kept"), help_text=None, required=True)
user2 = AutoCompleteSelectField('users', label=_("User that will be deleted"), help_text=None, required=True) user2 = AutoCompleteSelectField('users', label=_("User that will be deleted"), help_text=None, required=True)
class MergeUsersView(FormView): class MergeUsersView(FormView):
template_name = "rootplace/merge.jinja" template_name = "rootplace/merge.jinja"
form_class = MergeForm form_class = MergeForm
@ -107,3 +113,17 @@ class MergeUsersView(FormView):
def get_success_url(self): def get_success_url(self):
return reverse('core:user_profile', kwargs={'user_id': self.final_user.id}) return reverse('core:user_profile', kwargs={'user_id': self.final_user.id})
class MailingListAdminView(ListView):
template_name = "rootplace/mailing_lists.jinja"
model = Mailing
def dispatch(self, request, *args, **kwargs):
if not 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
return kwargs