Merge branch 'mailing' into 'master'

Mailing list system

See merge request !96
This commit is contained in:
Skia 2017-08-22 23:02:46 +02:00
commit 4ad923ff06
20 changed files with 822 additions and 108 deletions

View File

@ -48,5 +48,6 @@ urlpatterns = [
url(r'^', include(router.urls)), url(r'^', include(router.urls)),
url(r'^login/', include('rest_framework.urls', namespace='rest_framework')), url(r'^login/', include('rest_framework.urls', namespace='rest_framework')),
url(r'^markdown$', RenderMarkdown, name='api_markdown'), url(r'^markdown$', RenderMarkdown, name='api_markdown'),
url(r'^mailings$', FetchMailingLists, name='mailings_fetch')
] ]

View File

@ -22,9 +22,15 @@
# #
# #
from rest_framework.response import Response
from rest_framework import serializers from rest_framework import serializers
from rest_framework.decorators import api_view, renderer_classes
from rest_framework.renderers import StaticHTMLRenderer
from club.models import Club from django.conf import settings
from django.core.exceptions import PermissionDenied
from club.models import Club, Mailing
from api.views import RightModelViewSet from api.views import RightModelViewSet
@ -43,3 +49,15 @@ class ClubViewSet(RightModelViewSet):
serializer_class = ClubSerializer serializer_class = ClubSerializer
queryset = Club.objects.all() queryset = Club.objects.all()
@api_view(['GET'])
@renderer_classes((StaticHTMLRenderer,))
def FetchMailingLists(request):
key = request.GET.get('key', '')
if key != settings.SITH_MAILING_FETCH_KEY:
raise PermissionDenied
data = ''
for mailing in Mailing.objects.filter(is_moderated=True).all():
data += mailing.fetch_format()
return Response(data)

View File

@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
from django.conf import settings
import re
import django.core.validators
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('club', '0008_auto_20170515_2214'),
]
operations = [
migrations.CreateModel(
name='Mailing',
fields=[
('id', models.AutoField(auto_created=True, verbose_name='ID', serialize=False, primary_key=True)),
('email', models.CharField(max_length=256, unique=True, validators=[django.core.validators.RegexValidator(re.compile('(^[-!#$%&\'*+/=?^_`{}|~0-9A-Z]+(\\.[-!#$%&\'*+/=?^_`{}|~0-9A-Z]+)*\\Z|^"([\\001-\\010\\013\\014\\016-\\037!#-\\[\\]-\\177]|\\\\[\\001-\\011\\013\\014\\016-\\177])*"\\Z)', 34), 'Enter a valid address. Only the root of the address is needed.')], verbose_name='Email address')),
('is_moderated', models.BooleanField(default=False, verbose_name='is moderated')),
('club', models.ForeignKey(verbose_name='Club', related_name='mailings', to='club.Club')),
('moderator', models.ForeignKey(null=True, verbose_name='moderator', related_name='moderated_mailings', to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='MailingSubscription',
fields=[
('id', models.AutoField(auto_created=True, verbose_name='ID', serialize=False, primary_key=True)),
('email', models.EmailField(max_length=254, verbose_name='Email address')),
('mailing', models.ForeignKey(verbose_name='Mailing', related_name='subscriptions', to='club.Mailing')),
('user', models.ForeignKey(null=True, verbose_name='User', related_name='mailing_subscriptions', blank=True, to=settings.AUTH_USER_MODEL)),
],
),
migrations.AlterUniqueTogether(
name='mailingsubscription',
unique_together=set([('user', 'email', 'mailing')]),
),
]

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.
@ -26,12 +27,13 @@ from django.db import models
from django.core import validators from django.core import validators
from django.conf import settings from django.conf import settings
from django.utils.translation import ugettext_lazy as _ 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.db import transaction
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.utils import timezone from django.utils import timezone
from django.core.validators import RegexValidator, validate_email
from core.models import User, MetaGroup, Group, SithFile from core.models import User, MetaGroup, Group, SithFile, RealGroup, Notification
# Create your models here. # Create your models here.
@ -220,3 +222,99 @@ class Membership(models.Model):
def get_absolute_url(self): def get_absolute_url(self):
return reverse('club:club_members', kwargs={'club_id': self.club.id}) return reverse('club:club_members', kwargs={'club_id': self.club.id})
class Mailing(models.Model):
"""
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)
email = models.CharField(_('Email address'), unique=True, null=False, blank=False, max_length=256,
validators=[
RegexValidator(validate_email.user_regex,
_('Enter a valid address. Only the root of the address is needed.'))
])
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 self.can_moderate(self.moderator):
self.is_moderated = True
else:
self.moderator = None
super(Mailing, self).clean()
@property
def email_full(self):
return self.email + '@' + settings.SITH_MAILING_DOMAIN
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)
def can_view(self, 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 fetch_format(self):
resp = self.email + ': '
for sub in self.subscriptions.all():
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_full)
class MailingSubscription(models.Model):
"""
This class makes the link between user and mailing list
"""
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)
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"))
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):
return self.mailing.club.has_rights_in_club(user) or user.is_root or self.user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
def can_be_edited_by(self, user):
return (user is not None and user.id == self.user.id)
def fetch_format(self):
return self.email + ' '
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

@ -0,0 +1,60 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Mailing lists{% endtrans %}
{% endblock %}
{% block content %}
{% if has_objects %}
<b>{% trans %}Remember : mailing lists need to be moderated, if your new created list is not shown wait until moderation takes action{% endtrans %}</b>
{% for mailing in object_list %}
{% if mailing.is_moderated %}
<h2>{% trans %}Mailing{% endtrans %} {{ mailing.email_full }}
{%- if user.is_owner(mailing) -%}
<a href="{{ url('club:mailing_delete', mailing_id=mailing.id) }}"> - {% trans %}Delete{% endtrans %}</a>
{%- endif -%}
</h2>
<hr>
<table>
<tr>
<th>{% trans %}User{% endtrans %}</th>
<th colspan="2">{% trans %}Email{%endtrans%}</th>
</tr>
{% for subscriber in mailing.subscriptions.all() %}
<tr>
{% if subscriber.user %}
<td>{{ subscriber.user }}</td>
{% else %}
<td>{% trans %}Unregistered user{% endtrans %}</td>
{% endif %}
<td>{{ subscriber.email }}</td>
<td><a href="{{ url('club:mailing_subscription_delete', mailing_subscription_id=subscriber.id) }}">{% trans %}Delete{% endtrans %}</a></td>
</tr>
{% endfor %}
</table>
{% endif %}
{% endfor %}
{% else %}
<p>{% trans %}No mailing list existing for this club{% endtrans %}</p>
{% endif %}
{% if has_objects %}
<h2>{% trans %}New member{% endtrans %}</h2>
<form action="{{ url('club:mailing_subscription_create', club_id=club.id) }}" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ add_member.as_p() }}
<p><input type="submit" value="{% trans %}Add to mailing list{% endtrans %}" /></p>
</form>
{% endif %}
<h2>{% trans %}New mailing{% endtrans %}</h2>
<form action="{{ url('club:mailing_create', club_id=club.id) }}" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ add_mailing.as_p() }}
<p><input type="submit" value="{% trans %}Create mailing list{% endtrans %}" /></p>
</form>
{% endblock %}

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.
@ -38,5 +39,10 @@ urlpatterns = [
url(r'^(?P<club_id>[0-9]+)/sellings/csv$', ClubSellingCSVView.as_view(), name='sellings_csv'), url(r'^(?P<club_id>[0-9]+)/sellings/csv$', ClubSellingCSVView.as_view(), name='sellings_csv'),
url(r'^(?P<club_id>[0-9]+)/prop$', ClubEditPropView.as_view(), name='club_prop'), url(r'^(?P<club_id>[0-9]+)/prop$', ClubEditPropView.as_view(), name='club_prop'),
url(r'^(?P<club_id>[0-9]+)/tools$', ClubToolsView.as_view(), name='tools'), url(r'^(?P<club_id>[0-9]+)/tools$', ClubToolsView.as_view(), name='tools'),
url(r'^(?P<club_id>[0-9]+)/mailing$', ClubMailingView.as_view(action="display"), name='mailing'),
url(r'^(?P<club_id>[0-9]+)/mailing/new/mailing$', ClubMailingView.as_view(action="add_mailing"), name='mailing_create'),
url(r'^(?P<club_id>[0-9]+)/mailing/new/subscription$', ClubMailingView.as_view(action="add_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

@ -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.
@ -24,19 +25,63 @@
from django import forms from django import forms
from django.views.generic import ListView, DetailView, TemplateView from django.views.generic import ListView, DetailView, TemplateView
from django.views.generic.edit import DeleteView
from django.views.generic.detail import SingleObjectMixin
from django.views.generic.edit import UpdateView, CreateView from django.views.generic.edit import UpdateView, CreateView
from django.http import HttpResponseRedirect, HttpResponse from django.http import HttpResponseRedirect, HttpResponse
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse, reverse_lazy
from django.utils import timezone 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 core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin
from core.views.forms import SelectDate, SelectDateTime from core.views.forms import SelectDate, SelectDateTime
from club.models import Club, Membership from club.models import Club, Membership, Mailing, MailingSubscription
from sith.settings import SITH_MAXIMUM_FREE_ROLE from sith.settings import SITH_MAXIMUM_FREE_ROLE
from counter.models import Selling, Counter from counter.models import Selling, Counter
from core.models import User
from django.conf import settings
# Custom forms
class MailingForm(forms.ModelForm):
class Meta:
model = Mailing
fields = ('email', 'club', 'moderator')
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()
class MailingSubscriptionForm(forms.ModelForm):
class Meta:
model = MailingSubscription
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, is_moderated=True)
user = AutoCompleteSelectField('users', label=_('User'), help_text=None, required=False)
class ClubTabsMixin(TabedViewMixin): class ClubTabsMixin(TabedViewMixin):
@ -77,6 +122,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}),
@ -338,3 +388,112 @@ class ClubStatView(TemplateView):
kwargs = super(ClubStatView, self).get_context_data(**kwargs) kwargs = super(ClubStatView, self).get_context_data(**kwargs)
kwargs['club_list'] = Club.objects.all() kwargs['club_list'] = Club.objects.all()
return kwargs return kwargs
class ClubMailingView(ClubTabsMixin, ListView):
"""
A list of mailing for a given club
"""
action = None
model = Mailing
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_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
def dispatch(self, request, *args, **kwargs):
self.club = get_object_or_404(Club, pk=kwargs['club_id'])
self.user = request.user
self.object = self.club
if not self.authorized():
raise PermissionDenied
self.member_form = MailingSubscriptionForm(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):
res = super(ClubMailingView, self).get(request, *args, **kwargs)
if self.action != "display":
if self.action == "add_mailing":
form = MailingForm
model = Mailing
elif self.action == "add_member":
form = MailingSubscriptionForm
model = MailingSubscription
return MailingGenericCreateView.as_view(model=model, list_view=self, form_class=form)(request, *args, **kwargs)
return res
def get_queryset(self):
return Mailing.objects.filter(club_id=self.club.id).all()
def get_context_data(self, **kwargs):
kwargs = super(ClubMailingView, self).get_context_data(**kwargs)
kwargs['add_member'] = self.member_form
kwargs['add_mailing'] = self.mailing_form
kwargs['club'] = self.club
kwargs['user'] = self.user
kwargs['has_objects'] = len(kwargs['object_list']) > 0
return kwargs
class MailingGenericCreateView(CreateView, SingleObjectMixin):
"""
Create a new mailing list
"""
list_view = None
form_class = None
def get_context_data(self, **kwargs):
view_kwargs = self.list_view.get_context_data(**kwargs)
for key, data in super(MailingGenericCreateView, self).get_context_data(**kwargs).items():
view_kwargs[key] = data
view_kwargs[self.list_view.action] = view_kwargs['form']
return view_kwargs
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):
if not self.list_view.authorized():
raise PermissionDenied
self.template_name = self.list_view.template_name
return super(MailingGenericCreateView, self).dispatch(request, *args, **kwargs)
def get_success_url(self, **kwargs):
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"
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):
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):
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

@ -0,0 +1,45 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Mailing lists administration{% endtrans %}
{% endblock %}
{% macro display_mailings(list) %}
<table>
<tr>
<th>{% trans %}Email{% endtrans %}</th>
<th>{% trans %}Club{%endtrans%}</th>
<th>{% trans %}Actions{% endtrans %}</th>
</tr>
{% for mailing in list %}
<tr>
<td>{{ mailing.email_full }}</td>
<td><a href="{{ url('club:mailing', club_id=mailing.club.id) }}">{{ mailing.club }}</a></td>
<td>
<a href="{{ url('com:mailing_delete', mailing_id=mailing.id) }}">{% trans %}Delete{% endtrans %}</a> - {% if not mailing.is_moderated %}<a href="{{ url('com:mailing_moderate', mailing_id=mailing.id) }}">{% trans %}Moderate{% endtrans %}</a>{% else %}{% trans user=mailing.moderator %}Moderated by {{ user }}{% endtrans %}{% endif %}
</td>
</tr>
{% endfor %}
</table>
{% endmacro %}
{% block content %}
<h1>{% trans %}This page lists all mailing lists{% endtrans %}</h1>
{% if has_unmoderated %}
<h2>{% trans %}Not moderated mailing lists{% endtrans %}</h2>
{{ display_mailings(unmoderated) }}
{% endif %}
<h2>{% trans %}Moderated mailing lists{% endtrans %}</h2>
{% if has_moderated %}
{{ display_mailings(moderated) }}
{% else %}
<p>{% trans %}No mailing list existing{% endtrans %}</p>
{% endif %}
{% endblock %}

View File

@ -25,6 +25,7 @@
from django.conf.urls import url from django.conf.urls import url
from com.views import * from com.views import *
from club.views import MailingDeleteView
urlpatterns = [ urlpatterns = [
url(r'^sith/edit/alert$', AlertMsgEditView.as_view(), name='alert_edit'), url(r'^sith/edit/alert$', AlertMsgEditView.as_view(), name='alert_edit'),
@ -42,5 +43,8 @@ urlpatterns = [
url(r'^news/(?P<news_id>[0-9]+)/moderate$', NewsModerateView.as_view(), name='news_moderate'), url(r'^news/(?P<news_id>[0-9]+)/moderate$', NewsModerateView.as_view(), name='news_moderate'),
url(r'^news/(?P<news_id>[0-9]+)/edit$', NewsEditView.as_view(), name='news_edit'), url(r'^news/(?P<news_id>[0-9]+)/edit$', NewsEditView.as_view(), name='news_edit'),
url(r'^news/(?P<news_id>[0-9]+)$', NewsDetailView.as_view(), name='news_detail'), url(r'^news/(?P<news_id>[0-9]+)$', NewsDetailView.as_view(), name='news_detail'),
url(r'^mailings$', MailingListAdminView.as_view(), name='mailing_admin'),
url(r'^mailings/(?P<mailing_id>[0-9]+)/moderate$', MailingModerateView.as_view(), name='mailing_moderate'),
url(r'^mailings/(?P<mailing_id>[0-9]+)/delete$', MailingDeleteView.as_view(redirect_page='com:mailing_admin'), name='mailing_delete'),
] ]

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.
@ -24,7 +25,7 @@
from django.shortcuts import redirect, get_object_or_404 from django.shortcuts import redirect, get_object_or_404
from django.http import HttpResponseRedirect 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.edit import UpdateView, CreateView, DeleteView
from django.views.generic.detail import SingleObjectMixin from django.views.generic.detail import SingleObjectMixin
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@ -34,6 +35,7 @@ from django.utils import timezone
from django.conf import settings from django.conf import settings
from django.db.models import Max from django.db.models import Max
from django.forms.models import modelform_factory from django.forms.models import modelform_factory
from django.core.exceptions import PermissionDenied
from django import forms from django import forms
from datetime import timedelta from datetime import timedelta
@ -42,7 +44,7 @@ from com.models import Sith, News, NewsDate, Weekmail, WeekmailArticle
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin, CanCreateMixin, QuickNotifMixin from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin, CanCreateMixin, QuickNotifMixin
from core.views.forms import SelectDateTime from core.views.forms import SelectDateTime
from core.models import Notification, RealGroup from core.models import Notification, RealGroup
from club.models import Club from club.models import Club, Mailing
# Sith object # Sith object
@ -81,6 +83,11 @@ class ComTabsMixin(TabedViewMixin):
'slug': 'alert', 'slug': 'alert',
'name': _("Alert message"), 'name': _("Alert message"),
}) })
tab_list.append({
'url': reverse('com:mailing_admin'),
'slug': 'mailings',
'name': _("Mailing lists administration"),
})
return tab_list return tab_list
@ -414,3 +421,35 @@ class WeekmailArticleDeleteView(CanEditPropMixin, DeleteView):
template_name = 'core/delete_confirm.jinja' template_name = 'core/delete_confirm.jinja'
success_url = reverse_lazy('com:weekmail') success_url = reverse_lazy('com:weekmail')
pk_url_kwarg = "article_id" pk_url_kwarg = "article_id"
class MailingListAdminView(ComTabsMixin, ListView):
template_name = "com/mailing_admin.jinja"
model = Mailing
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):
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['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

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 = [
('core', '0020_auto_20170324_0917'),
]
operations = [
migrations.AlterField(
model_name='notification',
name='type',
field=models.CharField(verbose_name='type', default='GENERIC', max_length=32, choices=[('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'), ('NEW_PICTURES', "You've been identified on some pictures"), ('REFILLING', 'You just refilled of %s'), ('SELLING', 'You just bought %s'), ('GENERIC', 'You have a notification')]),
),
]

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 = [
('core', '0021_auto_20170822_1529'),
]
operations = [
migrations.AlterField(
model_name='notification',
name='type',
field=models.CharField(choices=[('MAILING_MODERATION', 'A new mailing list needs 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'), ('NEW_PICTURES', "You've been identified on some pictures"), ('REFILLING', 'You just refilled of %s'), ('SELLING', 'You just bought %s'), ('GENERIC', 'You have a notification')], default='GENERIC', max_length=32, verbose_name='type'),
),
]

View File

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

View File

@ -87,6 +87,7 @@
<li><a href="{{ url('com:alert_edit') }}">{% trans %}Edit alert message{% endtrans %}</a></li> <li><a href="{{ url('com:alert_edit') }}">{% trans %}Edit alert message{% endtrans %}</a></li>
<li><a href="{{ url('com:info_edit') }}">{% trans %}Edit information message{% endtrans %}</a></li> <li><a href="{{ url('com:info_edit') }}">{% trans %}Edit information message{% endtrans %}</a></li>
<li><a href="{{ url('core:file_moderation') }}">{% trans %}Moderate files{% endtrans %}</a></li> <li><a href="{{ url('core:file_moderation') }}">{% trans %}Moderate files{% endtrans %}</a></li>
<li><a href="{{ url('com:mailing_admin') }}">{% trans %}Mailing lists administration{% endtrans %}</a></li>
{% endif %} {% endif %}
{% if user.is_in_group(settings.SITH_GROUP_SAS_ADMIN_ID) %} {% if user.is_in_group(settings.SITH_GROUP_SAS_ADMIN_ID) %}
<li><a href="{{ url('sas:moderation') }}">{% trans %}Moderate pictures{% endtrans %}</a></li> <li><a href="{{ url('sas:moderation') }}">{% trans %}Moderate pictures{% 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,15 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('counter', '0014_auto_20170817_1537'),
('counter', '0014_auto_20170816_1521'),
]
operations = [
]

View File

@ -6,7 +6,7 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-08-21 21:00+0200\n" "POT-Creation-Date: 2017-08-22 22:36+0200\n"
"PO-Revision-Date: 2016-07-18\n" "PO-Revision-Date: 2016-07-18\n"
"Last-Translator: Skia <skia@libskia.so>\n" "Last-Translator: Skia <skia@libskia.so>\n"
"Language-Team: AE info <ae.info@utbm.fr>\n" "Language-Team: AE info <ae.info@utbm.fr>\n"
@ -17,7 +17,7 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n > 1);\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: accounting/models.py:61 accounting/models.py:110 accounting/models.py:138 #: accounting/models.py:61 accounting/models.py:110 accounting/models.py:138
#: accounting/models.py:197 club/models.py:44 #: accounting/models.py:197 club/models.py:46
#: core/templates/core/base.jinja:233 counter/models.py:113 #: core/templates/core/base.jinja:233 counter/models.py:113
#: counter/models.py:139 counter/models.py:183 forum/models.py:49 #: counter/models.py:139 counter/models.py:183 forum/models.py:49
#: launderette/models.py:38 launderette/models.py:84 launderette/models.py:110 #: launderette/models.py:38 launderette/models.py:84 launderette/models.py:110
@ -65,7 +65,7 @@ msgstr "IBAN"
msgid "account number" msgid "account number"
msgstr "numero de compte" msgstr "numero de compte"
#: accounting/models.py:113 accounting/models.py:139 club/models.py:187 #: accounting/models.py:113 accounting/models.py:139 club/models.py:189
#: com/models.py:65 com/models.py:156 counter/models.py:148 #: com/models.py:65 com/models.py:156 counter/models.py:148
#: counter/models.py:184 trombi/models.py:149 #: counter/models.py:184 trombi/models.py:149
msgid "club" msgid "club"
@ -88,12 +88,12 @@ msgstr "Compte club"
msgid "%(club_account)s on %(bank_account)s" msgid "%(club_account)s on %(bank_account)s"
msgstr "%(club_account)s sur %(bank_account)s" msgstr "%(club_account)s sur %(bank_account)s"
#: accounting/models.py:195 club/models.py:188 counter/models.py:463 #: accounting/models.py:195 club/models.py:190 counter/models.py:463
#: election/models.py:16 launderette/models.py:148 #: election/models.py:16 launderette/models.py:148
msgid "start date" msgid "start date"
msgstr "date de début" msgstr "date de début"
#: accounting/models.py:196 club/models.py:189 counter/models.py:464 #: accounting/models.py:196 club/models.py:191 counter/models.py:464
#: election/models.py:17 #: election/models.py:17
msgid "end date" msgid "end date"
msgstr "date de fin" msgstr "date de fin"
@ -172,8 +172,10 @@ msgstr "étiquette"
msgid "target type" msgid "target type"
msgstr "type de cible" msgstr "type de cible"
#: accounting/models.py:271 club/templates/club/club_members.jinja:8 #: accounting/models.py:271 club/models.py:288
#: club/templates/club/club_members.jinja:8
#: club/templates/club/club_old_members.jinja:8 #: club/templates/club/club_old_members.jinja:8
#: club/templates/club/mailing.jinja:22 club/views.py:84
#: counter/templates/counter/cash_summary_list.jinja:32 #: counter/templates/counter/cash_summary_list.jinja:32
#: counter/templates/counter/stats.jinja:15 #: counter/templates/counter/stats.jinja:15
#: counter/templates/counter/stats.jinja:52 #: counter/templates/counter/stats.jinja:52
@ -182,7 +184,9 @@ msgstr "type de cible"
msgid "User" msgid "User"
msgstr "Utilisateur" msgstr "Utilisateur"
#: accounting/models.py:271 club/templates/club/club_detail.jinja:5 #: accounting/models.py:271 club/models.py:232
#: club/templates/club/club_detail.jinja:5
#: com/templates/com/mailing_admin.jinja:11
#: com/templates/com/news_admin_list.jinja:21 #: com/templates/com/news_admin_list.jinja:21
#: com/templates/com/news_admin_list.jinja:50 #: com/templates/com/news_admin_list.jinja:50
#: com/templates/com/news_admin_list.jinja:81 #: com/templates/com/news_admin_list.jinja:81
@ -339,8 +343,9 @@ msgstr "Compte en banque : "
#: accounting/templates/accounting/club_account_details.jinja:59 #: accounting/templates/accounting/club_account_details.jinja:59
#: accounting/templates/accounting/label_list.jinja:25 #: accounting/templates/accounting/label_list.jinja:25
#: club/templates/club/club_sellings.jinja:50 #: club/templates/club/club_sellings.jinja:50
#: com/templates/com/weekmail.jinja:33 com/templates/com/weekmail.jinja:62 #: club/templates/club/mailing.jinja:16 club/templates/club/mailing.jinja:33
#: core/templates/core/file_detail.jinja:25 #: com/templates/com/mailing_admin.jinja:19 com/templates/com/weekmail.jinja:33
#: com/templates/com/weekmail.jinja:62 core/templates/core/file_detail.jinja:25
#: core/templates/core/file_detail.jinja:62 #: core/templates/core/file_detail.jinja:62
#: core/templates/core/file_moderation.jinja:24 #: core/templates/core/file_moderation.jinja:24
#: core/templates/core/group_list.jinja:13 core/templates/core/macros.jinja:53 #: core/templates/core/group_list.jinja:13 core/templates/core/macros.jinja:53
@ -366,7 +371,7 @@ msgid "Delete"
msgstr "Supprimer" msgstr "Supprimer"
#: accounting/templates/accounting/bank_account_details.jinja:17 #: accounting/templates/accounting/bank_account_details.jinja:17
#: club/views.py:51 core/views/user.py:164 sas/templates/sas/picture.jinja:86 #: club/views.py:96 core/views/user.py:164 sas/templates/sas/picture.jinja:86
msgid "Infos" msgid "Infos"
msgstr "Infos" msgstr "Infos"
@ -385,7 +390,7 @@ msgstr "Nouveau compte club"
#: accounting/templates/accounting/bank_account_details.jinja:26 #: accounting/templates/accounting/bank_account_details.jinja:26
#: accounting/templates/accounting/bank_account_list.jinja:21 #: accounting/templates/accounting/bank_account_list.jinja:21
#: accounting/templates/accounting/club_account_details.jinja:57 #: accounting/templates/accounting/club_account_details.jinja:57
#: accounting/templates/accounting/journal_details.jinja:83 club/views.py:73 #: accounting/templates/accounting/journal_details.jinja:83 club/views.py:118
#: com/templates/com/news_admin_list.jinja:37 #: com/templates/com/news_admin_list.jinja:37
#: com/templates/com/news_admin_list.jinja:64 #: com/templates/com/news_admin_list.jinja:64
#: com/templates/com/news_admin_list.jinja:109 #: com/templates/com/news_admin_list.jinja:109
@ -502,6 +507,7 @@ msgstr "Fermé"
#: accounting/templates/accounting/club_account_details.jinja:36 #: accounting/templates/accounting/club_account_details.jinja:36
#: accounting/templates/accounting/journal_details.jinja:41 #: accounting/templates/accounting/journal_details.jinja:41
#: com/templates/com/mailing_admin.jinja:12
#: com/templates/com/news_admin_list.jinja:24 #: com/templates/com/news_admin_list.jinja:24
#: com/templates/com/news_admin_list.jinja:52 #: com/templates/com/news_admin_list.jinja:52
#: com/templates/com/news_admin_list.jinja:85 #: com/templates/com/news_admin_list.jinja:85
@ -850,11 +856,11 @@ msgstr "Opérations sans étiquette"
msgid "Refound this account" msgid "Refound this account"
msgstr "Rembourser ce compte" msgstr "Rembourser ce compte"
#: club/models.py:46 #: club/models.py:48
msgid "unix name" msgid "unix name"
msgstr "nom unix" msgstr "nom unix"
#: club/models.py:50 #: club/models.py:52
msgid "" msgid ""
"Enter a valid unix name. This value may contain only letters, numbers ./-/_ " "Enter a valid unix name. This value may contain only letters, numbers ./-/_ "
"characters." "characters."
@ -862,56 +868,88 @@ msgstr ""
"Entrez un nom UNIX valide. Cette valeur peut contenir uniquement des " "Entrez un nom UNIX valide. Cette valeur peut contenir uniquement des "
"lettres, des nombres, et les caractères ./-/_" "lettres, des nombres, et les caractères ./-/_"
#: club/models.py:55 #: club/models.py:57
msgid "A club with that unix name already exists." msgid "A club with that unix name already exists."
msgstr "Un club avec ce nom UNIX existe déjà." msgstr "Un club avec ce nom UNIX existe déjà."
#: club/models.py:58 core/models.py:198 #: club/models.py:60 core/models.py:198
msgid "address" msgid "address"
msgstr "Adresse" msgstr "Adresse"
#: club/models.py:64 core/models.py:159 #: club/models.py:66 core/models.py:159
msgid "home" msgid "home"
msgstr "home" msgstr "home"
#: club/models.py:76 #: club/models.py:78
msgid "You can not make loops in clubs" msgid "You can not make loops in clubs"
msgstr "Vous ne pouvez pas faire de boucles dans les clubs" msgstr "Vous ne pouvez pas faire de boucles dans les clubs"
#: club/models.py:90 #: club/models.py:92
msgid "A club with that unix_name already exists" msgid "A club with that unix_name already exists"
msgstr "Un club avec ce nom UNIX existe déjà." msgstr "Un club avec ce nom UNIX existe déjà."
#: club/models.py:186 counter/models.py:461 counter/models.py:479 #: club/models.py:188 counter/models.py:461 counter/models.py:479
#: eboutic/models.py:38 eboutic/models.py:72 election/models.py:140 #: eboutic/models.py:38 eboutic/models.py:72 election/models.py:140
#: launderette/models.py:114 launderette/models.py:152 sas/models.py:158 #: launderette/models.py:114 launderette/models.py:152 sas/models.py:158
#: trombi/models.py:148 #: trombi/models.py:148
msgid "user" msgid "user"
msgstr "nom d'utilisateur" msgstr "nom d'utilisateur"
#: club/models.py:190 core/models.py:178 election/models.py:139 #: club/models.py:192 core/models.py:178 election/models.py:139
#: election/models.py:155 trombi/models.py:150 #: election/models.py:155 trombi/models.py:150
msgid "role" msgid "role"
msgstr "rôle" msgstr "rôle"
#: club/models.py:192 core/models.py:64 counter/models.py:114 #: club/models.py:194 core/models.py:64 counter/models.py:114
#: counter/models.py:140 election/models.py:13 election/models.py:93 #: counter/models.py:140 election/models.py:13 election/models.py:93
#: election/models.py:141 forum/models.py:50 forum/models.py:186 #: election/models.py:141 forum/models.py:50 forum/models.py:186
msgid "description" msgid "description"
msgstr "description" msgstr "description"
#: club/models.py:197 #: club/models.py:199
msgid "User must be subscriber to take part to a club" msgid "User must be subscriber to take part to a club"
msgstr "L'utilisateur doit être cotisant pour faire partie d'un club" msgstr "L'utilisateur doit être cotisant pour faire partie d'un club"
#: club/models.py:199 #: club/models.py:201
msgid "User is already member of that club" msgid "User is already member of that club"
msgstr "L'utilisateur est déjà membre de ce club" msgstr "L'utilisateur est déjà membre de ce club"
#: club/models.py:203 #: club/models.py:205
msgid "past member" msgid "past member"
msgstr "Anciens membres" msgstr "Anciens membres"
#: club/models.py:233 club/models.py:289
msgid "Email address"
msgstr "Adresse email"
#: club/models.py:236
msgid "Enter a valid address. Only the root of the address is needed."
msgstr "Entrez une adresse valide. Seule la racine de l'adresse est nécessaire."
#: club/models.py:238 com/models.py:67 core/models.py:629
msgid "is moderated"
msgstr "est modéré"
#: club/models.py:239 com/models.py:68
msgid "moderator"
msgstr "modérateur"
#: club/models.py:287 club/templates/club/mailing.jinja:14
msgid "Mailing"
msgstr "Mailing"
#: club/models.py:296
msgid "At least user or email is required"
msgstr "Au moins un utilisateur ou un email est nécessaire"
#: club/models.py:301
msgid "This email is already suscribed in this mailing"
msgstr "Cet email est déjà abonné à cette mailing"
#: club/models.py:319 club/templates/club/mailing.jinja:30
msgid "Unregistered user"
msgstr "Désabonner un utilisateur"
#: club/templates/club/club_list.jinja:4 club/templates/club/club_list.jinja:24 #: club/templates/club/club_list.jinja:4 club/templates/club/club_list.jinja:24
msgid "Club list" msgid "Club list"
msgstr "Liste des clubs" msgstr "Liste des clubs"
@ -977,8 +1015,8 @@ msgstr "Du"
msgid "To" msgid "To"
msgstr "Au" msgstr "Au"
#: club/templates/club/club_sellings.jinja:5 club/views.py:78 club/views.py:251 #: club/templates/club/club_sellings.jinja:5 club/views.py:123
#: counter/templates/counter/counter_main.jinja:19 #: club/views.py:301 counter/templates/counter/counter_main.jinja:19
#: counter/templates/counter/last_ops.jinja:35 #: counter/templates/counter/last_ops.jinja:35
msgid "Sellings" msgid "Sellings"
msgstr "Ventes" msgstr "Ventes"
@ -1004,7 +1042,7 @@ msgstr "unités"
msgid "Benefit: " msgid "Benefit: "
msgstr "Bénéfice : " msgstr "Bénéfice : "
#: club/templates/club/club_sellings.jinja:21 club/views.py:199 #: club/templates/club/club_sellings.jinja:21 club/views.py:249
#: core/templates/core/user_account_detail.jinja:18 #: core/templates/core/user_account_detail.jinja:18
#: core/templates/core/user_account_detail.jinja:51 #: core/templates/core/user_account_detail.jinja:51
#: counter/templates/counter/cash_summary_list.jinja:33 counter/views.py:134 #: counter/templates/counter/cash_summary_list.jinja:33 counter/views.py:134
@ -1053,7 +1091,7 @@ msgid "Payment method"
msgstr "Méthode de paiement" msgstr "Méthode de paiement"
#: club/templates/club/club_tools.jinja:4 #: club/templates/club/club_tools.jinja:4
#: core/templates/core/user_tools.jinja:98 #: core/templates/core/user_tools.jinja:99
msgid "Club tools" msgid "Club tools"
msgstr "Outils club" msgstr "Outils club"
@ -1089,48 +1127,92 @@ msgstr "Comptabilité : "
msgid "Manage launderettes" msgid "Manage launderettes"
msgstr "Gestion des laveries" msgstr "Gestion des laveries"
#: club/templates/club/mailing.jinja:4
msgid "Mailing lists"
msgstr "Mailing listes"
#: club/templates/club/mailing.jinja:10
msgid ""
"Remember : mailing lists need to be moderated, if your new created list is "
"not shown wait until moderation takes action"
msgstr ""
"Rappelez vous : les mailing listes doivent être modérées, si votre liste "
"nouvellement créee n'est pas affichée, attendez jusqu'à qu'un modérateur "
"entre en action"
#: club/templates/club/mailing.jinja:23
#: com/templates/com/mailing_admin.jinja:10
msgid "Email"
msgstr "Email"
#: club/templates/club/mailing.jinja:41
msgid "No mailing list existing for this club"
msgstr "Aucune mailing liste n'existe pour ce club"
#: club/templates/club/mailing.jinja:45
msgid "New member"
msgstr "Nouveau membre"
#: club/templates/club/mailing.jinja:49
msgid "Add to mailing list"
msgstr "Ajouter à la mailing liste"
#: club/templates/club/mailing.jinja:53
msgid "New mailing"
msgstr "Nouvelle mailing liste"
#: club/templates/club/mailing.jinja:57
msgid "Create mailing list"
msgstr "Créer une mailing liste"
#: club/templates/club/stats.jinja:4 club/templates/club/stats.jinja.py:9 #: club/templates/club/stats.jinja:4 club/templates/club/stats.jinja.py:9
msgid "Club stats" msgid "Club stats"
msgstr "Statistiques du club" msgstr "Statistiques du club"
#: club/views.py:57 #: club/views.py:102
msgid "Members" msgid "Members"
msgstr "Membres" msgstr "Membres"
#: club/views.py:62 #: club/views.py:107
msgid "Old members" msgid "Old members"
msgstr "Anciens membres" msgstr "Anciens membres"
#: club/views.py:68 core/templates/core/base.jinja:64 core/views/user.py:180 #: club/views.py:113 core/templates/core/base.jinja:64 core/views/user.py:180
#: sas/templates/sas/picture.jinja:95 trombi/views.py:55 #: sas/templates/sas/picture.jinja:95 trombi/views.py:55
msgid "Tools" msgid "Tools"
msgstr "Outils" msgstr "Outils"
#: club/views.py:84 counter/templates/counter/counter_list.jinja:21 #: club/views.py:128
#, fuzzy
#| msgid "File list"
msgid "Mailing list"
msgstr "Liste des fichiers"
#: club/views.py:134 counter/templates/counter/counter_list.jinja:21
#: counter/templates/counter/counter_list.jinja:42 #: counter/templates/counter/counter_list.jinja:42
#: counter/templates/counter/counter_list.jinja:57 #: counter/templates/counter/counter_list.jinja:57
msgid "Props" msgid "Props"
msgstr "Propriétés" msgstr "Propriétés"
#: club/views.py:130 core/views/forms.py:253 counter/views.py:91 #: club/views.py:180 core/views/forms.py:253 counter/views.py:91
#: trombi/views.py:124 #: trombi/views.py:124
msgid "Select user" msgid "Select user"
msgstr "Choisir un utilisateur" msgstr "Choisir un utilisateur"
#: club/views.py:180 sas/views.py:104 sas/views.py:157 sas/views.py:232 #: club/views.py:230 sas/views.py:104 sas/views.py:157 sas/views.py:232
msgid "You do not have the permission to do that" msgid "You do not have the permission to do that"
msgstr "Vous n'avez pas la permission de faire cela" msgstr "Vous n'avez pas la permission de faire cela"
#: club/views.py:197 counter/views.py:1097 #: club/views.py:247 counter/views.py:1097
msgid "Begin date" msgid "Begin date"
msgstr "Date de début" msgstr "Date de début"
#: club/views.py:198 com/views.py:130 counter/views.py:1098 #: club/views.py:248 com/views.py:137 counter/views.py:1098
#: election/views.py:135 subscription/views.py:47 #: election/views.py:135 subscription/views.py:47
msgid "End date" msgid "End date"
msgstr "Date de fin" msgstr "Date de fin"
#: club/views.py:213 core/templates/core/user_stats.jinja:27 #: club/views.py:263 core/templates/core/user_stats.jinja:27
#: counter/views.py:1188 #: counter/views.py:1188
msgid "Product" msgid "Product"
msgstr "Produit" msgstr "Produit"
@ -1191,14 +1273,6 @@ msgstr "type"
msgid "author" msgid "author"
msgstr "auteur" msgstr "auteur"
#: com/models.py:67 core/models.py:629
msgid "is moderated"
msgstr "est modéré"
#: com/models.py:68
msgid "moderator"
msgstr "modérateur"
#: com/models.py:93 #: com/models.py:93
msgid "news_date" msgid "news_date"
msgstr "date de la nouvelle" msgstr "date de la nouvelle"
@ -1239,6 +1313,44 @@ msgstr "weekmail"
msgid "rank" msgid "rank"
msgstr "rang" msgstr "rang"
#: com/templates/com/mailing_admin.jinja:4 com/views.py:89
#: core/templates/core/user_tools.jinja:90
msgid "Mailing lists administration"
msgstr "Administration des mailing listes"
#: com/templates/com/mailing_admin.jinja:19
#: com/templates/com/news_admin_list.jinja:65
#: com/templates/com/news_admin_list.jinja:149
#: com/templates/com/news_admin_list.jinja:221
#: com/templates/com/news_admin_list.jinja:293
#: com/templates/com/news_detail.jinja:26
#: core/templates/core/file_detail.jinja:65
#: core/templates/core/file_moderation.jinja:23
#: sas/templates/sas/moderation.jinja:17 sas/templates/sas/picture.jinja:122
msgid "Moderate"
msgstr "Modérer"
#: com/templates/com/mailing_admin.jinja:19
#, python-format
msgid "Moderated by %(user)s"
msgstr "Modéré par %(user)s"
#: com/templates/com/mailing_admin.jinja:28
msgid "This page lists all mailing lists"
msgstr "Cette page liste toutes les mailing listes"
#: com/templates/com/mailing_admin.jinja:31
msgid "Not moderated mailing lists"
msgstr "Mailing lists non modérées"
#: com/templates/com/mailing_admin.jinja:35
msgid "Moderated mailing lists"
msgstr "Modérer les mailing listes"
#: com/templates/com/mailing_admin.jinja:39
msgid "No mailing list existing"
msgstr "Aucune mailing list existante"
#: com/templates/com/news_admin_list.jinja:5 #: com/templates/com/news_admin_list.jinja:5
msgid "News admin" msgid "News admin"
msgstr "Administration des nouvelles" msgstr "Administration des nouvelles"
@ -1333,17 +1445,6 @@ msgstr ""
msgid "Notices to moderate" msgid "Notices to moderate"
msgstr "Nouvelles à modérer" msgstr "Nouvelles à modérer"
#: com/templates/com/news_admin_list.jinja:65
#: com/templates/com/news_admin_list.jinja:149
#: com/templates/com/news_admin_list.jinja:221
#: com/templates/com/news_admin_list.jinja:293
#: com/templates/com/news_detail.jinja:26
#: core/templates/core/file_detail.jinja:65
#: core/templates/core/file_moderation.jinja:23
#: sas/templates/sas/moderation.jinja:17 sas/templates/sas/picture.jinja:122
msgid "Moderate"
msgstr "Modérer"
#: com/templates/com/news_admin_list.jinja:72 #: com/templates/com/news_admin_list.jinja:72
#, fuzzy #, fuzzy
#| msgid "Weekly" #| msgid "Weekly"
@ -1471,7 +1572,7 @@ msgid "Coming soon... don't miss!"
msgstr "Prochainement... à ne pas rater!" msgstr "Prochainement... à ne pas rater!"
#: com/templates/com/weekmail.jinja:5 com/templates/com/weekmail.jinja.py:9 #: com/templates/com/weekmail.jinja:5 com/templates/com/weekmail.jinja.py:9
#: com/views.py:62 core/templates/core/user_tools.jinja:82 #: com/views.py:64 core/templates/core/user_tools.jinja:82
msgid "Weekmail" msgid "Weekmail"
msgstr "Weekmail" msgstr "Weekmail"
@ -1558,55 +1659,55 @@ msgstr "Astuce"
msgid "Final word" msgid "Final word"
msgstr "Le mot de la fin" msgstr "Le mot de la fin"
#: com/views.py:55 #: com/views.py:57
msgid "Communication administration" msgid "Communication administration"
msgstr "Administration de la communication" msgstr "Administration de la communication"
#: com/views.py:67 core/templates/core/user_tools.jinja:83 #: com/views.py:69 core/templates/core/user_tools.jinja:83
msgid "Weekmail destinations" msgid "Weekmail destinations"
msgstr "Destinataires du Weekmail" msgstr "Destinataires du Weekmail"
#: com/views.py:72 #: com/views.py:74
msgid "Index page" msgid "Index page"
msgstr "Page d'accueil" msgstr "Page d'accueil"
#: com/views.py:77 #: com/views.py:79
msgid "Info message" msgid "Info message"
msgstr "Message d'info" msgstr "Message d'info"
#: com/views.py:82 #: com/views.py:84
msgid "Alert message" msgid "Alert message"
msgstr "Message d'alerte" msgstr "Message d'alerte"
#: com/views.py:129 election/views.py:133 subscription/views.py:44 #: com/views.py:136 election/views.py:133 subscription/views.py:44
msgid "Start date" msgid "Start date"
msgstr "Date de début" msgstr "Date de début"
#: com/views.py:131 #: com/views.py:138
msgid "Until" msgid "Until"
msgstr "Jusqu'à" msgstr "Jusqu'à"
#: com/views.py:132 #: com/views.py:139
msgid "Automoderation" msgid "Automoderation"
msgstr "Automodération" msgstr "Automodération"
#: com/views.py:138 com/views.py:140 com/views.py:144 #: com/views.py:145 com/views.py:147 com/views.py:151
msgid "This field is required." msgid "This field is required."
msgstr "Ce champ est obligatoire." msgstr "Ce champ est obligatoire."
#: com/views.py:142 #: com/views.py:149
msgid "You crazy? You can not finish an event before starting it." msgid "You crazy? You can not finish an event before starting it."
msgstr "T'es fou? Un événement ne peut pas finir avant même de commencer." msgstr "T'es fou? Un événement ne peut pas finir avant même de commencer."
#: com/views.py:312 #: com/views.py:319
msgid "Delete and save to regenerate" msgid "Delete and save to regenerate"
msgstr "Supprimer et sauver pour regénérer" msgstr "Supprimer et sauver pour regénérer"
#: com/views.py:320 #: com/views.py:327
msgid "Weekmail of the " msgid "Weekmail of the "
msgstr "Weekmail du " msgstr "Weekmail du "
#: com/views.py:400 #: com/views.py:407
msgid "" msgid ""
"You must be a board member of the selected club to post in the Weekmail." "You must be a board member of the selected club to post in the Weekmail."
msgstr "" msgstr ""
@ -2642,6 +2743,14 @@ msgstr "Clubs actuels : "
msgid "Old club(s) :" msgid "Old club(s) :"
msgstr "Anciens clubs :" msgstr "Anciens clubs :"
#: core/templates/core/user_clubs.jinja:63
msgid "Subscribed mailing lists"
msgstr "Mailing listes abonnées"
#: core/templates/core/user_clubs.jinja:65
msgid "Unsubscribe"
msgstr "Se désabonner"
#: core/templates/core/user_detail.jinja:5 #: core/templates/core/user_detail.jinja:5
#, python-format #, python-format
msgid "%(user_name)s's profile" msgid "%(user_name)s's profile"
@ -2896,35 +3005,35 @@ msgstr "Éditer le message d'informations"
msgid "Moderate files" msgid "Moderate files"
msgstr "Modérer les fichiers" msgstr "Modérer les fichiers"
#: core/templates/core/user_tools.jinja:92 #: core/templates/core/user_tools.jinja:93
msgid "Moderate pictures" msgid "Moderate pictures"
msgstr "Modérer les photos" msgstr "Modérer les photos"
#: core/templates/core/user_tools.jinja:105 #: core/templates/core/user_tools.jinja:106
msgid "Elections" msgid "Elections"
msgstr "Élections" msgstr "Élections"
#: core/templates/core/user_tools.jinja:107 #: core/templates/core/user_tools.jinja:108
msgid "See available elections" msgid "See available elections"
msgstr "Voir les élections disponibles" msgstr "Voir les élections disponibles"
#: core/templates/core/user_tools.jinja:108 #: core/templates/core/user_tools.jinja:109
msgid "See archived elections" msgid "See archived elections"
msgstr "Voir les élections archivées" msgstr "Voir les élections archivées"
#: core/templates/core/user_tools.jinja:110 #: core/templates/core/user_tools.jinja:111
msgid "Create a new election" msgid "Create a new election"
msgstr "Créer une nouvelle élection" msgstr "Créer une nouvelle élection"
#: core/templates/core/user_tools.jinja:113 #: core/templates/core/user_tools.jinja:114
msgid "Other tools" msgid "Other tools"
msgstr "Autres outils" msgstr "Autres outils"
#: core/templates/core/user_tools.jinja:115 #: core/templates/core/user_tools.jinja:116
msgid "Convert dokuwiki/BBcode syntax to Markdown" msgid "Convert dokuwiki/BBcode syntax to Markdown"
msgstr "Convertir de la syntaxe dokuwiki/BBcode vers Markdown" msgstr "Convertir de la syntaxe dokuwiki/BBcode vers Markdown"
#: core/templates/core/user_tools.jinja:116 #: core/templates/core/user_tools.jinja:117
msgid "Trombi tools" msgid "Trombi tools"
msgstr "Outils Trombi" msgstr "Outils Trombi"
@ -4088,11 +4197,11 @@ msgstr "Fusionner deux utilisateurs"
msgid "Merge" msgid "Merge"
msgstr "Fusion" msgstr "Fusion"
#: rootplace/views.py:90 #: rootplace/views.py:91
msgid "User that will be kept" msgid "User that will be kept"
msgstr "Utilisateur qui sera conservé" msgstr "Utilisateur qui sera conservé"
#: rootplace/views.py:91 #: rootplace/views.py:92
msgid "User that will be deleted" msgid "User that will be deleted"
msgstr "Utilisateur qui sera supprimé" msgstr "Utilisateur qui sera supprimé"
@ -4300,10 +4409,8 @@ msgid "Sbarro/ESTA member"
msgstr "Membre de Sbarro ou de l'ESTA, 15 €" msgstr "Membre de Sbarro ou de l'ESTA, 15 €"
#: sith/settings.py:482 #: sith/settings.py:482
#, fuzzy
#| msgid "One semester"
msgid "One semester Welcome Week" msgid "One semester Welcome Week"
msgstr "Un semestre - Welcome Week" msgstr "Un semestre - Welcome Week 0 €"
#: sith/settings.py:504 #: sith/settings.py:504
msgid "President" msgid "President"
@ -4342,52 +4449,56 @@ msgid "Curious"
msgstr "Curieux" msgstr "Curieux"
#: sith/settings.py:546 #: sith/settings.py:546
msgid "A new mailing list needs to be moderated"
msgstr "Une nouvelle mailing list a besoin d'être modérée"
#: sith/settings.py:547
msgid "A fresh new to be moderated" msgid "A fresh new to be moderated"
msgstr "Une nouvelle toute neuve à modérer" msgstr "Une nouvelle toute neuve à modérer"
#: sith/settings.py:547 #: sith/settings.py:548
msgid "New files to be moderated" msgid "New files to be moderated"
msgstr "Nouveaux fichiers à modérer" msgstr "Nouveaux fichiers à modérer"
#: sith/settings.py:548 #: sith/settings.py:549
msgid "New pictures/album to be moderated in the SAS" msgid "New pictures/album to be moderated in the SAS"
msgstr "Nouvelles photos/albums à modérer dans le SAS" msgstr "Nouvelles photos/albums à modérer dans le SAS"
#: sith/settings.py:549 #: sith/settings.py:550
msgid "You've been identified on some pictures" msgid "You've been identified on some pictures"
msgstr "Vous avez été identifié sur des photos" msgstr "Vous avez été identifié sur des photos"
#: sith/settings.py:550 #: sith/settings.py:551
#, python-format #, python-format
msgid "You just refilled of %s €" msgid "You just refilled of %s €"
msgstr "Vous avez rechargé votre compte de %s €" msgstr "Vous avez rechargé votre compte de %s €"
#: sith/settings.py:551 #: sith/settings.py:552
#, python-format #, python-format
msgid "You just bought %s" msgid "You just bought %s"
msgstr "Vous avez acheté %s" msgstr "Vous avez acheté %s"
#: sith/settings.py:552 #: sith/settings.py:553
msgid "You have a notification" msgid "You have a notification"
msgstr "Vous avez une notification" msgstr "Vous avez une notification"
#: sith/settings.py:556 #: sith/settings.py:557
msgid "Success!" msgid "Success!"
msgstr "Succès !" msgstr "Succès !"
#: sith/settings.py:557 #: sith/settings.py:558
msgid "Fail!" msgid "Fail!"
msgstr "Échec !" msgstr "Échec !"
#: sith/settings.py:558 #: sith/settings.py:559
msgid "You successfully posted an article in the Weekmail" msgid "You successfully posted an article in the Weekmail"
msgstr "Article posté avec succès dans le Weekmail" msgstr "Article posté avec succès dans le Weekmail"
#: sith/settings.py:559 #: sith/settings.py:560
msgid "You successfully edited an article in the Weekmail" msgid "You successfully edited an article in the Weekmail"
msgstr "Article édité avec succès dans le Weekmail" msgstr "Article édité avec succès dans le Weekmail"
#: sith/settings.py:560 #: sith/settings.py:561
msgid "You successfully sent the Weekmail" msgid "You successfully sent the Weekmail"
msgstr "Weekmail envoyé avec succès" msgstr "Weekmail envoyé avec succès"

View File

@ -46,7 +46,7 @@ from django.core.files import File
from core.models import User, SithFile from core.models import User, SithFile
from core.utils import doku_to_markdown, bbcode_to_markdown from core.utils import doku_to_markdown, bbcode_to_markdown
from club.models import Club, Membership from club.models import Club, Membership, Mailing, MailingSubscription
from counter.models import Customer, Counter, Selling, Refilling, Product, ProductType, Permanency, Eticket from counter.models import Customer, Counter, Selling, Refilling, Product, ProductType, Permanency, Eticket
from subscription.models import Subscription from subscription.models import Subscription
from eboutic.models import Invoice, InvoiceItem from eboutic.models import Invoice, InvoiceItem
@ -1332,6 +1332,49 @@ def migrate_forum():
print("Forum migrated at %s" % datetime.datetime.now()) print("Forum migrated at %s" % datetime.datetime.now())
print("Running time: %s" % (datetime.datetime.now()-start)) print("Running time: %s" % (datetime.datetime.now()-start))
def migrate_mailings():
cur = db.cursor(MySQLdb.cursors.SSDictCursor)
print("Delete all mailings")
Mailing.objects.all().delete()
print("Migrating old mailing database")
cur.execute("""
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():
print(mailing)
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']),
moderator=moderator, is_moderated=(mailing['is_valid'] > 0)).save()
print("-------------------")
cur.execute("""
SELECT * FROM mailing_membres
""")
for mailing_sub in cur:
mailing = Mailing.objects.filter(id=mailing_sub['id_mailing'])
if mailing.exists():
print(mailing_sub)
mailing = mailing.first()
if mailing_sub['id_user'] and User.objects.filter(id=mailing_sub['id_user']).exists():
user = User.objects.get(id=mailing_sub['id_user'])
MailingSubscription(mailing=mailing, user=user, email=user.email).save()
elif mailing_sub['email']:
MailingSubscription(mailing=mailing, email=to_unicode(mailing_sub['email'])).save()
def main(): def main():
print("Start at %s" % start) print("Start at %s" % start)
# Core # Core
@ -1351,11 +1394,13 @@ def main():
# migrate_sas() # migrate_sas()
# reset_index('core', 'sas') # reset_index('core', 'sas')
# reset_sas_moderators() # reset_sas_moderators()
migrate_forum() # migrate_forum()
reset_index('forum') # reset_index('forum')
migrate_mailings()
end = datetime.datetime.now() end = datetime.datetime.now()
print("End at %s" % end) print("End at %s" % end)
print("Running time: %s" % (end - start)) print("Running time: %s" % (end - start))
if __name__ == "__main__": if __name__ == "__main__":
main() main()

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.
@ -22,7 +23,6 @@
# #
# #
from django.shortcuts import render
from django.utils.translation import ugettext as _ 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
@ -31,10 +31,10 @@ from django.core.exceptions import PermissionDenied
from ajax_select.fields import AutoCompleteSelectField from ajax_select.fields import AutoCompleteSelectField
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
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
u1.date_of_birth = u1.date_of_birth or u2.date_of_birth u1.date_of_birth = u1.date_of_birth or u2.date_of_birth
@ -86,10 +86,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
@ -106,4 +108,3 @@ 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})

View File

@ -543,6 +543,7 @@ SITH_LAUNDERETTE_PRICES = {
} }
SITH_NOTIFICATIONS = [ SITH_NOTIFICATIONS = [
('MAILING_MODERATION', _("A new mailing list needs to be moderated")),
('NEWS_MODERATION', _("A fresh new to be moderated")), ('NEWS_MODERATION', _("A fresh new to be moderated")),
('FILE_MODERATION', _("New files to be moderated")), ('FILE_MODERATION', _("New files to be moderated")),
('SAS_MODERATION', _("New pictures/album to be moderated in the SAS")), ('SAS_MODERATION', _("New pictures/album to be moderated in the SAS")),
@ -560,6 +561,11 @@ SITH_QUICK_NOTIF = {
'qn_weekmail_send_success': _("You successfully sent the Weekmail"), 'qn_weekmail_send_success': _("You successfully sent the Weekmail"),
} }
# Mailing related settings
SITH_MAILING_DOMAIN = 'utbm.fr'
SITH_MAILING_FETCH_KEY = 'IloveMails'
try: try:
from .settings_custom import * from .settings_custom import *
print("Custom settings imported") print("Custom settings imported")