@ -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
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user