From 1d07195881649a0dbf579059727d7176dbe39290 Mon Sep 17 00:00:00 2001 From: Bartuccio Antoine Date: Wed, 1 May 2019 22:52:22 +0200 Subject: [PATCH] clubs: add bulk deletion on mailing lists --- club/forms.py | 15 +- club/models.py | 14 +- club/templates/club/mailing.jinja | 55 +++++--- club/urls.py | 5 - club/views.py | 40 ++++-- locale/fr/LC_MESSAGES/django.po | 224 +++++++++++++++++------------- 6 files changed, 209 insertions(+), 144 deletions(-) diff --git a/club/forms.py b/club/forms.py index 0492577b..e0966fed 100644 --- a/club/forms.py +++ b/club/forms.py @@ -53,6 +53,7 @@ class MailingForm(forms.Form): ACTION_NEW_MAILING = 1 ACTION_NEW_SUBSCRIPTION = 2 + ACTION_REMOVE_SUBSCRIPTION = 3 subscription_users = AutoCompleteSelectMultipleField( "users", @@ -61,13 +62,14 @@ class MailingForm(forms.Form): required=False, ) - def __init__(self, club_id, user_id, *args, **kwargs): + def __init__(self, club_id, user_id, mailings, *args, **kwargs): super(MailingForm, self).__init__(*args, **kwargs) self.fields["action"] = forms.TypedChoiceField( ( (self.ACTION_NEW_MAILING, _("New Mailing")), (self.ACTION_NEW_SUBSCRIPTION, _("Subscribe")), + (self.ACTION_REMOVE_SUBSCRIPTION, _("Remove")), ), coerce=int, label=_("Action"), @@ -76,6 +78,15 @@ class MailingForm(forms.Form): widget=forms.HiddenInput(), ) + # Generate bulk removal forms, they are never required + for mailing in mailings: + self.fields["removal_" + str(mailing.id)] = forms.ModelMultipleChoiceField( + mailing.subscriptions.all(), + label=_("Remove"), + required=False, + widget=forms.CheckboxSelectMultiple, + ) + # Include fields for handling mailing creation mailing_fields = ("email", "club", "moderator") self.fields.update(forms.fields_for_model(Mailing, fields=mailing_fields)) @@ -133,8 +144,6 @@ class MailingForm(forms.Form): def clean(self): cleaned_data = super(MailingForm, self).clean() - print(cleaned_data) - if not "action" in cleaned_data: # If there is no action provided, we can stop here raise forms.ValidationError(_("An action is required"), code="invalid") diff --git a/club/models.py b/club/models.py index d446c080..50cacd0d 100644 --- a/club/models.py +++ b/club/models.py @@ -446,18 +446,20 @@ class MailingSubscription(models.Model): def can_be_edited_by(self, user): return self.user is not None and user.id == self.user.id - @property + @cached_property def get_email(self): if self.user and not self.email: return self.user.email return self.email + @cached_property + def get_username(self): + if self.user: + return str(self.user) + return _("Unregistered user") + def fetch_format(self): return self.get_email + " " def __str__(self): - if self.user: - user = str(self.user) - else: - user = _("Unregistered user") - return "(%s) - %s : %s" % (self.mailing, user, self.email) + return "(%s) - %s : %s" % (self.mailing, self.get_username, self.email) diff --git a/club/templates/club/mailing.jinja b/club/templates/club/mailing.jinja index 7d485338..1222a332 100644 --- a/club/templates/club/mailing.jinja +++ b/club/templates/club/mailing.jinja @@ -1,4 +1,5 @@ {% extends "core/base.jinja" %} +{% from 'core/macros.jinja' import select_all_checkbox %} {% block title %} {% trans %}Mailing lists{% endtrans %} @@ -10,8 +11,7 @@ {% 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 mailings %} - {% if mailing.is_moderated %} -

{% trans %}Mailing{% endtrans %} {{ mailing.email_full }} +

{% trans %}Mailing{% endtrans %} {{ mailing.email_full }} {%- if user.is_owner(mailing) -%} - {% trans %}Delete{% endtrans %} {%- endif -%} @@ -19,27 +19,38 @@
-
- + {% set form_mailing_removal = form["removal_" + mailing.id|string] %} + {% if form_mailing_removal.field.choices %} + {% set ms = dict(mailing.subscriptions.all() | groupby('id')) %} + +

{{ select_all_checkbox(form_mailing_removal.auto_id) }}

+ {% csrf_token %} + + + + + + + + + + + {% for widget in form_mailing_removal.subwidgets %} + {% set user = ms[widget.data.value][0] %} + + + + + + {% endfor %} + +
{% trans %}User{% endtrans %}{% trans %}Email{% endtrans %}{% trans %}Delete{% endtrans %}
{{ user.get_username }}{{ user.get_email }}{{ widget.tag() }}
+ {{ form_mailing_removal.errors }} +

-
- - - - - - {% for subscriber in mailing.subscriptions.all() %} - - {% if subscriber.user %} - - {% else %} - - {% endif %} - - - - {% endfor %} -
{% trans %}User{% endtrans %}{% trans %}Email{%endtrans%}
{{ subscriber.user }}{% trans %}Unregistered user{% endtrans %}{{ subscriber.get_email }}{% trans %}Delete{% endtrans %}
+ + {% else %} +

{% trans %}There is no subscriber for this mailing list{% endtrans %}

{% endif %} {% endfor %} diff --git a/club/urls.py b/club/urls.py index d560a560..c5a01e02 100644 --- a/club/urls.py +++ b/club/urls.py @@ -70,11 +70,6 @@ urlpatterns = [ MailingAutoGenerationView.as_view(), name="mailing_generate", ), - url( - r"^(?P[0-9]+)/mailing/clean$", - MailingAutoCleanView.as_view(), - name="mailing_clean", - ), url( r"^(?P[0-9]+)/mailing/delete$", MailingDeleteView.as_view(), diff --git a/club/views.py b/club/views.py index 06c8c7f3..151dab16 100644 --- a/club/views.py +++ b/club/views.py @@ -513,23 +513,29 @@ class ClubMailingView(ClubTabsMixin, CanEditMixin, DetailFormView): template_name = "club/mailing.jinja" current_tab = "mailing" - def get_form_kwargs(self, *args, **kwargs): - kwargs = super(ClubMailingView, self).get_form_kwargs(*args, **kwargs) + def get_form_kwargs(self): + kwargs = super(ClubMailingView, self).get_form_kwargs() kwargs["club_id"] = self.get_object().id kwargs["user_id"] = self.request.user.id + kwargs["mailings"] = self.mailings return kwargs + def dispatch(self, request, *args, **kwargs): + self.mailings = Mailing.objects.filter(club_id=self.get_object().id).all() + return super(ClubMailingView, self).dispatch(request, *args, **kwargs) + def get_context_data(self, **kwargs): kwargs = super(ClubMailingView, self).get_context_data(**kwargs) kwargs["club"] = self.get_object() kwargs["user"] = self.request.user - kwargs["mailings"] = Mailing.objects.filter(club_id=self.get_object().id).all() + kwargs["mailings"] = self.mailings kwargs["mailings_moderated"] = ( kwargs["mailings"].exclude(is_moderated=False).all() ) kwargs["form_actions"] = { "NEW_MALING": self.form_class.ACTION_NEW_MAILING, "NEW_SUBSCRIPTION": self.form_class.ACTION_NEW_SUBSCRIPTION, + "REMOVE_SUBSCRIPTION": self.form_class.ACTION_REMOVE_SUBSCRIPTION, } return kwargs @@ -565,6 +571,19 @@ class ClubMailingView(ClubTabsMixin, CanEditMixin, DetailFormView): sub.clean() sub.save() + def remove_subscription(self, cleaned_data): + """ + Remove specified users from a mailing list + """ + fields = [ + cleaned_data[key] + for key in cleaned_data.keys() + if key.startswith("removal_") + ] + for field in fields: + for sub in field: + sub.delete() + def form_valid(self, form): resp = super(ClubMailingView, self).form_valid(form) @@ -576,6 +595,9 @@ class ClubMailingView(ClubTabsMixin, CanEditMixin, DetailFormView): if cleaned_data["action"] == self.form_class.ACTION_NEW_SUBSCRIPTION: self.add_new_subscription(cleaned_data) + if cleaned_data["action"] == self.form_class.ACTION_REMOVE_SUBSCRIPTION: + self.remove_subscription(cleaned_data) + return resp def get_success_url(self, **kwargs): @@ -634,18 +656,6 @@ class MailingAutoGenerationView(View): return redirect("club:mailing", club_id=club.id) -class MailingAutoCleanView(View): - def dispatch(self, request, *args, **kwargs): - self.mailing = get_object_or_404(Mailing, pk=kwargs["mailing_id"]) - if not request.user.can_edit(self.mailing): - raise PermissionDenied - return super(MailingAutoCleanView, self).dispatch(request, *args, **kwargs) - - def get(self, request, *args, **kwargs): - self.mailing.subscriptions.all().delete() - return redirect("club:mailing", club_id=self.mailing.club.id) - - class PosterListView(ClubTabsMixin, PosterListBaseView, CanViewMixin): """List communication posters""" diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index d5f73570..ec15c62e 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-04-28 15:00+0200\n" +"POT-Creation-Date: 2019-05-01 22:45+0200\n" "PO-Revision-Date: 2016-07-18\n" "Last-Translator: Skia \n" "Language-Team: AE info \n" @@ -177,7 +177,7 @@ msgstr "type de cible" #: accounting/models.py:313 club/models.py:413 #: club/templates/club/club_members.jinja:16 #: club/templates/club/club_old_members.jinja:8 -#: club/templates/club/mailing.jinja:28 club/views.py:112 +#: club/templates/club/mailing.jinja:32 #: counter/templates/counter/cash_summary_list.jinja:32 #: counter/templates/counter/stats.jinja:15 #: counter/templates/counter/stats.jinja:52 @@ -345,7 +345,7 @@ msgstr "Compte en banque : " #: accounting/templates/accounting/club_account_details.jinja:60 #: accounting/templates/accounting/label_list.jinja:26 #: club/templates/club/club_sellings.jinja:50 -#: club/templates/club/mailing.jinja:16 club/templates/club/mailing.jinja:39 +#: club/templates/club/mailing.jinja:16 club/templates/club/mailing.jinja:34 #: com/templates/com/mailing_admin.jinja:19 #: com/templates/com/news_admin_list.jinja:41 #: com/templates/com/news_admin_list.jinja:70 @@ -386,7 +386,7 @@ msgid "Delete" msgstr "Supprimer" #: accounting/templates/accounting/bank_account_details.jinja:18 -#: club/views.py:129 core/views/user.py:205 sas/templates/sas/picture.jinja:86 +#: club/views.py:78 core/views/user.py:205 sas/templates/sas/picture.jinja:86 msgid "Infos" msgstr "Infos" @@ -405,7 +405,7 @@ msgstr "Nouveau compte club" #: accounting/templates/accounting/bank_account_details.jinja:27 #: accounting/templates/accounting/bank_account_list.jinja:22 #: accounting/templates/accounting/club_account_details.jinja:58 -#: accounting/templates/accounting/journal_details.jinja:89 club/views.py:175 +#: accounting/templates/accounting/journal_details.jinja:89 club/views.py:124 #: com/templates/com/news_admin_list.jinja:39 #: com/templates/com/news_admin_list.jinja:68 #: com/templates/com/news_admin_list.jinja:115 @@ -893,6 +893,101 @@ msgstr "Opérations sans étiquette" msgid "Refound this account" msgstr "Rembourser ce compte" +#: club/forms.py:60 club/forms.py:197 +msgid "Users to add" +msgstr "Utilisateurs à ajouter" + +#: club/forms.py:61 club/forms.py:198 core/views/group.py:63 +msgid "Search users to add (one or more)." +msgstr "Recherche les utilisateurs à ajouter (un ou plus)." + +#: club/forms.py:70 +#, fuzzy +#| msgid "New mailing" +msgid "New Mailing" +msgstr "Nouvelle mailing liste" + +#: club/forms.py:71 +#, fuzzy +#| msgid "Unsubscribe" +msgid "Subscribe" +msgstr "Se désabonner" + +#: club/forms.py:72 club/forms.py:85 com/templates/com/news_admin_list.jinja:40 +#: com/templates/com/news_admin_list.jinja:116 +#: com/templates/com/news_admin_list.jinja:198 +#: com/templates/com/news_admin_list.jinja:274 +msgid "Remove" +msgstr "Retirer" + +#: club/forms.py:75 launderette/views.py:228 +msgid "Action" +msgstr "Action" + +#: club/forms.py:122 +#, fuzzy +#| msgid "This field is required." +msgid "This field is required" +msgstr "Ce champ est obligatoire." + +#: club/forms.py:134 club/forms.py:259 +msgid "One of the selected users doesn't exist" +msgstr "Un des utilisateurs sélectionné n'existe pas" + +#: club/forms.py:138 +#, fuzzy +#| msgid "One of the selected users doesn't exist" +msgid "One of the selected users doesn't have an email address" +msgstr "Un des utilisateurs sélectionné n'existe pas" + +#: club/forms.py:149 +#, fuzzy +#| msgid "This field is required." +msgid "An action is required" +msgstr "Ce champ est obligatoire." + +#: club/forms.py:162 +msgid "You must specify at least an user or an email address" +msgstr "" + +#: club/forms.py:172 counter/views.py:1481 +msgid "Begin date" +msgstr "Date de début" + +#: club/forms.py:178 com/views.py:85 com/views.py:221 counter/views.py:1487 +#: election/views.py:190 subscription/views.py:52 +msgid "End date" +msgstr "Date de fin" + +#: club/forms.py:183 club/templates/club/club_sellings.jinja:21 +#: core/templates/core/user_account_detail.jinja:18 +#: core/templates/core/user_account_detail.jinja:51 +#: counter/templates/counter/cash_summary_list.jinja:33 counter/views.py:168 +msgid "Counter" +msgstr "Comptoir" + +#: club/forms.py:241 club/templates/club/club_members.jinja:21 +#: club/templates/club/club_members.jinja:46 +#: core/templates/core/user_clubs.jinja:29 +msgid "Mark as old" +msgstr "Marquer comme ancien" + +#: club/forms.py:263 +msgid "User must be subscriber to take part to a club" +msgstr "L'utilisateur doit être cotisant pour faire partie d'un club" + +#: club/forms.py:267 core/views/group.py:82 +msgid "You can not add the same user twice" +msgstr "Vous ne pouvez pas ajouter deux fois le même utilisateur" + +#: club/forms.py:288 +msgid "You should specify a role" +msgstr "Vous devez choisir un rôle" + +#: club/forms.py:299 sas/views.py:129 sas/views.py:195 sas/views.py:286 +msgid "You do not have the permission to do that" +msgstr "Vous n'avez pas la permission de faire cela" + #: club/models.py:51 msgid "unix name" msgstr "nom unix" @@ -988,9 +1083,9 @@ msgstr "Au moins un utilisateur ou un email est nécessaire" msgid "This email is already suscribed in this mailing" msgstr "Cet email est déjà abonné à cette mailing" -#: club/models.py:462 club/templates/club/mailing.jinja:36 +#: club/models.py:459 msgid "Unregistered user" -msgstr "Désabonner un utilisateur" +msgstr "Utilisateur non enregistré" #: club/templates/club/club_list.jinja:4 club/templates/club/club_list.jinja:37 msgid "Club list" @@ -1037,12 +1132,6 @@ msgstr "Description" msgid "Since" msgstr "Depuis" -#: club/templates/club/club_members.jinja:21 -#: club/templates/club/club_members.jinja:46 club/views.py:363 -#: core/templates/core/user_clubs.jinja:29 -msgid "Mark as old" -msgstr "Marquer comme ancien" - #: club/templates/club/club_members.jinja:50 msgid "There are no members in this club." msgstr "Il n'y a pas de membres dans ce club." @@ -1067,8 +1156,8 @@ msgstr "Du" msgid "To" msgstr "Au" -#: club/templates/club/club_sellings.jinja:5 club/views.py:195 -#: club/views.py:563 counter/templates/counter/counter_main.jinja:19 +#: club/templates/club/club_sellings.jinja:5 club/views.py:144 +#: club/views.py:378 counter/templates/counter/counter_main.jinja:19 #: counter/templates/counter/last_ops.jinja:35 msgid "Sellings" msgstr "Ventes" @@ -1094,13 +1183,6 @@ msgstr "unités" msgid "Benefit: " msgstr "Bénéfice : " -#: club/templates/club/club_sellings.jinja:21 club/views.py:502 -#: core/templates/core/user_account_detail.jinja:18 -#: core/templates/core/user_account_detail.jinja:51 -#: counter/templates/counter/cash_summary_list.jinja:33 counter/views.py:168 -msgid "Counter" -msgstr "Comptoir" - #: club/templates/club/club_sellings.jinja:22 #: core/templates/core/user_account_detail.jinja:19 #: core/templates/core/user_account_detail.jinja:52 @@ -1189,11 +1271,11 @@ msgstr "Comptabilité : " msgid "Manage launderettes" msgstr "Gestion des laveries" -#: club/templates/club/mailing.jinja:4 +#: club/templates/club/mailing.jinja:5 msgid "Mailing lists" msgstr "Mailing listes" -#: club/templates/club/mailing.jinja:10 +#: club/templates/club/mailing.jinja:11 msgid "" "Remember : mailing lists need to be moderated, if your new created list is " "not shown wait until moderation takes action" @@ -1206,34 +1288,38 @@ msgstr "" msgid "Generate mailing list" msgstr "Générer la liste de diffusion" -#: club/templates/club/mailing.jinja:23 -msgid "Clean mailing list" -msgstr "Néttoyer la liste de diffusion" - -#: club/templates/club/mailing.jinja:29 +#: club/templates/club/mailing.jinja:33 #: com/templates/com/mailing_admin.jinja:10 msgid "Email" msgstr "Email" -#: club/templates/club/mailing.jinja:47 +#: club/templates/club/mailing.jinja:49 +msgid "Remove from mailing list" +msgstr "Supprimer de la liste de diffusion" + +#: club/templates/club/mailing.jinja:53 +msgid "There is no subscriber for this mailing list" +msgstr "Il n'y a pas d'abonnés dans cette liste de diffusion" + +#: club/templates/club/mailing.jinja:58 msgid "No mailing list existing for this club" msgstr "Aucune mailing liste n'existe pour ce club" -#: club/templates/club/mailing.jinja:51 +#: club/templates/club/mailing.jinja:63 msgid "New member" msgstr "Nouveau membre" -#: club/templates/club/mailing.jinja:55 +#: club/templates/club/mailing.jinja:83 msgid "Add to mailing list" msgstr "Ajouter à la mailing liste" -#: club/templates/club/mailing.jinja:59 +#: club/templates/club/mailing.jinja:87 msgid "New mailing" -msgstr "Nouvelle mailing liste" +msgstr "Nouvelle liste de diffusion" -#: club/templates/club/mailing.jinja:63 +#: club/templates/club/mailing.jinja:100 msgid "Create mailing list" -msgstr "Créer une mailing liste" +msgstr "Créer une liste de diffusion" #: club/templates/club/page_history.jinja:8 msgid "No page existing for this club" @@ -1243,79 +1329,42 @@ msgstr "Aucune page n'existe pour ce club" msgid "Club stats" msgstr "Statistiques du club" -#: club/views.py:139 +#: club/views.py:88 msgid "Members" msgstr "Membres" -#: club/views.py:148 +#: club/views.py:97 msgid "Old members" msgstr "Anciens membres" -#: club/views.py:158 core/templates/core/page.jinja:33 +#: club/views.py:107 core/templates/core/page.jinja:33 msgid "History" msgstr "Historique" -#: club/views.py:166 core/templates/core/base.jinja:121 core/views/user.py:228 +#: club/views.py:115 core/templates/core/base.jinja:121 core/views/user.py:228 #: sas/templates/sas/picture.jinja:95 trombi/views.py:60 msgid "Tools" msgstr "Outils" -#: club/views.py:186 +#: club/views.py:135 msgid "Edit club page" msgstr "Éditer la page de club" -#: club/views.py:202 +#: club/views.py:151 msgid "Mailing list" msgstr "Listes de diffusion" -#: club/views.py:211 com/views.py:141 +#: club/views.py:160 com/views.py:141 msgid "Posters list" msgstr "Liste d'affiches" -#: club/views.py:221 counter/templates/counter/counter_list.jinja:21 +#: club/views.py:170 counter/templates/counter/counter_list.jinja:21 #: counter/templates/counter/counter_list.jinja:43 #: counter/templates/counter/counter_list.jinja:59 msgid "Props" msgstr "Propriétés" -#: club/views.py:319 -msgid "Users to add" -msgstr "Utilisateurs à ajouter" - -#: club/views.py:320 core/views/group.py:63 -msgid "Search users to add (one or more)." -msgstr "Recherche les utilisateurs à ajouter (un ou plus)." - -#: club/views.py:381 -msgid "One of the selected users doesn't exist" -msgstr "Un des utilisateurs sélectionné n'existe pas" - -#: club/views.py:385 -msgid "User must be subscriber to take part to a club" -msgstr "L'utilisateur doit être cotisant pour faire partie d'un club" - -#: club/views.py:389 core/views/group.py:82 -msgid "You can not add the same user twice" -msgstr "Vous ne pouvez pas ajouter deux fois le même utilisateur" - -#: club/views.py:410 -msgid "You should specify a role" -msgstr "Vous devez choisir un rôle" - -#: club/views.py:421 sas/views.py:129 sas/views.py:195 sas/views.py:286 -msgid "You do not have the permission to do that" -msgstr "Vous n'avez pas la permission de faire cela" - -#: club/views.py:491 counter/views.py:1481 -msgid "Begin date" -msgstr "Date de début" - -#: club/views.py:497 com/views.py:85 com/views.py:221 counter/views.py:1487 -#: election/views.py:190 subscription/views.py:52 -msgid "End date" -msgstr "Date de fin" - -#: club/views.py:520 core/templates/core/user_stats.jinja:27 +#: club/views.py:335 core/templates/core/user_stats.jinja:27 #: counter/views.py:1635 msgid "Product" msgstr "Produit" @@ -1547,13 +1596,6 @@ msgstr "Auteur" msgid "Moderator" msgstr "Modérateur" -#: com/templates/com/news_admin_list.jinja:40 -#: com/templates/com/news_admin_list.jinja:116 -#: com/templates/com/news_admin_list.jinja:198 -#: com/templates/com/news_admin_list.jinja:274 -msgid "Remove" -msgstr "Retirer" - #: com/templates/com/news_admin_list.jinja:47 msgid "Notices to moderate" msgstr "Informations à modérer" @@ -4612,10 +4654,6 @@ msgstr "Éditer la page de présentation" msgid "Book launderette slot" msgstr "Réserver un créneau de laverie" -#: launderette/views.py:228 -msgid "Action" -msgstr "Action" - #: launderette/views.py:240 msgid "Tokens, separated by spaces" msgstr "Jetons, séparés par des espaces"