diff --git a/gestion/mixins.py b/gestion/mixins.py new file mode 100644 index 0000000..f4469e9 --- /dev/null +++ b/gestion/mixins.py @@ -0,0 +1,7 @@ +from django.contrib.auth.mixins import UserPassesTestMixin + + +class ChefRequiredMixin(UserPassesTestMixin): + def test_func(self): + user = self.request.user + return (user is not None) and hasattr(user, "profile") and user.profile.is_chef diff --git a/pads/templates/pads/list.html b/pads/templates/pads/list.html new file mode 100644 index 0000000..69c2f2e --- /dev/null +++ b/pads/templates/pads/list.html @@ -0,0 +1,25 @@ +{% extends "gestion/base.html" %} + +{% block titre %}Liste des pads{% endblock %} + +{% block content %} +

Liste des pads

+ + {% if user.profile.is_chef %} +

Ajouter un pad

+ {% endif %} + + +{% endblock %} diff --git a/pads/templates/pads/liste.html b/pads/templates/pads/liste.html deleted file mode 100644 index b76760f..0000000 --- a/pads/templates/pads/liste.html +++ /dev/null @@ -1,24 +0,0 @@ -{% extends "gestion/base.html" %} -{% load getresponse %} -{% block titre %}Liste des pads{% endblock %} -{% block content %}

Liste des pads

-{% if error %} -

{{ error }}

-{% endif %} -{% if user.profile.is_chef %} -

Ajouter un pad

-{% endif %} - -{% endblock %} diff --git a/pads/tests.py b/pads/tests.py new file mode 100644 index 0000000..54fea79 --- /dev/null +++ b/pads/tests.py @@ -0,0 +1,184 @@ +from django.contrib.auth import get_user_model +from django.test import Client, TestCase +from django.urls import reverse, reverse_lazy +from django.utils import timezone + +from gestion.models import ErnestoUser +from pads.models import Pad + +User = get_user_model() + + +def new_user(username): + u = User.objects.create_user(username=username) + ErnestoUser.objects.create(user=u, slug=username, is_ernesto=True) + return u + + +class PadListTest(TestCase): + url = reverse_lazy("pads:list") + + def setUp(self): + for name in ["foo", "bar", "baz"]: + Pad.objects.create( + nom=name, url="example.com/{}".format(name), date=timezone.now() + ) + + def test_anonymous_cannot_get(self): + response = Client().get(self.url) + self.assertRedirects(response, "/login?next={}".format(self.url)) + + def test_ernesto_user_can_get(self): + client = Client() + client.force_login(new_user("toto")) + + response = client.get(self.url) + self.assertEqual(response.status_code, 200) + + +class PadCreateTest(TestCase): + url = reverse_lazy("pads:add") + + def test_anonymous_cannot_get(self): + response = Client().get(self.url) + self.assertRedirects(response, "/login?next={}".format(self.url)) + + def test_anonymous_cannot_post(self): + response = Client().post(self.url, {"nom": "foo", "url": "http://example.com"}) + self.assertRedirects(response, "/login?next={}".format(self.url)) + + def test_ernesto_cannot_get(self): + client = Client() + client.force_login(new_user("toto")) + response = client.get(self.url) + self.assertEqual(response.status_code, 403) + + def test_ernesto_cannot_post(self): + client = Client() + client.force_login(new_user("toto")) + response = client.post(self.url, {"nom": "foo", "url": "http://example.com"}) + self.assertEqual(response.status_code, 403) + + def test_chef_can_get(self): + user = new_user("toto") + user.profile.is_chef = True + user.profile.save() + client = Client() + client.force_login(user) + + response = client.get(self.url) + self.assertEqual(response.status_code, 200) + + def test_chef_can_post(self): + user = new_user("toto") + user.profile.is_chef = True + user.profile.save() + client = Client() + client.force_login(user) + + client.post(self.url, {"nom": "foo", "url": "http://example.com"}) + pads = Pad.objects.all() + self.assertEqual(pads.count(), 1) + pad = pads.get() + self.assertEqual(pad.nom, "foo") + self.assertEqual(pad.url, "http://example.com") + + +class PadUpdateTest(TestCase): + def setUp(self): + pad = Pad.objects.create( + nom="bar", url="http://djangoproject.com", date=timezone.now() + ) + self.url = reverse("pads:edit", args=(pad.id,)) + + def test_anonymous_cannot_get(self): + response = Client().get(self.url) + self.assertRedirects(response, "/login?next={}".format(self.url)) + + def test_anonymous_cannot_post(self): + response = Client().post(self.url, {"nom": "foo", "url": "http://example.com"}) + self.assertRedirects(response, "/login?next={}".format(self.url)) + + def test_ernesto_cannot_get(self): + client = Client() + client.force_login(new_user("toto")) + response = client.get(self.url) + self.assertEqual(response.status_code, 403) + + def test_ernesto_cannot_post(self): + client = Client() + client.force_login(new_user("toto")) + response = client.post(self.url, {"nom": "foo", "url": "http://example.com"}) + self.assertEqual(response.status_code, 403) + + def test_chef_can_get(self): + user = new_user("toto") + user.profile.is_chef = True + user.profile.save() + client = Client() + client.force_login(user) + + response = client.get(self.url) + self.assertEqual(response.status_code, 200) + + def test_chef_can_post(self): + user = new_user("toto") + user.profile.is_chef = True + user.profile.save() + client = Client() + client.force_login(user) + + client.post(self.url, {"nom": "foo", "url": "http://example.com"}) + pads = Pad.objects.all() + self.assertEqual(pads.count(), 1) + pad = pads.get() + self.assertEqual(pad.nom, "foo") + self.assertEqual(pad.url, "http://example.com") + + +class PadDeleteTest(TestCase): + def setUp(self): + pad = Pad.objects.create( + nom="bar", url="http://djangoproject.com", date=timezone.now() + ) + self.url = reverse("pads:delete", args=(pad.id,)) + + def test_anonymous_cannot_get(self): + response = Client().get(self.url) + self.assertRedirects(response, "/login?next={}".format(self.url)) + + def test_anonymous_cannot_post(self): + response = Client().post(self.url, {}) + self.assertRedirects(response, "/login?next={}".format(self.url)) + + def test_ernesto_cannot_get(self): + client = Client() + client.force_login(new_user("toto")) + response = client.get(self.url) + self.assertEqual(response.status_code, 403) + + def test_ernesto_cannot_post(self): + client = Client() + client.force_login(new_user("toto")) + response = client.post(self.url, {}) + self.assertEqual(response.status_code, 403) + + def test_chef_can_get(self): + user = new_user("toto") + user.profile.is_chef = True + user.profile.save() + client = Client() + client.force_login(user) + + response = client.get(self.url) + self.assertEqual(response.status_code, 200) + + def test_chef_can_post(self): + user = new_user("toto") + user.profile.is_chef = True + user.profile.save() + client = Client() + client.force_login(user) + + client.post(self.url, {}) + self.assertFalse(Pad.objects.exists()) diff --git a/pads/urls.py b/pads/urls.py index 3f56cbb..1aebfc3 100644 --- a/pads/urls.py +++ b/pads/urls.py @@ -1,13 +1,12 @@ from django.urls import path from pads import views -from pads.views import PadUpdate, PadDelete app_name = "pads" urlpatterns = [ - path("", views.liste_pads, name="list"), - path("ajouter", views.add_pad, name="add"), - path("edition/", PadUpdate.as_view(), name="edit"), - path("supprimer/", PadDelete.as_view(), name="delete"), + path("", views.PadList.as_view(), name="list"), + path("ajouter", views.PadCreate.as_view(), name="add"), + path("edition/", views.PadUpdate.as_view(), name="edit"), + path("supprimer/", views.PadDelete.as_view(), name="delete"), ] diff --git a/pads/views.py b/pads/views.py index 29db1b3..c3ef998 100644 --- a/pads/views.py +++ b/pads/views.py @@ -1,53 +1,41 @@ -from django.contrib.auth.decorators import login_required -from django.shortcuts import render +from django.contrib.auth.mixins import LoginRequiredMixin +from django.http import HttpResponseRedirect from django.urls import reverse_lazy -from django.views.generic import UpdateView, DeleteView -from django.utils.decorators import method_decorator -from datetime import date +from django.utils import timezone +from django.views.generic import CreateView, DeleteView, ListView, UpdateView +from gestion.mixins import ChefRequiredMixin from pads.models import Pad -from pads.forms import PadForm - -from partitions.decorators import chef_required -@login_required -def liste_pads(request): - pads = Pad.objects.all().order_by("-date") - return render(request, "pads/liste.html", locals()) - - -@chef_required -def add_pad(request): - if request.method == "POST": - form = PadForm(request.POST) - if form.is_valid(): - obj = form.save(commit=False) - obj.date = date.today() - obj.save() - envoi = True - else: - form = PadForm() - return render(request, "pads/create.html", locals()) - - -class PadUpdate(UpdateView): +class PadList(LoginRequiredMixin, ListView): model = Pad + context_object_name = "pads" + ordering = ["-date"] + template_name = "pads/list.html" + + +class PadCreate(ChefRequiredMixin, CreateView): + model = Pad + fields = ["nom", "url"] + template_name = "pads/create.html" + success_url = reverse_lazy("pads:list") + + def form_valid(self, form): + pad = form.save(commit=False) + pad.date = timezone.now() + pad.save() + return HttpResponseRedirect(self.success_url) + + +class PadUpdate(ChefRequiredMixin, UpdateView): + model = Pad + fields = ["nom", "url"] template_name = "pads/update.html" - success_url = reverse_lazy(liste_pads) - form_class = PadForm - - @method_decorator(chef_required) - def dispatch(self, *args, **kwargs): - return super(PadUpdate, self).dispatch(*args, **kwargs) + success_url = reverse_lazy("pads:list") -class PadDelete(DeleteView): +class PadDelete(ChefRequiredMixin, DeleteView): model = Pad template_name = "pads/delete.html" - success_url = reverse_lazy(liste_pads) - - @method_decorator(chef_required) - def dispatch(self, *args, **kwargs): - return super(PadDelete, self).dispatch(*args, **kwargs) -# Create your views here. + success_url = reverse_lazy("pads:list")