episode-form: add tracks inline formset
This commit is contained in:
parent
2e9ebaded2
commit
71f4d2473e
29
aircox/static/aircox/js/ckeditor-init.js
Normal file
29
aircox/static/aircox/js/ckeditor-init.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
/* global CKEDITOR, django */
|
||||
/* Modified in order to be manually loaded after vue.js */
|
||||
|
||||
function initialiseCKEditor() {
|
||||
var textareas = Array.prototype.slice.call(
|
||||
document.querySelectorAll("textarea[data-type=ckeditortype]"),
|
||||
)
|
||||
for (var i = 0; i < textareas.length; ++i) {
|
||||
var t = textareas[i]
|
||||
if (
|
||||
t.getAttribute("data-processed") == "0" &&
|
||||
t.id.indexOf("__prefix__") == -1
|
||||
) {
|
||||
t.setAttribute("data-processed", "1")
|
||||
var ext = JSON.parse(t.getAttribute("data-external-plugin-resources"))
|
||||
for (var j = 0; j < ext.length; ++j) {
|
||||
CKEDITOR.plugins.addExternal(ext[j][0], ext[j][1], ext[j][2])
|
||||
}
|
||||
CKEDITOR.replace(t.id, JSON.parse(t.getAttribute("data-config")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function initialiseCKEditorInInlinedForms() {
|
||||
if (typeof django === "object" && django.jQuery) {
|
||||
django.jQuery(document).on("formset:added", initialiseCKEditor)
|
||||
}
|
||||
}
|
||||
//})()
|
|
@ -1,12 +1,17 @@
|
|||
{% extends "aircox/basepage_detail.html" %}
|
||||
{% load static i18n humanize honeypot aircox %}
|
||||
|
||||
|
||||
{% block head_extra %}
|
||||
{{ form.media }}
|
||||
<script type="text/javascript" src="{% static "aircox/js/admin.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "aircox/js/ckeditor-init.js" %}"></script>
|
||||
<!-- <script type="text/javascript" src="{% static "ckeditor/ckeditor-init.js" %}"></script> -->
|
||||
<script type="text/javascript" src="{% static "ckeditor/ckeditor/ckeditor.js" %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block init-scripts %}
|
||||
aircox.init(null, {hotReload:false, initPlayer:false, initApp:true})
|
||||
initialiseCKEditor()
|
||||
initialiseCKEditorInInlinedForms()
|
||||
{% endblock %}
|
||||
|
||||
{% block comments %}
|
||||
|
@ -18,9 +23,15 @@
|
|||
<table>
|
||||
{{ form.as_table }}
|
||||
{% render_honeypot_field "website" %}
|
||||
|
||||
</table>
|
||||
<br/>
|
||||
<input type="submit" value="Update" class="button is-success">
|
||||
|
||||
{% include "aircox/playlist_inline.html" %}
|
||||
|
||||
<input type="submit" value="Update" class="button is-success">
|
||||
</form>
|
||||
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
|
70
aircox/templates/aircox/playlist_inline.html
Normal file
70
aircox/templates/aircox/playlist_inline.html
Normal file
|
@ -0,0 +1,70 @@
|
|||
{% comment %}Inline block to edit playlists{% endcomment %}
|
||||
{% load aircox aircox_admin static i18n %}
|
||||
|
||||
<div id="inline-tracks" class="box mb-5">
|
||||
{{ formset.non_form_errors }}
|
||||
<!-- formset.management_form -->
|
||||
|
||||
<a-playlist-editor
|
||||
:labels="{% track_inline_labels %}"
|
||||
:init-data="{% track_inline_data formset=formset %}"
|
||||
settings-url="{% url "api:user-settings" %}"
|
||||
data-prefix="{{ formset.prefix }}-">
|
||||
<template #title>
|
||||
<h5 class="title is-4">{% trans "Playlist" %}</h5>
|
||||
</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 field in fields %}
|
||||
{% if field != 'position' %}
|
||||
<input type="hidden"
|
||||
:name="'{{ formset.prefix }}-' + row + '-{{ field.name }}'"
|
||||
v-model="item.data[attr]"/>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</td>
|
||||
</template>
|
||||
{% for field in fields %}
|
||||
<template v-slot:row-{{ field }}="{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' %}?{{ field }}=${query}&field={{ field }}"
|
||||
:name="'{{ formset.prefix }}-' + cell.row + '-{{ field }}'"
|
||||
v-model="item.data[attr]"
|
||||
title="{{ field }}"
|
||||
@change="emit('change', col)"/>
|
||||
<p v-for="error in item.error(attr)" class="help is-danger">
|
||||
[[ error ]] !
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
{% endfor %}
|
||||
</a-playlist-editor>
|
||||
</div>
|
|
@ -34,3 +34,13 @@ def test_group_change_program(user, client, program):
|
|||
user.groups.add(program.editors)
|
||||
response = client.get(reverse("program-edit", kwargs={"pk": program.pk}))
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
@pytest.mark.django_db()
|
||||
def test_group_change_episode(user, client, program, episode):
|
||||
client.force_login(user)
|
||||
response = client.get(reverse("episode-edit", kwargs={"pk": episode.pk}))
|
||||
assert response.status_code == 403
|
||||
user.groups.add(program.editors)
|
||||
response = client.get(reverse("episode-edit", kwargs={"pk": episode.pk}))
|
||||
assert response.status_code == 200
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
from itertools import chain
|
||||
import json
|
||||
import pytest
|
||||
from django.urls import reverse
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
|
@ -38,3 +40,34 @@ def test_add_cover(user, client, program):
|
|||
assert r.status_code == 200
|
||||
p = Program.objects.get(pk=program.pk)
|
||||
assert "cover1.png" in p.cover.url
|
||||
|
||||
|
||||
@pytest.mark.django_db()
|
||||
def test_edit_tracklist(user, client, program, episode, tracks):
|
||||
user.groups.add(program.editors)
|
||||
client.force_login(user)
|
||||
episode.status = 0x10 # published
|
||||
episode.save()
|
||||
r = client.get(reverse("program-detail", kwargs={"slug": episode.program.slug}))
|
||||
assert r.status_code == 200
|
||||
r = client.get(reverse("episode-detail", kwargs={"slug": episode.slug}))
|
||||
assert r.status_code == 200
|
||||
r2 = client.get(reverse("episode-edit", kwargs={"pk": episode.pk}))
|
||||
assert r2.status_code == 200
|
||||
|
||||
tracklist = [t.id for t in episode.track_set.all().order_by("position")]
|
||||
tracklist_details_reversed = [(t.id, t.artist, t.title) for t in episode.track_set.all().order_by("-position")]
|
||||
tracklist_details_reversed = list(chain(*tracklist_details_reversed))
|
||||
data = """{"website": [""], "content": ["foobar"], "new_podcast": [""], "form-TOTAL_FORMS": ["3"],
|
||||
"form-INITIAL_FORMS": ["3"], "form-MIN_NUM_FORMS": ["0"], "form-MAX_NUM_FORMS": ["1000"], "form-0-position": ["0"],
|
||||
"form-0-id": ["%s"], "form-0-": ["", "", "", "", "", ""], "form-0-artist": ["%s"], "form-0-title": ["%s"],
|
||||
"form-0-tags": [""], "form-0-album": [""], "form-0-year": [""], "form-1-position": ["1"], "form-1-id": ["%s"],
|
||||
"form-1-": ["", "", "", "", "", ""], "form-1-artist": ["%s"], "form-1-title": ["%s"], "form-1-tags": [""],
|
||||
"form-1-album": [""], "form-1-year": [""], "form-2-position": ["2"], "form-2-id": ["%s"], "form-2-": ["", "", "",
|
||||
"", "", ""], "form-2-artist": ["%s"], "form-2-title": ["%s"], "form-2-tags": [""], "form-2-album": [""],
|
||||
"form-2-year": [""]}""" % tuple(
|
||||
tracklist_details_reversed
|
||||
)
|
||||
r = client.post(reverse("episode-edit", kwargs={"pk": episode.pk}), json.loads(data), follow=True)
|
||||
assert r.status_code == 200
|
||||
assert [t.id for t in episode.track_set.all().order_by("position")] == list(reversed(tracklist))
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
from django.contrib.auth.mixins import UserPassesTestMixin
|
||||
from django.forms import ModelForm, FileField
|
||||
from django.forms.models import modelformset_factory
|
||||
from django.urls import reverse
|
||||
|
||||
from ckeditor.fields import RichTextField
|
||||
from filer.models.filemodels import File
|
||||
|
||||
from aircox.controllers.sound_file import SoundFile
|
||||
from aircox.models import Track
|
||||
|
||||
from ..filters import EpisodeFilters
|
||||
from ..models import Episode, Program, StaticPage
|
||||
|
@ -67,11 +69,13 @@ class EpisodeForm(ModelForm):
|
|||
sound_file.sync(
|
||||
program=self.instance.program, episode=self.instance, type=0, is_public=True, is_downloadable=True
|
||||
)
|
||||
super().save(commit=commit)
|
||||
|
||||
|
||||
class EpisodeUpdateView(UserPassesTestMixin, BaseProgramMixin, PageUpdateView):
|
||||
model = Episode
|
||||
form_class = EpisodeForm
|
||||
template_name = "aircox/episode_form.html"
|
||||
|
||||
def get_sidebar_queryset(self):
|
||||
return super().get_sidebar_queryset().filter(parent=self.program)
|
||||
|
@ -82,3 +86,27 @@ class EpisodeUpdateView(UserPassesTestMixin, BaseProgramMixin, PageUpdateView):
|
|||
|
||||
def get_success_url(self):
|
||||
return reverse("episode-detail", kwargs={"slug": self.get_object().slug})
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
obj = Episode.objects.get(pk=self.kwargs["pk"])
|
||||
return obj
|
||||
|
||||
def get_formset(self, *args, **kwargs):
|
||||
fields = ("position", "artist", "title", "tags", "album", "year", "info")
|
||||
TrackFormSet = modelformset_factory(Track, fields=fields, extra=0)
|
||||
return TrackFormSet(*args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context["fields"] = ("position", "artist", "title", "tags", "album", "year", "info")
|
||||
context["formset"] = self.get_formset(queryset=Track.objects.filter(episode=self.object))
|
||||
return context
|
||||
|
||||
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)
|
||||
else:
|
||||
return super().form_valid(formset) # form_invalid(formset)
|
||||
|
|
Loading…
Reference in New Issue
Block a user