misc: edit programs in site
This commit is contained in:
		
							
								
								
									
										90
									
								
								aircox/templates/aircox/program_detail.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								aircox/templates/aircox/program_detail.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,90 @@
 | 
				
			|||||||
 | 
					{% extends "aircox/basepage_detail.html" %}
 | 
				
			||||||
 | 
					{% load static i18n humanize honeypot aircox %}
 | 
				
			||||||
 | 
					{% comment %}
 | 
				
			||||||
 | 
					Base template used to display a Page
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Context:
 | 
				
			||||||
 | 
					- page: page
 | 
				
			||||||
 | 
					- parent: parent page
 | 
				
			||||||
 | 
					{% endcomment %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block header_crumbs %}
 | 
				
			||||||
 | 
					{{ block.super }}
 | 
				
			||||||
 | 
					{% if page.category %}
 | 
				
			||||||
 | 
					{% if parent %} / {% endif %} {{ page.category.title }}
 | 
				
			||||||
 | 
					{% endif %}
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block top-nav-tools %}
 | 
				
			||||||
 | 
					{% has_perm page page.change_permission_codename simple=True as can_edit %}
 | 
				
			||||||
 | 
					{% if can_edit %}
 | 
				
			||||||
 | 
					<a class="navbar-item" href="{% url 'program-edit' page.pk %}"
 | 
				
			||||||
 | 
					    target="new">
 | 
				
			||||||
 | 
					    <span class="icon is-small">
 | 
				
			||||||
 | 
					        <i class="fa fa-pen"></i>
 | 
				
			||||||
 | 
					    </span> 
 | 
				
			||||||
 | 
					    <span>{% translate "Edit" %}</span>
 | 
				
			||||||
 | 
					</a>
 | 
				
			||||||
 | 
					{% endif %}
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block main %}
 | 
				
			||||||
 | 
					{{ block.super }}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block comments %}
 | 
				
			||||||
 | 
					{% if comments or comment_form %}
 | 
				
			||||||
 | 
					<section class="mt-6">
 | 
				
			||||||
 | 
					    <h4 class="title is-4">{% translate "Comments" %}</h4>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    {% for comment in comments %}
 | 
				
			||||||
 | 
					    <div class="media box">
 | 
				
			||||||
 | 
					        <div class="media-content">
 | 
				
			||||||
 | 
					            <p>
 | 
				
			||||||
 | 
					                <strong class="mr-2">{{ comment.nickname }}</strong>
 | 
				
			||||||
 | 
					                <time datetime="{{ comment.date }}" title="{{ comment.date }}">
 | 
				
			||||||
 | 
					                    <small>{{ comment.date|naturaltime }}</small>
 | 
				
			||||||
 | 
					                </time>
 | 
				
			||||||
 | 
					                <br>
 | 
				
			||||||
 | 
					                {{ comment.content }}
 | 
				
			||||||
 | 
					            </p>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    {% endfor %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    {% if comment_form %}
 | 
				
			||||||
 | 
					    <form method="POST">
 | 
				
			||||||
 | 
					        <h5 class="title is-5">{% translate "Post a comment" %}</h5>
 | 
				
			||||||
 | 
					        {% csrf_token %}
 | 
				
			||||||
 | 
					        {% render_honeypot_field "website" %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        {% for field in comment_form %}
 | 
				
			||||||
 | 
					        <div class="field is-horizontal">
 | 
				
			||||||
 | 
					            <div class="field-label is-normal">
 | 
				
			||||||
 | 
					                <label class="label">
 | 
				
			||||||
 | 
					                    {{ field.label_tag }}
 | 
				
			||||||
 | 
					                </label>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="field-body">
 | 
				
			||||||
 | 
					                <div class="field">
 | 
				
			||||||
 | 
					                    <p class="control is-expanded">{{ field }}</p>
 | 
				
			||||||
 | 
					                    {% if field.errors %}
 | 
				
			||||||
 | 
					                    <p class="help is-danger">{{ field.errors }}</p>
 | 
				
			||||||
 | 
					                    {% endif %}
 | 
				
			||||||
 | 
					                    {% if field.help_text %}
 | 
				
			||||||
 | 
					                    <p class="help">{{ field.help_text|safe }}</p>
 | 
				
			||||||
 | 
					                    {% endif %}
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        {% endfor %}
 | 
				
			||||||
 | 
					        <div class="has-text-right">
 | 
				
			||||||
 | 
					            <button type="reset" class="button is-danger">{% translate "Reset" %}</button>
 | 
				
			||||||
 | 
					            <button type="submit" class="button is-success">{% translate "Post comment" %}</button>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </form>
 | 
				
			||||||
 | 
					    {% endif %}
 | 
				
			||||||
 | 
					</section>
 | 
				
			||||||
 | 
					{% endif %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
							
								
								
									
										91
									
								
								aircox/templates/aircox/program_form.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								aircox/templates/aircox/program_form.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,91 @@
 | 
				
			|||||||
 | 
					{% extends "aircox/basepage_detail.html" %}
 | 
				
			||||||
 | 
					{% load static i18n humanize honeypot aircox %}
 | 
				
			||||||
 | 
					{% comment %}
 | 
				
			||||||
 | 
					Base template used to display a Page
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Context:
 | 
				
			||||||
 | 
					- page: page
 | 
				
			||||||
 | 
					- parent: parent page
 | 
				
			||||||
 | 
					{% endcomment %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block header_crumbs %}
 | 
				
			||||||
 | 
					{{ block.super }}
 | 
				
			||||||
 | 
					{% if page.category %}
 | 
				
			||||||
 | 
					{% if parent %} / {% endif %} {{ page.category.title }}
 | 
				
			||||||
 | 
					{% endif %}
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block top-nav-tools %}
 | 
				
			||||||
 | 
					<a class="navbar-item" href="{% url 'program-detail' object.slug %}"
 | 
				
			||||||
 | 
					    target="new">
 | 
				
			||||||
 | 
					    <span class="icon is-small">
 | 
				
			||||||
 | 
					        <i class="fa fa-eye"></i>
 | 
				
			||||||
 | 
					    </span> 
 | 
				
			||||||
 | 
					    <span>{% translate "View" %}</span>
 | 
				
			||||||
 | 
					</a>
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block main %}
 | 
				
			||||||
 | 
					<form method="post">{% csrf_token %}
 | 
				
			||||||
 | 
					        {{ form.as_p }}
 | 
				
			||||||
 | 
					        <input type="submit" value="Update" class="button is-success">
 | 
				
			||||||
 | 
					</form>
 | 
				
			||||||
 | 
					{{ block.super }}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block comments %}
 | 
				
			||||||
 | 
					{% if comments or comment_form %}
 | 
				
			||||||
 | 
					<section class="mt-6">
 | 
				
			||||||
 | 
					    <h4 class="title is-4">{% translate "Comments" %}</h4>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    {% for comment in comments %}
 | 
				
			||||||
 | 
					    <div class="media box">
 | 
				
			||||||
 | 
					        <div class="media-content">
 | 
				
			||||||
 | 
					            <p>
 | 
				
			||||||
 | 
					                <strong class="mr-2">{{ comment.nickname }}</strong>
 | 
				
			||||||
 | 
					                <time datetime="{{ comment.date }}" title="{{ comment.date }}">
 | 
				
			||||||
 | 
					                    <small>{{ comment.date|naturaltime }}</small>
 | 
				
			||||||
 | 
					                </time>
 | 
				
			||||||
 | 
					                <br>
 | 
				
			||||||
 | 
					                {{ comment.content }}
 | 
				
			||||||
 | 
					            </p>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    {% endfor %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    {% if comment_form %}
 | 
				
			||||||
 | 
					    <form method="POST">
 | 
				
			||||||
 | 
					        <h5 class="title is-5">{% translate "Post a comment" %}</h5>
 | 
				
			||||||
 | 
					        {% csrf_token %}
 | 
				
			||||||
 | 
					        {% render_honeypot_field "website" %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        {% for field in comment_form %}
 | 
				
			||||||
 | 
					        <div class="field is-horizontal">
 | 
				
			||||||
 | 
					            <div class="field-label is-normal">
 | 
				
			||||||
 | 
					                <label class="label">
 | 
				
			||||||
 | 
					                    {{ field.label_tag }}
 | 
				
			||||||
 | 
					                </label>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="field-body">
 | 
				
			||||||
 | 
					                <div class="field">
 | 
				
			||||||
 | 
					                    <p class="control is-expanded">{{ field }}</p>
 | 
				
			||||||
 | 
					                    {% if field.errors %}
 | 
				
			||||||
 | 
					                    <p class="help is-danger">{{ field.errors }}</p>
 | 
				
			||||||
 | 
					                    {% endif %}
 | 
				
			||||||
 | 
					                    {% if field.help_text %}
 | 
				
			||||||
 | 
					                    <p class="help">{{ field.help_text|safe }}</p>
 | 
				
			||||||
 | 
					                    {% endif %}
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        {% endfor %}
 | 
				
			||||||
 | 
					        <div class="has-text-right">
 | 
				
			||||||
 | 
					            <button type="reset" class="button is-danger">{% translate "Reset" %}</button>
 | 
				
			||||||
 | 
					            <button type="submit" class="button is-success">{% translate "Post comment" %}</button>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </form>
 | 
				
			||||||
 | 
					    {% endif %}
 | 
				
			||||||
 | 
					</section>
 | 
				
			||||||
 | 
					{% endif %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
import pytest
 | 
					import pytest
 | 
				
			||||||
from django.contrib.auth.models import User, Group
 | 
					from django.contrib.auth.models import User, Group
 | 
				
			||||||
 | 
					from django.urls import reverse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.django_db()
 | 
					@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
 | 
					    user = User.objects.get(pk=user.pk)  # reload user in order to have permissions set
 | 
				
			||||||
    assert program.editors in user.groups.all()
 | 
					    assert program.editors in user.groups.all()
 | 
				
			||||||
    assert user.has_perm("aircox.%s" % program.change_permission_codename)
 | 
					    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
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										17
									
								
								aircox/tests/test_program.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								aircox/tests/test_program.py
									
									
									
									
									
										Normal file
									
								
							@ -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 b"fa-pen" not in response.content
 | 
				
			||||||
 | 
					    user.groups.add(program.editors)
 | 
				
			||||||
 | 
					    response = client.get(reverse("program-detail", kwargs={"slug": program.slug}))
 | 
				
			||||||
 | 
					    assert b"fa-pen" 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
 | 
				
			||||||
@ -92,6 +92,11 @@ urls = [
 | 
				
			|||||||
        views.ProgramDetailView.as_view(),
 | 
					        views.ProgramDetailView.as_view(),
 | 
				
			||||||
        name="program-detail",
 | 
					        name="program-detail",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
 | 
					    path(
 | 
				
			||||||
 | 
					        _("program/<pk>/edit/"),
 | 
				
			||||||
 | 
					        views.ProgramUpdateView.as_view(),
 | 
				
			||||||
 | 
					        name="program-edit",
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
    path(
 | 
					    path(
 | 
				
			||||||
        _("programs/<slug:parent_slug>/episodes/"),
 | 
					        _("programs/<slug:parent_slug>/episodes/"),
 | 
				
			||||||
        views.EpisodeListView.as_view(),
 | 
					        views.EpisodeListView.as_view(),
 | 
				
			||||||
 | 
				
			|||||||
@ -16,6 +16,7 @@ from .program import (
 | 
				
			|||||||
    ProgramListView,
 | 
					    ProgramListView,
 | 
				
			||||||
    ProgramPageDetailView,
 | 
					    ProgramPageDetailView,
 | 
				
			||||||
    ProgramPageListView,
 | 
					    ProgramPageListView,
 | 
				
			||||||
 | 
					    ProgramUpdateView,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
__all__ = (
 | 
					__all__ = (
 | 
				
			||||||
@ -39,4 +40,5 @@ __all__ = (
 | 
				
			|||||||
    "ProgramListView",
 | 
					    "ProgramListView",
 | 
				
			||||||
    "ProgramPageDetailView",
 | 
					    "ProgramPageDetailView",
 | 
				
			||||||
    "ProgramPageListView",
 | 
					    "ProgramPageListView",
 | 
				
			||||||
 | 
					    "ProgramUpdateView",
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,7 @@
 | 
				
			|||||||
from django.http import Http404, HttpResponse
 | 
					from django.http import Http404, HttpResponse
 | 
				
			||||||
from django.utils.translation import gettext_lazy as _
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
from django.views.generic import DetailView, ListView
 | 
					from django.views.generic import DetailView, ListView
 | 
				
			||||||
 | 
					from django.views.generic.edit import UpdateView
 | 
				
			||||||
from honeypot.decorators import check_honeypot
 | 
					from honeypot.decorators import check_honeypot
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from ..filters import PageFilters
 | 
					from ..filters import PageFilters
 | 
				
			||||||
@ -138,3 +139,7 @@ class PageDetailView(BasePageDetailView):
 | 
				
			|||||||
        comment.page = self.object
 | 
					        comment.page = self.object
 | 
				
			||||||
        comment.save()
 | 
					        comment.save()
 | 
				
			||||||
        return self.get(request, *args, **kwargs)
 | 
					        return self.get(request, *args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PageUpdateView(BaseView, UpdateView):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,10 @@
 | 
				
			|||||||
from django.urls import reverse
 | 
					from django.urls import reverse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.contrib.auth.mixins import UserPassesTestMixin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from ..models import Page, Program, StaticPage
 | 
					from ..models import Page, Program, StaticPage
 | 
				
			||||||
from .mixins import ParentMixin
 | 
					from .mixins import ParentMixin
 | 
				
			||||||
from .page import PageDetailView, PageListView
 | 
					from .page import PageDetailView, PageListView, PageUpdateView
 | 
				
			||||||
 | 
					
 | 
				
			||||||
__all__ = ["ProgramPageDetailView", "ProgramDetailView", "ProgramPageListView"]
 | 
					__all__ = ["ProgramPageDetailView", "ProgramDetailView", "ProgramPageListView"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -23,10 +25,25 @@ class BaseProgramMixin:
 | 
				
			|||||||
class ProgramDetailView(BaseProgramMixin, PageDetailView):
 | 
					class ProgramDetailView(BaseProgramMixin, PageDetailView):
 | 
				
			||||||
    model = Program
 | 
					    model = Program
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_template_names(self):
 | 
				
			||||||
 | 
					        return super().get_template_names() + ["aircox/program_detail.html"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_sidebar_queryset(self):
 | 
					    def get_sidebar_queryset(self):
 | 
				
			||||||
        return super().get_sidebar_queryset().filter(parent=self.program)
 | 
					        return super().get_sidebar_queryset().filter(parent=self.program)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ProgramListView(PageListView):
 | 
					class ProgramListView(PageListView):
 | 
				
			||||||
    model = Program
 | 
					    model = Program
 | 
				
			||||||
    attach_to_value = StaticPage.ATTACH_TO_PROGRAMS
 | 
					    attach_to_value = StaticPage.ATTACH_TO_PROGRAMS
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user