fix
This commit is contained in:
commit
c79f040fa1
|
@ -1,7 +1,12 @@
|
|||
from django import forms
|
||||
from django.forms import ModelForm
|
||||
from django.forms import ModelForm, ImageField, FileField
|
||||
|
||||
from .models import Comment
|
||||
from ckeditor.fields import RichTextField
|
||||
from filer.models.imagemodels import Image
|
||||
from filer.models.filemodels import File
|
||||
|
||||
from aircox.models import Comment, Episode, Program
|
||||
from aircox.controllers.sound_file import SoundFile
|
||||
|
||||
|
||||
class CommentForm(ModelForm):
|
||||
|
@ -16,3 +21,38 @@ class CommentForm(ModelForm):
|
|||
class Meta:
|
||||
model = Comment
|
||||
fields = ["nickname", "email", "content"]
|
||||
|
||||
|
||||
class ProgramForm(ModelForm):
|
||||
content = RichTextField()
|
||||
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 EpisodeForm(ModelForm):
|
||||
content = RichTextField()
|
||||
new_podcast = FileField(required=False)
|
||||
|
||||
class Meta:
|
||||
model = Episode
|
||||
fields = ["content"]
|
||||
|
||||
def save(self, commit=True):
|
||||
file_obj = self.cleaned_data["new_podcast"]
|
||||
if file_obj:
|
||||
obj, _ = File.objects.get_or_create(original_filename=file_obj.name, file=file_obj)
|
||||
sound_file = SoundFile(obj.path)
|
||||
sound_file.sync(
|
||||
program=self.instance.program, episode=self.instance, type=0, is_public=True, is_downloadable=True
|
||||
)
|
||||
super().save(commit=commit)
|
||||
|
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
@ -125,6 +125,7 @@ class Program(Page):
|
|||
editors, created = Group.objects.get_or_create(name=self.editors_group_name)
|
||||
if created:
|
||||
self.editors = editors
|
||||
super().save()
|
||||
permission, _ = Permission.objects.get_or_create(
|
||||
name=f"change program {self.title}",
|
||||
codename=self.change_permission_codename,
|
||||
|
@ -159,7 +160,6 @@ class Program(Page):
|
|||
Sound.objects.filter(path__startswith=path_).update(file=Concat("file", Substr(F("file"), len(path_))))
|
||||
|
||||
self.set_group_ownership()
|
||||
super().save(*kargs, **kwargs)
|
||||
|
||||
|
||||
class ProgramChildQuerySet(PageQuerySet):
|
||||
|
|
|
@ -81,6 +81,9 @@ class Station(models.Model):
|
|||
max_length=64,
|
||||
default=_("Music stream"),
|
||||
)
|
||||
legal_label = models.CharField(
|
||||
_("Legal label"), max_length=64, blank=True, default="", help_text=_("Displayed at the bottom of pages.")
|
||||
)
|
||||
|
||||
objects = StationQuerySet.as_manager()
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ Usefull context:
|
|||
<div class="navs">
|
||||
{% block nav %}
|
||||
<nav class="nav primary" role="navigation" aria-label="main navigation">
|
||||
{% block nav-primary %}
|
||||
{% block primary-nav %}
|
||||
<a class="nav-brand" href="{% url "home" %}">
|
||||
<img src="{{ station.logo.url }}">
|
||||
</a>
|
||||
|
@ -60,7 +60,7 @@ Usefull context:
|
|||
aria-label="{% translate "Main menu" %}">
|
||||
</a-switch>
|
||||
<div class="nav-menu">
|
||||
{% block nav-primary-menu %}
|
||||
{% block primary-nav-menu %}
|
||||
{% nav_items "top" css_class="nav-item" active_class="active" as items %}
|
||||
{% for item, render in items %}
|
||||
{{ render }}
|
||||
|
@ -71,11 +71,9 @@ Usefull context:
|
|||
</a>
|
||||
{% endif %}
|
||||
{% if user.is_authenticated %}
|
||||
<a class="nav-item" href="{% url "profile" %}" target="new">
|
||||
{% translate "Profile" %}
|
||||
</a>
|
||||
<a class="nav-item" href="{% url 'logout' %}">
|
||||
<i title="{% translate 'disconnect' %}" class="fa fa-power-off"></i>
|
||||
<a class="nav-item" href="{% url "logout" %}" title="{% translate "Disconnect" %}"
|
||||
aria-label="{% translate "Disconnect" %}">
|
||||
<i class="fa fa-power-off"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
@ -98,8 +96,6 @@ Usefull context:
|
|||
{% endblock %}
|
||||
{% endspaceless %}
|
||||
|
||||
|
||||
|
||||
{% block header-container %}
|
||||
{% if page or cover or title %}
|
||||
<header class="container header preview preview-header {% if cover %}has-cover{% endif %}">
|
||||
|
@ -146,6 +142,24 @@ Usefull context:
|
|||
{% endblock %}
|
||||
</main>
|
||||
{% endblock %}
|
||||
|
||||
{% block footer-container %}
|
||||
<footer class="page-footer">
|
||||
{% block footer %}
|
||||
{% if request.station and request.station.legal_label %}
|
||||
{{ request.station.legal_label }} —
|
||||
{% endif %}
|
||||
|
||||
<a class="nav-item" href="{% url "profile" %}" target="new"
|
||||
title="{% translate "Profile" %}">
|
||||
<span class="small icon">
|
||||
<i class="fa fa-account">
|
||||
</span>
|
||||
</a>
|
||||
{% endblock %}
|
||||
</footer>
|
||||
{% endblock %}
|
||||
|
||||
</div>
|
||||
{% block player-container %}
|
||||
<div id="player">{% include "aircox/widgets/player.html" %}</div>
|
||||
|
|
|
@ -4,15 +4,19 @@
|
|||
{% if user.is_authenticated and can_edit %}
|
||||
{% with request.resolver_match.view_name as view_name %}
|
||||
|
||||
{% if view_name in 'program-edit,bla' %}
|
||||
<!--
|
||||
<a href="{% url 'program-detail' page.slug %}" target="_self">
|
||||
<span title="{% translate 'View' %} {{ page }}">{% translate 'View' %} 👁 </span>
|
||||
{% if view_name in 'page-edit,program-edit,episode-edit' %}
|
||||
<a href="{% url view_name|detail_view page.slug %}" target="_self">
|
||||
<small>
|
||||
<span title="{% translate 'View' %} {{ page }}">{% translate 'View' %} </span>
|
||||
<i class="fa-regular fa-eye"></i>
|
||||
</small>
|
||||
</a>
|
||||
-->
|
||||
{% else %}
|
||||
<a href="{% url view_name|edit_view page.pk %}" target="_self">
|
||||
<span title="{% translate 'Edit' %} {{ page }}">{% translate 'Edit' %} 🖉 </span>
|
||||
<small>
|
||||
<span title="{% translate 'Edit' %} {{ page }}">{% translate 'Edit' %} </span>
|
||||
<i class="fa-solid fa-pencil"></i>
|
||||
</small>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
|
|
@ -23,10 +23,10 @@
|
|||
<table>
|
||||
{{ form.as_table }}
|
||||
{% render_honeypot_field "website" %}
|
||||
|
||||
</table>
|
||||
<br/>
|
||||
<input type="submit" value="Update" class="button is-success">
|
||||
<hr>
|
||||
|
||||
{% include "aircox/playlist_inline.html" %}
|
||||
|
||||
|
|
|
@ -16,12 +16,23 @@
|
|||
<section class="container">
|
||||
<div>
|
||||
<form method="post" enctype="multipart/form-data">{% csrf_token %}
|
||||
<table>
|
||||
{{ form.as_table }}
|
||||
{% render_honeypot_field "website" %}
|
||||
</table>
|
||||
<br/>
|
||||
<input type="submit" value="Update" class="button is-success">
|
||||
{% csrf_token %}
|
||||
{% for field in form %}
|
||||
<div class="field is-horizontal">
|
||||
<label class="label">{{ field.label }}</label>
|
||||
<div class="control">{{ field }}</div>
|
||||
</div>
|
||||
{% 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 %}
|
||||
{% endfor %}
|
||||
|
||||
<div class="has-text-right">
|
||||
<button type="submit" class="button">{% translate "Update" %}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
@ -143,3 +143,8 @@ def do_verbose_name(obj, plural=False):
|
|||
@register.filter(name="edit_view")
|
||||
def do_edit_view(obj):
|
||||
return "%s-edit" % obj.split("-")[0]
|
||||
|
||||
|
||||
@register.filter(name="detail_view")
|
||||
def do_detail_view(obj):
|
||||
return "%s-detail" % obj.split("-")[0]
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from datetime import time, timedelta
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
|
@ -162,3 +163,10 @@ def tracks(episode, sound):
|
|||
@pytest.fixture
|
||||
def user():
|
||||
return User.objects.create_user(username="user1", password="bar")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def png_content():
|
||||
image_file = "{}/image.png".format(os.path.dirname(__file__))
|
||||
with open(image_file, "rb") as fh:
|
||||
return fh.read()
|
||||
|
|
BIN
aircox/tests/image.png
Normal file
BIN
aircox/tests/image.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 69 B |
|
@ -7,12 +7,6 @@ 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()
|
||||
def test_edit_program(user, client, program):
|
||||
client.force_login(user)
|
||||
|
@ -29,7 +23,7 @@ def test_edit_program(user, client, program):
|
|||
|
||||
|
||||
@pytest.mark.django_db()
|
||||
def test_add_cover(user, client, program):
|
||||
def test_add_cover(user, client, program, png_content):
|
||||
assert program.cover is None
|
||||
user.groups.add(program.editors)
|
||||
client.force_login(user)
|
||||
|
@ -58,16 +52,16 @@ def test_edit_tracklist(user, client, program, episode, tracks):
|
|||
tracklist = [t.id for t in episode.track_set.all().order_by("position")]
|
||||
tracklist_details_reversed = [(t.id, t.artist, t.title) for t in episode.track_set.all().order_by("-position")]
|
||||
tracklist_details_reversed = list(chain(*tracklist_details_reversed))
|
||||
data = """{"website": [""], "content": ["foobar"], "new_podcast": [""], "form-TOTAL_FORMS": ["3"],
|
||||
data = """{{"website": [""], "content": ["foobar"], "new_podcast": [""], "form-TOTAL_FORMS": ["3"],
|
||||
"form-INITIAL_FORMS": ["3"], "form-MIN_NUM_FORMS": ["0"], "form-MAX_NUM_FORMS": ["1000"], "form-0-position": ["0"],
|
||||
"form-0-id": ["%s"], "form-0-": ["", "", "", "", "", ""], "form-0-artist": ["%s"], "form-0-title": ["%s"],
|
||||
"form-0-tags": [""], "form-0-album": [""], "form-0-year": [""], "form-1-position": ["1"], "form-1-id": ["%s"],
|
||||
"form-1-": ["", "", "", "", "", ""], "form-1-artist": ["%s"], "form-1-title": ["%s"], "form-1-tags": [""],
|
||||
"form-1-album": [""], "form-1-year": [""], "form-2-position": ["2"], "form-2-id": ["%s"], "form-2-": ["", "", "",
|
||||
"", "", ""], "form-2-artist": ["%s"], "form-2-title": ["%s"], "form-2-tags": [""], "form-2-album": [""],
|
||||
"form-2-year": [""]}""" % tuple(
|
||||
tracklist_details_reversed
|
||||
"form-0-id": ["{}"], "form-0-": ["", "", "", "", "", ""], "form-0-artist": ["{}"], "form-0-title": ["{}"],
|
||||
"form-0-tags": [""], "form-0-album": [""], "form-0-year": [""], "form-1-position": ["1"], "form-1-id": ["{}"],
|
||||
"form-1-": ["", "", "", "", "", ""], "form-1-artist": ["{}"], "form-1-title": ["{}"], "form-1-tags": [""],
|
||||
"form-1-album": [""], "form-1-year": [""], "form-2-position": ["2"], "form-2-id": ["{}"], "form-2-": ["", "", "",
|
||||
"", "", ""], "form-2-artist": ["{}"], "form-2-title": ["{}"], "form-2-tags": [""], "form-2-album": [""],
|
||||
"form-2-year": [""]}}""".format(
|
||||
*tracklist_details_reversed
|
||||
)
|
||||
r = client.post(reverse("episode-edit", kwargs={"pk": episode.pk}), json.loads(data), follow=True)
|
||||
assert r.status_code == 200
|
||||
assert [t.id for t in episode.track_set.all().order_by("position")] == list(reversed(tracklist))
|
||||
assert set(episode.track_set.all().values_list("id", flat=True)) == set(tracklist)
|
||||
|
|
|
@ -132,6 +132,6 @@ urls = [
|
|||
views.errors.NoStationErrorView.as_view(),
|
||||
name="errors-no-station",
|
||||
),
|
||||
path("gestion/", views.profile, name="profile"),
|
||||
path("accounts/profile/", views.profile, name="profile"),
|
||||
path(_("manage/"), views.ProfileView.as_view(), name="profile"),
|
||||
path("accounts/profile/", views.ProfileView.as_view(), name="profile"),
|
||||
]
|
||||
|
|
|
@ -11,7 +11,7 @@ from .page import (
|
|||
PageDetailView,
|
||||
PageListView,
|
||||
)
|
||||
from .profile import profile
|
||||
from .profile import ProfileView
|
||||
from .program import (
|
||||
ProgramDetailView,
|
||||
ProgramListView,
|
||||
|
@ -40,7 +40,7 @@ __all__ = (
|
|||
"BasePageListView",
|
||||
"PageDetailView",
|
||||
"PageListView",
|
||||
"profile",
|
||||
"ProfileView",
|
||||
"ProgramDetailView",
|
||||
"ProgramListView",
|
||||
"ProgramPageDetailView",
|
||||
|
|
|
@ -1,16 +1,10 @@
|
|||
from django.contrib.auth.mixins import UserPassesTestMixin
|
||||
from django.forms import ModelForm, FileField
|
||||
from django.forms.models import modelformset_factory
|
||||
from django.urls import reverse
|
||||
|
||||
from ckeditor.fields import RichTextField
|
||||
from filer.models.filemodels import File
|
||||
|
||||
from aircox.controllers.sound_file import SoundFile
|
||||
from aircox.models import Track
|
||||
|
||||
from aircox.forms import EpisodeForm
|
||||
from aircox.models import Episode, Program, StaticPage, Track
|
||||
from ..filters import EpisodeFilters
|
||||
from ..models import Episode, Program, StaticPage
|
||||
from .page import PageListView
|
||||
from .program import ProgramPageDetailView, BaseProgramMixin
|
||||
from .page import PageUpdateView
|
||||
|
@ -53,25 +47,6 @@ class PodcastListView(EpisodeListView):
|
|||
queryset = Episode.objects.published().with_podcasts().order_by("-pub_date")
|
||||
|
||||
|
||||
class EpisodeForm(ModelForm):
|
||||
content = RichTextField()
|
||||
new_podcast = FileField(required=False)
|
||||
|
||||
class Meta:
|
||||
model = Episode
|
||||
fields = ["content"]
|
||||
|
||||
def save(self, commit=True):
|
||||
file_obj = self.cleaned_data["new_podcast"]
|
||||
if file_obj:
|
||||
obj, _ = File.objects.get_or_create(original_filename=file_obj.name, file=file_obj)
|
||||
sound_file = SoundFile(obj.path)
|
||||
sound_file.sync(
|
||||
program=self.instance.program, episode=self.instance, type=0, is_public=True, is_downloadable=True
|
||||
)
|
||||
super().save(commit=commit)
|
||||
|
||||
|
||||
class EpisodeUpdateView(UserPassesTestMixin, BaseProgramMixin, PageUpdateView):
|
||||
model = Episode
|
||||
form_class = EpisodeForm
|
||||
|
|
|
@ -16,6 +16,7 @@ __all__ = [
|
|||
"BasePageDetailView",
|
||||
"PageDetailView",
|
||||
"PageListView",
|
||||
"PageUpdateView",
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
from django.contrib.auth.decorators import login_required
|
||||
from django.template.response import TemplateResponse
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.views.generic.base import TemplateView
|
||||
|
||||
from aircox.models import Program
|
||||
from aircox.views import BaseView
|
||||
|
||||
|
||||
@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)
|
||||
class ProfileView(LoginRequiredMixin, BaseView, TemplateView):
|
||||
template_name = "accounts/profile.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
groups = self.request.user.groups.all()
|
||||
programs = Program.objects.filter(editors__in=groups)
|
||||
kwargs.update({"user": self.request.user, "programs": programs})
|
||||
return super().get_context_data(**kwargs)
|
||||
|
|
|
@ -1,14 +1,10 @@
|
|||
import random
|
||||
|
||||
from django.contrib.auth.mixins import UserPassesTestMixin
|
||||
from django.forms import ModelForm, ImageField
|
||||
from django.urls import reverse
|
||||
|
||||
from ckeditor.fields import RichTextField
|
||||
from filer.models.imagemodels import Image
|
||||
|
||||
|
||||
from ..models import Article, Episode, Page, Program, StaticPage
|
||||
from aircox.forms import ProgramForm
|
||||
from aircox.models import Article, Episode, Page, Program, StaticPage
|
||||
from .mixins import ParentMixin
|
||||
from .page import PageDetailView, PageListView, PageUpdateView
|
||||
|
||||
|
@ -56,22 +52,6 @@ class ProgramDetailView(BaseProgramMixin, PageDetailView):
|
|||
return super().get_template_names() + ["aircox/program_detail.html"]
|
||||
|
||||
|
||||
class ProgramForm(ModelForm):
|
||||
content = RichTextField()
|
||||
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
|
||||
form_class = ProgramForm
|
||||
|
|
Loading…
Reference in New Issue
Block a user