Compare commits
	
		
			24 Commits
		
	
	
		
			07f3881dde
			...
			1aababe2ae
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 1aababe2ae | |||
| 0dd961e0bb | |||
| f9da318a38 | |||
| 26fa426416 | |||
| 71f4d2473e | |||
| 2e9ebaded2 | |||
| c6a4196319 | |||
| be224d0efb | |||
| 89f80ad103 | |||
| 6d556fcd5d | |||
| 4201d50f4b | |||
| 6c942f36fa | |||
| d51b9ee58b | |||
| 1a27ae2a76 | |||
| e5862ee59b | |||
| 8f88b15536 | |||
| 10dfe3811b | |||
| f71c201020 | |||
| 0812f3a0a1 | |||
| ad2ed17c34 | |||
| 9db69580e0 | |||
| 4ead6b154b | |||
| 811cc97e07 | |||
| b794e24d0c | 
@ -1,12 +1,7 @@
 | 
			
		||||
from django import forms
 | 
			
		||||
from django.forms import ModelForm, ImageField, FileField
 | 
			
		||||
from django.forms import ModelForm
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
from .models import Comment
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CommentForm(ModelForm):
 | 
			
		||||
@ -21,38 +16,3 @@ 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)
 | 
			
		||||
 | 
			
		||||
@ -125,7 +125,6 @@ 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,
 | 
			
		||||
@ -160,6 +159,7 @@ 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):
 | 
			
		||||
 | 
			
		||||
@ -59,6 +59,8 @@ def do_get_tracks(obj):
 | 
			
		||||
@register.simple_tag(name="has_perm", takes_context=True)
 | 
			
		||||
def do_has_perm(context, obj, perm, user=None, simple=False):
 | 
			
		||||
    """Return True if ``user.has_perm('[APP].[perm]_[MODEL]')``"""
 | 
			
		||||
    if not obj:
 | 
			
		||||
        return
 | 
			
		||||
    if user is None:
 | 
			
		||||
        user = context["request"].user
 | 
			
		||||
    if simple:
 | 
			
		||||
@ -102,6 +104,8 @@ def do_player_live_attr(context):
 | 
			
		||||
@register.simple_tag(name="nav_items", takes_context=True)
 | 
			
		||||
def do_nav_items(context, menu, **kwargs):
 | 
			
		||||
    """Render navigation items for the provided menu name."""
 | 
			
		||||
    if not getattr(context["request"], "station"):
 | 
			
		||||
        return []
 | 
			
		||||
    station, request = context["station"], context["request"]
 | 
			
		||||
    return [(item, item.render(request, **kwargs)) for item in station.navitem_set.filter(menu=menu)]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,6 @@
 | 
			
		||||
from datetime import time, timedelta
 | 
			
		||||
import itertools
 | 
			
		||||
import logging
 | 
			
		||||
import os
 | 
			
		||||
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
from django.contrib.auth.models import User
 | 
			
		||||
@ -163,10 +162,3 @@ 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()
 | 
			
		||||
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 69 B  | 
@ -7,6 +7,12 @@ 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)
 | 
			
		||||
@ -23,7 +29,7 @@ def test_edit_program(user, client, program):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.django_db()
 | 
			
		||||
def test_add_cover(user, client, program, png_content):
 | 
			
		||||
def test_add_cover(user, client, program):
 | 
			
		||||
    assert program.cover is None
 | 
			
		||||
    user.groups.add(program.editors)
 | 
			
		||||
    client.force_login(user)
 | 
			
		||||
@ -52,16 +58,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": ["{}"], "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
 | 
			
		||||
    "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
 | 
			
		||||
    )
 | 
			
		||||
    r = client.post(reverse("episode-edit", kwargs={"pk": episode.pk}), json.loads(data), follow=True)
 | 
			
		||||
    assert r.status_code == 200
 | 
			
		||||
    assert set(episode.track_set.all().values_list("id", flat=True)) == set(tracklist)
 | 
			
		||||
    assert [t.id for t in episode.track_set.all().order_by("position")] == list(reversed(tracklist))
 | 
			
		||||
 | 
			
		||||
@ -132,6 +132,6 @@ urls = [
 | 
			
		||||
        views.errors.NoStationErrorView.as_view(),
 | 
			
		||||
        name="errors-no-station",
 | 
			
		||||
    ),
 | 
			
		||||
    path("gestion/", views.ProfileView.as_view(), name="profile"),
 | 
			
		||||
    path("accounts/profile/", views.ProfileView.as_view(), name="profile"),
 | 
			
		||||
    path("gestion/", views.profile, name="profile"),
 | 
			
		||||
    path("accounts/profile/", views.profile, name="profile"),
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
@ -11,7 +11,7 @@ from .page import (
 | 
			
		||||
    PageDetailView,
 | 
			
		||||
    PageListView,
 | 
			
		||||
)
 | 
			
		||||
from .profile import ProfileView
 | 
			
		||||
from .profile import profile
 | 
			
		||||
from .program import (
 | 
			
		||||
    ProgramDetailView,
 | 
			
		||||
    ProgramListView,
 | 
			
		||||
@ -40,7 +40,7 @@ __all__ = (
 | 
			
		||||
    "BasePageListView",
 | 
			
		||||
    "PageDetailView",
 | 
			
		||||
    "PageListView",
 | 
			
		||||
    "ProfileView",
 | 
			
		||||
    "profile",
 | 
			
		||||
    "ProgramDetailView",
 | 
			
		||||
    "ProgramListView",
 | 
			
		||||
    "ProgramPageDetailView",
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,16 @@
 | 
			
		||||
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 aircox.forms import EpisodeForm
 | 
			
		||||
from aircox.models import Episode, Program, StaticPage, Track
 | 
			
		||||
from ckeditor.fields import RichTextField
 | 
			
		||||
from filer.models.filemodels import File
 | 
			
		||||
 | 
			
		||||
from aircox.controllers.sound_file import SoundFile
 | 
			
		||||
from aircox.models import Track
 | 
			
		||||
 | 
			
		||||
from ..filters import EpisodeFilters
 | 
			
		||||
from ..models import Episode, Program, StaticPage
 | 
			
		||||
from .page import PageListView
 | 
			
		||||
from .program import ProgramPageDetailView, BaseProgramMixin
 | 
			
		||||
from .page import PageUpdateView
 | 
			
		||||
@ -47,6 +53,25 @@ 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,7 +16,6 @@ __all__ = [
 | 
			
		||||
    "BasePageDetailView",
 | 
			
		||||
    "PageDetailView",
 | 
			
		||||
    "PageListView",
 | 
			
		||||
    "PageUpdateView",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,15 +1,15 @@
 | 
			
		||||
from django.contrib.auth.mixins import LoginRequiredMixin
 | 
			
		||||
from django.views.generic.base import TemplateView
 | 
			
		||||
from django.contrib.auth.decorators import login_required
 | 
			
		||||
from django.template.response import TemplateResponse
 | 
			
		||||
 | 
			
		||||
from aircox.models import Program
 | 
			
		||||
from aircox.views import BaseView
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
@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)
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,14 @@
 | 
			
		||||
import random
 | 
			
		||||
 | 
			
		||||
from django.contrib.auth.mixins import UserPassesTestMixin
 | 
			
		||||
from django.forms import ModelForm, ImageField
 | 
			
		||||
from django.urls import reverse
 | 
			
		||||
 | 
			
		||||
from aircox.forms import ProgramForm
 | 
			
		||||
from aircox.models import Article, Episode, Page, Program, StaticPage
 | 
			
		||||
from ckeditor.fields import RichTextField
 | 
			
		||||
from filer.models.imagemodels import Image
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from ..models import Article, Episode, Page, Program, StaticPage
 | 
			
		||||
from .mixins import ParentMixin
 | 
			
		||||
from .page import PageDetailView, PageListView, PageUpdateView
 | 
			
		||||
 | 
			
		||||
@ -52,6 +56,22 @@ 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
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user