From c8b0d1c5fbd69acb34a75754ad2709d900efcf3f Mon Sep 17 00:00:00 2001 From: Christophe Siraut Date: Fri, 10 Nov 2023 11:27:30 +0100 Subject: [PATCH] misc: edit programs in site --- aircox/templates/aircox/program_detail.html | 12 +++++++++++ aircox/templates/aircox/program_form.html | 23 +++++++++++++++++++++ aircox/tests/test_permissions.py | 11 ++++++++++ aircox/tests/test_program.py | 17 +++++++++++++++ aircox/urls.py | 5 +++++ aircox/views/__init__.py | 2 ++ aircox/views/page.py | 9 ++++++++ aircox/views/program.py | 18 ++++++++++++++++ 8 files changed, 97 insertions(+) create mode 100644 aircox/templates/aircox/program_form.html create mode 100644 aircox/tests/test_program.py diff --git a/aircox/templates/aircox/program_detail.html b/aircox/templates/aircox/program_detail.html index cfd6e75..28e732e 100644 --- a/aircox/templates/aircox/program_detail.html +++ b/aircox/templates/aircox/program_detail.html @@ -2,6 +2,18 @@ {% comment %}Detail page of a show{% endcomment %} {% load i18n aircox %} +{% block top-nav-tools %} +{% has_perm page page.change_permission_codename simple=True as can_edit %} +{% if can_edit %} + + + +   + {% translate "Edit" %} + +{% endif %} +{% endblock %} + {% block content-container %} {% with schedules=program.schedule_set.all %} {% if schedules %} diff --git a/aircox/templates/aircox/program_form.html b/aircox/templates/aircox/program_form.html new file mode 100644 index 0000000..0116ea4 --- /dev/null +++ b/aircox/templates/aircox/program_form.html @@ -0,0 +1,23 @@ +{% extends "aircox/basepage_detail.html" %} +{% load static i18n humanize honeypot aircox %} + + +{% block top-nav-tools %} + + + +   + {% translate "View" %} + +{% endblock %} + +{% block main %} +
{% csrf_token %} + + {{ form.as_table }} + {% render_honeypot_field "website" %} +
+
+ +
+{% endblock %} diff --git a/aircox/tests/test_permissions.py b/aircox/tests/test_permissions.py index 25866b3..a561f33 100644 --- a/aircox/tests/test_permissions.py +++ b/aircox/tests/test_permissions.py @@ -1,5 +1,6 @@ import pytest from django.contrib.auth.models import User, Group +from django.urls import reverse @pytest.mark.django_db() @@ -23,3 +24,13 @@ def test_group_can_change_program(user, client, program): user = User.objects.get(pk=user.pk) # reload user in order to have permissions set assert program.editors in user.groups.all() assert user.has_perm("aircox.%s" % program.change_permission_codename) + + +@pytest.mark.django_db() +def test_group_change_program(user, client, program): + client.force_login(user) + response = client.get(reverse("program-edit", kwargs={"pk": program.pk})) + assert response.status_code == 403 + user.groups.add(program.editors) + response = client.get(reverse("program-edit", kwargs={"pk": program.pk})) + assert response.status_code == 200 diff --git a/aircox/tests/test_program.py b/aircox/tests/test_program.py new file mode 100644 index 0000000..bf56b7f --- /dev/null +++ b/aircox/tests/test_program.py @@ -0,0 +1,17 @@ +import pytest +from django.urls import reverse + + +@pytest.mark.django_db() +def test_edit_program(user, client, program): + client.force_login(user) + response = client.get(reverse("program-detail", kwargs={"slug": program.slug})) + assert response.status_code == 200 + assert "🖉 ".encode() not in response.content + user.groups.add(program.editors) + response = client.get(reverse("program-detail", kwargs={"slug": program.slug})) + assert "🖉 ".encode() in response.content + assert b"foobar" not in response.content + response = client.post(reverse("program-edit", kwargs={"pk": program.pk}), {"content": "foobar"}) + response = client.get(reverse("program-detail", kwargs={"slug": program.slug})) + assert b"foobar" in response.content diff --git a/aircox/urls.py b/aircox/urls.py index f23c7d0..2783cdb 100755 --- a/aircox/urls.py +++ b/aircox/urls.py @@ -117,6 +117,11 @@ urls = [ path(_("podcasts/"), views.PodcastListView.as_view(), name="podcast-list"), path(_("podcasts/c//"), views.PodcastListView.as_view(), name="podcast-list"), # ---- others + path( + _("program//edit/"), + views.ProgramUpdateView.as_view(), + name="program-edit", + ), path( "errors/no-station", views.errors.NoStationErrorView.as_view(), diff --git a/aircox/views/__init__.py b/aircox/views/__init__.py index aa660e5..40b0213 100644 --- a/aircox/views/__init__.py +++ b/aircox/views/__init__.py @@ -16,6 +16,7 @@ from .program import ( ProgramListView, ProgramPageDetailView, ProgramPageListView, + ProgramUpdateView, ) __all__ = ( @@ -41,6 +42,7 @@ __all__ = ( "ProgramListView", "ProgramPageDetailView", "ProgramPageListView", + "ProgramUpdateView", "attached", ) diff --git a/aircox/views/page.py b/aircox/views/page.py index 0713a38..6b6494a 100644 --- a/aircox/views/page.py +++ b/aircox/views/page.py @@ -1,6 +1,7 @@ from django.http import HttpResponse from django.utils.translation import gettext_lazy as _ from django.views.generic import DetailView, ListView +from django.views.generic.edit import UpdateView from django.urls import reverse from honeypot.decorators import check_honeypot @@ -15,6 +16,7 @@ __all__ = [ "BasePageDetailView", "PageDetailView", "PageListView", + "PageUpdateView", ] @@ -180,3 +182,10 @@ class PageDetailView(BasePageDetailView): comment.page = self.object comment.save() return self.get(request, *args, **kwargs) + + +class PageUpdateView(BaseView, UpdateView): + context_object_name = "page" + + def get_page(self): + return self.object diff --git a/aircox/views/program.py b/aircox/views/program.py index c66b397..1b1e66a 100644 --- a/aircox/views/program.py +++ b/aircox/views/program.py @@ -46,6 +46,24 @@ class ProgramDetailView(BaseProgramMixin, PageDetailView): **kwargs, ) + def get_template_names(self): + return super().get_template_names() + ["aircox/program_detail.html"] + + +class ProgramUpdateView(UserPassesTestMixin, BaseProgramMixin, PageUpdateView): + model = Program + fields = ["content"] + + def get_sidebar_queryset(self): + return super().get_sidebar_queryset().filter(parent=self.program) + + def test_func(self): + program = self.get_object() + return self.request.user.has_perm("aircox.%s" % program.change_permission_codename) + + def get_success_url(self): + return reverse("program-detail", kwargs={"slug": self.get_object().slug}) + class ProgramListView(PageListView): model = Program