diff --git a/templates/tutorialv2/messages/remove_author_pm.md b/templates/tutorialv2/messages/remove_author_pm.md index 6139777298..62a377a677 100644 --- a/templates/tutorialv2/messages/remove_author_pm.md +++ b/templates/tutorialv2/messages/remove_author_pm.md @@ -1,8 +1,8 @@ {% load i18n %} -{% blocktrans with title=content.title|safe type=type|safe user=user|safe %} +{% blocktrans with title=content.title|safe user=user|safe %} Bonjour {{ user }}, -Vous avez été retiré de la rédaction du contenu « {{ title }} ». +Vous avez été retiré de la rédaction de « {{ title }} ». {% endblocktrans %} diff --git a/zds/tutorialv2/models/database.py b/zds/tutorialv2/models/database.py index 8f0a50ddb2..0fa03ea662 100644 --- a/zds/tutorialv2/models/database.py +++ b/zds/tutorialv2/models/database.py @@ -320,6 +320,21 @@ def is_author(self, user: User) -> bool: # This is fast because there are few authors and the QuerySet is usually prefetched and cached. return user in self.authors.all() + def remove_author(self, user: User) -> bool: + """ + Remove a user from the authors and remove his access to the gallery. + If the user is not an author, do nothing. + If the user is the last author, do nothing. This method will not delete the content. + Return ``True`` if the user was effectively removed from the authors and ``False`` otherwise. + """ + if self.is_author(user) and self.authors.count() > 1: + gallery = UserGallery.objects.filter(user__pk=user.pk, gallery__pk=self.gallery.pk).first() + if gallery: + gallery.delete() + self.authors.remove(user) + return True + return False + def is_permanently_unpublished(self): """Is this content permanently unpublished by a moderator ?""" diff --git a/zds/tutorialv2/models/events.py b/zds/tutorialv2/models/events.py index 8617d105c1..6788422492 100644 --- a/zds/tutorialv2/models/events.py +++ b/zds/tutorialv2/models/events.py @@ -4,7 +4,7 @@ from zds.tutorialv2.models.database import PublishableContent from zds.tutorialv2 import signals -from zds.tutorialv2.views.authors import AddAuthorToContent, RemoveAuthorFromContent +from zds.tutorialv2.views.authors import AddAuthorToContent, RemoveAuthorView from zds.tutorialv2.views.beta import ManageBetaContent from zds.tutorialv2.views.canonical import EditCanonicalLinkView from zds.tutorialv2.views.contributors import AddContributorToContent, RemoveContributorFromContent @@ -98,7 +98,7 @@ def record_event_beta_management(sender, performer, signal, content, version, ac @receiver(signals.authors_management, sender=AddAuthorToContent) -@receiver(signals.authors_management, sender=RemoveAuthorFromContent) +@receiver(signals.authors_management, sender=RemoveAuthorView) def record_event_author_management(sender, performer, signal, content, author, action, **_): Event( performer=performer, diff --git a/zds/tutorialv2/urls/urls_contents.py b/zds/tutorialv2/urls/urls_contents.py index 89da39e1e1..1f32dde98c 100644 --- a/zds/tutorialv2/urls/urls_contents.py +++ b/zds/tutorialv2/urls/urls_contents.py @@ -38,7 +38,7 @@ ) from zds.tutorialv2.views.history import DisplayHistory, DisplayDiff from zds.tutorialv2.views.help import ContentsWithHelps, ChangeHelp -from zds.tutorialv2.views.authors import AddAuthorToContent, RemoveAuthorFromContent +from zds.tutorialv2.views.authors import AddAuthorToContent, RemoveAuthorView from zds.tutorialv2.views.redirect import RedirectOldContentOfAuthor from zds.tutorialv2.views.archives import DownloadContent, UpdateContentWithArchive, CreateContentFromArchive from zds.tutorialv2.views.contributors import ( @@ -215,7 +215,7 @@ def get_version_pages(): path("ajouter-contributeur//", AddContributorToContent.as_view(), name="add-contributor"), path("enlever-contributeur//", RemoveContributorFromContent.as_view(), name="remove-contributor"), path("ajouter-auteur//", AddAuthorToContent.as_view(), name="add-author"), - path("enlever-auteur//", RemoveAuthorFromContent.as_view(), name="remove-author"), + path("enlever-auteur//", RemoveAuthorView.as_view(), name="remove-author"), path("modifier-titre//", EditTitle.as_view(), name="edit-title"), path("modifier-sous-titre//", EditSubtitle.as_view(), name="edit-subtitle"), path("modifier-miniature//", EditThumbnailView.as_view(), name="edit-thumbnail"), diff --git a/zds/tutorialv2/views/authors.py b/zds/tutorialv2/views/authors.py index e77d673884..1f1471e401 100644 --- a/zds/tutorialv2/views/authors.py +++ b/zds/tutorialv2/views/authors.py @@ -3,7 +3,6 @@ from crispy_forms.layout import Layout, Field from django import forms from django.contrib import messages -from django.contrib.auth.models import User from django.shortcuts import redirect from django.template.loader import render_to_string from django.urls import reverse @@ -139,63 +138,24 @@ def form_invalid(self, form): return super().form_valid(form) -class RemoveAuthorFromContent(LoggedWithReadWriteHability, SingleContentFormViewMixin): +class RemoveAuthorView(LoggedWithReadWriteHability, SingleContentFormViewMixin): form_class = RemoveAuthorForm must_be_author = True authorized_for_staff = True - - @staticmethod - def remove_author(content, user): - """Remove a user from the authors and ensure that he is access to the content's gallery is also removed. - The last author is not removed. - - :param content: the content - :type content: zds.tutorialv2.models.database.PublishableContent - :param user: the author - :type user: User - :return: ``True`` if the author was removed, ``False`` otherwise - """ - if user in content.authors.all() and content.authors.count() > 1: - gallery = UserGallery.objects.filter(user__pk=user.pk, gallery__pk=content.gallery.pk).first() - - if gallery: - gallery.delete() - - content.authors.remove(user) - return True - - return False + http_method_names = ["post"] def form_valid(self, form): + content = self.object current_user = False users = form.cleaned_data["users"] - _type = (_("cet article"), _("de l'article")) - if self.object.is_tutorial: - _type = (_("ce tutoriel"), _("du tutoriel")) - elif self.object.is_opinion: - _type = (_("ce billet"), _("du billet")) - bot = get_bot_account() for user in users: - if RemoveAuthorFromContent.remove_author(self.object, user): + if content.remove_author(user): if user.pk == self.request.user.pk: current_user = True elif is_reachable(user): - send_mp( - bot, - [user], - format_lazy("{}{}", _("Retrait de la rédaction "), _type[1]), - self.versioned_object.title, - render_to_string( - "tutorialv2/messages/remove_author_pm.md", - { - "content": self.object, - "user": user.username, - }, - ), - hat=get_hat_from_settings("validation"), - ) + self.notify_by_private_message(user, bot) signals.authors_management.send( sender=self.__class__, content=self.object, @@ -206,36 +166,54 @@ def form_valid(self, form): else: # if user is incorrect or alone messages.error( self.request, - _("Vous êtes le seul auteur de {} ou le membre sélectionné en a déjà quitté la rédaction.").format( - _type[0] + _( + "Vous êtes le seul auteur de la publication ou le membre sélectionné en a déjà quitté la rédaction." ), ) return redirect(self.object.get_absolute_url()) - self.object.save() + content.save() - authors_list = "" + if current_user: # Redirect self-removing authors to their publications list + messages.success(self.request, _("Vous avez bien quitté la rédaction de la publication.")) + self.success_url = reverse("content:find-all", args=[self.request.user.username]) + else: # The user removed another user from the authors + authors_list = self.users_list(users) + messages.success( + self.request, + _("Vous avez enlevé {} de la liste des auteurs et autrices de la publication.").format(authors_list), + ) + self.success_url = self.object.get_absolute_url() - for index, user in enumerate(form.cleaned_data["users"]): + return super().form_valid(form) + + @staticmethod + def users_list(users): + authors_list = "" + for index, user in enumerate(users): if index > 0: if index == len(users) - 1: authors_list += _(" et ") else: authors_list += _(", ") authors_list += user.username - - if not current_user: # if the removed author is not current user - messages.success( - self.request, - _("Vous avez enlevé {} de la liste des auteurs et autrices de {}.").format(authors_list, _type[0]), - ) - self.success_url = self.object.get_absolute_url() - else: # if current user is leaving the content's redaction, redirect him to a more suitable page - messages.success(self.request, _("Vous avez bien quitté la rédaction de {}.").format(_type[0])) - self.success_url = reverse( - self.object.type.lower() + ":find-" + self.object.type.lower(), args=[self.request.user.username] - ) - return super().form_valid(form) + return authors_list + + def notify_by_private_message(self, user, bot): + send_mp( + bot, + [user], + _("Retrait de la rédaction de la publication"), + self.versioned_object.title, + render_to_string( + "tutorialv2/messages/remove_author_pm.md", + { + "content": self.object, + "user": user.username, + }, + ), + hat=get_hat_from_settings("validation"), + ) def form_invalid(self, form): messages.error(self.request, _("Les auteurs sélectionnés n'existent pas.")) diff --git a/zds/tutorialv2/views/contents.py b/zds/tutorialv2/views/contents.py index 036258b610..4e38a44da3 100644 --- a/zds/tutorialv2/views/contents.py +++ b/zds/tutorialv2/views/contents.py @@ -27,7 +27,7 @@ ) from zds.tutorialv2.models.database import PublishableContent, Validation from zds.tutorialv2.utils import init_new_repo -from zds.tutorialv2.views.authors import RemoveAuthorFromContent +from zds.tutorialv2.views.authors import RemoveAuthorView from zds.utils.forms import IncludeEasyMDE from zds.utils.models import get_hat_from_settings from zds.mp.utils import send_mp, send_message_mp @@ -450,11 +450,10 @@ def form_valid(self, form): elif self.object.is_opinion: _type = _("ce billet") - if self.object.authors.count() > 1: # if more than one author, just remove author from list - RemoveAuthorFromContent.remove_author(self.object, self.request.user) + if self.object.remove_author(self.request.user): messages.success(self.request, _("Vous avez quitté la rédaction de {}.").format(_type)) - else: + # If removing the author failed, it is the last author, and we must delete the content. validation = Validation.objects.filter(content=self.object).order_by("-date_proposition").first() if validation and validation.status == "PENDING_V": # if the validation have a validator, warn him by PM