make EpisodeUpdateView work
This commit is contained in:
parent
1f716891ac
commit
8d4b4c5896
|
@ -32,6 +32,7 @@ class SoundInline(admin.TabularInline):
|
||||||
"is_good_quality",
|
"is_good_quality",
|
||||||
"is_public",
|
"is_public",
|
||||||
"is_downloadable",
|
"is_downloadable",
|
||||||
|
"is_removed",
|
||||||
]
|
]
|
||||||
readonly_fields = ["type", "audio", "duration", "is_good_quality"]
|
readonly_fields = ["type", "audio", "duration", "is_good_quality"]
|
||||||
extra = 0
|
extra = 0
|
||||||
|
@ -89,11 +90,7 @@ class SoundAdmin(SortableAdminBase, admin.ModelAdmin):
|
||||||
related.short_description = _("Program / Episode")
|
related.short_description = _("Program / Episode")
|
||||||
|
|
||||||
def audio(self, obj):
|
def audio(self, obj):
|
||||||
return (
|
return mark_safe('<audio src="{}" controls></audio>'.format(obj.file.url)) if not obj.is_removed else ""
|
||||||
mark_safe('<audio src="{}" controls></audio>'.format(obj.file.url))
|
|
||||||
if obj.type != Sound.TYPE_REMOVED
|
|
||||||
else ""
|
|
||||||
)
|
|
||||||
|
|
||||||
audio.short_description = _("Audio")
|
audio.short_description = _("Audio")
|
||||||
|
|
||||||
|
|
|
@ -99,7 +99,7 @@ class SoundFile:
|
||||||
sound = Sound.objects.path(self.path).first()
|
sound = Sound.objects.path(self.path).first()
|
||||||
if sound:
|
if sound:
|
||||||
if keep_deleted:
|
if keep_deleted:
|
||||||
sound.type = sound.TYPE_REMOVED
|
sound.is_removed = True
|
||||||
sound.check_on_file()
|
sound.check_on_file()
|
||||||
sound.save()
|
sound.save()
|
||||||
return sound
|
return sound
|
||||||
|
|
|
@ -80,6 +80,7 @@ TrackFormSet = modelformset_factory(
|
||||||
"tags",
|
"tags",
|
||||||
"album",
|
"album",
|
||||||
],
|
],
|
||||||
|
can_delete=True,
|
||||||
extra=0,
|
extra=0,
|
||||||
)
|
)
|
||||||
"""Track formset used in EpisodeUpdateView."""
|
"""Track formset used in EpisodeUpdateView."""
|
||||||
|
@ -94,6 +95,7 @@ SoundFormSet = modelformset_factory(
|
||||||
"is_downloadable",
|
"is_downloadable",
|
||||||
"duration",
|
"duration",
|
||||||
],
|
],
|
||||||
|
can_delete=True,
|
||||||
extra=0,
|
extra=0,
|
||||||
)
|
)
|
||||||
"""Sound formset used in EpisodeUpdateView."""
|
"""Sound formset used in EpisodeUpdateView."""
|
||||||
|
|
|
@ -6,8 +6,9 @@ from .log import Log, LogQuerySet
|
||||||
from .page import Category, Comment, NavItem, Page, PageQuerySet, StaticPage
|
from .page import Category, Comment, NavItem, Page, PageQuerySet, StaticPage
|
||||||
from .program import Program, ProgramChildQuerySet, ProgramQuerySet, Stream
|
from .program import Program, ProgramChildQuerySet, ProgramQuerySet, Stream
|
||||||
from .schedule import Schedule
|
from .schedule import Schedule
|
||||||
from .sound import Sound, SoundQuerySet, Track
|
from .sound import Sound, SoundQuerySet
|
||||||
from .station import Port, Station, StationQuerySet
|
from .station import Port, Station, StationQuerySet
|
||||||
|
from .track import Track
|
||||||
from .user_settings import UserSettings
|
from .user_settings import UserSettings
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
|
|
|
@ -62,6 +62,7 @@ class Episode(Page):
|
||||||
podcast["name"] = f"{self.title} - {archive_index}"
|
podcast["name"] = f"{self.title} - {archive_index}"
|
||||||
else:
|
else:
|
||||||
podcast["name"] = self.title
|
podcast["name"] = self.title
|
||||||
|
archive_index += 1
|
||||||
|
|
||||||
podcasts[index]["cover"] = cover
|
podcasts[index]["cover"] = cover
|
||||||
podcasts[index]["page_url"] = self.get_absolute_url()
|
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 django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from .diffusion import Diffusion
|
from .diffusion import Diffusion
|
||||||
from .sound import Sound, Track
|
from .sound import Sound
|
||||||
from .station import Station
|
from .station import Station
|
||||||
|
from .track import Track
|
||||||
from .page import Renderable
|
from .page import Renderable
|
||||||
|
|
||||||
logger = logging.getLogger("aircox")
|
logger = logging.getLogger("aircox")
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import os
|
||||||
|
|
||||||
from django.contrib.auth.models import Group, Permission, User
|
from django.contrib.auth.models import Group, Permission, User
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models import signals
|
from django.db.models import signals
|
||||||
|
@ -11,6 +13,7 @@ from .episode import Episode
|
||||||
from .page import Page
|
from .page import Page
|
||||||
from .program import Program
|
from .program import Program
|
||||||
from .schedule import Schedule
|
from .schedule import Schedule
|
||||||
|
from .sound import Sound
|
||||||
|
|
||||||
|
|
||||||
# Add a default group to a user when it is created. It also assigns a list
|
# 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)
|
@receiver(signals.post_delete, sender=Diffusion)
|
||||||
def diffusion_post_delete(sender, instance, *args, **kwargs):
|
def diffusion_post_delete(sender, instance, *args, **kwargs):
|
||||||
Episode.objects.filter(diffusion__isnull=True, content__isnull=True, sound__isnull=True).delete()
|
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.db.models import Q
|
||||||
from django.utils import timezone as tz
|
from django.utils import timezone as tz
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from taggit.managers import TaggableManager
|
|
||||||
|
|
||||||
from aircox.conf import settings
|
from aircox.conf import settings
|
||||||
|
|
||||||
|
@ -16,7 +15,7 @@ from .program import Program
|
||||||
logger = logging.getLogger("aircox")
|
logger = logging.getLogger("aircox")
|
||||||
|
|
||||||
|
|
||||||
__all__ = ("Sound", "SoundQuerySet", "Track")
|
__all__ = ("Sound", "SoundQuerySet")
|
||||||
|
|
||||||
|
|
||||||
class SoundQuerySet(models.QuerySet):
|
class SoundQuerySet(models.QuerySet):
|
||||||
|
@ -33,7 +32,7 @@ class SoundQuerySet(models.QuerySet):
|
||||||
return self.filter(episode__diffusion__id=id)
|
return self.filter(episode__diffusion__id=id)
|
||||||
|
|
||||||
def available(self):
|
def available(self):
|
||||||
return self.exclude(type=Sound.TYPE_REMOVED)
|
return self.exclude(is_removed=False)
|
||||||
|
|
||||||
def public(self):
|
def public(self):
|
||||||
"""Return sounds available as podcasts."""
|
"""Return sounds available as podcasts."""
|
||||||
|
@ -85,12 +84,10 @@ class Sound(models.Model):
|
||||||
TYPE_OTHER = 0x00
|
TYPE_OTHER = 0x00
|
||||||
TYPE_ARCHIVE = 0x01
|
TYPE_ARCHIVE = 0x01
|
||||||
TYPE_EXCERPT = 0x02
|
TYPE_EXCERPT = 0x02
|
||||||
TYPE_REMOVED = 0x03
|
|
||||||
TYPE_CHOICES = (
|
TYPE_CHOICES = (
|
||||||
(TYPE_OTHER, _("other")),
|
(TYPE_OTHER, _("other")),
|
||||||
(TYPE_ARCHIVE, _("archive")),
|
(TYPE_ARCHIVE, _("archive")),
|
||||||
(TYPE_EXCERPT, _("excerpt")),
|
(TYPE_EXCERPT, _("excerpt")),
|
||||||
(TYPE_REMOVED, _("removed")),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
name = models.CharField(_("name"), max_length=64)
|
name = models.CharField(_("name"), max_length=64)
|
||||||
|
@ -116,6 +113,7 @@ class Sound(models.Model):
|
||||||
default=0,
|
default=0,
|
||||||
help_text=_("position in the playlist"),
|
help_text=_("position in the playlist"),
|
||||||
)
|
)
|
||||||
|
is_removed = models.BooleanField(_("removed"), default=False, help_text=_("file has been removed"))
|
||||||
|
|
||||||
def _upload_to(self, filename):
|
def _upload_to(self, filename):
|
||||||
subdir = settings.SOUND_ARCHIVES_SUBDIR if self.type == self.TYPE_ARCHIVE else settings.SOUND_EXCERPTS_SUBDIR
|
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.
|
Return True if there was changes.
|
||||||
"""
|
"""
|
||||||
if not self.file_exists():
|
if not self.file_exists():
|
||||||
if self.type == self.TYPE_REMOVED:
|
if self.is_removed:
|
||||||
return
|
return
|
||||||
logger.debug("sound %s: has been removed", self.file.name)
|
logger.debug("sound %s: has been removed", self.file.name)
|
||||||
self.type = self.TYPE_REMOVED
|
self.is_removed = True
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# not anymore removed
|
# not anymore removed
|
||||||
changed = False
|
changed = False
|
||||||
|
|
||||||
if self.type == self.TYPE_REMOVED and self.program:
|
if self.is_removed and self.program:
|
||||||
changed = True
|
changed = True
|
||||||
self.type = (
|
self.type = (
|
||||||
self.TYPE_ARCHIVE if self.file.name.startswith(self.program.archives_path) else self.TYPE_EXCERPT
|
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):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.__check_name()
|
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 }}
|
{{ admin_formset.non_form_errors }}
|
||||||
|
|
||||||
<a-tracklist-editor
|
<a-tracklist-editor
|
||||||
:labels="{% track_inline_labels %}"
|
:labels="{% inline_labels %}"
|
||||||
:init-data="{% track_inline_data formset=formset %}"
|
:init-data="{% formset_inline_data formset=formset %}"
|
||||||
settings-url="{% url "api:user-settings" %}"
|
settings-url="{% url "api:user-settings" %}"
|
||||||
data-prefix="{{ formset.prefix }}-">
|
data-prefix="{{ formset.prefix }}-">
|
||||||
<template #title>
|
<template #title>
|
||||||
|
|
|
@ -11,15 +11,15 @@ Context:
|
||||||
{% load aircox %}
|
{% load aircox %}
|
||||||
|
|
||||||
{% if field.is_hidden or hidden %}
|
{% 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 %}
|
{% 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 %}
|
{% 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 %}
|
{% for value, label in field.widget.choices %}
|
||||||
<option value="{{ value }}">{{ label }}</option>
|
<option value="{{ value }}">{{ label }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
{% else %}
|
{% 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 %}
|
{% endif %}
|
||||||
|
|
|
@ -29,7 +29,12 @@ Context:
|
||||||
<input type="hidden" name="{{ formset.prefix }}-TOTAL_FORMS"
|
<input type="hidden" name="{{ formset.prefix }}-TOTAL_FORMS"
|
||||||
:value="items.length || 0"/>
|
:value="items.length || 0"/>
|
||||||
<input type="hidden" name="{{ formset.prefix }}-INITIAL_FORMS"
|
<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"
|
<input type="hidden" name="{{ formset.prefix }}-MIN_NUM_FORMS"
|
||||||
value="{{ formset.min_num }}"/>
|
value="{{ formset.min_num }}"/>
|
||||||
<input type="hidden" name="{{ formset.prefix }}-MAX_NUM_FORMS"
|
<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 %}
|
{% if not field.widget.is_hidden and not field.is_readonly %}
|
||||||
<template v-slot:row-{{ name }}="{item,cell,value,attr,emit}">
|
<template v-slot:row-{{ name }}="{item,cell,value,attr,emit}">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
|
{% with full_name="'"|add:formset.prefix|add:"-' + cell.row + '-"|add:name|add:"'" %}
|
||||||
{% block row-field %}
|
{% block row-field %}
|
||||||
<div class="control">
|
<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>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
{% endwith %}
|
||||||
<p v-for="error in item.error(attr)" class="help is-danger">
|
<p v-for="error in item.error(attr)" class="help is-danger">
|
||||||
[[ error ]] !
|
[[ error ]] !
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
{% extends "./list_editor.html" %}
|
{% extends "./list_editor.html" %}
|
||||||
|
|
||||||
{% block outer %}
|
{% block outer %}
|
||||||
|
{% with no_initial_form_count=True %}
|
||||||
{% with tag_id="inline-sounds" %}
|
{% with tag_id="inline-sounds" %}
|
||||||
{% with tag="a-sound-list-editor" %}
|
{% with tag="a-sound-list-editor" %}
|
||||||
{{ block.super }}
|
{{ block.super }}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
{% endwith %}
|
||||||
{% endblock %}
|
{% 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):
|
def get_tracklist_formset(self, episode, **kwargs):
|
||||||
kwargs.update(
|
kwargs.update(
|
||||||
{
|
{
|
||||||
|
"prefix": "tracks",
|
||||||
"queryset": self.get_tracklist_queryset(episode),
|
"queryset": self.get_tracklist_queryset(episode),
|
||||||
"initial": {
|
"initial": {
|
||||||
"episode": episode.id,
|
"episode": episode.id,
|
||||||
|
@ -78,6 +79,7 @@ class EpisodeUpdateView(UserPassesTestMixin, BaseProgramMixin, PageUpdateView):
|
||||||
def get_soundlist_formset(self, episode, **kwargs):
|
def get_soundlist_formset(self, episode, **kwargs):
|
||||||
kwargs.update(
|
kwargs.update(
|
||||||
{
|
{
|
||||||
|
"prefix": "sounds",
|
||||||
"queryset": self.get_soundlist_queryset(episode),
|
"queryset": self.get_soundlist_queryset(episode),
|
||||||
"initial": {
|
"initial": {
|
||||||
"program": episode.parent_id,
|
"program": episode.parent_id,
|
||||||
|
@ -102,20 +104,29 @@ class EpisodeUpdateView(UserPassesTestMixin, BaseProgramMixin, PageUpdateView):
|
||||||
return forms.SoundCreateForm(**kwargs)
|
return forms.SoundCreateForm(**kwargs)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
kwargs.update(
|
forms = (
|
||||||
{
|
("soundlist_formset", self.get_soundlist_formset),
|
||||||
"soundlist_formset": self.get_soundlist_formset(self.object),
|
("tracklist_formset", self.get_tracklist_formset),
|
||||||
"tracklist_formset": self.get_tracklist_formset(self.object),
|
("sound_form", self.get_sound_form),
|
||||||
"sound_form": self.get_sound_form(self.object),
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
for key, func in forms:
|
||||||
|
if key not in kwargs:
|
||||||
|
kwargs[key] = func(self.object)
|
||||||
return super().get_context_data(**kwargs)
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
super().post(request, *args, **kwargs)
|
resp = super().post(request, *args, **kwargs)
|
||||||
formset = self.get_formset(request.POST)
|
|
||||||
if formset.is_valid():
|
formsets = {
|
||||||
formset.save()
|
"soundlist_formset": self.get_soundlist_formset(self.object, data=request.POST),
|
||||||
return super().form_valid(formset)
|
"tracklist_formset": self.get_tracklist_formset(self.object, data=request.POST),
|
||||||
else:
|
}
|
||||||
return super().form_valid(formset) # form_invalid(formset)
|
invalid = False
|
||||||
|
for formset in formsets.values():
|
||||||
|
if not formset.is_valid():
|
||||||
|
invalid = True
|
||||||
|
else:
|
||||||
|
formset.save()
|
||||||
|
if invalid:
|
||||||
|
return self.get(request, **formsets)
|
||||||
|
return resp
|
||||||
|
|
|
@ -196,7 +196,6 @@ class PageUpdateView(BaseView, UpdateView):
|
||||||
context_object_name = "page"
|
context_object_name = "page"
|
||||||
template_name = "aircox/page_form.html"
|
template_name = "aircox/page_form.html"
|
||||||
|
|
||||||
# FIXME: remove?
|
|
||||||
def get_page(self):
|
def get_page(self):
|
||||||
return self.object
|
return self.object
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,12 @@ class SoundViewSet(BaseAPIView, viewsets.ModelViewSet):
|
||||||
filter_backends = (drf_filters.DjangoFilterBackend,)
|
filter_backends = (drf_filters.DjangoFilterBackend,)
|
||||||
filterset_class = filters.SoundFilterSet
|
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):
|
class TrackROViewSet(viewsets.ReadOnlyModelViewSet):
|
||||||
"""Track viewset used for auto completion."""
|
"""Track viewset used for auto completion."""
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
<a-modal ref="modal" :title="labels && labels.add_sound">
|
<a-modal ref="modal" :title="labels && labels.add_sound">
|
||||||
<template #default>
|
<template #default>
|
||||||
<a-file-upload ref="file-upload" :url="soundUploadUrl" :label="labels.select_file" submitLabel="" @load="uploadDone"
|
<a-file-upload ref="file-upload" :url="soundUploadUrl" :label="labels.select_file" submitLabel="" @load="uploadDone"
|
||||||
|
|
||||||
>
|
>
|
||||||
<template #preview="{upload}">
|
<template #preview="{upload}">
|
||||||
<slot name="upload-preview" :upload="upload"></slot>
|
<slot name="upload-preview" :upload="upload"></slot>
|
||||||
|
@ -24,6 +23,7 @@
|
||||||
</template>
|
</template>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
|
|
||||||
|
<slot name="top" :set="set" :items="set.items"></slot>
|
||||||
<a-rows :set="set" :columns="columns"
|
<a-rows :set="set" :columns="columns"
|
||||||
:labels="initData.fields" :allow-create="true" :orderable="true"
|
:labels="initData.fields" :allow-create="true" :orderable="true"
|
||||||
@move="listItemMove">
|
@move="listItemMove">
|
||||||
|
|
|
@ -54,11 +54,13 @@ window.aircox = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onKeyPress(event) {
|
onKeyPress(/*event*/) {
|
||||||
|
/*
|
||||||
if(event.key == " ") {
|
if(event.key == " ") {
|
||||||
this.player.togglePlay()
|
this.player.togglePlay()
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue
Block a user