-
Notifications
You must be signed in to change notification settings - Fork 162
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Ajoute un formulaire pour modifier les catégories d'une publication (#…
…6603) * Supprime la gestion des catégories du formulaire "Éditer" * Ajoute un formulaire pour modifier les catégories d'une publication * Ajoute un lien vers la modification des catégories
- Loading branch information
Showing
10 changed files
with
267 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
{% extends "tutorialv2/base.html" %} | ||
{% load crispy_forms_tags %} | ||
{% load i18n %} | ||
|
||
{% block title %} | ||
{% trans "Modifier les catégories de " %}{{ content.title }} | ||
{% endblock %} | ||
|
||
{% block breadcrumb %} | ||
<li><a href="{{ content.get_absolute_url }}">{{ content.title }}</a></li> | ||
<li>{% trans "Modifier les catégories" %}</li> | ||
{% endblock %} | ||
|
||
{% block headline %} | ||
<h1 {% if content.image %}class="illu"{% endif %}> | ||
{% if content.image %} | ||
<img src="{{content.image.physical.tutorial_illu.url }}" alt=""> | ||
{% endif %} | ||
{% blocktrans with title=content.title %}Modifier les catégories de « {{ title }} »{% endblocktrans %} | ||
</h1> | ||
{% endblock %} | ||
|
||
|
||
{% block content %} | ||
{% crispy form %} | ||
{% endblock %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
133 changes: 133 additions & 0 deletions
133
zds/tutorialv2/tests/tests_views/tests_editcategoriesview.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
from django.test import TestCase | ||
from django.urls import reverse | ||
from django.utils.html import escape | ||
|
||
from zds.tutorialv2.publication_utils import publish_content | ||
from zds.tutorialv2.tests import TutorialTestMixin, override_for_contents | ||
from zds.member.tests.factories import ProfileFactory, StaffProfileFactory | ||
from zds.tutorialv2.tests.factories import PublishableContentFactory | ||
from zds.tutorialv2.views.categories import EditCategoriesForm | ||
from zds.utils.tests.factories import SubCategoryFactory | ||
|
||
|
||
def publish(content): | ||
"""Emulate the publication of a content.""" | ||
published = publish_content(content, content.load_version()) | ||
content.public_version = published | ||
content.save() | ||
|
||
|
||
@override_for_contents() | ||
class PermissionTests(TutorialTestMixin, TestCase): | ||
"""Test permissions and associated behaviors, such as redirections and status codes.""" | ||
|
||
def setUp(self): | ||
self.author = ProfileFactory().user | ||
self.category = SubCategoryFactory() | ||
content = PublishableContentFactory(author_list=[self.author]) | ||
|
||
self.target_url = reverse("content:edit-categories", kwargs={"pk": content.pk}) | ||
self.form_data = {"subcategory": self.category.pk} | ||
self.login_url = reverse("member-login") + "?next=" + self.target_url | ||
self.content_url = reverse("content:view", kwargs={"pk": content.pk, "slug": content.slug}) | ||
|
||
def get(self): | ||
return self.client.get(self.target_url) | ||
|
||
def post(self): | ||
return self.client.post(self.target_url, self.form_data) | ||
|
||
def test_not_authenticated(self): | ||
"""Test that unauthenticated users are redirected to the login page.""" | ||
self.client.logout() # ensure no user is authenticated | ||
|
||
with self.subTest(msg="GET"): | ||
response = self.get() | ||
self.assertRedirects(response, self.login_url) | ||
|
||
with self.subTest(msg="POST"): | ||
response = self.post() | ||
self.assertRedirects(response, self.login_url) | ||
|
||
def test_authenticated_author(self): | ||
"""Test that authors can reach the page.""" | ||
self.client.force_login(self.author) | ||
|
||
with self.subTest(msg="GET"): | ||
response = self.get() | ||
self.assertEqual(response.status_code, 200) | ||
|
||
with self.subTest(msg="POST"): | ||
response = self.post() | ||
self.assertRedirects(response, self.content_url) | ||
|
||
def test_authenticated_staff(self): | ||
"""Test that staffs can reach the page.""" | ||
staff = StaffProfileFactory().user | ||
self.client.force_login(staff) | ||
|
||
with self.subTest(msg="GET"): | ||
response = self.get() | ||
self.assertEqual(response.status_code, 200) | ||
|
||
with self.subTest(msg="POST"): | ||
response = self.post() | ||
self.assertRedirects(response, self.content_url) | ||
|
||
def test_authenticated_outsider(self): | ||
"""Test that unauthorized users get a 403.""" | ||
outsider = ProfileFactory().user | ||
self.client.force_login(outsider) | ||
|
||
with self.subTest(msg="GET"): | ||
response = self.get() | ||
self.assertEqual(response.status_code, 403) | ||
|
||
with self.subTest(msg="POST"): | ||
response = self.get() | ||
self.assertEqual(response.status_code, 403) | ||
|
||
|
||
@override_for_contents() | ||
class FunctionalTests(TutorialTestMixin, TestCase): | ||
"""Test the behavior of the feature.""" | ||
|
||
def setUp(self): | ||
self.author = StaffProfileFactory().user | ||
self.content = PublishableContentFactory(author_list=[self.author], add_category=False) | ||
|
||
self.category_0 = SubCategoryFactory() | ||
self.category_1 = SubCategoryFactory() | ||
|
||
self.url = reverse("content:edit-categories", kwargs={"pk": self.content.pk}) | ||
|
||
self.client.force_login(self.author) | ||
|
||
def test_add_category(self): | ||
form_data = {"subcategory": [str(self.category_0.pk)]} | ||
self.client.post(self.url, form_data) | ||
|
||
categories_real = self.content.subcategory.all() | ||
categories_expected = [self.category_0] | ||
self.assertQuerysetEqual(categories_real, categories_expected) | ||
|
||
def test_remove_category(self): | ||
self.content.subcategory.add(self.category_0) | ||
self.assertQuerysetEqual(self.content.subcategory.all(), [self.category_0]) | ||
|
||
form_data = {"subcategory": []} | ||
self.client.post(self.url, form_data) | ||
|
||
categories_real = self.content.subcategory.all() | ||
categories_expected = [] | ||
self.assertQuerysetEqual(categories_real, categories_expected) | ||
|
||
def test_remove_published(self): | ||
self.content.subcategory.add(self.category_0) | ||
self.assertQuerysetEqual(self.content.subcategory.all(), [self.category_0]) | ||
publish(self.content) | ||
|
||
form_data = {"subcategory": []} | ||
response = self.client.post(self.url, form_data, follow=True) | ||
self.assertContains(response, escape(EditCategoriesForm.error_messages["no_category_but_public"])) | ||
self.assertQuerysetEqual(self.content.subcategory.all(), [self.category_0]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
from crispy_forms.bootstrap import StrictButton | ||
from crispy_forms.helper import FormHelper | ||
from crispy_forms.layout import Layout, Field | ||
|
||
from django import forms | ||
from django.core.exceptions import ValidationError | ||
from django.urls import reverse | ||
from django.utils.translation import gettext_lazy as _ | ||
from django.views.generic import FormView | ||
|
||
from zds.member.decorator import LoggedWithReadWriteHability | ||
from zds.tutorialv2.mixins import SingleContentFormViewMixin | ||
from zds.tutorialv2.models.database import PublishableContent | ||
from zds.utils.models import SubCategory | ||
|
||
|
||
class EditCategoriesForm(forms.Form): | ||
subcategory = forms.ModelMultipleChoiceField( | ||
label=_("Sélectionnez les catégories qui correspondent à la publication."), | ||
queryset=SubCategory.objects.order_by("title").all(), | ||
required=False, | ||
widget=forms.CheckboxSelectMultiple(), | ||
) | ||
|
||
error_messages = { | ||
"no_category_but_public": _("Vous devez choisir au moins une catégorie, car ce contenu est déjà publié.") | ||
} | ||
|
||
def __init__(self, content, *args, **kwargs): | ||
super().__init__(*args, **kwargs) | ||
|
||
self.content = content | ||
|
||
self.helper = FormHelper() | ||
self.helper.form_class = "content-wrapper" | ||
self.helper.form_method = "post" | ||
self.helper.layout = Layout( | ||
Field("subcategory", template="crispy/checkboxselectmultiple.html"), | ||
StrictButton(_("Valider"), type="submit"), | ||
) | ||
|
||
def clean_subcategory(self): | ||
subcategory = self.cleaned_data["subcategory"] | ||
# Forbid removing all categories of a validated content | ||
if self.content.in_public() and not subcategory: | ||
raise ValidationError(message=self.error_messages["no_category_but_public"]) | ||
return subcategory | ||
|
||
|
||
class EditCategoriesView(LoggedWithReadWriteHability, SingleContentFormViewMixin, FormView): | ||
template_name = "tutorialv2/edit/categories.html" | ||
model = PublishableContent | ||
form_class = EditCategoriesForm | ||
|
||
def get_initial(self): | ||
initial = super().get_initial() | ||
initial["subcategory"] = self.object.subcategory.all() | ||
return initial | ||
|
||
def get_form_kwargs(self): | ||
kwargs = super().get_form_kwargs() | ||
kwargs["content"] = self.object | ||
return kwargs | ||
|
||
def form_valid(self, form): | ||
content = self.object | ||
|
||
content.subcategory.clear() | ||
for subcat in form.cleaned_data["subcategory"]: | ||
content.subcategory.add(subcat) | ||
|
||
self.success_url = reverse("content:view", args=[content.pk, content.slug]) | ||
|
||
return super().form_valid(form) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.