From 398393511ed7731ea98b3ad7033bd5f06465c14f Mon Sep 17 00:00:00 2001 From: Arnaud-D <35631001+Arnaud-D@users.noreply.github.com> Date: Sun, 2 Feb 2025 09:40:17 +0100 Subject: [PATCH] Refacto WarnTypo --- zds/member/models.py | 2 - zds/tutorialv2/urls/urls_contents.py | 3 +- zds/tutorialv2/views/display/container.py | 2 +- zds/tutorialv2/views/display/content.py | 2 +- zds/tutorialv2/views/misc.py | 155 +--------------------- zds/tutorialv2/views/warntypo.py | 151 +++++++++++++++++++++ 6 files changed, 156 insertions(+), 159 deletions(-) create mode 100644 zds/tutorialv2/views/warntypo.py diff --git a/zds/member/models.py b/zds/member/models.py index 53fefa63d9..16017a72f9 100644 --- a/zds/member/models.py +++ b/zds/member/models.py @@ -1,6 +1,4 @@ from datetime import datetime -from hashlib import md5 -import logging from django.conf import settings from django.contrib.auth.models import User diff --git a/zds/tutorialv2/urls/urls_contents.py b/zds/tutorialv2/urls/urls_contents.py index 6274330219..b87e6f1dfd 100644 --- a/zds/tutorialv2/urls/urls_contents.py +++ b/zds/tutorialv2/urls/urls_contents.py @@ -51,7 +51,8 @@ from zds.tutorialv2.views.lists import TagsListView, ContentOfAuthor, ListContentReactions from zds.tutorialv2.views.alerts import SendContentAlert, SolveContentAlert -from zds.tutorialv2.views.misc import RequestFeaturedContent, FollowNewContent, WarnTypoView +from zds.tutorialv2.views.misc import RequestFeaturedContent, FollowNewContent +from zds.tutorialv2.views.warntypo import WarnTypoView from zds.tutorialv2.views.statistics import ContentStatisticsView from zds.tutorialv2.views.comments import ( SendNoteFormView, diff --git a/zds/tutorialv2/views/display/container.py b/zds/tutorialv2/views/display/container.py index cb313bbdef..0e007182ca 100644 --- a/zds/tutorialv2/views/display/container.py +++ b/zds/tutorialv2/views/display/container.py @@ -3,7 +3,7 @@ from django.urls import reverse from django.utils.translation import gettext_lazy as _ -from zds.tutorialv2.views.misc import WarnTypoForm +from zds.tutorialv2.views.warntypo import WarnTypoForm from zds.tutorialv2.mixins import SingleContentDetailViewMixin, SingleOnlineContentDetailViewMixin from zds.tutorialv2.models.database import PublishableContent from zds.tutorialv2.utils import search_container_or_404, get_target_tagged_tree diff --git a/zds/tutorialv2/views/display/content.py b/zds/tutorialv2/views/display/content.py index 55c45a1685..9c02b5ee6f 100644 --- a/zds/tutorialv2/views/display/content.py +++ b/zds/tutorialv2/views/display/content.py @@ -24,7 +24,7 @@ UnpickOpinionForm, PromoteOpinionToArticleForm, ) -from zds.tutorialv2.views.misc import WarnTypoForm +from zds.tutorialv2.views.warntypo import WarnTypoForm from zds.tutorialv2.views.canonical import EditCanonicalLinkForm from zds.tutorialv2.views.contributors import ContributionForm from zds.tutorialv2.views.suggestions import SearchSuggestionForm diff --git a/zds/tutorialv2/views/misc.py b/zds/tutorialv2/views/misc.py index 7b4325517e..5d335e5f7c 100644 --- a/zds/tutorialv2/views/misc.py +++ b/zds/tutorialv2/views/misc.py @@ -1,26 +1,15 @@ -from crispy_forms.bootstrap import StrictButton -from crispy_forms.helper import FormHelper -from crispy_forms.layout import Layout, Field, HTML, Hidden, ButtonHolder -from django import forms -from django.contrib import messages from django.contrib.auth.models import User from django.core.exceptions import PermissionDenied from django.db import transaction from django.http import HttpResponse, Http404 from django.shortcuts import redirect -from django.template.loader import render_to_string -from django.urls import reverse -from django.utils.translation import gettext_lazy as _ from django.views.generic import FormView from zds import json_handler from zds.featured.mixins import FeatureableMixin from zds.member.decorator import LoggedWithReadWriteHability -from zds.member.models import Profile from zds.notification.models import NewPublicationSubscription -from zds.tutorialv2.mixins import SingleOnlineContentViewMixin, SingleContentFormViewMixin -from zds.tutorialv2.utils import search_container_or_404 -from zds.mp.utils import send_mp +from zds.tutorialv2.mixins import SingleOnlineContentViewMixin from zds.utils.misc import is_ajax @@ -76,145 +65,3 @@ def post(self, request, *args, **kwargs): if is_ajax(self.request): return HttpResponse(json_handler.dumps(response), content_type="application/json") return redirect(request.META.get("HTTP_REFERER")) - - -class WarnTypoForm(forms.Form): - text = forms.CharField( - label="", - required=True, - widget=forms.Textarea(attrs={"placeholder": _("Expliquez la faute"), "rows": "3", "id": "warn_text"}), - ) - - target = forms.CharField(widget=forms.HiddenInput(), required=False) - version = forms.CharField(widget=forms.HiddenInput(), required=True) - - def __init__(self, content, targeted, public=True, *args, **kwargs): - super().__init__(*args, **kwargs) - - self.content = content - self.targeted = targeted - - # Modal form, send back to previous page if any - if public: - self.previous_page_url = targeted.get_absolute_url_online() - else: - self.previous_page_url = targeted.get_absolute_url_beta() - - if targeted.get_tree_depth() == 0: - pm_title = _("J'ai trouvé une faute dans « {} ».").format(targeted.title) - else: - pm_title = _("J'ai trouvé une faute dans le chapitre « {} ».").format(targeted.title) - - usernames = "" - num_of_authors = content.authors.count() - for index, user in enumerate(content.authors.all()): - if index != 0: - usernames += "&" - usernames += "username=" + user.username - - msg = _('
Pas assez de place ? Envoyez un MP {} !').format( - reverse("mp:create"), pm_title, usernames, _("à l'auteur") if num_of_authors == 1 else _("aux auteurs") - ) - - version = content.sha_beta - if public: - version = content.sha_public - - # create form - self.helper = FormHelper() - self.helper.form_action = reverse("content:warn-typo") + f"?pk={content.pk}" - self.helper.form_method = "post" - self.helper.form_class = "modal modal-flex" - self.helper.form_id = "warn-typo-modal" - self.helper.layout = Layout( - Field("target"), - Field("text"), - HTML(msg), - Hidden("pk", "{{ content.pk }}"), - Hidden("version", version), - ButtonHolder(StrictButton(_("Envoyer"), type="submit", css_class="btn-submit")), - ) - - def clean(self): - cleaned_data = super().clean() - - text = cleaned_data.get("text") - - if text is None or not text.strip(): - self._errors["text"] = self.error_class([_("Vous devez indiquer la faute commise.")]) - if "text" in cleaned_data: - del cleaned_data["text"] - - elif len(text) < 3: - self._errors["text"] = self.error_class([_("Votre commentaire doit faire au moins 3 caractères.")]) - if "text" in cleaned_data: - del cleaned_data["text"] - - return cleaned_data - - -class WarnTypoView(SingleContentFormViewMixin): - modal_form = True - form_class = WarnTypoForm - must_be_author = False - - http_method_names = ["post"] - object = None - - def get_form_kwargs(self): - kwargs = super().get_form_kwargs() - - versioned = self.get_versioned_object() - kwargs["content"] = versioned - kwargs["targeted"] = versioned - - if "target" in self.request.POST and self.request.POST["target"] != "": - kwargs["targeted"] = search_container_or_404(versioned, self.request.POST["target"]) - - kwargs["public"] = True - - if versioned.is_beta: - kwargs["public"] = False - elif not versioned.is_public: - raise PermissionDenied - - return kwargs - - def form_valid(self, form): - user = self.request.user - authors = list(Profile.objects.contactable_members().filter(user__in=self.object.authors.all())) - authors = list(author.user for author in authors) - - # Check if the warning is done on a public or beta version - is_public = False - if form.content.is_public: - is_public = True - elif not form.content.is_beta: - raise Http404("Le contenu n'est ni public, ni en bêta.") - - if not authors: - if self.object.authors.count() > 1: - messages.error(self.request, _("Les auteurs sont malheureusement injoignables.")) - else: - messages.error(self.request, _("L'auteur est malheureusement injoignable.")) - - elif user in authors: # Author is trying to PM himself - messages.error(self.request, _("Impossible d'envoyer la proposition de correction : vous êtes auteur.")) - - else: # Send correction - text = "\n".join(["> " + line for line in form.cleaned_data["text"].split("\n")]) - pm_title = _("J'ai trouvé une faute dans « {} ».").format(form.content.title) - msg = render_to_string( - "tutorialv2/messages/warn_typo.md", - { - "user": user, - "content": form.content, - "target": form.targeted, - "public": is_public, - "text": text, - }, - ) - send_mp(user, authors, pm_title, "", msg, leave=False) - messages.success(self.request, _("Merci pour votre proposition de correction.")) - - return redirect(form.previous_page_url) diff --git a/zds/tutorialv2/views/warntypo.py b/zds/tutorialv2/views/warntypo.py new file mode 100644 index 0000000000..4b0ef44037 --- /dev/null +++ b/zds/tutorialv2/views/warntypo.py @@ -0,0 +1,151 @@ +from crispy_forms.bootstrap import StrictButton +from crispy_forms.helper import FormHelper +from crispy_forms.layout import Layout, Field, HTML, Hidden, ButtonHolder +from django import forms +from django.contrib import messages + +from django.core.exceptions import PermissionDenied +from django.http import Http404 +from django.shortcuts import redirect +from django.template.loader import render_to_string +from django.urls import reverse +from django.utils.translation import gettext_lazy as _ + +from zds.mp.models import filter_reachable +from zds.mp.utils import send_mp +from zds.tutorialv2.mixins import SingleContentFormViewMixin +from zds.tutorialv2.utils import search_container_or_404 + + +class WarnTypoForm(forms.Form): + text = forms.CharField( + label="", + required=True, + widget=forms.Textarea(attrs={"placeholder": _("Expliquez la faute"), "rows": "3", "id": "warn_text"}), + ) + + target = forms.CharField(widget=forms.HiddenInput(), required=False) + version = forms.CharField(widget=forms.HiddenInput(), required=True) + + def __init__(self, content, targeted, public=True, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.content = content + self.targeted = targeted + + # Manage targeted version + self.previous_page_url = targeted.get_absolute_url_beta() + version = content.sha_beta + if public: + self.previous_page_url = targeted.get_absolute_url_online() + version = content.sha_public + + # Create form + self.helper = FormHelper() + self.helper.form_action = reverse("content:warn-typo") + f"?pk={content.pk}" + self.helper.form_method = "post" + self.helper.form_class = "modal modal-flex" + self.helper.form_id = "warn-typo-modal" + self.helper.layout = Layout( + Field("target"), + Field("text"), + HTML(self.get_link_for_pm(content, targeted)), + Hidden("pk", "{{ content.pk }}"), + Hidden("version", version), + ButtonHolder(StrictButton(_("Envoyer"), type="submit", css_class="btn-submit")), + ) + + @staticmethod + def get_link_for_pm(content, targeted): + if targeted.get_tree_depth() == 0: + pm_title = _("J'ai trouvé une faute dans « {} ».").format(targeted.title) + else: + pm_title = _("J'ai trouvé une faute dans le chapitre « {} ».").format(targeted.title) + recipients = filter_reachable(content.authors.all()) + usernames = "&".join([f"username={recipient.username}" for recipient in recipients]) + plural = _("aux auteurs") if len(recipients) > 1 else _("à l'auteur") + msg = _('
Pas assez de place ? Envoyez un MP {} !').format( + reverse("mp:create"), pm_title, usernames, plural + ) + return msg + + def clean(self): + cleaned_data = super().clean() + + text = cleaned_data.get("text") + + if text is None or not text.strip(): + self._errors["text"] = self.error_class([_("Vous devez indiquer la faute commise.")]) + if "text" in cleaned_data: + del cleaned_data["text"] + elif len(text) < 3: + self._errors["text"] = self.error_class([_("Votre commentaire doit faire au moins 3 caractères.")]) + if "text" in cleaned_data: + del cleaned_data["text"] + + return cleaned_data + + +class WarnTypoView(SingleContentFormViewMixin): + modal_form = True + form_class = WarnTypoForm + must_be_author = False + + http_method_names = ["post"] + + def get_form_kwargs(self): + kwargs = super().get_form_kwargs() + + versioned = self.get_versioned_object() + kwargs["content"] = versioned + kwargs["targeted"] = versioned + + if "target" in self.request.POST and self.request.POST["target"] != "": + kwargs["targeted"] = search_container_or_404(versioned, self.request.POST["target"]) + + kwargs["public"] = True + + if versioned.is_beta: + kwargs["public"] = False + elif not versioned.is_public: + raise PermissionDenied + + return kwargs + + def form_valid(self, form): + # Check if the warning is done on a public or beta version + is_public = False + if form.content.is_public: + is_public = True + elif not form.content.is_beta: + raise Http404("Le contenu n'est ni public, ni en bêta.") + + user = self.request.user + recipients = filter_reachable(self.object.authors.all()) + if not recipients: + if self.object.authors.count() > 1: + messages.error(self.request, _("Les auteurs sont malheureusement injoignables.")) + else: + messages.error(self.request, _("L'auteur est malheureusement injoignable.")) + elif user in recipients: + messages.error(self.request, _("Impossible d'envoyer la proposition de correction : vous êtes auteur.")) + else: + self.send_pm(form, is_public, recipients, user) + messages.success(self.request, _("Merci pour votre proposition de correction.")) + + return redirect(form.previous_page_url) + + def send_pm(self, form, is_public, recipients, sender): + text = "\n".join(["> " + line for line in form.cleaned_data["text"].split("\n")]) + title = _("J'ai trouvé une faute dans « {} ».").format(form.content.title) + body = render_to_string( + "tutorialv2/messages/warn_typo.md", + { + "user": sender, + "content": form.content, + "target": form.targeted, + "public": is_public, + "text": text, + }, + ) + send_mp(sender, recipients, title, "", body, leave=False)