@ -53,6 +53,12 @@ class SoundFilterSet(filters.FilterSet):
 | 
				
			|||||||
    episode = filters.NumberFilter(field_name="episode_id")
 | 
					    episode = filters.NumberFilter(field_name="episode_id")
 | 
				
			||||||
    search = filters.CharFilter(field_name="search", method="search_filter")
 | 
					    search = filters.CharFilter(field_name="search", method="search_filter")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = models.Sound
 | 
				
			||||||
 | 
					        fields = {
 | 
				
			||||||
 | 
					            "episode": ["in", "exact", "isnull"],
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def search_filter(self, queryset, name, value):
 | 
					    def search_filter(self, queryset, name, value):
 | 
				
			||||||
        return queryset.search(value)
 | 
					        return queryset.search(value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,7 @@ from django.forms.models import modelformset_factory
 | 
				
			|||||||
from aircox import models
 | 
					from aircox import models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
__all__ = ("CommentForm", "PageForm", "ProgramForm", "EpisodeForm", "SoundForm", "TrackFormSet")
 | 
					__all__ = ("CommentForm", "PageForm", "ProgramForm", "EpisodeForm", "SoundForm", "SoundFormSet", "TrackFormSet")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CommentForm(forms.ModelForm):
 | 
					class CommentForm(forms.ModelForm):
 | 
				
			||||||
@ -60,7 +60,15 @@ class SoundForm(forms.ModelForm):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
        model = models.Sound
 | 
					        model = models.Sound
 | 
				
			||||||
        fields = ["name", "program", "episode", "type", "position", "duration", "is_public", "is_downloadable"]
 | 
					        fields = ["name", "program", "episode", "file", "type", "position", "duration", "is_public", "is_downloadable"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SoundCreateForm(forms.ModelForm):
 | 
				
			||||||
 | 
					    """SoundForm used in EpisodeUpdateView."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = models.Sound
 | 
				
			||||||
 | 
					        fields = ["name", "episode", "program", "file", "type", "is_public", "is_downloadable"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TrackFormSet = modelformset_factory(
 | 
					TrackFormSet = modelformset_factory(
 | 
				
			||||||
@ -84,6 +92,7 @@ SoundFormSet = modelformset_factory(
 | 
				
			|||||||
        "type",
 | 
					        "type",
 | 
				
			||||||
        "is_public",
 | 
					        "is_public",
 | 
				
			||||||
        "is_downloadable",
 | 
					        "is_downloadable",
 | 
				
			||||||
 | 
					        "duration",
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    extra=0,
 | 
					    extra=0,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
				
			|||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										25
									
								
								aircox/templates/aircox/dashboard/form_field.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								aircox/templates/aircox/dashboard/form_field.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					{% 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 ":value" attribute
 | 
				
			||||||
 | 
					- vbind: if True, use ":value" instead of "value"
 | 
				
			||||||
 | 
					- hidden: if True, hidden field
 | 
				
			||||||
 | 
					{% endcomment %}
 | 
				
			||||||
 | 
					{% load aircox %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% if field.is_hidden or hidden %}
 | 
				
			||||||
 | 
					<input type="hidden" name="{{ name }}" {% if vbind %}:value{% else %}value{% endif %}="{{ value|default:"" }}">
 | 
				
			||||||
 | 
					{% elif field|is_checkbox %}
 | 
				
			||||||
 | 
					<input type="checkbox" class="checkbox" name="{{ name }}" {% if vbind %}:checked="{{ value }}"{% elif value %}checked{% endif %}>
 | 
				
			||||||
 | 
					{% elif field|is_select %}
 | 
				
			||||||
 | 
					<select name="{{ name }}" class="select" {% if vbind %}:value{% else %}value{% endif %}="{{ 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:"" }}">
 | 
				
			||||||
 | 
					{% endif %}
 | 
				
			||||||
@ -1,24 +1,31 @@
 | 
				
			|||||||
{% comment %}
 | 
					{% comment %}
 | 
				
			||||||
 | 
					Base template for list editor based on formsets (tracklist_editor, playlist_editor).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Context:
 | 
					Context:
 | 
				
			||||||
- formset: formset
 | 
					- tag_id: id of parent component
 | 
				
			||||||
 | 
					- tag: vue component tag (a-playlist-editor, etc.)
 | 
				
			||||||
 | 
					- formset: formset used to render the list editor
 | 
				
			||||||
{% endcomment %}
 | 
					{% endcomment %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% load aircox aircox_admin static i18n %}
 | 
					{% load aircox aircox_admin static i18n %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% with formset.form.base_fields as fields %}
 | 
					{% with formset.form.base_fields as fields %}
 | 
				
			||||||
<div id="inline-sounds">
 | 
					{% block outer %}
 | 
				
			||||||
 | 
					<div id="{{ tag_id }}">
 | 
				
			||||||
    {{ formset.non_form_errors }}
 | 
					    {{ formset.non_form_errors }}
 | 
				
			||||||
    <!-- formset.management_form -->
 | 
					    <!-- formset.management_form -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <a-playlist-editor
 | 
					    <{{ tag }}
 | 
				
			||||||
 | 
					            {% block tag-attrs %}
 | 
				
			||||||
            :labels="{% inline_labels %}"
 | 
					            :labels="{% inline_labels %}"
 | 
				
			||||||
            :init-data="{% formset_inline_data formset=formset %}"
 | 
					            :init-data="{% formset_inline_data formset=formset %}"
 | 
				
			||||||
            :default-columns="[{% for f in fields.keys %}{% if f != "position" %}'{{ f }}',{% endif %}{% endfor %}]"
 | 
					            :default-columns="[{% for f in fields.keys %}{% if f != "position" %}'{{ f }}',{% endif %}{% endfor %}]"
 | 
				
			||||||
            settings-url="{% url "api:user-settings" %}"
 | 
					            settings-url="{% url "api:user-settings" %}"
 | 
				
			||||||
            data-prefix="{{ formset.prefix }}-">
 | 
					            data-prefix="{{ formset.prefix }}-"
 | 
				
			||||||
        <template #title>
 | 
					            {% endblock %}>
 | 
				
			||||||
            <h3 class="title is-2">{% trans "Playlist" %}</h3>
 | 
					    {% block inner %}
 | 
				
			||||||
        </template>
 | 
					 | 
				
			||||||
        <template #top="{items}">
 | 
					        <template #top="{items}">
 | 
				
			||||||
 | 
					            {% block top %}
 | 
				
			||||||
            <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"
 | 
				
			||||||
@ -27,16 +34,20 @@ Context:
 | 
				
			|||||||
                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"
 | 
				
			||||||
                value="{{ formset.max_num }}"/>
 | 
					                value="{{ formset.max_num }}"/>
 | 
				
			||||||
 | 
					            {% endblock %}
 | 
				
			||||||
        </template>
 | 
					        </template>
 | 
				
			||||||
        <template #rows-header-head>
 | 
					        <template #rows-header-head>
 | 
				
			||||||
            <th style="max-width:2em" title="{% trans "Sound Position" %}"
 | 
					            {% block rows-header-head %}
 | 
				
			||||||
                    aria-description="{% trans "Sound Position" %}">
 | 
					            <th style="max-width:2em" title="{{ fields.position.help_text }}"
 | 
				
			||||||
 | 
					                    aria-description="{{ fields.position.help_text }}">
 | 
				
			||||||
                <span class="icon">
 | 
					                <span class="icon">
 | 
				
			||||||
                    <i class="fa fa-arrow-down-1-9"></i>
 | 
					                    <i class="fa fa-arrow-down-1-9"></i>
 | 
				
			||||||
                </span>
 | 
					                </span>
 | 
				
			||||||
            </th>
 | 
					            </th>
 | 
				
			||||||
 | 
					            {% endblock %}
 | 
				
			||||||
        </template>
 | 
					        </template>
 | 
				
			||||||
        <template v-slot:row-head="{item,row}">
 | 
					        <template v-slot:row-head="{item,row}">
 | 
				
			||||||
 | 
					            {% block row-head %}
 | 
				
			||||||
            <td>
 | 
					            <td>
 | 
				
			||||||
                [[ row+1 ]]
 | 
					                [[ row+1 ]]
 | 
				
			||||||
                <input type="hidden"
 | 
					                <input type="hidden"
 | 
				
			||||||
@ -54,14 +65,17 @@ Context:
 | 
				
			|||||||
                {% endif %}
 | 
					                {% endif %}
 | 
				
			||||||
                {% endfor %}
 | 
					                {% endfor %}
 | 
				
			||||||
            </td>
 | 
					            </td>
 | 
				
			||||||
 | 
					            {% endblock %}
 | 
				
			||||||
        </template>
 | 
					        </template>
 | 
				
			||||||
        {% for name, field in fields.items %}
 | 
					        {% for name, field in fields.items %}
 | 
				
			||||||
        {% 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">
 | 
				
			||||||
 | 
					                {% block row-field %}
 | 
				
			||||||
                <div class="control">
 | 
					                <div class="control">
 | 
				
			||||||
                {% include "./form_field.html" with field=field name=name value="item.data."|add:name %}
 | 
					                {% include "./form_field.html" with value="item.data."|add:name vbind=1 %}
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
 | 
					                {% endblock %}
 | 
				
			||||||
                <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>
 | 
				
			||||||
@ -69,6 +83,8 @@ Context:
 | 
				
			|||||||
        </template>
 | 
					        </template>
 | 
				
			||||||
        {% endif %}
 | 
					        {% endif %}
 | 
				
			||||||
        {% endfor %}
 | 
					        {% endfor %}
 | 
				
			||||||
    </a-playlist-editor>
 | 
					    {% endblock %}
 | 
				
			||||||
 | 
					    </{{ tag }}>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
{% endwith %}
 | 
					{% endwith %}
 | 
				
			||||||
							
								
								
									
										40
									
								
								aircox/templates/aircox/dashboard/soundlist_editor.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								aircox/templates/aircox/dashboard/soundlist_editor.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					{% extends "./list_editor.html" %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block outer %}
 | 
				
			||||||
 | 
					    {% with tag_id="inline-sounds" %}
 | 
				
			||||||
 | 
					    {% with tag="a-sound-list-editor" %}
 | 
				
			||||||
 | 
					        {{ block.super }}
 | 
				
			||||||
 | 
					    {% endwith %}
 | 
				
			||||||
 | 
					    {% endwith %}
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block tag-attrs %}
 | 
				
			||||||
 | 
					{{ block.super }}
 | 
				
			||||||
 | 
					sound-list-url="{% url "api:sound-list" %}?program={{ object.pk }}&episode__isnull"
 | 
				
			||||||
 | 
					sound-upload-url="{% url "api:sound-list" %}"
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block inner %}
 | 
				
			||||||
 | 
					{{ block.super }}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<template #upload-form>
 | 
				
			||||||
 | 
					    {% for field in sound_form %}
 | 
				
			||||||
 | 
					    {% with field.name as name %}
 | 
				
			||||||
 | 
					    {% with field.initial as value %}
 | 
				
			||||||
 | 
					    {% with field.field as field %}
 | 
				
			||||||
 | 
					    {% if name in "episode,program" %}
 | 
				
			||||||
 | 
					        {% include "./form_field.html" with value=value hidden=True %}
 | 
				
			||||||
 | 
					    {% elif name != "file" %}
 | 
				
			||||||
 | 
					    <div class="field is-horizontal">
 | 
				
			||||||
 | 
					        <label class="label mr-3">{{ field.label }}</label>
 | 
				
			||||||
 | 
					        {% include "./form_field.html" with value=value %}
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    {% endif %}
 | 
				
			||||||
 | 
					    {% endwith %}
 | 
				
			||||||
 | 
					    {% endwith %}
 | 
				
			||||||
 | 
					    {% endwith %}
 | 
				
			||||||
 | 
					    {% endfor %}
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
							
								
								
									
										19
									
								
								aircox/templates/aircox/dashboard/tracklist_editor.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								aircox/templates/aircox/dashboard/tracklist_editor.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					{% extends "./list_editor.html" %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block outer %}
 | 
				
			||||||
 | 
					    {% with tag_id="inline-tracks" %}
 | 
				
			||||||
 | 
					    {% with tag="a-track-list-editor" %}
 | 
				
			||||||
 | 
					        {{ block.super }}
 | 
				
			||||||
 | 
					    {% endwith %}
 | 
				
			||||||
 | 
					    {% endwith %}
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block row-field %}
 | 
				
			||||||
 | 
					<a-autocomplete
 | 
				
			||||||
 | 
					        :input-class="['input', item.error(attr) ? 'is-danger' : 'half-field']"
 | 
				
			||||||
 | 
					        url="{% url 'api:track-autocomplete' %}?{{ name }}=${query}&field={{ name }}"
 | 
				
			||||||
 | 
					        :name="'{{ formset.prefix }}-' + cell.row + '-{{ name }}'"
 | 
				
			||||||
 | 
					        v-model="item.data[attr]"
 | 
				
			||||||
 | 
					        title="{{ name }}"
 | 
				
			||||||
 | 
					        @change="emit('change', col)"/>
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
@ -2,41 +2,15 @@
 | 
				
			|||||||
{% load static i18n humanize honeypot aircox %}
 | 
					{% load static i18n humanize honeypot aircox %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% block page_form %}
 | 
					{% block page_form %}
 | 
				
			||||||
<a-modal ref="sound-edit-modal" v-if="item" title="{% translate "Edit sound" %}">
 | 
					 | 
				
			||||||
    <template #default="{item}">
 | 
					 | 
				
			||||||
        {% for field in sound_form %}
 | 
					 | 
				
			||||||
        {% if field.name in "episode,program" %}
 | 
					 | 
				
			||||||
        <input type="hidden" name="{{ field.name }}" value="{{ field.value }}" />
 | 
					 | 
				
			||||||
        {% else %}
 | 
					 | 
				
			||||||
        <div class="field">
 | 
					 | 
				
			||||||
            {% if field|is_checkbox %}
 | 
					 | 
				
			||||||
            <label class="label">
 | 
					 | 
				
			||||||
                <input type="text" name="{{ field.name }}" :value="item.{{ field.name }}">
 | 
					 | 
				
			||||||
                {{ field.label }}
 | 
					 | 
				
			||||||
            </label>
 | 
					 | 
				
			||||||
            {% else %}
 | 
					 | 
				
			||||||
            <label class="label">{{ field.label }}</label>
 | 
					 | 
				
			||||||
            <div class="control">
 | 
					 | 
				
			||||||
                <input type="text" name="{{ field.name }}" :value="item.{{ field.name }}">
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
            {% endif %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <p class="help">{{ field.help_text }}</p>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        {% endif %}
 | 
					 | 
				
			||||||
        {% endfor %}
 | 
					 | 
				
			||||||
    </template>
 | 
					 | 
				
			||||||
</a-modal>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<a-episode :page="{title: "{{ object.title }}", podcasts: {{ object.sounds|json }}}">
 | 
					<a-episode :page="{title: "{{ object.title }}", podcasts: {{ object.sounds|json }}}">
 | 
				
			||||||
    <template v-slot="{podcasts,page}">
 | 
					    <template v-slot="{podcasts,page}">
 | 
				
			||||||
        {{ block.super }}
 | 
					        {{ block.super }}
 | 
				
			||||||
        <hr/>
 | 
					        <hr/>
 | 
				
			||||||
        {% include "./widgets/tracklist_editor.html" with formset=tracklist_formset %}
 | 
					        {% include "./dashboard/tracklist_editor.html" with formset=tracklist_formset %}
 | 
				
			||||||
        <hr/>
 | 
					        <hr/>
 | 
				
			||||||
        <section class="container">
 | 
					        <section class="container">
 | 
				
			||||||
            <h3 class="title">{% translate "Sound files" %}</h3>
 | 
					            <h3 class="title">{% translate "Sound files" %}</h3>
 | 
				
			||||||
            {% include "./widgets/playlist_editor.html" with formset=playlist_formset %}
 | 
					            {% include "./dashboard/soundlist_editor.html" with formset=soundlist_formset %}
 | 
				
			||||||
        </section>
 | 
					        </section>
 | 
				
			||||||
    </template>
 | 
					    </template>
 | 
				
			||||||
</a-episode>
 | 
					</a-episode>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,21 +0,0 @@
 | 
				
			|||||||
{% 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 ":value" attribute
 | 
					 | 
				
			||||||
{% endcomment %}
 | 
					 | 
				
			||||||
{% load aircox %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{% if field|is_checkbox %}
 | 
					 | 
				
			||||||
<input type="checkbox" class="checkbox" name="{{ name }}" :checked="{{ value }}">
 | 
					 | 
				
			||||||
{% elif field|is_select %}
 | 
					 | 
				
			||||||
<select name="{{ name }}" class="select" :value="{{ value }}">
 | 
					 | 
				
			||||||
    {% for value, label in field.widget.choices %}
 | 
					 | 
				
			||||||
    <option value="{{ value }}">{{ label }}</option>
 | 
					 | 
				
			||||||
    {% endfor %}
 | 
					 | 
				
			||||||
</select>
 | 
					 | 
				
			||||||
{% else %}
 | 
					 | 
				
			||||||
<input type="text" class="input" name="{{ name }}" :value="{{ value }}">
 | 
					 | 
				
			||||||
{% endif %}
 | 
					 | 
				
			||||||
@ -1,80 +0,0 @@
 | 
				
			|||||||
{% comment %}
 | 
					 | 
				
			||||||
Context:
 | 
					 | 
				
			||||||
- formset: playlist's track formset
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{% endcomment %}
 | 
					 | 
				
			||||||
{% load aircox aircox_admin static i18n %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{% with formset.form.base_fields as fields %}
 | 
					 | 
				
			||||||
<div id="inline-tracks">
 | 
					 | 
				
			||||||
    {{ formset.non_form_errors }}
 | 
					 | 
				
			||||||
    <!-- formset.management_form -->
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    <a-tracklist-editor
 | 
					 | 
				
			||||||
            :labels="{% inline_labels %}"
 | 
					 | 
				
			||||||
            :init-data="{% formset_inline_data formset=formset %}"
 | 
					 | 
				
			||||||
            :default-columns="[{% for f in fields.keys %}{% if f != "position" %}'{{ f }}',{% endif %}{% endfor %}]"
 | 
					 | 
				
			||||||
            settings-url="{% url "api:user-settings" %}"
 | 
					 | 
				
			||||||
            data-prefix="{{ formset.prefix }}-">
 | 
					 | 
				
			||||||
        <template #title>
 | 
					 | 
				
			||||||
            <h3 class="title is-2">{% trans "Playlist" %}</h3>
 | 
					 | 
				
			||||||
        </template>
 | 
					 | 
				
			||||||
        <template #top="{items}">
 | 
					 | 
				
			||||||
            <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 }}"/>
 | 
					 | 
				
			||||||
            <input type="hidden" name="{{ formset.prefix }}-MIN_NUM_FORMS"
 | 
					 | 
				
			||||||
                value="{{ formset.min_num }}"/>
 | 
					 | 
				
			||||||
            <input type="hidden" name="{{ formset.prefix }}-MAX_NUM_FORMS"
 | 
					 | 
				
			||||||
                value="{{ formset.max_num }}"/>
 | 
					 | 
				
			||||||
        </template>
 | 
					 | 
				
			||||||
        <template #rows-header-head>
 | 
					 | 
				
			||||||
            <th style="max-width:2em" title="{% trans "Track Position" %}"
 | 
					 | 
				
			||||||
                    aria-description="{% trans "Track Position" %}">
 | 
					 | 
				
			||||||
                <span class="icon">
 | 
					 | 
				
			||||||
                    <i class="fa fa-arrow-down-1-9"></i>
 | 
					 | 
				
			||||||
                </span>
 | 
					 | 
				
			||||||
            </th>
 | 
					 | 
				
			||||||
        </template>
 | 
					 | 
				
			||||||
        <template v-slot:row-head="{item,row}">
 | 
					 | 
				
			||||||
            <td>
 | 
					 | 
				
			||||||
                [[ row+1 ]]
 | 
					 | 
				
			||||||
                <input type="hidden"
 | 
					 | 
				
			||||||
                    :name="'{{ formset.prefix }}-' + row + '-position'"
 | 
					 | 
				
			||||||
                    :value="row"/>
 | 
					 | 
				
			||||||
                <input t-if="item.data.id" type="hidden"
 | 
					 | 
				
			||||||
                    :name="'{{ formset.prefix }}-' + row + '-id'"
 | 
					 | 
				
			||||||
                    :value="item.data.id || item.id"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                {% for name, field in fields.items %}
 | 
					 | 
				
			||||||
                {% if name != 'position' and field.widget.is_hidden %}
 | 
					 | 
				
			||||||
                <input type="hidden"
 | 
					 | 
				
			||||||
                    :name="'{{ formset.prefix }}-' + row + '-{{ name }}'"
 | 
					 | 
				
			||||||
                    v-model="item.data[attr]"/>
 | 
					 | 
				
			||||||
                {% endif %}
 | 
					 | 
				
			||||||
                {% endfor %}
 | 
					 | 
				
			||||||
            </td>
 | 
					 | 
				
			||||||
        </template>
 | 
					 | 
				
			||||||
        {% for name, field in fields.items %}
 | 
					 | 
				
			||||||
        {% if not field.widget.is_hidden and not field.is_readonly %}
 | 
					 | 
				
			||||||
        ---
 | 
					 | 
				
			||||||
        <template v-slot:row-{{ name }}="{item,cell,value,attr,emit}">
 | 
					 | 
				
			||||||
            <div class="field">
 | 
					 | 
				
			||||||
                <a-autocomplete
 | 
					 | 
				
			||||||
                        :input-class="['input', item.error(attr) ? 'is-danger' : 'half-field']"
 | 
					 | 
				
			||||||
                        url="{% url 'api:track-autocomplete' %}?{{ name }}=${query}&field={{ name }}"
 | 
					 | 
				
			||||||
                        :name="'{{ formset.prefix }}-' + cell.row + '-{{ name }}'"
 | 
					 | 
				
			||||||
                        v-model="item.data[attr]"
 | 
					 | 
				
			||||||
                        title="{{ name }}"
 | 
					 | 
				
			||||||
                        @change="emit('change', col)"/>
 | 
					 | 
				
			||||||
                <p v-for="error in item.error(attr)" class="help is-danger">
 | 
					 | 
				
			||||||
                    [[ error ]] !
 | 
					 | 
				
			||||||
                </p>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </template>
 | 
					 | 
				
			||||||
        {% endif %}
 | 
					 | 
				
			||||||
        {% endfor %}
 | 
					 | 
				
			||||||
    </a-tracklist-editor>
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
{% endwith %}
 | 
					 | 
				
			||||||
@ -40,6 +40,10 @@ def do_formset_inline_data(context, formset):
 | 
				
			|||||||
        item = {name: form[name].value() for name in form.fields.keys()}
 | 
					        item = {name: form[name].value() for name in form.fields.keys()}
 | 
				
			||||||
        item["__errors__"] = form.errors
 | 
					        item["__errors__"] = form.errors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # hack for sound list
 | 
				
			||||||
 | 
					        if duration := item.get("duration"):
 | 
				
			||||||
 | 
					            item["duration"] = duration.strftime("%H:%M")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # hack for playlist editor
 | 
					        # hack for playlist editor
 | 
				
			||||||
        tags = item.get("tags")
 | 
					        tags = item.get("tags")
 | 
				
			||||||
        if tags and not isinstance(tags, str):
 | 
					        if tags and not isinstance(tags, str):
 | 
				
			||||||
@ -60,9 +64,13 @@ inline_labels_ = {
 | 
				
			|||||||
    "remove_item": _("Remove"),
 | 
					    "remove_item": _("Remove"),
 | 
				
			||||||
    "save_settings": _("Save Settings"),
 | 
					    "save_settings": _("Save Settings"),
 | 
				
			||||||
    "discard_changes": _("Discard changes"),
 | 
					    "discard_changes": _("Discard changes"),
 | 
				
			||||||
 | 
					    "select_file": _("Select a file"),
 | 
				
			||||||
 | 
					    "submit": _("Submit"),
 | 
				
			||||||
    # track list
 | 
					    # track list
 | 
				
			||||||
    "columns": _("Columns"),
 | 
					    "columns": _("Columns"),
 | 
				
			||||||
    "timestamp": _("Timestamp"),
 | 
					    "timestamp": _("Timestamp"),
 | 
				
			||||||
 | 
					    # sound list
 | 
				
			||||||
 | 
					    "add_sound": _("Add a sound"),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
from django.contrib.auth.mixins import UserPassesTestMixin
 | 
					from django.contrib.auth.mixins import UserPassesTestMixin
 | 
				
			||||||
from django.urls import reverse
 | 
					from django.urls import reverse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from aircox.models import Episode, Program, StaticPage, Track
 | 
					from aircox.models import Episode, Program, StaticPage, Sound, Track
 | 
				
			||||||
from aircox import forms
 | 
					from aircox import forms
 | 
				
			||||||
from ..filters import EpisodeFilters
 | 
					from ..filters import EpisodeFilters
 | 
				
			||||||
from .page import PageListView
 | 
					from .page import PageListView
 | 
				
			||||||
@ -72,13 +72,13 @@ class EpisodeUpdateView(UserPassesTestMixin, BaseProgramMixin, PageUpdateView):
 | 
				
			|||||||
        )
 | 
					        )
 | 
				
			||||||
        return forms.TrackFormSet(**kwargs)
 | 
					        return forms.TrackFormSet(**kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_playlist_queryset(self, episode):
 | 
					    def get_soundlist_queryset(self, episode):
 | 
				
			||||||
        return episode.sound_set.all()
 | 
					        return episode.sound_set.all()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_playlist_formset(self, episode, **kwargs):
 | 
					    def get_soundlist_formset(self, episode, **kwargs):
 | 
				
			||||||
        kwargs.update(
 | 
					        kwargs.update(
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                "queryset": self.get_playlist_queryset(episode),
 | 
					                "queryset": self.get_soundlist_queryset(episode),
 | 
				
			||||||
                "initial": {
 | 
					                "initial": {
 | 
				
			||||||
                    "program": episode.parent_id,
 | 
					                    "program": episode.parent_id,
 | 
				
			||||||
                    "episode": episode.id,
 | 
					                    "episode": episode.id,
 | 
				
			||||||
@ -87,11 +87,26 @@ class EpisodeUpdateView(UserPassesTestMixin, BaseProgramMixin, PageUpdateView):
 | 
				
			|||||||
        )
 | 
					        )
 | 
				
			||||||
        return forms.SoundFormSet(**kwargs)
 | 
					        return forms.SoundFormSet(**kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_sound_form(self, episode, **kwargs):
 | 
				
			||||||
 | 
					        kwargs.update(
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                "initial": {
 | 
				
			||||||
 | 
					                    "program": episode.parent_id,
 | 
				
			||||||
 | 
					                    "episode": episode.pk,
 | 
				
			||||||
 | 
					                    "name": episode.title,
 | 
				
			||||||
 | 
					                    "is_public": True,
 | 
				
			||||||
 | 
					                    "type": Sound.TYPE_ARCHIVE,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        return forms.SoundCreateForm(**kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_context_data(self, **kwargs):
 | 
					    def get_context_data(self, **kwargs):
 | 
				
			||||||
        kwargs.update(
 | 
					        kwargs.update(
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                "playlist_formset": self.get_playlist_formset(self.object),
 | 
					                "soundlist_formset": self.get_soundlist_formset(self.object),
 | 
				
			||||||
                "tracklist_formset": self.get_tracklist_formset(self.object),
 | 
					                "tracklist_formset": self.get_tracklist_formset(self.object),
 | 
				
			||||||
 | 
					                "sound_form": self.get_sound_form(self.object),
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        return super().get_context_data(**kwargs)
 | 
					        return super().get_context_data(**kwargs)
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,7 @@
 | 
				
			|||||||
from django_filters import rest_framework as drf_filters
 | 
					from django_filters import rest_framework as drf_filters
 | 
				
			||||||
from rest_framework import status, viewsets
 | 
					from rest_framework import status, viewsets, parsers, permissions
 | 
				
			||||||
from rest_framework.decorators import action
 | 
					from rest_framework.decorators import action
 | 
				
			||||||
from rest_framework.permissions import IsAuthenticated
 | 
					 | 
				
			||||||
from rest_framework.response import Response
 | 
					from rest_framework.response import Response
 | 
				
			||||||
from rest_framework.parsers import MultiPartParser
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from filer.models.imagemodels import Image
 | 
					from filer.models.imagemodels import Image
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -20,7 +18,7 @@ __all__ = (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ImageViewSet(viewsets.ModelViewSet):
 | 
					class ImageViewSet(viewsets.ModelViewSet):
 | 
				
			||||||
    parsers = (MultiPartParser,)
 | 
					    parsers = (parsers.MultiPartParser,)
 | 
				
			||||||
    serializer_class = admin.ImageSerializer
 | 
					    serializer_class = admin.ImageSerializer
 | 
				
			||||||
    queryset = Image.objects.all().order_by("-uploaded_at")
 | 
					    queryset = Image.objects.all().order_by("-uploaded_at")
 | 
				
			||||||
    filter_backends = (drf_filters.DjangoFilterBackend,)
 | 
					    filter_backends = (drf_filters.DjangoFilterBackend,)
 | 
				
			||||||
@ -37,6 +35,8 @@ class ImageViewSet(viewsets.ModelViewSet):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SoundViewSet(BaseAPIView, viewsets.ModelViewSet):
 | 
					class SoundViewSet(BaseAPIView, viewsets.ModelViewSet):
 | 
				
			||||||
 | 
					    parsers = (parsers.MultiPartParser,)
 | 
				
			||||||
 | 
					    permissions = (permissions.IsAuthenticatedOrReadOnly,)
 | 
				
			||||||
    serializer_class = SoundSerializer
 | 
					    serializer_class = SoundSerializer
 | 
				
			||||||
    queryset = models.Sound.objects.available().order_by("-pk")
 | 
					    queryset = models.Sound.objects.available().order_by("-pk")
 | 
				
			||||||
    filter_backends = (drf_filters.DjangoFilterBackend,)
 | 
					    filter_backends = (drf_filters.DjangoFilterBackend,)
 | 
				
			||||||
@ -47,7 +47,7 @@ class TrackROViewSet(viewsets.ReadOnlyModelViewSet):
 | 
				
			|||||||
    """Track viewset used for auto completion."""
 | 
					    """Track viewset used for auto completion."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    serializer_class = admin.TrackSerializer
 | 
					    serializer_class = admin.TrackSerializer
 | 
				
			||||||
    permission_classes = [IsAuthenticated]
 | 
					    permission_classes = (permissions.IsAuthenticated,)
 | 
				
			||||||
    filter_backends = (drf_filters.DjangoFilterBackend,)
 | 
					    filter_backends = (drf_filters.DjangoFilterBackend,)
 | 
				
			||||||
    filterset_class = filters.TrackFilterSet
 | 
					    filterset_class = filters.TrackFilterSet
 | 
				
			||||||
    queryset = models.Track.objects.all()
 | 
					    queryset = models.Track.objects.all()
 | 
				
			||||||
@ -70,7 +70,7 @@ class UserSettingsViewSet(viewsets.ViewSet):
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    serializer_class = admin.UserSettingsSerializer
 | 
					    serializer_class = admin.UserSettingsSerializer
 | 
				
			||||||
    permission_classes = [IsAuthenticated]
 | 
					    permission_classes = (permissions.IsAuthenticated,)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_serializer(self, instance=None, **kwargs):
 | 
					    def get_serializer(self, instance=None, **kwargs):
 | 
				
			||||||
        return self.serializer_class(instance=instance, context={"user": self.request.user}, **kwargs)
 | 
					        return self.serializer_class(instance=instance, context={"user": self.request.user}, **kwargs)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										110
									
								
								assets/src/components/AFileUpload.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								assets/src/components/AFileUpload.vue
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,110 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					    <div ref="list" class="a-select-file-list">
 | 
				
			||||||
 | 
					        <form ref="form" class="flex-column" v-if="state == STATE.DEFAULT">
 | 
				
			||||||
 | 
					            <slot name="form"></slot>
 | 
				
			||||||
 | 
					            <div class="field is-horizontal">
 | 
				
			||||||
 | 
					                <label class="label">{{ label }}</label>
 | 
				
			||||||
 | 
					                <input type="file" ref="uploadFile" :name="fieldName" @change="onFileChange"/>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="flex-row align-right" v-if="submitLabel">
 | 
				
			||||||
 | 
					                <button type="button" class="button small" @click="submit">
 | 
				
			||||||
 | 
					                    {{ submitLabel }}
 | 
				
			||||||
 | 
					                </button>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </form>
 | 
				
			||||||
 | 
					        <div class="flex-column" v-else>
 | 
				
			||||||
 | 
					            <slot name="preview" :fileUrl="fileUrl" :file="file" :loaded="loaded" :total="total"></slot>
 | 
				
			||||||
 | 
					            <div class="flex-row">
 | 
				
			||||||
 | 
					                <progress :max="total" :value="loaded"/>
 | 
				
			||||||
 | 
					                <button type="button" class="button small square ml-2" @click="abort">
 | 
				
			||||||
 | 
					                    <span class="icon small">
 | 
				
			||||||
 | 
					                        <i class="fa fa-close"></i>
 | 
				
			||||||
 | 
					                    </span>
 | 
				
			||||||
 | 
					                </button>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					<script>
 | 
				
			||||||
 | 
					import {getCsrf} from "../model"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default {
 | 
				
			||||||
 | 
					    emit: ["fileChange", "load"],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    props: {
 | 
				
			||||||
 | 
					        url: { type: String },
 | 
				
			||||||
 | 
					        fieldName: { type: String, default: "file" },
 | 
				
			||||||
 | 
					        label: { type: String, default: "Select a file" },
 | 
				
			||||||
 | 
					        submitLabel: { type: String, default: "Upload" },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    data() {
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            STATE: {
 | 
				
			||||||
 | 
					                DEFAULT: 0,
 | 
				
			||||||
 | 
					                UPLOADING: 1,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            state: 0,
 | 
				
			||||||
 | 
					            upload: {},
 | 
				
			||||||
 | 
					            file: null,
 | 
				
			||||||
 | 
					            fileUrl: null,
 | 
				
			||||||
 | 
					            total: 0,
 | 
				
			||||||
 | 
					            loaded: 0,
 | 
				
			||||||
 | 
					            request: null,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    methods: {
 | 
				
			||||||
 | 
					        abort() {
 | 
				
			||||||
 | 
					            this.request && this.request.abort()
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        onFileChange() {
 | 
				
			||||||
 | 
					            const [file] = this.$refs.uploadFile.files
 | 
				
			||||||
 | 
					            if(!file)
 | 
				
			||||||
 | 
					                return
 | 
				
			||||||
 | 
					            this._setUploadFile(file)
 | 
				
			||||||
 | 
					            this.$emit("fileChange", {upload: this, file: this.file, fileUrl: this.fileUrl})
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        submit() {
 | 
				
			||||||
 | 
					            const req = new XMLHttpRequest()
 | 
				
			||||||
 | 
					            req.open("POST", this.url)
 | 
				
			||||||
 | 
					            req.upload.addEventListener("progress", (e) => this.onUploadProgress(e))
 | 
				
			||||||
 | 
					            req.addEventListener("load", (e) => this.onUploadDone(e))
 | 
				
			||||||
 | 
					            req.addEventListener("abort", (e) => this.onUploadDone(e))
 | 
				
			||||||
 | 
					            req.addEventListener("error", (e) => this.onUploadDone(e))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const formData = new FormData(this.$refs.form);
 | 
				
			||||||
 | 
					            formData.append('csrfmiddlewaretoken', getCsrf())
 | 
				
			||||||
 | 
					            req.send(formData)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this._resetUpload(this.STATE.UPLOADING, false, req)
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        onUploadProgress(event) {
 | 
				
			||||||
 | 
					            this.loaded = event.loaded
 | 
				
			||||||
 | 
					            this.total = event.total
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        onUploadDone(event) {
 | 
				
			||||||
 | 
					            this.$emit("load", event)
 | 
				
			||||||
 | 
					            this._resetUpload(this.STATE.DEFAULT, true)
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        _setUploadFile(file) {
 | 
				
			||||||
 | 
					            this.file = file
 | 
				
			||||||
 | 
					            this.fileURL = file && URL.createObjectURL(file)
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        _resetUpload(state, resetFile=false, request=null) {
 | 
				
			||||||
 | 
					            this.state = state
 | 
				
			||||||
 | 
					            this.loaded = 0
 | 
				
			||||||
 | 
					            this.total = 0
 | 
				
			||||||
 | 
					            this.request = request
 | 
				
			||||||
 | 
					            if(resetFile)
 | 
				
			||||||
 | 
					                this.file = null
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    },}
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
@ -1,5 +1,29 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <div class="a-playlist-editor">
 | 
					    <div class="a-playlist-editor">
 | 
				
			||||||
 | 
					        <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>
 | 
				
			||||||
 | 
					                    </template>
 | 
				
			||||||
 | 
					                    <template #form>
 | 
				
			||||||
 | 
					                        <slot name="upload-form"></slot>
 | 
				
			||||||
 | 
					                    </template>
 | 
				
			||||||
 | 
					                </a-file-upload>
 | 
				
			||||||
 | 
					            </template>
 | 
				
			||||||
 | 
					            <template #footer>
 | 
				
			||||||
 | 
					                <button type="button" class="button"
 | 
				
			||||||
 | 
					                        @click.stop="$refs['file-upload'].submit()">
 | 
				
			||||||
 | 
					                    <span class="icon">
 | 
				
			||||||
 | 
					                        <i class="fa fa-upload"></i>
 | 
				
			||||||
 | 
					                    </span>
 | 
				
			||||||
 | 
					                    <span>{{ labels.submit }}</span>
 | 
				
			||||||
 | 
					                </button>
 | 
				
			||||||
 | 
					            </template>
 | 
				
			||||||
 | 
					        </a-modal>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <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">
 | 
				
			||||||
@ -21,7 +45,7 @@
 | 
				
			|||||||
                    <span class="icon"><i class="fa fa-rotate" /></span>
 | 
					                    <span class="icon"><i class="fa fa-rotate" /></span>
 | 
				
			||||||
                </button>
 | 
					                </button>
 | 
				
			||||||
            <button type="button" class="button square is-primary p-2"
 | 
					            <button type="button" class="button square is-primary p-2"
 | 
				
			||||||
                    @click="this.set.push(new this.set.model())"
 | 
					                        @click="$refs.modal.open()"
 | 
				
			||||||
                        :title="labels.add_sound"
 | 
					                        :title="labels.add_sound"
 | 
				
			||||||
                        :aria-label="labels.add_sound"
 | 
					                        :aria-label="labels.add_sound"
 | 
				
			||||||
                        >
 | 
					                        >
 | 
				
			||||||
@ -38,16 +62,19 @@ import Model, {Set} from '../model'
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// import AActionButton from './AActionButton'
 | 
					// import AActionButton from './AActionButton'
 | 
				
			||||||
import ARows from './ARows'
 | 
					import ARows from './ARows'
 | 
				
			||||||
// import AModal from "./AModal"
 | 
					import AModal from "./AModal"
 | 
				
			||||||
 | 
					import AFileUpload from "./AFileUpload"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
    components: {ARows},
 | 
					    components: {ARows, AModal, AFileUpload},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    props: {
 | 
					    props: {
 | 
				
			||||||
        initData: Object,
 | 
					        initData: Object,
 | 
				
			||||||
        dataPrefix: String,
 | 
					        dataPrefix: String,
 | 
				
			||||||
        labels: Object,
 | 
					        labels: Object,
 | 
				
			||||||
        settingsUrl: String,
 | 
					        settingsUrl: String,
 | 
				
			||||||
 | 
					        soundListUrl: String,
 | 
				
			||||||
 | 
					        soundUploadUrl: String,
 | 
				
			||||||
        columns: {
 | 
					        columns: {
 | 
				
			||||||
            type: Array,
 | 
					            type: Array,
 | 
				
			||||||
            default: () => ['name', "type", 'is_public', 'is_downloadable']
 | 
					            default: () => ['name', "type", 'is_public', 'is_downloadable']
 | 
				
			||||||
@ -89,6 +116,14 @@ export default {
 | 
				
			|||||||
            // if(settings)
 | 
					            // if(settings)
 | 
				
			||||||
            //     this.settingsSaved(settings)
 | 
					            //     this.settingsSaved(settings)
 | 
				
			||||||
         },
 | 
					         },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        uploadDone(event) {
 | 
				
			||||||
 | 
					            const req = event.target
 | 
				
			||||||
 | 
					            if(req.status == 201) {
 | 
				
			||||||
 | 
					                const item = JSON.parse(req.response)
 | 
				
			||||||
 | 
					                this.set.push(item)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					         },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -12,11 +12,12 @@ import ASoundItem from './ASoundItem'
 | 
				
			|||||||
import ASwitch from './ASwitch'
 | 
					import ASwitch from './ASwitch'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import AModal from "./AModal"
 | 
					import AModal from "./AModal"
 | 
				
			||||||
 | 
					import AFileUpload from "./AFileUpload"
 | 
				
			||||||
import ASelectFile from "./ASelectFile"
 | 
					import ASelectFile from "./ASelectFile"
 | 
				
			||||||
import AStatistics from './AStatistics'
 | 
					import AStatistics from './AStatistics'
 | 
				
			||||||
import AStreamer from './AStreamer'
 | 
					import AStreamer from './AStreamer'
 | 
				
			||||||
import ATracklistEditor from './ATracklistEditor'
 | 
					import ATrackListEditor from './ATrackListEditor'
 | 
				
			||||||
import APlaylistEditor from './APlaylistEditor'
 | 
					import ASoundListEditor from './ASoundListEditor'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Core components
 | 
					 * Core components
 | 
				
			||||||
@ -31,10 +32,10 @@ export default base
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export const admin = {
 | 
					export const admin = {
 | 
				
			||||||
    ...base,
 | 
					    ...base,
 | 
				
			||||||
    AStatistics, AStreamer, ATracklistEditor
 | 
					    AStatistics, AStreamer, ATrackListEditor
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const dashboard = {
 | 
					export const dashboard = {
 | 
				
			||||||
    ...base,
 | 
					    ...base,
 | 
				
			||||||
    AActionButton, ASelectFile, AModal, ATracklistEditor, APlaylistEditor
 | 
					    AActionButton, AFileUpload, ASelectFile, AModal, ATrackListEditor, ASoundListEditor
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user