Compare commits

..

8 Commits

17 changed files with 146 additions and 83 deletions

View File

@ -0,0 +1,3 @@
def station(request):
station = request.station
return {"station": station, "audio_streams": station.streams}

View File

@ -85,7 +85,7 @@ class Program(Page):
@property
def editors_group_name(self):
return "{self.title} editors"
return f"{self.title} editors"
@property
def change_permission_codename(self):

View File

@ -0,0 +1,15 @@
{% extends "aircox/base.html" %}
{% load i18n aircox %}
{% block head_title %}
{% block title %}{{ user.username }}{% endblock %}
{% endblock %}
{% block main %}
<h2 class="subtitle is-3">Mes émissions</h2>
<ul>
{% for p in programs %}
<li><a href="{% url 'program-detail' slug=p.slug %}">{{ p.title }}</a></li>
{% endfor %}
</ul>
{% endblock %}

View File

@ -68,6 +68,7 @@ Usefull context:
<div class="navbar-end">
{% block top-nav-tools %}
{% endblock %}
{% block top-nav-end %}
<div class="navbar-item">
<form action="{% url 'page-list' %}" method="GET">
@ -81,6 +82,12 @@ Usefull context:
</form>
</div>
{% endblock %}
{% if user.is_authenticated %}
<div class="navbar-item">
<a href="{% url 'profile' %}">{{ user.username }}</a> &nbsp; <a href="{% url 'logout' %}"> <i class="fa fa-power-off"></i></a>
</div>
{% endif %}
</div>
</div>
</div>

View File

@ -1,19 +1,6 @@
{% 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 %}"
@ -27,65 +14,11 @@ Context:
{% 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 %}
<table>
{{ form.as_table }}
{% 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 %}
</table>
<br/>
<input type="submit" value="Update" class="button is-success">
</form>
{% endblock %}

View File

@ -0,0 +1,20 @@
{% extends "aircox/base.html" %}
{% load i18n aircox %}
{% block main %}
<h2>{% trans "Log in" %}</h2>
<br/>
<form method="post" action="{% url 'login' %}">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<br/>
<button type="submit">{% trans "Log in" %}</button>
<input type="hidden" name="next" value="{{ next }}">
</form>
{{ block.super }}
{% endblock %}

View File

@ -0,0 +1,22 @@
import pytest
from django.urls import reverse
@pytest.mark.django_db()
def test_authenticate(user, client, program):
r = client.get(reverse("login"))
assert r.status_code == 200
assert b"id_username" in r.content
r = client.post(reverse("login"), kwargs={"username": "foo", "password": "bar"})
assert b"errorlist" in r.content
assert client.login(username="user1", password="bar")
@pytest.mark.django_db()
def test_profile_programs(user, client, program):
client.force_login(user)
r = client.get(reverse("profile"))
assert program.title not in r.content.decode("utf-8")
user.groups.add(program.editors)
r = client.get(reverse("profile"))
assert program.title in r.content.decode("utf-8")

View File

@ -1,5 +1,14 @@
import pytest
from django.urls import reverse
from django.core.files.uploadedfile import SimpleUploadedFile
from aircox.models import Program
png_content = (
b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x02\x00\x00\x00\x90wS\xde"
+ b"\x00\x00\x00\x0cIDATx\x9cc`\xf8\xcf\x00\x00\x02\x02\x01\x00{\t\x81x\x00\x00\x00\x00IEND\xaeB`\x82"
)
@pytest.mark.django_db()
@ -15,3 +24,17 @@ def test_edit_program(user, client, program):
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
@pytest.mark.django_db()
def test_add_cover(user, client, program):
assert program.cover is None
user.groups.add(program.editors)
client.force_login(user)
cover = SimpleUploadedFile("cover1.png", png_content, content_type="image/png")
r = client.post(
reverse("program-edit", kwargs={"pk": program.pk}), {"content": "foobar", "new_cover": cover}, follow=True
)
assert r.status_code == 200
p = Program.objects.get(pk=program.pk)
assert "cover1.png" in p.cover.url

View File

@ -54,13 +54,11 @@ class TestBaseView:
context = base_view.get_context_data()
assert context == {
"view": base_view,
"station": station,
"page": None, # get_page() returns None
"has_sidebar": base_view.has_sidebar,
"has_filters": False,
"sidebar_object_list": published_pages[: base_view.list_count],
"sidebar_list_url": base_view.get_sidebar_url(),
"audio_streams": station.streams,
"model": base_view.model,
}

View File

@ -117,4 +117,6 @@ urls = [
views.errors.NoStationErrorView.as_view(),
name="errors-no-station",
),
path("gestion/", views.profile, name="profile"),
path("accounts/profile/", views.profile, name="profile"),
]

View File

@ -11,6 +11,7 @@ from .page import (
PageDetailView,
PageListView,
)
from .profile import profile
from .program import (
ProgramDetailView,
ProgramListView,
@ -36,6 +37,7 @@ __all__ = (
"BasePageListView",
"PageDetailView",
"PageListView",
"profile",
"ProgramDetailView",
"ProgramListView",
"ProgramPageDetailView",

View File

@ -33,7 +33,6 @@ class BaseView(TemplateResponseMixin, ContextMixin):
return None
def get_context_data(self, **kwargs):
kwargs.setdefault("station", self.station)
kwargs.setdefault("page", self.get_page())
kwargs.setdefault("has_filters", self.has_filters)
@ -44,9 +43,6 @@ class BaseView(TemplateResponseMixin, ContextMixin):
kwargs["sidebar_object_list"] = sidebar_object_list[: self.list_count]
kwargs["sidebar_list_url"] = self.get_sidebar_url()
if "audio_streams" not in kwargs:
kwargs["audio_streams"] = self.station.streams
if "model" not in kwargs:
model = getattr(self, "model", None) or hasattr(self, "object") and type(self.object)
kwargs["model"] = model

View File

@ -142,4 +142,7 @@ class PageDetailView(BasePageDetailView):
class PageUpdateView(BaseView, UpdateView):
pass
context_object_name = "page"
def get_page(self):
return self.object

15
aircox/views/profile.py Normal file
View File

@ -0,0 +1,15 @@
from django.contrib.auth.decorators import login_required
from django.template.response import TemplateResponse
from aircox.models import Program
@login_required
def profile(request):
programs = []
ugroups = request.user.groups.all()
for p in Program.objects.all():
if p.editors in ugroups:
programs.append(p)
context = {"user": request.user, "programs": programs}
return TemplateResponse(request, "accounts/profile.html", context)

View File

@ -1,6 +1,8 @@
from django.contrib.auth.mixins import UserPassesTestMixin
from django.forms import ModelForm, ImageField
from django.urls import reverse
from django.contrib.auth.mixins import UserPassesTestMixin
from filer.models.imagemodels import Image
from ..models import Page, Program, StaticPage
from .mixins import ParentMixin
@ -32,9 +34,24 @@ class ProgramDetailView(BaseProgramMixin, PageDetailView):
return super().get_sidebar_queryset().filter(parent=self.program)
class ProgramForm(ModelForm):
new_cover = ImageField(required=False)
class Meta:
model = Program
fields = ["content"]
def save(self, commit=True):
file_obj = self.cleaned_data["new_cover"]
if file_obj:
obj, _ = Image.objects.get_or_create(original_filename=file_obj.name, file=file_obj)
self.instance.cover = obj
super().save(commit=commit)
class ProgramUpdateView(UserPassesTestMixin, BaseProgramMixin, PageUpdateView):
model = Program
fields = ["content"]
form_class = ProgramForm
def get_sidebar_queryset(self):
return super().get_sidebar_queryset().filter(parent=self.program)
@ -43,6 +60,9 @@ class ProgramUpdateView(UserPassesTestMixin, BaseProgramMixin, PageUpdateView):
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

View File

@ -237,6 +237,7 @@ TEMPLATES = [
"django.template.context_processors.static",
"django.template.context_processors.tz",
"django.contrib.messages.context_processors.messages",
"aircox.context_processors.station",
),
"loaders": (
"django.template.loaders.filesystem.Loader",
@ -248,3 +249,5 @@ TEMPLATES = [
WSGI_APPLICATION = "instance.wsgi.application"
LOGOUT_REDIRECT_URL = "/"

View File

@ -23,6 +23,7 @@ import aircox.urls
urlpatterns = aircox.urls.urls + [
path("admin/", admin.site.urls),
path("accounts/", include("django.contrib.auth.urls")),
path("ckeditor/", include("ckeditor_uploader.urls")),
path("filer/", include("filer.urls")),
]