#132 | #121: backoffice / dev-1.0-121 #131

Merged
thomas merged 151 commits from dev-1.0-121 into develop-1.0 2024-04-28 20:02:14 +00:00
8 changed files with 183 additions and 5 deletions
Showing only changes of commit 7e0e6e9652 - Show all commits

View File

@ -55,3 +55,4 @@ class EpisodeForm(ModelForm):
sound_file.sync( sound_file.sync(
program=self.instance.program, episode=self.instance, type=0, is_public=True, is_downloadable=True program=self.instance.program, episode=self.instance, type=0, is_public=True, is_downloadable=True
) )
super().save(commit=commit)

View 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)
}
}
//})()

View File

@ -1,12 +1,17 @@
{% extends "aircox/basepage_detail.html" %} {% extends "aircox/basepage_detail.html" %}
{% load static i18n humanize honeypot aircox %} {% load static i18n humanize honeypot aircox %}
{% block head_extra %} {% 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 %} {% endblock %}
{% block init-scripts %} {% block init-scripts %}
aircox.init(null, {hotReload:false, initPlayer:false, initApp:true})
initialiseCKEditor()
initialiseCKEditorInInlinedForms()
{% endblock %} {% endblock %}
{% block comments %} {% block comments %}
@ -21,6 +26,11 @@
</table> </table>
<br/> <br/>
<input type="submit" value="Update" class="button is-success"> <input type="submit" value="Update" class="button is-success">
{% include "aircox/playlist_inline.html" %}
<input type="submit" value="Update" class="button is-success">
</form> </form>
</section> </section>
{% endblock %} {% endblock %}

View 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>

View File

@ -34,3 +34,13 @@ def test_group_change_program(user, client, program):
user.groups.add(program.editors) user.groups.add(program.editors)
response = client.get(reverse("program-edit", kwargs={"pk": program.pk})) response = client.get(reverse("program-edit", kwargs={"pk": program.pk}))
assert response.status_code == 200 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

View File

@ -1,3 +1,5 @@
from itertools import chain
import json
import pytest import pytest
from django.urls import reverse from django.urls import reverse
from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.uploadedfile import SimpleUploadedFile
@ -32,3 +34,34 @@ def test_add_cover(user, client, program, png_content):
assert r.status_code == 200 assert r.status_code == 200
p = Program.objects.get(pk=program.pk) p = Program.objects.get(pk=program.pk)
assert "cover1.png" in p.cover.url 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": ["{}"], "form-0-": ["", "", "", "", "", ""], "form-0-artist": ["{}"], "form-0-title": ["{}"],
"form-0-tags": [""], "form-0-album": [""], "form-0-year": [""], "form-1-position": ["1"], "form-1-id": ["{}"],
"form-1-": ["", "", "", "", "", ""], "form-1-artist": ["{}"], "form-1-title": ["{}"], "form-1-tags": [""],
"form-1-album": [""], "form-1-year": [""], "form-2-position": ["2"], "form-2-id": ["{}"], "form-2-": ["", "", "",
"", "", ""], "form-2-artist": ["{}"], "form-2-title": ["{}"], "form-2-tags": [""], "form-2-album": [""],
"form-2-year": [""]}}""".format(
*tracklist_details_reversed
)
r = client.post(reverse("episode-edit", kwargs={"pk": episode.pk}), json.loads(data), follow=True)
assert r.status_code == 200
assert set(episode.track_set.all().values_list("id", flat=True)) == set(tracklist)

View File

@ -1,10 +1,10 @@
from django.contrib.auth.mixins import UserPassesTestMixin from django.contrib.auth.mixins import UserPassesTestMixin
from django.forms.models import modelformset_factory
from django.urls import reverse from django.urls import reverse
from aircox.forms import EpisodeForm from aircox.forms import EpisodeForm
from aircox.models import Episode, Program, StaticPage, Track
from ..filters import EpisodeFilters from ..filters import EpisodeFilters
from ..models import Episode, Program, StaticPage
from .page import PageListView from .page import PageListView
from .program import ProgramPageDetailView, BaseProgramMixin from .program import ProgramPageDetailView, BaseProgramMixin
from .page import PageUpdateView from .page import PageUpdateView
@ -50,6 +50,7 @@ class PodcastListView(EpisodeListView):
class EpisodeUpdateView(UserPassesTestMixin, BaseProgramMixin, PageUpdateView): class EpisodeUpdateView(UserPassesTestMixin, BaseProgramMixin, PageUpdateView):
model = Episode model = Episode
form_class = EpisodeForm form_class = EpisodeForm
template_name = "aircox/episode_form.html"
def get_sidebar_queryset(self): def get_sidebar_queryset(self):
return super().get_sidebar_queryset().filter(parent=self.program) return super().get_sidebar_queryset().filter(parent=self.program)
@ -60,3 +61,27 @@ class EpisodeUpdateView(UserPassesTestMixin, BaseProgramMixin, PageUpdateView):
def get_success_url(self): def get_success_url(self):
return reverse("episode-detail", kwargs={"slug": self.get_object().slug}) 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)

View File

@ -63,7 +63,7 @@ class ParentMixin:
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
parent = kwargs.setdefault("parent", self.parent) parent = kwargs.setdefault("parent", self.parent)
if parent is not None: if parent is not None and parent.cover:
kwargs.setdefault("cover", parent.cover.url) kwargs.setdefault("cover", parent.cover.url)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)