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")