make EpisodeUpdateView work
This commit is contained in:
		@ -32,6 +32,7 @@ class SoundInline(admin.TabularInline):
 | 
			
		||||
        "is_good_quality",
 | 
			
		||||
        "is_public",
 | 
			
		||||
        "is_downloadable",
 | 
			
		||||
        "is_removed",
 | 
			
		||||
    ]
 | 
			
		||||
    readonly_fields = ["type", "audio", "duration", "is_good_quality"]
 | 
			
		||||
    extra = 0
 | 
			
		||||
@ -89,11 +90,7 @@ class SoundAdmin(SortableAdminBase, admin.ModelAdmin):
 | 
			
		||||
    related.short_description = _("Program / Episode")
 | 
			
		||||
 | 
			
		||||
    def audio(self, obj):
 | 
			
		||||
        return (
 | 
			
		||||
            mark_safe('<audio src="{}" controls></audio>'.format(obj.file.url))
 | 
			
		||||
            if obj.type != Sound.TYPE_REMOVED
 | 
			
		||||
            else ""
 | 
			
		||||
        )
 | 
			
		||||
        return mark_safe('<audio src="{}" controls></audio>'.format(obj.file.url)) if not obj.is_removed else ""
 | 
			
		||||
 | 
			
		||||
    audio.short_description = _("Audio")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -99,7 +99,7 @@ class SoundFile:
 | 
			
		||||
            sound = Sound.objects.path(self.path).first()
 | 
			
		||||
            if sound:
 | 
			
		||||
                if keep_deleted:
 | 
			
		||||
                    sound.type = sound.TYPE_REMOVED
 | 
			
		||||
                    sound.is_removed = True
 | 
			
		||||
                    sound.check_on_file()
 | 
			
		||||
                    sound.save()
 | 
			
		||||
            return sound
 | 
			
		||||
 | 
			
		||||
@ -80,6 +80,7 @@ TrackFormSet = modelformset_factory(
 | 
			
		||||
        "tags",
 | 
			
		||||
        "album",
 | 
			
		||||
    ],
 | 
			
		||||
    can_delete=True,
 | 
			
		||||
    extra=0,
 | 
			
		||||
)
 | 
			
		||||
"""Track formset used in EpisodeUpdateView."""
 | 
			
		||||
@ -94,6 +95,7 @@ SoundFormSet = modelformset_factory(
 | 
			
		||||
        "is_downloadable",
 | 
			
		||||
        "duration",
 | 
			
		||||
    ],
 | 
			
		||||
    can_delete=True,
 | 
			
		||||
    extra=0,
 | 
			
		||||
)
 | 
			
		||||
"""Sound formset used in EpisodeUpdateView."""
 | 
			
		||||
 | 
			
		||||
@ -6,8 +6,9 @@ from .log import Log, LogQuerySet
 | 
			
		||||
from .page import Category, Comment, NavItem, Page, PageQuerySet, StaticPage
 | 
			
		||||
from .program import Program, ProgramChildQuerySet, ProgramQuerySet, Stream
 | 
			
		||||
from .schedule import Schedule
 | 
			
		||||
from .sound import Sound, SoundQuerySet, Track
 | 
			
		||||
from .sound import Sound, SoundQuerySet
 | 
			
		||||
from .station import Port, Station, StationQuerySet
 | 
			
		||||
from .track import Track
 | 
			
		||||
from .user_settings import UserSettings
 | 
			
		||||
 | 
			
		||||
__all__ = (
 | 
			
		||||
 | 
			
		||||
@ -62,6 +62,7 @@ class Episode(Page):
 | 
			
		||||
                    podcast["name"] = f"{self.title} - {archive_index}"
 | 
			
		||||
                else:
 | 
			
		||||
                    podcast["name"] = self.title
 | 
			
		||||
                archive_index += 1
 | 
			
		||||
 | 
			
		||||
            podcasts[index]["cover"] = cover
 | 
			
		||||
            podcasts[index]["page_url"] = self.get_absolute_url()
 | 
			
		||||
 | 
			
		||||
@ -8,8 +8,9 @@ from django.utils import timezone as tz
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from .diffusion import Diffusion
 | 
			
		||||
from .sound import Sound, Track
 | 
			
		||||
from .sound import Sound
 | 
			
		||||
from .station import Station
 | 
			
		||||
from .track import Track
 | 
			
		||||
from .page import Renderable
 | 
			
		||||
 | 
			
		||||
logger = logging.getLogger("aircox")
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,5 @@
 | 
			
		||||
import os
 | 
			
		||||
 | 
			
		||||
from django.contrib.auth.models import Group, Permission, User
 | 
			
		||||
from django.db import transaction
 | 
			
		||||
from django.db.models import signals
 | 
			
		||||
@ -11,6 +13,7 @@ from .episode import Episode
 | 
			
		||||
from .page import Page
 | 
			
		||||
from .program import Program
 | 
			
		||||
from .schedule import Schedule
 | 
			
		||||
from .sound import Sound
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Add a default group to a user when it is created. It also assigns a list
 | 
			
		||||
@ -94,3 +97,15 @@ def schedule_pre_delete(sender, instance, *args, **kwargs):
 | 
			
		||||
@receiver(signals.post_delete, sender=Diffusion)
 | 
			
		||||
def diffusion_post_delete(sender, instance, *args, **kwargs):
 | 
			
		||||
    Episode.objects.filter(diffusion__isnull=True, content__isnull=True, sound__isnull=True).delete()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@receiver(signals.post_delete, sender=Sound)
 | 
			
		||||
def delete_file(sender, instance, *args, **kwargs):
 | 
			
		||||
    """Deletes file on `post_delete`"""
 | 
			
		||||
    if not instance.file:
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    path = instance.file.path
 | 
			
		||||
    qs = sender.objects.filter(file=path)
 | 
			
		||||
    if not qs.exists() and os.path.exists(path):
 | 
			
		||||
        os.remove(path)
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,6 @@ from django.db import models
 | 
			
		||||
from django.db.models import Q
 | 
			
		||||
from django.utils import timezone as tz
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
from taggit.managers import TaggableManager
 | 
			
		||||
 | 
			
		||||
from aircox.conf import settings
 | 
			
		||||
 | 
			
		||||
@ -16,7 +15,7 @@ from .program import Program
 | 
			
		||||
logger = logging.getLogger("aircox")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ("Sound", "SoundQuerySet", "Track")
 | 
			
		||||
__all__ = ("Sound", "SoundQuerySet")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SoundQuerySet(models.QuerySet):
 | 
			
		||||
@ -33,7 +32,7 @@ class SoundQuerySet(models.QuerySet):
 | 
			
		||||
        return self.filter(episode__diffusion__id=id)
 | 
			
		||||
 | 
			
		||||
    def available(self):
 | 
			
		||||
        return self.exclude(type=Sound.TYPE_REMOVED)
 | 
			
		||||
        return self.exclude(is_removed=False)
 | 
			
		||||
 | 
			
		||||
    def public(self):
 | 
			
		||||
        """Return sounds available as podcasts."""
 | 
			
		||||
@ -85,12 +84,10 @@ class Sound(models.Model):
 | 
			
		||||
    TYPE_OTHER = 0x00
 | 
			
		||||
    TYPE_ARCHIVE = 0x01
 | 
			
		||||
    TYPE_EXCERPT = 0x02
 | 
			
		||||
    TYPE_REMOVED = 0x03
 | 
			
		||||
    TYPE_CHOICES = (
 | 
			
		||||
        (TYPE_OTHER, _("other")),
 | 
			
		||||
        (TYPE_ARCHIVE, _("archive")),
 | 
			
		||||
        (TYPE_EXCERPT, _("excerpt")),
 | 
			
		||||
        (TYPE_REMOVED, _("removed")),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    name = models.CharField(_("name"), max_length=64)
 | 
			
		||||
@ -116,6 +113,7 @@ class Sound(models.Model):
 | 
			
		||||
        default=0,
 | 
			
		||||
        help_text=_("position in the playlist"),
 | 
			
		||||
    )
 | 
			
		||||
    is_removed = models.BooleanField(_("removed"), default=False, help_text=_("file has been removed"))
 | 
			
		||||
 | 
			
		||||
    def _upload_to(self, filename):
 | 
			
		||||
        subdir = settings.SOUND_ARCHIVES_SUBDIR if self.type == self.TYPE_ARCHIVE else settings.SOUND_EXCERPTS_SUBDIR
 | 
			
		||||
@ -201,16 +199,16 @@ class Sound(models.Model):
 | 
			
		||||
        Return True if there was changes.
 | 
			
		||||
        """
 | 
			
		||||
        if not self.file_exists():
 | 
			
		||||
            if self.type == self.TYPE_REMOVED:
 | 
			
		||||
            if self.is_removed:
 | 
			
		||||
                return
 | 
			
		||||
            logger.debug("sound %s: has been removed", self.file.name)
 | 
			
		||||
            self.type = self.TYPE_REMOVED
 | 
			
		||||
            self.is_removed = True
 | 
			
		||||
            return True
 | 
			
		||||
 | 
			
		||||
        # not anymore removed
 | 
			
		||||
        changed = False
 | 
			
		||||
 | 
			
		||||
        if self.type == self.TYPE_REMOVED and self.program:
 | 
			
		||||
        if self.is_removed and self.program:
 | 
			
		||||
            changed = True
 | 
			
		||||
            self.type = (
 | 
			
		||||
                self.TYPE_ARCHIVE if self.file.name.startswith(self.program.archives_path) else self.TYPE_EXCERPT
 | 
			
		||||
@ -240,65 +238,3 @@ class Sound(models.Model):
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.__check_name()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Track(models.Model):
 | 
			
		||||
    """Track of a playlist of an object.
 | 
			
		||||
 | 
			
		||||
    The position can either be expressed as the position in the playlist
 | 
			
		||||
    or as the moment in seconds it started.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    episode = models.ForeignKey(
 | 
			
		||||
        Episode,
 | 
			
		||||
        models.CASCADE,
 | 
			
		||||
        blank=True,
 | 
			
		||||
        null=True,
 | 
			
		||||
        verbose_name=_("episode"),
 | 
			
		||||
    )
 | 
			
		||||
    sound = models.ForeignKey(
 | 
			
		||||
        Sound,
 | 
			
		||||
        models.CASCADE,
 | 
			
		||||
        blank=True,
 | 
			
		||||
        null=True,
 | 
			
		||||
        verbose_name=_("sound"),
 | 
			
		||||
    )
 | 
			
		||||
    position = models.PositiveSmallIntegerField(
 | 
			
		||||
        _("order"),
 | 
			
		||||
        default=0,
 | 
			
		||||
        help_text=_("position in the playlist"),
 | 
			
		||||
    )
 | 
			
		||||
    timestamp = models.PositiveSmallIntegerField(
 | 
			
		||||
        _("timestamp"),
 | 
			
		||||
        blank=True,
 | 
			
		||||
        null=True,
 | 
			
		||||
        help_text=_("position (in seconds)"),
 | 
			
		||||
    )
 | 
			
		||||
    title = models.CharField(_("title"), max_length=128)
 | 
			
		||||
    artist = models.CharField(_("artist"), max_length=128)
 | 
			
		||||
    album = models.CharField(_("album"), max_length=128, null=True, blank=True)
 | 
			
		||||
    tags = TaggableManager(verbose_name=_("tags"), blank=True)
 | 
			
		||||
    year = models.IntegerField(_("year"), blank=True, null=True)
 | 
			
		||||
    # FIXME: remove?
 | 
			
		||||
    info = models.CharField(
 | 
			
		||||
        _("information"),
 | 
			
		||||
        max_length=128,
 | 
			
		||||
        blank=True,
 | 
			
		||||
        null=True,
 | 
			
		||||
        help_text=_(
 | 
			
		||||
            "additional informations about this track, such as " "the version, if is it a remix, features, etc."
 | 
			
		||||
        ),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        verbose_name = _("Track")
 | 
			
		||||
        verbose_name_plural = _("Tracks")
 | 
			
		||||
        ordering = ("position",)
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return "{self.artist} -- {self.title} -- {self.position}".format(self=self)
 | 
			
		||||
 | 
			
		||||
    def save(self, *args, **kwargs):
 | 
			
		||||
        if (self.sound is None and self.episode is None) or (self.sound is not None and self.episode is not None):
 | 
			
		||||
            raise ValueError("sound XOR episode is required")
 | 
			
		||||
        super().save(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										72
									
								
								aircox/models/track.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								aircox/models/track.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,72 @@
 | 
			
		||||
from django.db import models
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
from taggit.managers import TaggableManager
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from .episode import Episode
 | 
			
		||||
from .sound import Sound
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ("Track",)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Track(models.Model):
 | 
			
		||||
    """Track of a playlist of an object.
 | 
			
		||||
 | 
			
		||||
    The position can either be expressed as the position in the playlist
 | 
			
		||||
    or as the moment in seconds it started.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    episode = models.ForeignKey(
 | 
			
		||||
        Episode,
 | 
			
		||||
        models.CASCADE,
 | 
			
		||||
        blank=True,
 | 
			
		||||
        null=True,
 | 
			
		||||
        verbose_name=_("episode"),
 | 
			
		||||
    )
 | 
			
		||||
    sound = models.ForeignKey(
 | 
			
		||||
        Sound,
 | 
			
		||||
        models.CASCADE,
 | 
			
		||||
        blank=True,
 | 
			
		||||
        null=True,
 | 
			
		||||
        verbose_name=_("sound"),
 | 
			
		||||
    )
 | 
			
		||||
    position = models.PositiveSmallIntegerField(
 | 
			
		||||
        _("order"),
 | 
			
		||||
        default=0,
 | 
			
		||||
        help_text=_("position in the playlist"),
 | 
			
		||||
    )
 | 
			
		||||
    timestamp = models.PositiveSmallIntegerField(
 | 
			
		||||
        _("timestamp"),
 | 
			
		||||
        blank=True,
 | 
			
		||||
        null=True,
 | 
			
		||||
        help_text=_("position (in seconds)"),
 | 
			
		||||
    )
 | 
			
		||||
    title = models.CharField(_("title"), max_length=128)
 | 
			
		||||
    artist = models.CharField(_("artist"), max_length=128)
 | 
			
		||||
    album = models.CharField(_("album"), max_length=128, null=True, blank=True)
 | 
			
		||||
    tags = TaggableManager(verbose_name=_("tags"), blank=True)
 | 
			
		||||
    year = models.IntegerField(_("year"), blank=True, null=True)
 | 
			
		||||
    # FIXME: remove?
 | 
			
		||||
    info = models.CharField(
 | 
			
		||||
        _("information"),
 | 
			
		||||
        max_length=128,
 | 
			
		||||
        blank=True,
 | 
			
		||||
        null=True,
 | 
			
		||||
        help_text=_(
 | 
			
		||||
            "additional informations about this track, such as " "the version, if is it a remix, features, etc."
 | 
			
		||||
        ),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        verbose_name = _("Track")
 | 
			
		||||
        verbose_name_plural = _("Tracks")
 | 
			
		||||
        ordering = ("position",)
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return "{self.artist} -- {self.title} -- {self.position}".format(self=self)
 | 
			
		||||
 | 
			
		||||
    def save(self, *args, **kwargs):
 | 
			
		||||
        if (self.sound is None and self.episode is None) or (self.sound is not None and self.episode is not None):
 | 
			
		||||
            raise ValueError("sound XOR episode is required")
 | 
			
		||||
        super().save(*args, **kwargs)
 | 
			
		||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@ -8,8 +8,8 @@
 | 
			
		||||
    {{ admin_formset.non_form_errors }}
 | 
			
		||||
 | 
			
		||||
    <a-tracklist-editor
 | 
			
		||||
            :labels="{% track_inline_labels %}"
 | 
			
		||||
            :init-data="{% track_inline_data formset=formset %}"
 | 
			
		||||
            :labels="{% inline_labels %}"
 | 
			
		||||
            :init-data="{% formset_inline_data formset=formset %}"
 | 
			
		||||
            settings-url="{% url "api:user-settings" %}"
 | 
			
		||||
            data-prefix="{{ formset.prefix }}-">
 | 
			
		||||
        <template #title>
 | 
			
		||||
 | 
			
		||||
@ -11,15 +11,15 @@ Context:
 | 
			
		||||
{% load aircox %}
 | 
			
		||||
 | 
			
		||||
{% if field.is_hidden or hidden %}
 | 
			
		||||
<input type="hidden" name="{{ name }}" {% if vbind %}:value{% else %}value{% endif %}="{{ value|default:"" }}">
 | 
			
		||||
<input type="hidden" name="{{ name }}" value="{{ value|default:"" }}">
 | 
			
		||||
{% elif field|is_checkbox %}
 | 
			
		||||
<input type="checkbox" class="checkbox" name="{{ name }}" {% if vbind %}:checked="{{ value }}"{% elif value %}checked{% endif %}>
 | 
			
		||||
<input type="checkbox" class="checkbox" name="{{ name }}" {% if value %}checked{% endif %}>
 | 
			
		||||
{% elif field|is_select %}
 | 
			
		||||
<select name="{{ name }}" class="select" {% if vbind %}:value{% else %}value{% endif %}="{{ value|default:"" }}">
 | 
			
		||||
<select name="{{ name }}" class="select" value="{{ value|default:"" }}">
 | 
			
		||||
    {% for value, label in field.widget.choices %}
 | 
			
		||||
    <option value="{{ value }}">{{ label }}</option>
 | 
			
		||||
    {% endfor %}
 | 
			
		||||
</select>
 | 
			
		||||
{% else %}
 | 
			
		||||
<input type="text" class="input" name="{{ name }}" {% if vbind %}:value{% else %}value{% endif %}="{{ value|default:"" }}">
 | 
			
		||||
<input type="text" class="input" name="{{ name }}" value="{{ value|default:"" }}">
 | 
			
		||||
{% endif %}
 | 
			
		||||
 | 
			
		||||
@ -29,7 +29,12 @@ Context:
 | 
			
		||||
            <input type="hidden" name="{{ formset.prefix }}-TOTAL_FORMS"
 | 
			
		||||
                :value="items.length || 0"/>
 | 
			
		||||
            <input type="hidden" name="{{ formset.prefix }}-INITIAL_FORMS"
 | 
			
		||||
                value="{{ formset.initial_form_count }}"/>
 | 
			
		||||
                {% if no_initial_form_count %}
 | 
			
		||||
                :value="items.length || 0"
 | 
			
		||||
                {% else %}
 | 
			
		||||
                value="{{ formset.initial_form_count }}"
 | 
			
		||||
                {% endif %}
 | 
			
		||||
                />
 | 
			
		||||
            <input type="hidden" name="{{ formset.prefix }}-MIN_NUM_FORMS"
 | 
			
		||||
                value="{{ formset.min_num }}"/>
 | 
			
		||||
            <input type="hidden" name="{{ formset.prefix }}-MAX_NUM_FORMS"
 | 
			
		||||
@ -71,11 +76,13 @@ Context:
 | 
			
		||||
        {% if not field.widget.is_hidden and not field.is_readonly %}
 | 
			
		||||
        <template v-slot:row-{{ name }}="{item,cell,value,attr,emit}">
 | 
			
		||||
            <div class="field">
 | 
			
		||||
                {% with full_name="'"|add:formset.prefix|add:"-' + cell.row + '-"|add:name|add:"'" %}
 | 
			
		||||
                {% block row-field %}
 | 
			
		||||
                <div class="control">
 | 
			
		||||
                {% include "./form_field.html" with value="item.data."|add:name vbind=1 %}
 | 
			
		||||
                {% include "./v_form_field.html" with value="item.data."|add:name name=full_name %}
 | 
			
		||||
                </div>
 | 
			
		||||
                {% endblock %}
 | 
			
		||||
                {% endwith %}
 | 
			
		||||
                <p v-for="error in item.error(attr)" class="help is-danger">
 | 
			
		||||
                    [[ error ]] !
 | 
			
		||||
                </p>
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,13 @@
 | 
			
		||||
{% extends "./list_editor.html" %}
 | 
			
		||||
 | 
			
		||||
{% block outer %}
 | 
			
		||||
    {% with no_initial_form_count=True %}
 | 
			
		||||
    {% with tag_id="inline-sounds" %}
 | 
			
		||||
    {% with tag="a-sound-list-editor" %}
 | 
			
		||||
        {{ block.super }}
 | 
			
		||||
    {% endwith %}
 | 
			
		||||
    {% endwith %}
 | 
			
		||||
    {% endwith %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										24
									
								
								aircox/templates/aircox/dashboard/v_form_field.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								aircox/templates/aircox/dashboard/v_form_field.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
			
		||||
{% comment %}
 | 
			
		||||
Render a form field instance as field (to be used when no model instance is provided). Value is binded as vue, class to Bulma
 | 
			
		||||
 | 
			
		||||
Context:
 | 
			
		||||
- name: field name
 | 
			
		||||
- field: form field
 | 
			
		||||
- value: input ":v-model" attribute
 | 
			
		||||
- hidden: if True, hidden field
 | 
			
		||||
{% endcomment %}
 | 
			
		||||
{% load aircox %}
 | 
			
		||||
 | 
			
		||||
{% if field.is_hidden or hidden %}
 | 
			
		||||
<input type="hidden" :name="{{ name }}" :value="{{ value|default:"" }}">
 | 
			
		||||
{% elif field|is_checkbox %}
 | 
			
		||||
<input type="checkbox" class="checkbox" :name="{{ name }}" v-model="{{ value }}">
 | 
			
		||||
{% elif field|is_select %}
 | 
			
		||||
<select :name="{{ name }}" class="select" v-model="{{ value|default:"" }}">
 | 
			
		||||
    {% for value, label in field.widget.choices %}
 | 
			
		||||
    <option value="{{ value }}">{{ label }}</option>
 | 
			
		||||
    {% endfor %}
 | 
			
		||||
</select>
 | 
			
		||||
{% else %}
 | 
			
		||||
<input type="text" class="input" :name="{{ name }}" v-model="{{ value|default:"" }}">
 | 
			
		||||
{% endif %}
 | 
			
		||||
@ -64,6 +64,7 @@ class EpisodeUpdateView(UserPassesTestMixin, BaseProgramMixin, PageUpdateView):
 | 
			
		||||
    def get_tracklist_formset(self, episode, **kwargs):
 | 
			
		||||
        kwargs.update(
 | 
			
		||||
            {
 | 
			
		||||
                "prefix": "tracks",
 | 
			
		||||
                "queryset": self.get_tracklist_queryset(episode),
 | 
			
		||||
                "initial": {
 | 
			
		||||
                    "episode": episode.id,
 | 
			
		||||
@ -78,6 +79,7 @@ class EpisodeUpdateView(UserPassesTestMixin, BaseProgramMixin, PageUpdateView):
 | 
			
		||||
    def get_soundlist_formset(self, episode, **kwargs):
 | 
			
		||||
        kwargs.update(
 | 
			
		||||
            {
 | 
			
		||||
                "prefix": "sounds",
 | 
			
		||||
                "queryset": self.get_soundlist_queryset(episode),
 | 
			
		||||
                "initial": {
 | 
			
		||||
                    "program": episode.parent_id,
 | 
			
		||||
@ -102,20 +104,29 @@ class EpisodeUpdateView(UserPassesTestMixin, BaseProgramMixin, PageUpdateView):
 | 
			
		||||
        return forms.SoundCreateForm(**kwargs)
 | 
			
		||||
 | 
			
		||||
    def get_context_data(self, **kwargs):
 | 
			
		||||
        kwargs.update(
 | 
			
		||||
            {
 | 
			
		||||
                "soundlist_formset": self.get_soundlist_formset(self.object),
 | 
			
		||||
                "tracklist_formset": self.get_tracklist_formset(self.object),
 | 
			
		||||
                "sound_form": self.get_sound_form(self.object),
 | 
			
		||||
            }
 | 
			
		||||
        forms = (
 | 
			
		||||
            ("soundlist_formset", self.get_soundlist_formset),
 | 
			
		||||
            ("tracklist_formset", self.get_tracklist_formset),
 | 
			
		||||
            ("sound_form", self.get_sound_form),
 | 
			
		||||
        )
 | 
			
		||||
        for key, func in forms:
 | 
			
		||||
            if key not in kwargs:
 | 
			
		||||
                kwargs[key] = func(self.object)
 | 
			
		||||
        return super().get_context_data(**kwargs)
 | 
			
		||||
 | 
			
		||||
    def post(self, request, *args, **kwargs):
 | 
			
		||||
        super().post(request, *args, **kwargs)
 | 
			
		||||
        formset = self.get_formset(request.POST)
 | 
			
		||||
        if formset.is_valid():
 | 
			
		||||
            formset.save()
 | 
			
		||||
            return super().form_valid(formset)
 | 
			
		||||
        resp = super().post(request, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
        formsets = {
 | 
			
		||||
            "soundlist_formset": self.get_soundlist_formset(self.object, data=request.POST),
 | 
			
		||||
            "tracklist_formset": self.get_tracklist_formset(self.object, data=request.POST),
 | 
			
		||||
        }
 | 
			
		||||
        invalid = False
 | 
			
		||||
        for formset in formsets.values():
 | 
			
		||||
            if not formset.is_valid():
 | 
			
		||||
                invalid = True
 | 
			
		||||
            else:
 | 
			
		||||
            return super().form_valid(formset)  # form_invalid(formset)
 | 
			
		||||
                formset.save()
 | 
			
		||||
        if invalid:
 | 
			
		||||
            return self.get(request, **formsets)
 | 
			
		||||
        return resp
 | 
			
		||||
 | 
			
		||||
@ -196,7 +196,6 @@ class PageUpdateView(BaseView, UpdateView):
 | 
			
		||||
    context_object_name = "page"
 | 
			
		||||
    template_name = "aircox/page_form.html"
 | 
			
		||||
 | 
			
		||||
    # FIXME: remove?
 | 
			
		||||
    def get_page(self):
 | 
			
		||||
        return self.object
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -42,6 +42,12 @@ class SoundViewSet(BaseAPIView, viewsets.ModelViewSet):
 | 
			
		||||
    filter_backends = (drf_filters.DjangoFilterBackend,)
 | 
			
		||||
    filterset_class = filters.SoundFilterSet
 | 
			
		||||
 | 
			
		||||
    def perform_create(self, serializer):
 | 
			
		||||
        obj = serializer.save()
 | 
			
		||||
        # FIXME: hack to avoid "TYPE_REMOVED" status
 | 
			
		||||
        # -> file is saved to fs after object is saved to db
 | 
			
		||||
        obj.save()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TrackROViewSet(viewsets.ReadOnlyModelViewSet):
 | 
			
		||||
    """Track viewset used for auto completion."""
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,6 @@
 | 
			
		||||
        <a-modal ref="modal" :title="labels && labels.add_sound">
 | 
			
		||||
            <template #default>
 | 
			
		||||
                <a-file-upload ref="file-upload" :url="soundUploadUrl" :label="labels.select_file" submitLabel="" @load="uploadDone"
 | 
			
		||||
 | 
			
		||||
                    >
 | 
			
		||||
                    <template #preview="{upload}">
 | 
			
		||||
                        <slot name="upload-preview" :upload="upload"></slot>
 | 
			
		||||
@ -24,6 +23,7 @@
 | 
			
		||||
            </template>
 | 
			
		||||
        </a-modal>
 | 
			
		||||
 | 
			
		||||
        <slot name="top" :set="set" :items="set.items"></slot>
 | 
			
		||||
        <a-rows :set="set" :columns="columns"
 | 
			
		||||
                :labels="initData.fields" :allow-create="true" :orderable="true"
 | 
			
		||||
                @move="listItemMove">
 | 
			
		||||
 | 
			
		||||
@ -54,11 +54,13 @@ window.aircox = {
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    onKeyPress(event) {
 | 
			
		||||
    onKeyPress(/*event*/) {
 | 
			
		||||
        /*
 | 
			
		||||
        if(event.key == " ") {
 | 
			
		||||
            this.player.togglePlay()
 | 
			
		||||
            event.stopPropagation()
 | 
			
		||||
        }
 | 
			
		||||
        */
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user