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

cfr #121

Co-authored-by: Christophe Siraut <d@tobald.eu.org>
Co-authored-by: bkfox <thomas bkfox net>
Co-authored-by: Thomas Kairos <thomas@bkfox.net>
Reviewed-on: rc/aircox#131
Co-authored-by: Chris Tactic <ctactic@noreply.git.radiocampus.be>
Co-committed-by: Chris Tactic <ctactic@noreply.git.radiocampus.be>
This commit is contained in:
2024-04-28 22:02:09 +02:00
committed by Thomas Kairos
parent 1e17a1334a
commit 55123c386d
348 changed files with 124397 additions and 17879 deletions

View File

@ -1,44 +0,0 @@
{% extends "admin/change_form.html" %}
{% comment %}Admin edit template to edit pages.{% endcomment %}
{% load admin_urls i18n static %}
{% if not is_popup %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% translate 'Home' %}</a>
&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
&rsaquo; {% if has_view_permission %}<a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>{% else %}{{ opts.verbose_name_plural|capfirst }}{% endif %}
{% if parent %}
&rsaquo; <a href="{% url opts|admin_urlname:"changelist" %}?parent={{parent.id}}">{{ parent.title }}</a>
{% endif %}
&rsaquo; {% if add %}{% blocktranslate with name=opts.verbose_name %}Add {{ name }}{% endblocktranslate %}{% else %}{{ original|truncatewords:"18" }}{% endif %}
</div>
{% endblock %}
{% endif %}
{% block submit_buttons_bottom %}
{% if has_change_permission %}
<div class="columns is-size-5">
<div class="column has-text-left">
{% if original and not original.is_trash %}
<button type="submit" name="status" value="32" class="button is-danger is-size-6">{% translate "Move to trash" %}</button>
{% endif %}
{% if original and not original.is_draft %}
<button type="submit" name="status" value="0" class="button is-warning is-size-6">{% translate "Mark as draft" %}</button>
{% endif %}
</div>
<div class="column has-text-right">
<button type="submit" class="button is-secondary is-size-6">{% translate "Save" %}</button>
<button type="submit" name="_continue" class="button is-secondary is-size-6">{% translate "Save and continue" %}</button>
{% if not original.is_published %}
<button type="submit" name="status" value="16" class="button is-primary is-size-6">{% translate "Publish" %}</button>
{% endif %}
</div>
{% endif %}
</div>
{% endblock %}

View File

@ -1,19 +0,0 @@
{% extends "admin/change_list.html" %}
{% load admin_urls i18n %}
{% if not is_popup %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% translate 'Home' %}</a>
&rsaquo; <a href="{% url 'admin:app_list' app_label=cl.opts.app_label %}">{{ cl.opts.app_config.verbose_name }}</a>
{% if parent %}
&rsaquo; <a href="{% url cl.opts|admin_urlname:'changelist' %}">{{ cl.opts.verbose_name_plural|capfirst }}</a>
<b>&rsaquo; {{ parent.title }}</b>
{% else %}
&rsaquo; {{ cl.opts.verbose_name_plural|capfirst }}
{% endif %}
</div>
{% endblock %}
{% endif %}

View File

@ -1,85 +0,0 @@
{% comment %}Inline block to edit playlists{% endcomment %}
{% load aircox aircox_admin static i18n %}
{% with inline_admin_formset as admin_formset %}
{% with admin_formset.formset as formset %}
<div id="inline-tracks" class="box mb-5">
{{ admin_formset.non_form_errors }}
<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 v-slot: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 admin_formset.fields %}
{% if field.name != 'position' and field.widget.is_hidden %}
<input type="hidden"
:name="'{{ formset.prefix }}-' + row + '-{{ field.name }}'"
v-model="item.data[attr]"/>
{% endif %}
{% endfor %}
</td>
</template>
{% for field in admin_formset.fields %}
{% if not field.widget.is_hidden and not field.is_readonly %}
<template v-slot:row-{{ field.name }}="{item,cell,value,attr,emit}">
<div class="field">
{% if field.name in 'artist,title,album' %}
<a-autocomplete
:input-class="['input', item.error(attr) ? 'is-danger' : 'half-field']"
url="{% url 'api:track-autocomplete' %}?{{ field.name }}=${query}&field={{ field.name }}"
{% else %}
<div class="control">
<input type="{{ widget.type }}"
:class="['input', item.error(attr) ? 'is-danger' : 'half-field']"
{% endif %}
:name="'{{ formset.prefix }}-' + cell.row + '-{{ field.name }}'"
v-model="item.data[attr]"
title="{{ field.help }}"
@change="emit('change', col)"/>
{% if field.name not in 'artist,title,album' %}
</div>
{% endif %}
<p v-for="error in item.error(attr)" class="help is-danger">
[[ error ]] !
</p>
</div>
</template>
{% endif %}
{% endfor %}
</a-playlist-editor>
</div>
{% endwith %}
{% endwith %}

View File

@ -1,82 +0,0 @@
{% extends "admin/base_site.html" %}
{% comment %}Admin tool displaying logs statistics{% endcomment %}
{% load i18n aircox %}
{% block content %}{{ block.super }}
<div id="app">
{# TODO: date subtitle #}
<nav class="navbar" role="menu">
{% with "admin:tools-stats" as url_name %}
{% include "aircox/widgets/dates_menu.html" %}
{% endwith %}
</nav>
<a-statistics class="column">
<template v-slot="{counts}">
<table class="table is-hoverable is-fullwidth">
<thead>
<tr>
<th>{% translate "Time" %}</th>
<th>{% translate "Episode" %}</th>
<th>{% translate "Track" %}</th>
<th>{% translate "Tags" %}</th>
</tr>
</thead>
<tbody>
{% for object in object_list %}
{% with object|get_tracks as tracks %}
{% for track in tracks %}
<tr>
{% if forloop.first %}
<td rowspan="{{ tracks|length }}">{{ object.start|time:"H:i" }} {% if object|is_diffusion %} - {{ object.end|time:"H:i" }}{% endif %}</td>
<td rowspan="{{ tracks|length }}">{{ object.episode|default:"" }}</td>
{% endif %}
{% with track as object %}
<td>{% include "aircox/widgets/track_item.html" %}</td>
{% endwith %}
{% with track.tags.all|join:', ' as tags %}
<td>
{% if tags %}
<label class="checkbox">
<input type="checkbox" checked value="{{ tags|escape }}" name="data">
{{ tags }}
</label>
{% endif %}
</td>
{% endwith %}
</tr>
{% empty %}
{% if object|is_diffusion %}
<tr>
<td>{{ object.start|time:"H:i" }} - {{ object.end|time:"H:i" }}</td>
<td>{{ object.episode|default:"" }}</td>
<td></td>
<td></td>
</tr>
{% endif %}
{% endfor %}
{% endwith %}
{% endfor %}
</tbody>
<tfoot>
<tr>
<td class="is-size-6">{% translate "Total" %}</td>
<td colspan="100">
<div class="columns is-size-6">
<span v-for="(count, tag) in counts" class="column">
<b>[[ tag ]]</b>
[[ count ]]
</span>
</div>
</td>
</tr>
</tfoot>
</table>
</template>
</a-statistics>
</div>
{% endblock %}

View File

@ -1,224 +0,0 @@
{% load i18n static aircox_admin %}<!DOCTYPE html>
{% get_current_language as LANGUAGE_CODE %}{% get_current_language_bidi as LANGUAGE_BIDI %}
<html lang="{{ LANGUAGE_CODE|default:"en-us" }}" {% if LANGUAGE_BIDI %}dir="rtl"{% endif %}>
<head>
<title>{% block title %}{% endblock %}</title>
<!-- <link rel="stylesheet" type="text/css" href="{% static "aircox/vendor.css" %}"> -->
<link rel="stylesheet" type="text/css" href="{% static "admin/css/base.css" %}">
<link rel="stylesheet" type="text/css" href="{% static "aircox/css/chunk-common.css" %}"/>
<link rel="stylesheet" type="text/css" href="{% static "aircox/css/chunk-vendors.css" %}"/>
<link rel="stylesheet" type="text/css" href="{% static "aircox/css/admin.css" %}"/>
<script src="{% static "aircox/js/chunk-common.js" %}"></script>
<script src="{% static "aircox/js/chunk-vendors.js" %}"></script>
<script src="{% static "aircox/js/admin.js" %}"></script>
{% block extrastyle %}{% endblock %}
{% if LANGUAGE_BIDI %}<link rel="stylesheet" type="text/css" href="{% block stylesheet_rtl %}{% static "admin/css/rtl.css" %}{% endblock %}">{% endif %}
{% block extrahead %}{% endblock %}
{% block responsive %}
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0">
<link rel="stylesheet" type="text/css" href="{% static "admin/css/responsive.css" %}">
{% if LANGUAGE_BIDI %}<link rel="stylesheet" type="text/css" href="{% static "admin/css/responsive_rtl.css" %}">{% endif %}
{% endblock %}
{% block blockbots %}<meta name="robots" content="NONE,NOARCHIVE">{% endblock %}
</head>
{% load i18n %}
<body class="{% if is_popup %}popup {% endif %}{% block bodyclass %}{% endblock %}"
data-admin-utc-offset="{% now "Z" %}">
<script id="init-script">
function vuePre(selector) {
const elms = document.querySelectorAll(selector)
for(const elm of elms) {
elm.setAttribute('v-pre', true)
}
}
window.addEventListener('load', function() {
{% block init-scripts %}
aircox.init(null, {
hotReload: false,
{% if not init_app %}
initBuilder: false,
{% endif %}
{% if init_el %}
el: "{{ init_el }}",
{% endif %}
})
{% endblock %}
})
</script>
<!-- Container -->
<div class="admin">
{% if not is_popup %}
<!-- Header -->
<nav class="navbar is-dark has-shadow">
<div class="navbar-brand">
{% block branding %}{% endblock %}
</div>
<div class="navbar-menu">
{% block usertools %}
<div class="navbar-start">
{# Today's diffusions #}
<div class="navbar-item has-dropdown is-hoverable">
<span class="icon-text navbar-link">
<span class="icon"><i class="fa-regular fa-calendar-days"></i></span>
<span>{% translate "Today" %}</span>
</span>
<div class="navbar-dropdown is-boxed">
{% for diffusion in diffusions %}
<a class="navbar-item {% if diffusion.is_now %}has-background-primary{% endif %}" href="{% url "admin:aircox_episode_change" diffusion.episode.pk %}">
{{ diffusion.start|time }} |
{{ diffusion.episode.title }}
</a>
{% endfor %}
</div>
</div>
{# Articles #}
<div class="navbar-item has-dropdown is-hoverable">
<a class="icon-text navbar-link" href="{% url "admin:aircox_article_changelist" %}">
<span class="icon"><i class="fa fa-newspaper"></i></span>
<span>{% translate "Articles" %}</span>
</a>
<div class="navbar-dropdown is-boxed">
<input type="text" onkeyup="aircox.filter_menu(event)"
placeholder="{% translate "Search" %}" class="navbar-item input" />
<hr class="navbar-divider"/>
{% for program in programs %}
<a class="navbar-item" href="{% url "admin:aircox_article_changelist" %}?parent={{ program.pk }}">
{{ program.title }}</a>
{% endfor %}
</div>
</div>
{# Programs #}
<div class="navbar-item has-dropdown is-hoverable">
<a class="icon-text navbar-link" href="{% url "admin:aircox_program_changelist" %}">
<span class="icon"><i class="fa fa-folder"></i></span>
<span>{% translate "Programs" %}</span>
</a>
<div class="navbar-dropdown is-boxed">
<input type="text" onkeyup="aircox.filter_menu(event)"
placeholder="{% translate "Search" %}" class="navbar-item input" />
<hr class="navbar-divider"/>
{% for program in programs %}
<a class="navbar-item" href="{% url "admin:aircox_program_change" program.pk %}">
{{ program.title }}</a>
{% endfor %}
</div>
</div>
{# Episodes #}
<div class="navbar-item has-dropdown is-hoverable">
<a class="icon-text navbar-link" href="{% url "admin:aircox_episode_changelist" %}">
<span class="icon"><i class="fa fa-calendar-check"></i></span>
<span>{% translate "Episodes" %}</span>
</a>
<div class="navbar-dropdown is-boxed">
<input type="text" onkeyup="aircox.filter_menu(event)"
placeholder="{% translate "Search" %}" class="navbar-item input" />
<hr class="navbar-divider"/>
{% for program in programs %}
<a class="navbar-item" href="{% url "admin:aircox_episode_changelist" %}?parent={{ program.pk }}">
{{ program.title }}</a>
{% endfor %}
</div>
</div>
</div>
<div class="navbar-end">
<div class="navbar-item has-dropdown is-hoverable">
<a href="#" class="icon-text navbar-link">
<span class="icon"><i class="fa-solid fa-screwdriver-wrench"></i></span>
<span>{% translate "Tools" %}</span>
</a>
<div class="navbar-dropdown is-boxed is-right">
{% get_admin_tools as admin_tools %}
{% for label, url in admin_tools %}
<a href="{{ url }}" class="navbar-item">{{ label }}</a>
{% endfor %}
</div>
</div>
<div class="navbar-item has-dropdown is-hoverable">
<a href="{% url "admin:auth_user_change" user.pk %}" class="icon-text navbar-link">
<span class="icon"><i class="fa fa-user"></i></span>
<span>{% firstof user.get_short_name user.get_username %}</span>
</a>
<div class="navbar-dropdown is-boxed is-right">
{% block userlinks %}
{% if site_url %}
<a href="{{ site_url }}" class="navbar-item">{% translate 'View site' %}</a>
{% endif %}
{% if user.is_active and user.is_staff %}
{% url 'django-admindocs-docroot' as docsroot %}
{% if docsroot %}
<a href="{{ docsroot }}" class="navbar-item">{% translate 'Documentation' %}</a>
{% endif %}
{% endif %}
{% if user.has_usable_password %}
<a href="{% url 'admin:password_change' %}" class="navbar-item">{% translate 'Change password' %}</a>
{% endif %}
<hr class="navbar-divider" />
<a href="{% url 'admin:logout' %}" class="navbar-item">{% translate 'Log out' %}</a>
{% endblock %}
</div>
</div>
</div>
{% endblock %}
</div>
{% block nav-global %}{% endblock %}
</nav>
<!-- END Header -->
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% translate 'Home' %}</a>
{% if title %} &rsaquo; {{ title }}{% endif %}
</div>
{% endblock %}
{% endif %}
{% block messages %}
{% if messages %}
<ul class="messagelist">{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message|capfirst }}</li>
{% endfor %}</ul>
{% endif %}
{% endblock messages %}
<!-- Content -->
<div id="app">
{% block app %}
<div id="content" class="{% block coltype %}colM{% endblock %}">
{% block pretitle %}{% endblock %}
{% block content_title %}{% if title %}<h1 class="title is-3">{{ title }}</h1>{% endif %}{% endblock %}
{% block content %}
{% block object-tools %}{% endblock %}
{{ content }}
{% endblock %}
{% block sidebar %}{% endblock %}
<br class="clear">
</div>
{% endblock %}
</div>
<!-- END Content -->
{% block footer %}<div id="footer"></div>{% endblock %}
</div>
<!-- END Container -->
{% block player %}
{% if request.station %}
<div id="player">{% include "aircox/widgets/player.html" %}</div>
{% endif %}
{% endblock %}
</body>
</html>

View File

@ -1,8 +0,0 @@
{% extends "admin/base_site.html" %}
{% load static %}
{% block branding %}
<a href="{% url 'admin:index' %}">
<img src="{% static "aircox/logo.png" %}"/>
</a>
{% endblock %}

View File

@ -1,5 +0,0 @@
{% extends "admin/change_form.html" %}
{% block content %}
<div v-pre>{{ block.super }}</div>
{% endblock %}

View File

@ -1,89 +0,0 @@
{% extends "admin/base_site.html" %}
{% load i18n admin_urls static admin_list %}
{% block extrastyle %}
{{ block.super }}
{% if cl.formset %}
<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}">
{% endif %}
{% if cl.formset or action_form %}
<script type="text/javascript" src="{% url 'admin:jsi18n' %}"></script>
{% endif %}
{{ media.css }}
{% if not actions_on_top and not actions_on_bottom %}
<style>
#changelist table thead th:first-child {width: inherit}
</style>
{% endif %}
{% endblock %}
{% block extrahead %}
{{ block.super }}
{{ media.js }}
{% endblock %}
{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} change-list{% endblock %}
{% if not is_popup %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% translate 'Home' %}</a>
&rsaquo; <a href="{% url 'admin:app_list' app_label=cl.opts.app_label %}">{{ cl.opts.app_config.verbose_name }}</a>
&rsaquo; {{ cl.opts.verbose_name_plural|capfirst }}
</div>
{% endblock %}
{% endif %}
{% block coltype %}flex{% endblock %}
{% block content %}
<div id="content-main">
{% block object-tools %}
<ul class="object-tools">
{% block object-tools-items %}
{% change_list_object_tools %}
{% endblock %}
</ul>
{% endblock %}
{% if cl.formset and cl.formset.errors %}
<p class="errornote">
{% if cl.formset.total_error_count == 1 %}{% translate "Please correct the error below." %}{% else %}{% trans "Please correct the errors below." %}{% endif %}
</p>
{{ cl.formset.non_form_errors }}
{% endif %}
<div class="columns is-fullwidth module {% if cl.has_filters %} filtered{% endif %}" id="changelist">
<div class="column">
<form id="changelist-form" method="post"{% if cl.formset and cl.formset.is_multipart %} enctype="multipart/form-data"{% endif %} novalidate>{% csrf_token %}
{% if cl.formset %}
<div>{{ cl.formset.management_form }}</div>
{% endif %}
{% block result_list %}
{% if action_form and actions_on_top and cl.show_admin_actions %}{% admin_actions %}{% endif %}
{% result_list cl %}
{% if action_form and actions_on_bottom and cl.show_admin_actions %}{% admin_actions %}{% endif %}
{% endblock %}
{% block pagination %}{% pagination cl %}{% endblock %}
</form>
</div>
<div class="column is-one-quarter">
{% block search %}{% search_form cl %}{% endblock %}
<hr>
{% block date_hierarchy %}{% if cl.date_hierarchy %}{% date_hierarchy cl %}{% endif %}{% endblock %}
{% block filters %}
{% if cl.has_filters %}
<div id="changelist-filter">
<h2>{% translate 'Filter' %}</h2>
{% for spec in cl.filter_specs %}{% admin_list_filter cl spec %}{% endfor %}
</div>
{% endif %}
{% endblock %}
</div>
</div>
</div>
{% endblock %}

View File

@ -1,94 +0,0 @@
{% extends "admin/index.html" %}
{% load i18n thumbnail %}
{% block app %}
<div class="section">
<div class="columns">
<div class="column">
<div class="box">
<h1 class="title icon-text is-4">
<span class="icon"><i class="fa-regular fa-calendar-days"></i></span>
<span>{% translate "Today" %}</span>
</h1>
{% if diffusions %}
<table class="table is-fullwidth is-striped">
<tbody>
{% for diffusion in diffusions %}
{% with episode=diffusion.episode %}
<tr {% if diffusion.is_now %}class="is-selected"{% endif %}>
<td>{{ diffusion.start|time }} - {{ diffusion.end|time }}</td>
<td><img src="{% thumbnail episode.cover 64x64 crop %}"/></td>
<td>
<a href="{% url "admin:aircox_episode_change" episode.pk %}">{{ episode.title }}</a>
&nbsp;
{% if diffusion.type == diffusion.TYPE_ON_AIR %}
<span class="tag is-info">
<span class="icon is-small">
{% if diffusion.is_live %}
<i class="fa fa-microphone"
title="{% translate "Live diffusion" %}"></i>
{% else %}
<i class="fa fa-music"
title="{% translate "Differed diffusion" %}"></i>
{% endif %}
</span>
&nbsp;
{{ diffusion.get_type_display }}
</span>
{% elif diffusion.type == diffusion.TYPE_CANCEL %}
<span class="tag is-danger">
{{ diffusion.get_type_display }}</span>
{% elif diffusion.type == diffusion.TYPE_UNCONFIRMED %}
<span class="tag is-warning">
{{ diffusion.get_type_display }}</span>
{% endif %}
</td>
</tr>
{% endwith %}
{% endfor %}
</tbody>
</table>
{% else %}
<div class="block has-text-centered">
{% trans "No diffusion is scheduled for today." %}
</div>
{% endif %}
</div>
<div class="box">
<h1 class="title is-4 icon-text">
<span class="icon"><i class="fa-regular fa-comments"></i></span>
<span>{% translate "Latest comments" %}</span>
</h1>
{% if comments %}
{% include "aircox/widgets/page_list.html" with object_list=comments with_title=True %}
<div class="has-text-centered">
<a href="{% url "admin:aircox_comment_changelist" %}" class="float-center">{% translate "All comments" %}</a>
</div>
{% else %}
<p class="block has-text-centered">{% trans "No comment posted yet" %}</p>
{% endif %}
</div>
</div>
<div class="column">
<div class="box">
<h1 class="title is-4 icon-text">
<span class="icon"><i class="fa-regular fa-newspaper"></i></span>
<span>{% translate "Latest publications" %}</span>
</h1>
{% if latests %}
{% include "aircox/widgets/page_list.html" with object_list=latests no_actions=True %}
{% endif %}
</div>
<div class="box">
<h1 class="title is-4 icon-text">
<span class="icon"><i class="fa fa-screwdriver-wrench"></i></span>
<span>{% translate "Administration" %}</span>
</h1>
{% include "admin/app_list.html" with app_list=app_list show_changelinks=True %}
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,30 +1,2 @@
{% extends "aircox/page_detail.html" %}
{% comment %}Detail page for regular articles{% endcomment %}
{% load i18n %}
{% block sidebar %}
{{ block.super }}
{% if sidebar_object_list %}
<section>
{% comment %}Translators: in page detail sidebar{% endcomment %}
<h4 class="title is-4">{% translate "Latest news" %}</h4>
{% for object in sidebar_object_list %}
{% include "aircox/widgets/page_item.html" %}
{% endfor %}
<br>
<nav class="pagination is-centered">
<ul class="pagination-list">
<li>
<a href="{% url "article-list" %}" class="pagination-link"
aria-label="{% translate "Show all news" %}">
{% translate "More news" %}
</a>
</li>
</ul>
</nav>
</section>
{% endif %}
{% endblock %}

View File

@ -1,3 +1,4 @@
{% load static i18n thumbnail aircox %}<!doctype html>
{% comment %}
Base website template. It displays various elements depending on context
variables.
@ -6,14 +7,10 @@ Usefull context:
- cover: image cover
- site: current website
- model: view model or displayed `object`'s
- sidebar_object_list: item to display in sidebar
- sidebar_url_name: url name sidebar item complete list
- sidebar_url_parent: parent page for sidebar items complete list
{% endcomment %}
{% load static i18n thumbnail aircox %}
<html>
<head>
{% block head %}
<meta charset="utf-8" />
<meta name="application-name" content="aircox" />
<meta name="description" content="{{ site.description }}" />
@ -22,24 +19,36 @@ Usefull context:
<link rel="icon" href="{% thumbnail site.favicon 32x32 crop %}" />
{% block assets %}
<link rel="stylesheet" type="text/css" href="{% static "aircox/css/chunk-common.css" %}"/>
<link rel="stylesheet" type="text/css" href="{% static "aircox/css/chunk-vendors.css" %}"/>
<script src="{% static "aircox/js/chunk-common.js" %}"></script>
<script src="{% static "aircox/js/chunk-vendors.js" %}"></script>
<script src="{% static "aircox/js/core.js" %}"></script>
{% static "vue/vue.esm-browser.js" as vue_url %}
<script type="importmap">
{
"imports": {
"vue": "{{vue_url}}"
{% block assets-import-map %}{% endblock %}
}
}
</script>
<script type="module" src="{{vue_url}}"></script>
<link rel="stylesheet" type="text/css" href="{% static "fontawesome-free/css/all.min.css" %}"/>
<link rel="stylesheet" type="text/css" href="{% static "aircox/index.css" %}"/>
<link rel="stylesheet" type="text/css" href="{% static "aircox/public.css" %}"/>
<script type="module" src="{% if app_js_url %}{{ app_js_url }}{% else %}{% static "aircox/public.js" %}{% endif %}"></script>
{% endblock %}
<title>
{% block head_title %}
{% if page and page.title %}{{ page.title }} &mdash; {{ station.name }}
{% else %}{{ station.name }}
{% block head-title %}
{% if page and page.title %}{{ page.title }} &mdash;
{% endif %}
{{ station.name }}
{% endblock %}
</title>
{% block head_extra %}{% endblock %}
{% endblock %}
</head>
<body>
<body {% if request.is_mobile %}class="mobile"{% endif %}>
{% block body %}
<script id="init-script">
window.addEventListener('load', function() {
{% block init-scripts %}
@ -48,123 +57,119 @@ Usefull context:
})
</script>
<div id="app">
{% block top-nav-container %}
<nav class="navbar has-shadow" role="navigation" aria-label="main navigation">
<div class="container">
<div class="navbar-brand">
<a href="/" title="{% translate "Home" %}" class="navbar-item">
<img src="{{ station.logo.url }}" class="logo"/>
</a>
</div>
<div class="navbar-menu">
<div class="navbar-start">
{% block top-nav %}
{% nav_items "top" css_class="navbar-item" active_class="is-active" as items %}
{% for item, render in items %}
{{ render }}
{% endfor %}
{% endblock %}
</div>
<div class="navbar-end">
{% block top-nav-tools %}
{% endblock %}
{% block top-nav-end %}
<div class="navbar-item">
<form action="{% url 'page-list' %}" method="GET">
<div class="control has-icons-left">
<span class="icon is-small is-left">
<i class="fa fa-search"></i>
</span>
<input type="text" name="q" class="input"
placeholder="{% translate "Search" %}" />
</div>
</form>
</div>
{% endblock %}
</div>
</div>
</div>
</nav>
{% endblock %}
<div class="container">
<div class="columns is-desktop">
<main class="column page">
<header class="header">
{% block header %}
<h1 class="title is-1">
{% block title %}
{% if page and page.title %}
{{ page.title }}
{% endif %}
{% endblock %}
</h1>
<h3 class="subtitle is-3">
{% block subtitle %}{% endblock %}
</h3>
<div class="columns is-size-4">
{% block header_nav %}
<span class="column">
{% block header_crumbs %}
{% if parent %}
<a href="{{ parent.get_absolute_url }}">
{{ parent.title }}</a></li>
{% endif %}
{% endblock %}
</span>
{% endblock %}
</div>
{% endblock %}
</header>
{% block main %}
{% block content %}
{% if page and page.content %}
<section class="page-content mb-2">{{ page.content|safe }}</section>
{% endif %}
{% endblock %}
{% endblock main %}
</main>
{% if has_sidebar %}
{% comment %}Translators: main sidebar {% endcomment %}
<aside class="column is-one-third-desktop">
{# FIXME: block cover into sidebar one #}
{% block cover %}
{% if page and page.cover %}
<img class="cover mb-4" src="{{ page.cover.url }}" class="cover"/>
{% endif %}
{% block app %}
<div class="navs">
{% block nav %}
<nav class="nav primary" role="navigation" aria-label="main navigation">
{% block primary-nav %}
<a class="nav-brand" href="{% url "home" %}">
<img src="{{ station.logo.url }}">
</a>
<a-switch class="button burger"
el=".nav.primary .nav-menu" group="nav"
aria-label="{% translate "Main menu" %}">
</a-switch>
<div class="nav-menu">
{% block primary-nav-menu %}
{% nav_items "top" css_class="nav-item" active_class="active" as items %}
{% for item, render in items %}
{{ render }}
{% endfor %}
{% endblock %}
{% with is_thin=True %}
{% block sidebar %}
{% if sidebar_object_list %}
{% with object_list=sidebar_object_list %}
{% with list_url=sidebar_list_url %}
{% with has_headline=False %}
<section>
<h4 class="title is-4">
{% block sidebar_title %}{% translate "Recently" %}{% endblock %}
</h4>
{% include "aircox/widgets/page_list.html" %}
{% endwith %}
{% endwith %}
{% endwith %}
{% if user.is_authenticated %}
{% include "./widgets/nav.html" %}
{% endif %}
</section>
{% endblock %}
{% endwith %}
</aside>
{% endif %}
</div>
</div>
{% endblock %}
</nav>
{% block secondary-nav %}{% endblock %}
{% endblock %}
</div>
<hr>
{% block main-container %}
<main class="page">
{% block main %}
{% spaceless %}
{% block breadcrumbs-container %}
<div class="breadcrumbs container">
{% block breadcrumbs %}{% endblock %}
</div>
{% endblock %}
{% endspaceless %}
{% block header-container %}
<header class="container header preview preview-header {% if cover %}has-cover{% endif %}">
{% block header %}
{% spaceless %}
<figure class="header-cover">
{% block header-cover %}
{% if cover %}
<img src="{{ cover }}" ref="cover" class="cover">
{% endif %}
{% endblock %}
</figure>
{% endspaceless %}
<div class="headings preview-card-headings">
{% block headings %}
<div>
{% block title-container %}
<h1 class="title is-1 {% block title-class %}{% endblock %}">{% block title %}{{ title|default:"" }}{% endblock %}</h1>
{% endblock %}
</div>
<div>
{% spaceless %}
<span class="subtitle is-2">
{% block subtitle %}
{% if subtitle %}
{{ subtitle }}
{% endif %}
{% endblock %}
</span>
{% endspaceless %}
</div>
{% endblock %}
</div>
{% endblock %}
</header>
{% endblock %}
{% block content-container %}
{% if page and page.content %}
<section class="container content page-content">
{% block content %}
{{ page.display_content|safe }}
{% endblock %}
</section>
{% endif %}
{% endblock %}
{% endblock %}
</main>
{% endblock %}
{% block footer-container %}
<footer class="page-footer">
{% block footer %}
{% comment %}
{% nav_items "footer" css_class="nav-item" active_class="active" as items %}
{% for item, render in items %}
{{ render }}
{% endfor %}
{% endcomment %}
{% endblock %}
{% if request.station and request.station.legal_label %}
{{ request.station.legal_label }} &mdash;
{% endif %}
</footer>
{% endblock %}
</div>
{% block player-container %}
<div id="player">{% include "aircox/widgets/player.html" %}</div>
{% endblock %}
{% endblock %}
{% endblock %}
</body>
</html>

View File

@ -1,8 +0,0 @@
{% extends "aircox/base.html" %}
{% comment %}Display detail of a BasePage{% endcomment %}
{% block head_title %}
{% block title %}{{ page.title }}{% endblock %}
&mdash;
{{ station.name }}
{% endblock %}

View File

@ -1,86 +1,31 @@
{% extends "aircox/base.html" %}
{% extends "./public.html" %}
{% comment %}Display a list of BasePages{% endcomment %}
{% load i18n aircox %}
{% block head_title %}
{% block title %}
{% if not page or not page.title %}
{% if not parent %}{{ view.model|verbose_name:True|title }}
{% else %}
{% with parent.title as title %}
{% with model|default:"Publications"|verbose_name:True|capfirst as model %}
{% comment %}Translators: title when pages are filtered for a specific parent page, e.g.: Articles of My Incredible Show{% endcomment %}
{% blocktranslate %}{{ model }} of {{ title }}{% endblocktranslate %}
{% endwith %}
{% endwith %}
{% endif %}
{% else %}{{ block.super }}
{% endif %}
{% endblock %}
&mdash;
{{ station.name }}
{% endblock %}
{% block main %}
{{ block.super }}
{% block main %}{{ block.super }}
{% block before_list %}{% endblock %}
<section role="list">
{% block pages_list %}
{% block list-container %}
<section class="container clear-both list grid {{ list_class|default:"" }}" role="list">
{% block list %}
{% with has_headline=True %}
{% for object in object_list %}
{% block list_object %}
{% include object.item_template_name|default:item_template_name %}
{% endblock %}
{% empty %}
{% blocktranslate %}There is nothing published here...{% endblocktranslate %}
{% endfor %}
{% for object in object_list %}
{% block list_object %}
{% page_widget item_widget|default:"item" object %}
{% endblock %}
{% empty %}
{% blocktranslate %}There is nothing published here...{% endblocktranslate %}
{% endfor %}
{% endwith %}
{% endblock %}
</section>
{% if is_paginated %}
<hr/>
{% update_query request.GET.copy page=None as GET %}
{% with GET.urlencode as GET %}
<nav class="pagination is-centered" role="pagination" aria-label="{% translate "pagination" %}">
{% block pagination %}
{% if page_obj.has_previous %}
<a href="?{{ GET }}&page={{ page_obj.previous_page_number }}" class="pagination-previous">
{% else %}
<a class="pagination-previous" disabled>
{% endif %}
{% comment %}Translators: Bottom of the list, "previous page"{% endcomment %}
{% translate "Previous" %}</a>
{% if page_obj.has_next %}
<a href="?{{ GET }}&page={{ page_obj.next_page_number }}" class="pagination-next">
{% else %}
<a class="pagination-next" disabled>
{% endif %}
{% comment %}Translators: Bottom of the list, "Nextpage"{% endcomment %}
{% translate "Next" %}</a>
<ul class="pagination-list">
{% for i in paginator.page_range %}
<li>
{% comment %}
<form action="?{{ GET }}">
{% for get in GET %}
<input type="hidden" name="{{ get.0 }}" value="{{ get.1 }}" />
{% endfor %}
<input type="number" name="page" value="{{ page_obj.number }}" />
</form>
{% endcomment %}
<a class="pagination-link {% if page_obj.number == i %}is-current{% endif %}"
href="?{{ GET }}&page={{ i }}">{{ i }}</a>
</li>
{% endfor %}
</ul>
{% endblock %}
</nav>
{% endwith %}
{% endif %}
{% block list-pagination %}
{% include "./widgets/list_pagination.html" %}
{% endblock %}
{% endblock %}
</div>
{% endblock %}

View File

@ -0,0 +1,16 @@
{% extends "../base.html" %}
{% load static i18n %}
{% block assets %}
{% static "aircox/admin.js" as app_js_url %}
{{ block.super }}
<link rel="stylesheet" type="text/css" href="{% static "aircox/admin.css" %}"/>
{% endblock %}
{% block head-title %}
{% block title %}
{% if page and page.title %}{{ page.title }}{% endif %}
{% endblock %}
{% if page and page.title %}&mdash;{% endif %}
{{ station.name }}
{% endblock %}

View File

@ -0,0 +1,51 @@
{% extends "./base.html" %}
{% load i18n aircox %}
{% block subtitle %}
<span class="icon">
<i class="fa fa-user"></i>
</span>
{{ block.super }}
{% if user.is_superuser %}
&mdash; {% translate "administrator" %}
{% endif %}
{% endblock %}
{% block content-container %}
<section class="container grid-2 gap-4">
<div>
<h2 class="title is-2 mb-3">{% translate "Next diffusions" %}</h2>
<div class="box box-shadow p-3" style="max-height: 35rem; overflow-y:auto;">
{% for object in next_diffs|slice:"0:25" %}
{% page_widget "item" object.episode diffusion=object timetable=True admin=True is_tiny=True %}
{% empty %}
<div>{% translate "No diffusion to display" %}</div>
{% endfor %}
</div>
</div>
<div>
<h2 class="title is-2 mb-3">{% translate "Programs" %}</h2>
<div class="box box-shadow p-3" style="max-height: 35rem; overflow-y:auto;">
{% for object in programs %}
{% page_widget "item" object admin=True is_tiny=True %}
{% empty %}
<div>{% translate "No program to display" %}</div>
{% endfor %}
</div>
</div>
{% if comments %}
<div>
<h2 class="title is-2 mb-3">{% translate "Last Comments" %}</h2>
<div class="box box-shadow p-3" style="max-height: 35rem; overflow-y:auto;">
{% for object in comments|slice:"0:25" %}
{% page_widget "item" object admin=True is_tiny=True %}
{% endfor %}
</div>
</div>
{% endif %}
</section>
{% endblock %}

View File

@ -0,0 +1,104 @@
{% extends "./base.html" %}
{% load i18n aircox %}
{% block title %}{% translate "Statistics" %}{% endblock %}
{% block content-container %}
<div class="">
<form method="GET" class="box mt-3 mb-3">
<h3 class="title is-3">{% translate "Filter by date" %}</h3>
<div class="flex-row gap-3" style="align-items: flex-end">
<div class="field mb-0">
<label class="label">{% translate "from" %}</label>
<input type="date" class="input" name="min_date" value="{{ min_date|date:"Y-m-d" }}"/>
</div>
<div class="field mb-0">
<label class="label">{% translate "... to" %}</label>
<input type="date" class="input" name="max_date" value="{{ max_date|date:"Y-m-d" }}"/>
</div>
<button class="button">{% translate "Apply" %}</button>
</div>
</form>
<a-statistics class="column">
<template v-slot="{counts}">
<table class="table is-hoverable is-fullwidth">
<thead>
<tr>
<th>{% translate "Time" %}</th>
<th>{% translate "Episode" %} / {% translate "Track" %}</th>
<th>{% translate "Tags" %}</th>
</tr>
</thead>
<tbody>
{% regroup object_list by date.date as by_date %}
{% for date, objects in by_date %}
<tr>
<th colspan="3">
{{ date|date:"l - d F Y" }}
</th>
</tr>
{% for object in objects %}
{% with object|is_diffusion as is_diff %}
{% if is_diff %}
<tr class="bg-main">
<td>{{ object.start|time:"H:i" }} - {{ object.end|time:"H:i" }}</td>
<td colspan="2">
<a href="{% url "episode-detail" slug=object.episode.slug %}" target="new">{{ object.episode|default:"" }}</a>
</td>
</tr>
{% endif %}
{% with object|get_tracks as tracks %}
{% for track in tracks %}
<tr {% if is_diff %}class="bg-main-light"{% endif %}>
{% if forloop.first %}
<td rowspan="{{ tracks|length }}">{{ object.start|time:"H:i" }} {% if object|is_diffusion %} - {{ object.end|time:"H:i" }}{% endif %}</td>
{% endif %}
<td>
{% if object.source %}{{ object.source }} / {% endif %}
{% include "aircox/widgets/track_item.html" with object=track %}
</td>
{% with track.tags.all|join:', ' as tags %}
<td>
{% if tags and tags.strip %}
<label class="checkbox">
<input type="checkbox" checked value="{{ tags|escape }}" name="data">
{{ tags }}
</label>
{% endif %}
</td>
{% endwith %}
</tr>
{% empty %}
{% if is_diff %}
<tr class="bg-main-light">
<td colspan="3">{% translate "No tracks" %}</td>
</tr>
{% endif %}
{% endfor %}
{% endwith %}
{% endwith %}
{% endfor %}
{% endfor %}
</tbody>
<tfoot>
<tr>
<td class="is-size-6">{% translate "Totals" %}</td>
<td colspan="2">
<span v-for="(count, tag) in counts" class="mr-4">
<b>[[ tag ]]</b>
[[ count ]]
</span>
</td>
</tr>
</tfoot>
</table>
</template>
</a-statistics>
</div>
{% endblock %}

View File

@ -0,0 +1,77 @@
{% extends "./base.html" %}
{% load i18n aircox %}
{% block title %}{% translate "Users" %}{% endblock %}
{% block content-container %}
<div class="container">
{% if user.is_superuser %}
<div class="message is-info mt-3">
<div class="message-body">
{% translate "Group and editors' changes will be visible only after page reload." %}
</div>
</div>
{% endif %}
<table class="table is-stripped is-fullwidth">
<thead>
<td>{% trans "User" %}</td>
<td>{% trans "Infos" %}</td>
<td>{% trans "Programs" %}</td>
<td></td>
</thead>
<tbody>
{% for obj in object_list %}
<tr>
<td>
<div>
{% if obj.first_name or obj.last_name %}
<b>{{ obj.first_name }} {{ obj.last_name }}</b>
&mdash;
{% endif %}
{{ obj.username }}
</div>
{% if obj.email %}
<div class="text-light smaller">{{ obj.email }}</div>
{% endif %}
</td>
<td>
{% if not obj.is_active %}
<span class="tag is-danger">{% trans "Inactive" %}</span>
{% endif %}
{% if obj.is_superuser %}
<span class="tag is-warning">{% trans "Admin" %}</span>
{% elif obj.is_staff %}
<span class="tag is-info">{% trans "Staff" %}</span>
{% endif %}
</td>
<td>
{% for p in obj.programs %}
<a href="{{ p.get_absolute_url }}">{{ p.title }}</a>
{% if not forloop.last %}
<br/>
{% endif %}
{% endfor %}
</td>
<td>
{% if user.is_superuser %}
<button type="button" class="button secondary"
@click="$refs['user-groups-modal'].open({id: {{ obj.id }}, username: '{{ obj.username }}' })">
{% trans "Groups" %}
</button>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% include "aircox/widgets/list_pagination.html" %}
{% if user.is_superuser %}
{% include "./widgets/user_groups.html" %}
{% endif %}
{% endblock %}

View 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.widget.is_hidden or hidden %}
<input type="hidden" name="{{ name }}" value="{{ value|default:"" }}">
{% elif field|is_checkbox %}
<input type="checkbox" class="checkbox" name="{{ name }}" {% if value %}checked{% endif %}>
{% elif field|is_select %}
<select name="{{ name }}" class="select" value="{{ value|default:"" }}">
{% for value, label in field.widget.choices %}
<option value="{{ value }}">{{ label }}</option>
{% endfor %}
</select>
{% else %}
<input type="text" class="input" name="{{ name }}" value="{{ value|default:"" }}">
{% endif %}

View File

@ -0,0 +1,34 @@
{% load i18n %}
<a-modal ref="group-users-modal">
<template #title="{item}">[[ item?.name ]]</template>
<template #default="{item}">
<a-many-to-many-edit v-if="item" ref="group-users"
:url="'{% url 'api:usergroup-list' %}?group=' + item.id"
commit-url="{% url 'api:usergroup-commit' %}"
:source_id="item.id" source_field="group"
target_field="user"
:autocomplete="{url:'{% url 'api:user-autocomplete' %}?search=${query}&amp;not_in_group=' + item.id, 'label-field':'username', 'value-field':'id'}"
>
<template #items-title>{% translate "Members" %}</template>
<template #item="{item}">
<b class="mr-3">[[ item.data.user.username ]]</b>
<span>[[ item.data.user.first_name ]] [[ item.data.user.last_name ]]</span>
</template>
<template #autocomplete-item="{item}">
<b class="mr-3">[[ item.username ]]</b>
<span class="text-light">[[ item.first_name ]] [[ item.last_name ]]</span>
&mdash;
<i>[[ item.email ]]</i>
</template>
</a-many-to-many-edit>
</template>
<template #footer="{item, close}">
<button type="button" class="button" @click="$refs['group-users'].save(); close()">
Save
</button>
</template>
</a-modal>

View File

@ -0,0 +1,54 @@
{% comment %}
Base template for list editor based on formsets (tracklist_editor, playlist_editor).
Context:
- tag_id: id of parent component
- tag: vue component tag (a-playlist-editor, etc.)
- related_field: field name that target object
- object: related object
- formset: formset used to render the list editor
- formset_data: formset data
{% endcomment %}
{% load aircox aircox_admin static i18n %}
{% with formset.form.base_fields as fields %}
{% block outer %}
<div id="{{ tag_id }}">
{{ formset.non_form_errors }}
<!-- formset.management_form -->
<{{ tag }}
{% block tag-attrs %}
:form-data="{{ formset_data|json }}"
:labels="window.aircox.labels"
:init-data="{% formset_inline_data formset=formset %}"
:columns="[{% for n, f in fields.items %}{% if not f.widget.is_hidden %}'{{ n }}',{% endif %}{% endfor %} ]"
settings-url="{% url "api:user-settings" %}"
data-prefix="{{ formset.prefix }}-"
{% endblock %}>
{% block inner %}
<template #rows-header-head>
{% block rows-header-head %}
<th style="max-width:2em" title="{{ fields.position.help_text }}"
aria-description="{{ fields.position.help_text }}">
<span class="icon">
<i class="fa fa-arrow-down-1-9"></i>
</span>
</th>
{% endblock %}
</template>
{% for name, field in fields.items %}
{% if not field.widget.is_hidden and not field.is_readonly %}
<template v-slot:control-{{ name }}="{item,cell,value,attr,emit,inputName}">
{% block row-control %}
{% include "./v_form_field.html" with value="item.data."|add:name name="inputName" %}
{% endblock %}
</template>
{% endif %}
{% endfor %}
{% endblock %}
</{{ tag }}>
</div>
{% endblock %}
{% endwith %}

View File

@ -0,0 +1,46 @@
{% extends "./list_editor.html" %}
{% comment %}
Context:
- object: episode
{% endcomment %}
{% block outer %}
{% with tag_id="inline-sounds" %}
{% with tag="a-sound-list-editor" %}
{% with related_field="episode" %}
{{ block.super }}
{% endwith %}
{% endwith %}
{% endwith %}
{% endblock %}
{% block tag-attrs %}
{{ block.super }}
sound-list-url="{% url "api:sound-list" %}?program={{ object.parent_id }}"
sound-upload-url="{% url "api:sound-list" %}"
sound-delete-url="{% url "api:sound-detail" pk=123 %}"
{% endblock %}
{% block inner %}
{{ block.super }}
<template #upload-form>
{% for field in sound_form %}
{% with field.name as name %}
{% if name in "program" %}
{% include "aircox/forms/form_field.html" with value=field.initial field=field.field hidden=True %}
{% elif name != "file" %}
<div class="field is-horizontal">
<label class="label mr-3">{{ field.label }}</label>
{% include "aircox/forms/form_field.html" with value=field.initial field=field.field %}
</div>
{% endif %}
{% endwith %}
{% endfor %}
</template>
<template #row-delete="{cell}">
<input type="checkbox" :name="'{{ formset.prefix }}-' + cell.row + '-DELETE'">
</template>
{% endblock %}

View File

@ -0,0 +1,34 @@
{% extends "./list_editor.html" %}
{% load i18n %}
{% block outer %}
{% with tag_id="inline-tracks" %}
{% with tag="a-track-list-editor" %}
{% with related_field="episode" %}
{{ block.super }}
{% endwith %}
{% endwith %}
{% endwith %}
{% endblock %}
{% block inner %}
<template #title><h3 class="title">{% translate "Track list" %}</h3></template>
{{ block.super }}
{% endblock %}
{% block row-control %}
{% if name == "tags" %}
<input type="text" class="input"
:name="inputName"
v-model="item.data[attr]"
@change="emit('change', cell.col)"
>
{% else %}
<a-autocomplete
:input-class="['input', item.error(attr) ? 'is-danger' : 'half-field']"
url="{% url 'api:track-autocomplete' %}?{{ name }}=${query}&field={{ name }}"
:name="inputName"
v-model="item.data[attr]"
@change="emit('change', cell.col)"/>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,30 @@
{% load i18n %}
<a-modal ref="user-groups-modal">
<template #title="{item}">[[ item?.username ]]</template>
<template #default="{item}">
<a-many-to-many-edit v-if="item" ref="user-groups"
:url="'{% url 'api:usergroup-list' %}?user=' + item.id"
commit-url="{% url 'api:usergroup-commit' %}"
:source_id="item.id" source_field="user"
target_field="group"
:autocomplete="{url:'{% url 'api:group-autocomplete' %}?search=${query}&amp;no_user=' + item.id, 'label-field':'name', 'value-field':'id'}"
>
<template #items-title>{% translate "Groups" %}</template>
<template #item="{item}">
[[ item.data.group.name ]]
</template>
<template #autocomplete-item="{item}">
[[ item.name ]]
</template>
</a-many-to-many-edit>
</template>
<template #footer="{item, close}">
<button type="button" class="button" @click="$refs['user-groups'].save(); close()">
Save
</button>
</template>
</a-modal>

View 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.widget.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 %}

View File

@ -14,16 +14,18 @@
{% block subtitle %}{{ date|date:"l d F Y" }}{% endblock %}
{% block before_list %}
{% with "diffusion-list" as url_name %}
{% include "aircox/widgets/dates_menu.html" %}
{% endwith %}
{% block secondary-nav %}
<nav class="nav secondary">
{% include "./widgets/dates_menu.html" with url_name="diffusion-list" %}
</nav>
{% endblock %}
{% block pages_list %}
{% with hide_schedule=True %}
<section role="list">
{% include 'aircox/widgets/diffusion_list.html' %}
<section role="list" class="list">
{% for object in object_list %}
{% page_widget "item" object.episode diffusion=object timetable=True %}
{% endfor %}
</section>
{% endwith %}
{% endblock %}

View File

@ -2,79 +2,51 @@
{% comment %}List of a show's episodes for a specific{% endcomment %}
{% load i18n aircox %}
{% include "aircox/program_sidebar.html" %}
{% block content-container %}
{% block content %}
<a-episode :page="{title: &quot;{{ page.title }}&quot;, podcasts: {{ object.podcasts|json }}}">
<template v-slot="{podcasts,page}">
{{ block.super }}
{{ block.super }}
{% if object.podcasts %}
<section>
<a-playlist v-if="page" :set="podcasts"
name="{{ page.title }}"
list-class="menu-list" item-class="menu-item"
:player="player" :actions="['play']"
@select="player.playItems('queue', $event.item)">
<template v-slot:header>
<h4 class="title is-4">{% translate "Podcasts" %}</h4>
</template>
</a-playlist>
{% comment %}
{% for object in podcasts %}
{% include "aircox/widgets/podcast_item.html" %}
{% endfor %}
{% endcomment %}
</section>
{% endif %}
{% if object.podcasts %}
{% spaceless %}
<section class="container no-border">
<h2 class="title is-2">{% translate "Podcasts" %}</h2>
<a-playlist v-if="page" :set="podcasts"
name="{{ page.title }}"
list-class="menu-list" item-class="menu-item"
:player="player" :actions="['play', 'pin']"
@select="player.playItems('queue', $event.item)">
</a-playlist>
</section>
{% endspaceless %}
{% endif %}
{% if tracks %}
<section>
<h4 class="title is-4">{% translate "Playlist" %}</h4>
<ol>
{% for track in tracks %}
<li><span>{{ track.title }}</span>
<span class="has-text-grey-dark has-text-weight-light">
&mdash; {{ track.artist }}
{% if track.info %}(<i>{{ track.info }}</i>){% endif %}
</span>
</li>
{% endfor %}
</ol>
</section>
{% endif %}
</template></a-episode>
{% endblock %}
{% block sidebar %}
<section>
<h4 class="title is-4">{% translate "Diffusions" %}</h4>
<ul>
{% for diffusion in object.diffusion_set.all %}
<li>
{% with diffusion.start as start %}
{% with diffusion.end as end %}
<time datetime="{{ start }}">{{ start|date:"D. d F Y, H:i" }}</time>
&mdash;
<time datetime="{{ end }}">{{ end|date:"H:i" }}</time>
{% endwith %}
{% endwith %}
<small>
{% if diffusion.initial %}
{% with diffusion.initial.date as date %}
<span title="{% blocktranslate %}Rerun of {{ date }}{% endblocktranslate %}">
({% translate "rerun" %})
</span>
{% endwith %}
{% endif %}
</small>
<br>
</li>
{% endfor %}
</ul>
</section>
{{ block.super }}
{% if tracks %}
<section class="container">
<h2 class="title is-2">{% translate "Playlist" %}</h2>
<table class="table is-hoverable is-fullwidth">
<thead>
<tr>
<th></th>
<th>{% translate "Artist" %}</th>
<th>{% translate "Title" %}</th>
<th></th>
</thead>
<tbody>
{% for track in tracks %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ track.artist }}</td>
<td>{{ track.title }}</td>
<td>{{ track.info|default:"" }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</section>
{% endif %}
</template>
</a-episode>
{% endblock %}

View File

@ -0,0 +1,15 @@
{% extends "./page_form.html" %}
{% load static i18n humanize honeypot aircox %}
{% block page-form %}
<a-episode :page="{title: &quot;{{ object.title }}&quot;, podcasts: {{ object.sounds|json }}}">
<template v-slot="{podcasts,page}">
{{ block.super }}
<hr/>
{% include "./dashboard/widgets/tracklist_editor.html" with formset=tracklist_formset formset_data=tracklist_formset_data %}
<hr/>
<h2 class="title is-2">{% translate "Podcasts" %}</h2>
{% include "./dashboard/widgets/soundlist_editor.html" with formset=soundlist_formset formset_data=soundlist_formset_data %}
</template>
</a-episode>
{% endblock %}

View 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.widget.is_hidden or hidden %}
<input type="hidden" name="{{ name }}" value="{{ value|default:"" }}">
{% elif field|is_checkbox %}
<input type="checkbox" class="checkbox" name="{{ name }}" {% if value %}checked{% endif %}>
{% elif field|is_select %}
<select name="{{ name }}" class="select" value="{{ value|default:"" }}">
{% for value, label in field.widget.choices %}
<option value="{{ value }}">{{ label }}</option>
{% endfor %}
</select>
{% else %}
<input type="text" class="input" name="{{ name }}" value="{{ value|default:"" }}">
{% endif %}

View File

@ -0,0 +1,53 @@
{% comment %}
Base template for list editor based on formsets (tracklist_editor, playlist_editor).
Context:
- tag_id: id of parent component
- tag: vue component tag (a-playlist-editor, etc.)
- related_field: field name that target object
- object: related object
- formset: formset used to render the list editor
- formset_data: formset data
{% endcomment %}
{% load aircox aircox_admin static i18n %}
{% with formset.form.base_fields as fields %}
{% block outer %}
<div id="{{ tag_id }}">
{{ formset.non_form_errors }}
<!-- formset.management_form -->
<{{ tag|default:"a-form-set" }} ref="formset"
{% block tag-attrs %}
:form-data="{{ formset_data|json }}"
:labels="window.aircox.labels"
:columns="[{% for n, f in fields.items %}{% if not f.widget.is_hidden %}'{{ n }}',{% endif %}{% endfor %} ]"
settings-url="{% url "api:user-settings" %}"
data-prefix="{{ formset.prefix }}-"
{% endblock %}>
{% block inner %}
<template #rows-header-head>
{% block rows-header-head %}
<th style="max-width:2em" title="{{ fields.position.help_text }}"
aria-description="{{ fields.position.help_text }}">
<span class="icon">
<i class="fa fa-arrow-down-1-9"></i>
</span>
</th>
{% endblock %}
</template>
{% for name, field in fields.items %}
{% if not field.widget.is_hidden and not field.is_readonly %}
<template v-slot:control-{{ name }}="{context,item,cell,value,attr,emit,inputName}">
{% block row-control %}
{% include "./v_form_field.html" with value="item.data."|add:name name="inputName" %}
{% endblock %}
</template>
{% endif %}
{% endfor %}
{% endblock %}
</{{ tag }}>
</div>
{% endblock %}
{% endwith %}

View 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.widget.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 %}

View File

@ -1,85 +1,74 @@
{% extends "aircox/page_list.html" %}
{% comment %}Home page{% endcomment %}
{% load i18n %}
{% extends "./public.html" %}
{% load i18n aircox %}
{% block head_title %}{{ station.name }}{% endblock %}
{% block title %}
{% if not page or not page.title %}{{ station.name }}
{% else %}{{ block.super }}
{% endif %}
{% endblock %}
{% block title %}{% if page %}{{ block.super }}{% endif %}{% endblock %}
{% block before_list %}{% endblock %}
{% block pages_list %}
{% if page and page.content %}<hr/>{% endif %}
{% block breadcrumbs-container %}{% endblock %}
{% block content-container %}
{{ block.super }}
{% if next_diffs %}
<div class="columns">
{% with render_card=True %}
{% for object in next_diffs %}
{% with is_primary=object.is_now %}
<div class="column is-relative">
<h4 class="card-super-title" title="{{ object.start }}">
{% if is_primary %}
<span class="fas fa-play"></span>
<time datetime="{{ object.start }}">
{% translate "Currently" %}
</time>
{% else %}
{{ object.start|date:"H:i" }}
{% endif %}
<section class="container">
<h2 class="title">
{% with station.name as station %}
{% blocktrans %}Today on {{ station }}{% endblocktrans %}
{% endwith %}
</h2>
{% if object.episode.category %}
// {{ object.episode.category.title }}
{% endif %}
</h4>
{% include object.item_template_name %}
<div class="mb-3">
{% with next_diffs.0 as obj %}
{% page_widget "wide" obj.episode diffusion=obj timetable=True %}
{% endwith %}
</div>
{% endwith %}
{% endfor %}
{% endwith %}
</div>
{% endif %}
{% if object_list %}
<h4 class="title is-4">{% translate "Today" %}</h4>
<section role="list">
{% include 'aircox/widgets/diffusion_list.html' %}
<hr/>
<a-carousel section-class="card-grid">
{% for obj in next_diffs|slice:"1:" %}
{% if object != diffusion %}
{% page_widget "card" obj.episode diffusion=obj timetable=True %}
{% endif %}
{% endfor %}
</a-carousel>
</section>
{% endif %}
{% endblock %}
{% block pagination %}
<ul class="pagination-list">
<li>
<a href="{% url "page-list" %}" class="pagination-link"
aria-label="{% translate "Show all publication" %}">
{% translate "More publications..." %}
{% if logs %}
<section class="container">
<h2 class="title">{% translate "It just happened" %}</h2>
<div class="grid" role="list">
{% include "./widgets/logs.html" with object_list=logs %}
</div>
<nav class="nav-urls">
<a href="{% url "timetable-list" %}"
aria-label="{% translate "Show all program's for today" %}">
{% translate "Today" %}
</a>
</li>
</ul>
</nav>
</section>
{% endif %}
{% if podcasts %}
<section class="container">
<h2 class="title is-3 p-2">{% translate "Last podcasts" %}</h2>
{% include "./widgets/carousel.html" with objects=podcasts url_name="podcast-list" url_label=_("All podcasts") %}
</section>
{% endif %}
{% if publications %}
<section class="container">
<h2 class="title">{% translate "Last publications" %}</h2>
{% include "./widgets/carousel.html" with objects=publications url_name="page-list" url_label=_("All publications") %}
</section>
{% endif %}
{% endblock %}
{% block sidebar %}
<section>
<h4 class="title is-4">{% translate "Previously on air" %}</h4>
{% with has_cover=False %}
{% with logs as object_list %}
{% include "aircox/widgets/log_list.html" %}
{% endwith %}
{% endwith %}
</section>
<section>
<h4 class="title is-4">{% translate "Last publications" %}</h4>
{% with hide_schedule=True %}
{% with has_headline=False %}
{% for object in last_publications %}
{% include object.item_template_name|default:'aircox/widgets/page_item.html' %}
{% endfor %}
{% endwith %}
{% endwith %}
</section>
{% endblock %}
{% block pages_list %}{% endblock %}

View File

@ -1,29 +0,0 @@
{% extends "aircox/page_list.html" %}
{% comment %}List of logs for a specific date{% endcomment %}
{% load i18n humanize aircox %}
{% block title %}
{% if not page or not page.title %}
{% with station.name as station %}
{% blocktranslate %}That happened on {{ station }}{% endblocktranslate %}
{% endwith %}
{% else %}
{{ block.super }}
{% endif %}
{% endblock %}
{% block subtitle %}{{ date|date:"l d F Y" }}{% endblock %}
{% block before_list %}
{% with "log-list" as url_name %}
{% include "aircox/widgets/dates_menu.html" %}
{% endwith %}
{% endblock %}
{% block pages_list %}
<section>
{# <h4 class="subtitle size-4">{{ date }}</h4> #}
{% include "aircox/widgets/log_list.html" %}
</section>
{% endblock %}

View File

@ -1,4 +1,4 @@
{% extends "aircox/basepage_detail.html" %}
{% extends "aircox/public.html" %}
{% load static i18n humanize honeypot aircox %}
{% comment %}
Base template used to display a Page
@ -6,83 +6,88 @@ Base template used to display a Page
Context:
- page: page
- parent: parent page
- related_objects: list of object to display as related publications
- related_url: url to the full list of related_objects
{% endcomment %}
{% block header_crumbs %}
{{ block.super }}
{% if page.category %}
{% if parent %} / {% endif %} {{ page.category.title }}
{% block breadcrumbs %}
{% if parent %}
{% include "./widgets/breadcrumbs.html" with page=parent %}
{% if page %}
<a href="{% url page.list_url_name parent_slug=parent.slug %}">
{{ page|verbose_name:True }}
</a>
{% endif %}
{% elif page %}
{% include "./widgets/breadcrumbs.html" with page=page no_title=True %}
{% endif %}
{% endblock %}
{% block top-nav-tools %}
{% has_perm page "change" as can_edit %}
{% if can_edit %}
<a class="navbar-item" href="{{ page|admin_url:'change' }}"
target="new">
<span class="icon is-small">
<i class="fa fa-pen"></i>
</span>&nbsp;
<span>{% translate "Edit" %}</span>
</a>
{% endif %}
{% block title-container %}
{{ block.super }}
{% block page-actions %}
{% include "aircox/widgets/page_actions.html" %}
{% endblock %}
{% endblock %}
{% block main %}
{{ block.super }}
{% block related %}
{% if related_objects %}
<section class="container">
{% with models=object|verbose_name:True %}
<h2 class="title is-2">{% blocktranslate %}Related {{models}}{% endblocktranslate %}</h2>
{% include "./widgets/carousel.html" with objects=related_objects url_name=object.list_url_name url_category=object.category %}
{% endwith %}
</section>
{% endif %}
{% endblock %}
{% block comments %}
{% if comments or comment_form %}
<section class="mt-6">
<h4 class="title is-4">{% translate "Comments" %}</h4>
{% if comments %}
<section class="container">
<h2 class="title is-2">{% translate "Comments" %}</h2>
{% for comment in comments %}
<div class="media box">
<div class="media-content">
<p>
<strong class="mr-2">{{ comment.nickname }}</strong>
<time datetime="{{ comment.date }}" title="{{ comment.date }}">
<small>{{ comment.date|naturaltime }}</small>
</time>
<br>
{{ comment.content }}
</p>
</div>
</div>
{% for object in comments %}
{% page_widget "item" object %}
{% endfor %}
</section>
{% endif %}
{% if comment_form %}
{% if comment_form %}
<section class="container">
<h2 class="title is-2">{% translate "Post a comment" %}</h2>
<form method="POST">
<h5 class="title is-5">{% translate "Post a comment" %}</h5>
{% csrf_token %}
{% render_honeypot_field "website" %}
{% for field in comment_form %}
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">
{{ field.label_tag }}
</label>
</div>
<div class="field-body">
<div class="field">
<p class="control is-expanded">{{ field }}</p>
{% if field.errors %}
<p class="help is-danger">{{ field.errors }}</p>
{% endif %}
{% if field.help_text %}
<p class="help">{{ field.help_text|safe }}</p>
{% endif %}
</div>
<div class="field">
<div class="control">
{{ comment_form.content }}
</div>
</div>
{% for field in comment_form %}
{% if field.name != "content" %}
<div class="field is-horizontal">
<label class="label">{{ field.label }}</label>
<div class="control">{{ field }}</div>
</div>
{% if field.errors %}
<p class="help is-danger">{{ field.errors }}</p>
{% endif %}
{% if field.help_text %}
<p class="help">{{ field.help_text|safe }}</p>
{% endif %}
{% endif %}
{% endfor %}
<div class="has-text-right">
<button type="reset" class="button is-danger">{% translate "Reset" %}</button>
<button type="submit" class="button is-success">{% translate "Post comment" %}</button>
<button type="submit" class="button">{% translate "Post comment" %}</button>
</div>
</form>
{% endif %}
</section>
{% endif %}

View File

@ -0,0 +1,129 @@
{% extends "./dashboard/base.html" %}
{% load static aircox aircox_admin i18n %}
{% block init-scripts %}
aircox.labels = {% inline_labels %}
{{ block.super }}
{% endblock %}
{% block header-cover %}{% endblock %}
{% block title %}
{% if not object %}
{% with view.model|verbose_name as model %}
{% blocktranslate %}Create a {{model}}{% endblocktranslate %}
{% endwith %}
{% else %}
{{ block.super }}
{% endif %}
{% endblock %}
{% block content-container %}
<a-select-file ref="cover-select"
:labels="window.aircox.labels"
list-url="{% url "api:image-list" %}"
upload-url="{% url "api:image-list" %}"
delete-url="{% url "api:image-detail" pk=123 %}"
title="{% translate "Select an image" %}" list-class="grid-4"
@select="(event) => fileSelected('cover-select', 'cover-input', $refs.cover)"
>
<template #upload-preview="{upload}">
<img :src="upload.fileURL" class="upload-preview blink"/>
</template>
<template #default="{item, load, lastUrl}">
<div class="flex-column is-fullheight" v-if="item">
<figure class="flex-grow-1">
<img :src="item.file"/>
</figure>
<div>
<label class="label small">[[ item.name || item.original_filename ]]</label>
<a-action-button
class="has-text-danger small float-right"
icon="fa fa-trash"
confirm="{% translate "Are you sure you want to remove this item from server?" %}"
method="DELETE"
:url="'{% url "api:image-detail" pk="123" %}'.replace('123', item.id)"
@done="load(lastUrl)">
</a-action-button>
</div>
</div>
</template>
</a-select-file>
<section class="container">
<form method="post" enctype="multipart/form-data">
{% block page-form %}
{% csrf_token %}
<div class="flex-row">
<div class="flex-grow-3">
{% for field in form %}
{% if field.name not in "cover,content" %}
<div class="field {% if field.name != "content" %}is-horizontal{% endif %}">
<label class="label">{{ field.label }}</label>
<div class="control clear-unset">
{% if field.name == "pub_date" %}
<input type="datetime-local" class="input" name="{{ field.name }}"
value="{{ field.value|date:"Y-m-d" }}T{{ field.value|date:"H:i" }}"/>
{% else %}
{% include "aircox/forms/form_field.html" with field=field.field name=field.name value=field.initial %}
{% endif %}
</div>
<p class="help">{{ field.help_text }}</p>
</div>
{% endif %}
{% if field.errors %}
<p class="help is-danger">{{ field.errors }}</p>
{% endif %}
{% if field.help_text %}
<p class="help">{{ field.help_text|safe }}</p>
{% endif %}
{% endfor %}
</div>
<div class="flex-grow-1">
{% with form.cover as field %}
{% block page-form-cover %}
<input type="hidden" name="{{ field.name }}" value="{{ field.value }}" ref="cover-input"/>
{% spaceless %}
<figure class="header-cover">
<img src="{{ cover }}" ref="cover" class="cover">
<button type="button" class="button" @click="$refs['cover-select'].open()">
{% translate "Change cover" %}
</button>
</figure>
{% endspaceless %}
{% endblock %}
{% endwith %}
</div>
</div>
{% with form.content as field %}
{% block page-form-content %}
<div>
<div class="field {% if field.name != "content" %}is-horizontal{% endif %}">
<label class="label">{{ field.label }}</label>
<div class="control clear-unset">
<textarea name="{{ field.name }}" class="is-fullwidth height-25">{{ field.value|default:""|striptags|safe }}</textarea>
</div>
<p class="help">{{ field.help_text }}</p>
</div>
{% if field.errors %}
<p class="help is-danger">{{ field.errors }}</p>
{% endif %}
{% if field.help_text %}
<p class="help">{{ field.help_text|safe }}</p>
{% endif %}
</div>
{% endblock %}
{% endwith %}
{% endblock %}
<hr/>
<div class="flex-row">
<div class="flex-grow-1">{% block page-form-actions %}{% endblock %}</div>
<div class="has-text-right">
<button type="submit" class="button">{% translate "Update" %}</button>
</div>
</form>
</section>
{% endblock %}

View File

@ -2,61 +2,60 @@
{% comment %}Display a list of Pages{% endcomment %}
{% load i18n aircox %}
{% block before_list %}
{{ block.super }}
{% if view.has_filters and object_list %}
<form method="GET" action="" class="media">
<div class="media-content">
{% block filters %}
<div class="field is-horizontal">
<div class="field-label">
<label class="label">{% translate "Search" %}</label>
</div>
<div class="field-body">
<div class="field">
<div class="control has-icons-left">
<span class="icon is-small is-left">
<i class="fa fa-search"></i>
</span>
<input class="input" type="text" name="q"
value="{{ filterset_data.q }}"
placeholder="{% translate "Search content" %}">
</div>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label">
<label class="label">{% translate "Categories" %}</label>
</div>
<div class="field-body">
<div class="field is-narrow">
<div class="control">
{% for label, value in categories %}
<label class="checkbox">
<input type="checkbox" class="checkbox" name="category__id__in"
value="{{ value }}"
{% if value in filterset_data.category__id__in %}checked{% endif %} />
{{ label }}
</label>
{% endfor %}
</div>
</div>
</div>
</div>
{% endblock %}
{% block secondary-nav %}
{% if not parent and categories %}
<nav class="nav secondary">
<div class="nav-menu nav-categories">
{% for cat in categories %}
<a class="nav-item{% if cat == category %} active{% endif %}"
href="{% url request.resolver_match.url_name category_slug=cat.slug %}">
{{ cat.title }}
</a>
{% endfor %}
</div>
<div class="media-right">
<div class="field is-grouped is-grouped-right">
<div class="control">
<button class="button is-primary"/>{% translate "Apply" %}</button>
</div>
<div class="control">
<a href="?" class="button is-secondary">{% translate "Reset" %}</a>
</div>
</div>
</div>
</form>
<a-switch class="button burger"
el=".nav-categories" group="nav" icon="fas fa-tags"
aria-label="{% translate "Categories" %}">
</a-switch>
</nav>
{% endif %}
{% endblock %}
{% block title %}
{% if parent %}{{ parent.title }}
{% else %}{{ block.super }}
{% endif %}
{% endblock %}
{% block header %}
{% if page and not object %}
{% with page as object %}
{{ block.super }}
{% endwith %}
{% else %}
{{ block.super }}
{% endif %}
{% endblock %}
{% block breadcrumbs %}
{% if parent and model.list_url_name %}
{% include "./widgets/breadcrumbs.html" with page=parent %}
<a href="{% url model.list_url_name %}">{{ model|verbose_name:True }}</a>
{% elif page and model.list_url_name %}
<a href="{% url model.list_url_name %}">{{ page.title }}</a>
{% if category %}
<a href="{% url request.resolver_match.url_name category_slug=category.slug %}">
{{ category.title }}
</a>
{% endif %}
{% else %}
<a href="{% url request.resolver_match.url_name %}">{{ model|verbose_name:True }}</a>
{% if category %}
<a href="{% url request.resolver_match.url_name category_slug=category.slug %}">
{{ category.title }}
</a>
{% endif %}
{% endif %}
{% endblock %}
{% block content-container %}{% endblock %}

View File

@ -1,67 +1,53 @@
{% extends "aircox/page_detail.html" %}
{% comment %}Detail page of a show{% endcomment %}
{% load i18n %}
{% load i18n aircox %}
{% include "aircox/program_sidebar.html" %}
{% block header_nav %}
{% endblock %}
{% block content %}
{{ block.super }}
<br>
{% with has_headline=False %}
{% if articles %}
<section>
<h4 class="title is-4">{% translate "Articles" %}</h4>
{% for object in articles %}
{% include "aircox/widgets/page_item.html" %}
{% block content-container %}
{% with schedules=object.schedule_set.all %}
{% if schedules %}
<header class="container schedules">
{% for schedule in schedules %}
<div class="schedule">
<div class="heading">
<span class="day">{{ schedule.get_frequency_display }}</span>
{% with schedule.start|date:"H:i" as start %}
{% with schedule.end|date:"H:i" as end %}
<time datetime="{{ start }}">{{ start }}</time>
&mdash;
<time datetime="{{ end }}">{{ end }}</time>
{% endwith %}
{% endwith %}
<small>
{% if schedule.is_rerun %}
{% with schedule.initial.date as date %}
<span title="{% blocktranslate %}Rerun of {{ date }}{% endblocktranslate %}">
({% translate "Rerun" %})
</span>
{% endwith %}
{% endif %}
</small>
</div>
</div>
{% endfor %}
<br>
<nav class="pagination is-centered">
<ul class="pagination-list">
<li>
<a href="{% url "article-list" parent_slug=program.slug %}"
class="pagination-link"
aria-label="{% translate "Show all program's articles" %}">
{% translate "More articles" %}
</a>
</li>
</ul>
</nav>
</section>
</header>
{% endif %}
{% endwith %}
{% endblock %}
{% block sidebar %}
<section>
<h4 class="title is-4">{% translate "Diffusions" %}</h4>
{% for schedule in program.schedule_set.all %}
{{ schedule.get_frequency_display }}
{% with schedule.start|date:"H:i" as start %}
{% with schedule.end|date:"H:i" as end %}
<time datetime="{{ start }}">{{ start }}</time>
&mdash;
<time datetime="{{ end }}">{{ end }}</time>
{% endwith %}
{% endwith %}
<small>
{% if schedule.initial %}
{% with schedule.initial.date as date %}
<span title="{% blocktranslate %}Rerun of {{ date }}{% endblocktranslate %}">
({% translate "Rerun" %})
</span>
{% endwith %}
{% endif %}
</small>
<br>
{% endfor %}
</section>
{{ block.super }}
{% if episodes %}
<section class="container">
<h2 class="title is-2">{% translate "Last Episodes" %}</h2>
{% include "./widgets/carousel.html" with objects=episodes url_name="episode-list" url_parent=object url_label=_("All episodes") %}
</section>
{% endif %}
{% if articles %}
<section class="container">
<h2 class="title is-2">{% translate "Last Articles" %}</h2>
{% include "./widgets/carousel.html" with objects=articles url_name="article-list" url_parent=object url_label=_("All articles") %}
</section>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,26 @@
{% extends "./page_form.html" %}
{% load static i18n humanize honeypot aircox %}
{% block head_extra %}
{{ form.media }}
{% endblock %}
{% block page-form-actions %}
{% if object and object.pk and request.user.is_superuser %}
<button type="button"
class="button secondary"
@click="$refs['group-users-modal'].open({id: {{ object.editors_group_id }}, name: '{{ object.editors_group.name }}' })">{% translate "Editors" %}</button>
{{ block.super }}
{% endif %}
{% endblock %}
{% block main %}
{{ block.super }}
{% if object and object.pk and request.user.is_superuser %}
{% include "./dashboard/widgets/group_users.html" %}
{% endif %}
{% endblock %}

View File

@ -1,6 +0,0 @@
{% block sidebar_title %}
{% with program.title as program %}
{% blocktranslate %}Recently on {{ program }}{% endblocktranslate %}
{% endwith %}
{% endblock %}

View File

@ -0,0 +1,18 @@
{% extends "./base.html" %}
{% comment %}
Override is a trick here: it allows to change title at two different different
places inside the page: inside `<title>` tag, and inside the page
content.
{% endcomment %}
{% block head-title %}
{% block title %}
{% if page and page.title %}{{ page.title }}{% endif %}
{% endblock %}
{% if page and page.title %}&mdash;{% endif %}
{{ station.name }}
{% endblock %}
{% block header %}{% if page %}{{ block.super }}{% endif %}{% endblock %}

View File

@ -0,0 +1,28 @@
{% extends "aircox/page_list.html" %}
{% comment %}List of diffusions as a timetable{% endcomment %}
{% load i18n aircox humanize %}
{% block subtitle %}{{ date|date:"l d F Y" }}{% endblock %}
{% block secondary-nav %}
<nav class="nav secondary">
{% include "./widgets/dates_menu.html" with url_name=view.redirect_date_url %}
</nav>
{% endblock %}
{% block breadcrumbs %}
{{ block.super }}
<a href="{% url "timetable-list" date=date %}">{{ date|date:"l d F Y" }}</a>
{% endblock %}
{% block list-container %}
{% with list_class="grid" %}
{{ block.super }}
{% endwith %}
{% endblock %}
{% block list %}
{% include "./widgets/logs.html" with object_list=object_list timetable=True %}
{% endblock %}

View File

@ -0,0 +1,4 @@
{% extends "./page.html" %}
{% load humanize %}
{% block subtitle %}{{ object.pub_date.date }}{% endblock %}

View File

@ -0,0 +1,4 @@
<a-autocomplete
url="{{url}}"
{% if ":name" not in widget.attrs %}name="{{ name|default:widget.name }}"{% endif %}{% if widget.value != None %} model-value="{{ widget.value|stringformat:'s' }}"{% endif %}
{% include "django/forms/widgets/attrs.html" %} {{ extra|default:"" }}/>

View File

@ -11,63 +11,48 @@ Context variables:
- is_thin (=False): if True, smaller cover and display less info
{% endcomment %}
{% if render_card %}
<article class="card {% if is_primary %}is-primary{% endif %}">
<header class="card-image">
<a href="{{ object.get_absolute_url }}">
<figure class="image is-4by3">
<img src="{% thumbnail object.cover|default:station.default_cover 480x480 %}">
</figure>
</a>
{% block outer %}
<article class="preview preview-item{% if is_primary %}is-primary{% endif %}{% block card_class %}{% endblock %}">
{% block inner %}
<header class="headings"
style="background-image: url({{ object.cover.url }})">
{% block headings %}
<div>
<span class="heading subtitle">{% block subtitle %}{% endblock %}</span>
</div>
{% endblock %}
</header>
<div class="card-header">
<h4 class="title">
<a href="{{ object.get_absolute_url }}">
{% block card_title %}{{ object.title }}{% endblock %}
</a>
</h4>
</div>
</article>
<div class="">
<div>
<h2 class="heading title">{% block title %}{% endblock %}</h2>
</div>
{% else %}
<article class="media item {% block css %}{% endblock%}">
{% if has_cover|default_if_none:True %}
<div class="media-left">
{% if is_thin %}
<img src="{% thumbnail object.cover|default:station.default_cover 64x64 crop=scale %}"
class="cover is-tiny">
{% else %}
<img src="{% thumbnail object.cover|default:station.default_cover 128x128 crop=scale %}"
class="cover is-small">
{% endif %}
</div>
{% endif %}
<div class="media-content">
<h5 class="title is-5 has-text-weight-normal">
{% block title %}
{% if object.is_published %}
<a href="{{ object.get_absolute_url }}">{{ object.title }}</a>
{% else %}
{{ object.title }}
<summary class="heading-container">
{% block content %}
{% if content and with_content %}
{% autoescape off %}
{{ content|striptags|truncatewords:64|linebreaks }}
{% endautoescape %}
{% endif %}
{% endblock %}
</h5>
<div class="subtitle is-6 has-text-weight-light">
{% block subtitle %}
{% if object.category %}{{ object.category.title }}{% endif %}
{% endblock %}
</div>
</summary>
{% if has_headline|default_if_none:True %}
<div class="headline">
{% block headline %}{{ object.headline }}{% endblock %}
<div class="actions">
{% block actions %}
<a class="button float-right" href="{{ object.get_absolute_url|escape }}">
<span class="icon">
<i class="fas fa-external-link"></i>
</span>
<label>{% translate "More infos" %}</label>
</a>
{% endblock %}
</div>
{% endif %}
</div>
{% endblock %}
{% if not no_actions %}
{% block actions %}{% endblock %}
{% if with_container %}
</div>
{% endif %}
</article>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,15 @@
{% load aircox %}
<a href="{% url page.list_url_name %}">
{{ page|verbose_name:True }}
</a>
{% if page.category and not no_cat %}
<a href="{% url page.list_url_name category_slug=page.category.slug %}">
{{ page.category.title }}
</a>
{% endif %}
{% if not no_title %}
<a href="{{ page.get_absolute_url }}">
{{ page.title|truncatechars:24 }}
</a>
{% endif %}

View File

@ -0,0 +1,23 @@
{% extends "./preview.html" %}
{% load i18n %}
{% block tag-class %}{{ block.super }} preview-card{% endblock %}
{% block inner %}
<div class="card-content">
{% if cover %}
{% if url %}<a href="{{ url }}">{% endif %}
<figure style="background-image: url({{ cover }});" class="preview-cover">
<img src="{{ cover }}" class="hide">
</figure>
{% if url %}</a>{% endif %}
{% endif %}
<footer class="actions">
{% block actions %}{{ block.super }}{% endblock %}
</footer>
</div>
{% block headings-container %}{{ block.super }}{% endblock %}
{% endblock %}

View File

@ -0,0 +1,28 @@
{% load aircox %}
{% comment %}
Context:
- objects: list of objects to display
- url_name: url name to show the full list
- url_parent: parent page for the full list
- url_label: label of url button
{% endcomment %}
<a-carousel>
{% for object in objects %}
{% page_widget "card" object %}
{% endfor %}
</a-carousel>
{% if url_name %}
<nav class="nav-urls">
{% if url_parent %}
<a href="{% url url_name parent_slug=url_parent.slug %}">
{% elif url_category %}
<a href="{% url url_name category_slug=url_category.slug %}">
{% else %}
<a href="{% url url_name %}">
{% endif %}
{{ url_label|default:_("Show all") }}
</a>
</nav>
{% endif %}

View File

@ -0,0 +1,55 @@
{% extends "./page.html" %}
{% load i18n humanize aircox %}
{% block tag-class %}{{ block.super }} comment{% endblock %}
{% block outer %}
{% with url=object.get_absolute_url %}
{% if with_title %}
{{ block.super }}
{{ block.super }}
{% else %}
{{ block.super }}
{{ block.super }}
{% endif %}
{% endwith %}
{% endblock %}
{% block title %}
{{ object.nickname }} &mdash; {{ object.date }}
{% endblock %}
{% block subtitle %}
{% if with_title %}
{{ object.parent.title }}
{% endif %}
{% endblock %}
{% block content %}{{ object.content }}{% endblock %}
{% block actions %}
{{ block.super }}
{% if admin %}
{% if user.is_staff %}
<a href="{% url "admin:aircox_comment_change" object.pk %}" class="button"
title="{% trans "Edit comment" %}"
aria-label="{% trans "Edit comment" %}">
<span class="fa fa-edit"></span>
</a>
{% endif %}
<a-action-button class="button is-danger"
title="{% trans "Delete comment" %}"
aria-label="{% trans "Delete comment" %}"
url="{% url "api:comment-detail" object.pk %}"
icon="fa fa-trash-alt"
method="delete"
confirm="{% translate "Delete comment?" %}"
@done="deleteElements('#{{ object|object_id }}')"
/>
{# <a href="mailto:{{ object.email }}">{{ object.nickname }}</a> #}
{% endif %}
{% endblock %}

View File

@ -11,36 +11,33 @@ An empty date results to a title or a separator
{% endcomment %}
{% load i18n %}
<div class="media" role="menu"
aria-label="{% translate "pick a date" %}">
<div class="media-content">
<div class="tabs is-toggle">
<ul>
{% for day in dates %}
<li class="{% if day == date %}is-active{% endif %}">
<a href="{% url url_name date=day %}">
{{ day|date:"D. d" }}
</a>
</li>
{% endfor %}
</ul>
</div>
</div>
<a-switch class="button burger"
el=".nav-dates" icon="far fa-calendar" group="nav"
aria-label="{% translate "Dates" %}">
</a-switch>
<div class="media-right">
<form action="{% url url_name %}" method="GET" class="navbar-body"
aria-label="{% translate "Jump to date" %}">
<div class="field has-addons">
<div class="control has-icons-left">
<span class="icon is-small is-left"><span class="far fa-calendar"></span></span>
<input type="{{ date_input|default:"date" }}" class="input date"
name="date" value="{{ date|date:"Y-m-d" }}">
</div>
<div class="control">
{% comment %}Translators: form button to select a date{% endcomment %}
<button class="button is-primary">{% translate "Go" %}</button>
<div class="nav-menu nav-dates">
{% for day in dates %}
<a href="{% url url_name date=day %}" class="nav-item {% if day == date %}active{% endif %}">
{{ day|date:"l d" }}
</a>
{% endfor %}
<a-dropdown class="nav-item align-right flex-grow-0 dropdown is-right"
content-class="dropdown-menu"
button-tag="span" button-class="dropdown-trigger"
button-icon-open="fa-solid fa-plus" button-icon-close="fa-solid fa-minus">
<template #default>
<div class="dropdown-content">
<div class="dropdown-item">
<h4>{% translate "Pick a date" %}</h4>
<v-calendar mode="date" borderless
:initial-page="{month: {{date.month}}, year: {{date.year}}}"
@dayclick="(event) => window.aircox.pickDate({% url url_name %}, event)"
color="yellow"
/>
</div>
</div>
</form>
</div>
</template>
</a-dropdown>
</div>

View File

@ -3,19 +3,4 @@ Context:
- object_list: object list
- date: date for list
{% endcomment %}
<table id="timetable{% if date %}-{{ date|date:"Y-m-d" }}{% endif %}" class="timetable">
{% for diffusion in object_list %}
<tr class="{% if diffusion.is_now %}has-background-primary{% endif %}">
<td class="pr-2 pb-2">
<time datetime="{{ diffusion.start|date:"c" }}">
{{ diffusion.start|date:"H:i" }} - {{ diffusion.end|date:"H:i" }}
</time>
</td>
<td class="pb-2">
{% with diffusion.episode as object %}
{% include "aircox/widgets/episode_item.html" %}
{% endwith %}
</td>
</tr>
{% endfor %}
</table>
{% load aircox %}

View File

@ -0,0 +1,26 @@
{% comment %}
Context:
- object: diffusion
{% endcomment %}
{% load i18n %}
{% if object.type == object.TYPE_ON_AIR %}
<span class="tag is-info">
<span class="icon is-small">
{% if object.is_live %}
<i class="fa fa-microphone"
title="{% translate "Live diffusion" %}"></i>
{% else %}
<i class="fa fa-music"
title="{% translate "Differed diffusion" %}"></i>
{% endif %}
</span>
&nbsp;
{{ object.get_type_display }}
</span>
{% elif object.type == object.TYPE_CANCEL %}
<span class="tag is-danger">
{{ object.get_type_display }}</span>
{% elif object.type == object.TYPE_UNCONFIRMED %}
<span class="tag is-warning">
{{ object.get_type_display }}</span>
{% endif %}

View File

@ -0,0 +1,71 @@
{% extends "./page.html" %}
{% load i18n humanize aircox %}
{% block outer %}
{% with diffusion.is_now as is_active %}
{{ block.super }}
{% endwith %}
{% endblock %}
{% block subtitle %}
{% if diffusion %}
{% if timetable %}
{{ diffusion.start|date:"H:i" }}
&mdash;
{{ diffusion.end|date:"H:i" }}
{% else %}
{{ diffusion.start|naturalday }},
{{ diffusion.start|date:"H:i" }}
{% endif %}
{% else %}
{{ block.super }}
{% endif %}
{% endblock %}
{% block actions-container %}
{% if admin and diffusion %}
<div class="flex-row">
<div class="flex-grow-1">
{% if diffusion.type == diffusion.TYPE_ON_AIR %}
<span class="tag is-info">
<span class="icon is-small">
{% if diffusion.is_live %}
<i class="fa fa-microphone"
title="{% translate "Live diffusion" %}"></i>
{% else %}
<i class="fa fa-music"
title="{% translate "Differed diffusion" %}"></i>
{% endif %}
</span>
&nbsp;
{{ diffusion.get_type_display }}
</span>
{% elif diffusion.type == diffusion.TYPE_CANCEL %}
<span class="tag is-danger">
{{ diffusion.get_type_display }}</span>
{% elif diffusion.type == diffusion.TYPE_UNCONFIRMED %}
<span class="tag is-warning">
{{ diffusion.get_type_display }}</span>
{% endif %}
</div>
{{ block.super }}
</div>
{% else %}
{{ block.super }}
{% endif %}
{% endblock %}
{% block actions %}
{{ block.super }}
{% if object.sound_set.count %}
<button class="button action" @click="player.playButtonClick($event)"
data-sounds="{{ object.podcasts|json }}">
<span class="icon is-small">
<span class="fas fa-play"></span>
</span>
<label>{% translate "Listen" %}</label>
</button>
{% endif %}
{% endblock %}

View File

@ -1,58 +1,36 @@
{% extends "aircox/widgets/page_item.html" %}
{% comment %}
List item for an episode.
Context variables:
- object: episode
- diffusion: episode's diffusion
- hide_schedule: if True, do not display start time
{% endcomment %}
{% load i18n easy_thumbnails_tags aircox %}
{% extends "./basepage_item.html" %}
{% load i18n humanize %}
{% block title %}
{% if not object.is_published and object.program.is_published %}
<a href="{{ object.program.get_absolute_url }}">
{{ object.program.title }}
{% if diffusion %}
&mdash;
{{ diffusion.start|date:"d F" }}
{% endif %}
</a>
<a href="{{ object.program.get_absolute_url }}">
{{ object.program.title }}
</a>
{% else %}
{{ block.super }}
{{ block.super }}
{% endif %}
{% endblock %}
{% block class %}
{% if object.is_now %}is-active{% endif %}
{% endblock %}
{% block subtitle %}
{{ block.super }}
{% if diffusion %}
{% if not hide_schedule %}
{% if object.category %}&mdash;{% endif %}
<time datetime="{{ diffusion.start|date:"c" }}" title="{{ diffusion.start }}">
{{ diffusion.start|date:"d M, H:i" }}
</time>
{% endif %}
{{ diffusion.start|naturalday }},
{{ diffusion.start|date:"g:i" }}
{% else %}
{{ block.super }}
{% endif %}
{% endblock %}
{% if diffusion.initial %}
{% with diffusion.initial.date as date %}
<span title="{% blocktranslate %}Rerun of {{ date }}{% endblocktranslate %}">
{% translate "(rerun)" %}
</span>
{% block content %}
{% if not object.content %}
{% with object.parent.content as content %}
{{ block.super }}
{% endwith %}
{% endif %}
{% endif %}
{% endblock %}
{% block actions %}
{% if object.sound_set.public.count %}
<button class="button" @click="player.playButtonClick($event)"
data-sounds="{{ object.podcasts|json }}">
<span class="icon is-small">
<span class="fas fa-play"></span>
</span>
</button>
{% else %}
{{ block.super }}
{% endif %}
{% endblock %}

View File

@ -0,0 +1,34 @@
{% extends "./preview.html" %}
{% load i18n aircox %}
{% block tag-class %}{{ block.super }} list-item is-fullwidth{% endblock %}
{% block headings %}
<a href="{{ url|escape }}" class="heading title {% block title-class %}{% endblock %}">
{% block title %}{{ title|default:"" }}{% endblock %}
</a>
<span class="heading subtitle {% block subtitle-class %}{% endblock %}">
{% block subtitle %}{{ subtitle|default:"" }}{% endblock %}
</span>
{% endblock %}
{% block inner %}
{% block headings-container %}{{ block.super }}{% endblock %}
{% block content-container %}
<div class="media">
{% if object.cover and not no_cover %}
<a href="{{ object.get_absolute_url }}"
class="media-left preview-cover small"
style="background-image: url({{ object.cover.url }})">
</a>
{% endif %}
<div class="media-content flex-column">
<section class="content flex-grow-1">
{% block content %}{{ block.super }}{% endblock %}
</section>
{% block actions-container %}{{ block.super }}{% endblock %}
</div>
</div>
{% endblock %}
{% endblock %}

View File

@ -0,0 +1,39 @@
{% comment %}
Context:
- is_paginated: if True, page is paginated
- page_obj: page object from list view;
{% endcomment %}
{% load i18n aircox %}
{% if is_paginated %}
<hr/>
{% update_query request.GET.copy page=None as GET %}
{% with GET.urlencode as GET %}
<nav class="nav-urls is-centered" role="pagination" aria-label="{% translate "pagination" %}">
<ul class="urls">
{% if page_obj.has_previous %}
{% comment %}Translators: Bottom of the list, "previous page"{% endcomment %}
<a href="?{{ GET }}&page={{ page_obj.previous_page_number }}" class="left"
title="{% translate "Previous" %}"
aria-label="{% translate "Previous" %}">
<span class="icon"><i class="fa fa-chevron-left"></i></span>
</a>
{% endif %}
<span>
{{ page_obj.number }} / {{ page_obj.paginator.num_pages }}
</span>
{% if page_obj.has_next %}
{% comment %}Translators: Bottom of the list, "Nextpage"{% endcomment %}
<a href="?{{ GET }}&page={{ page_obj.next_page_number }}" class="right"
title="{% translate "Next" %}"
aria-label="{% translate "Next" %}">
<span class="icon"><i class="fa fa-chevron-right"></i></span>
</a>
{% endif %}
</ul>
</nav>
{% endwith %}
{% endif %}

View File

@ -0,0 +1,23 @@
{% load i18n aircox %}
{% comment %}
List item for a log, either for a logged track or diffusion (as diffusion).
Context objects:
- object: object to render
- hide_schedule: if true, hide the schedule
In case of modification, you might want to check on `assets/vue/player.vue`
for design review.
{% endcomment %}
{% block outer %}
{% if object|is_diffusion %}
{% page_widget widget object.episode diffusion=object timetable=timetable|default:False %}
{% elif object|is_log %}
{% include "./track_item.html" with object=object.track log=object timetable=timetable|default:False %}
{% else %}
{% for obj in object %}
{% include "./track_item.html" with object=obj.track log=obj timetable=timetable|default:False %}
{% endfor %}
{% endif %}
{% endblock %}

View File

@ -1,22 +0,0 @@
{% load i18n aircox %}
{% comment %}
List item for a log, either for a logged track or diffusion (as diffusion).
Context objects:
- object: object to render
- hide_schedule: if true, hide the schedule
In case of modification, you might want to check on `assets/vue/player.vue`
for design review.
{% endcomment %}
{% if object|is_diffusion %}
{% with object as diffusion %}
{% include "aircox/widgets/diffusion_item.html" %}
{% endwith %}
{% else %}
{% with object.track as object %}
{% include "aircox/widgets/track_item.html" %}
{% endwith %}
{% endif %}

View File

@ -1,30 +0,0 @@
{% comment %}
Render list of logs (as widget).
Context:
- object_list: list of logs to display
- is_thin: if True, hide some information in order to fit in a thin container
{% endcomment %}
{% load aircox %}
{% with True as hide_schedule %}
<table class="table is-striped is-hoverable is-fullwidth" role="list">
{% for object in object_list %}
<tr {% if object|is_diffusion and object.is_now %}class="is-selected"{% endif %}>
<td>
{% if object|is_diffusion %}
<time datetime="{{ object.start }}" title="{{ object.start }}">
{{ object.start|date:"H:i" }}
{% if not is_thin %} - {{ object.end|date:"H:i" }}{% endif %}
</time>
{% else %}
<time datetime="{{ object.date }}" title="{{ object.date }}">
{{ object.date|date:"H:i" }}
</time>
{% endif %}
</td>
<td>{% include "aircox/widgets/log_item.html" %}</td>
</tr>
{% endfor %}
</table>
{% endwith %}

View File

@ -0,0 +1,35 @@
{% comment %}
Context:
- object_list: list of logs
- timetable: defaults to False
- widget: defaults to "item"
{% endcomment %}
{% load aircox %}
{% with timetable|default:False as timetable %}
{% with widget|default:"item" as widget %}
{% for object in object_list %}
{% if object.episode %}
{% page_widget widget object.episode diffusion=object timetable=True %}
{% elif object|is_log %}
{% include "./track_item.html" with object=object.track log=object timetable=True %}
{% else %}
<div class="preview list-item logs">
<header class="headings">
<span class="heading title">
<span class="icon pr-2">
<i class="fas fa-music"></i>
</span>
{{ station.music_stream_title }}
</span>
</header>
<div class="media d-block content">
{% for obj in object %}
{% include "./track_item.html" with object=obj.track log=obj timetable=True %}
{% endfor %}
</div>
</div>
{% endif %}
{% endfor %}
{% endwith %}
{% endwith %}

View File

@ -0,0 +1,50 @@
{% load aircox i18n %}
<div class="dropdown is-hoverable is-right">
<div class="dropdown-trigger">
<button class="button square" aria-haspopup="true" aria-controls="dropdown-menu" type="button">
<span class="icon">
<i class="fa fa-user" aria-hidden="true"></i>
</span>
</button>
</div>
<div class="dropdown-menu" id="dropdown-menu" role="menu" style="z-index:200">
<div class="dropdown-content">
{% block user-menu %}
<a class="dropdown-item" href="{% url "dashboard" %}" data-force-reload="1">
{% translate "Dashboard" %}
</a>
{% if user|has_perm:"list_user" %}
<a class="dropdown-item" href="{% url "user-list" %}" data-force-reload="1">
{% translate "Users" %}
</a>
{% endif %}
{% endblock %}
{% comment %}
{% block edit-menu %}
{% if request.user|has_perm:"aircox.create_program" %}
<a class="dropdown-item" href="{% url "program-create" %}">
{% translate "Create Program" %}
</a>
{% endif %}
{% endblock %}
{% endcomment %}
{% if user.is_superuser %}
<hr class="dropdown-divider" />
{% block admin-menu %}
<a class="dropdown-item" href="{% url "admin:index" %}" target="new">
{% translate "Admin" %}
</a>
<a class="dropdown-item" href="{% url "dashboard-statistics" %}">
{% translate "Statistics" %}
</a>
{% endblock %}
<hr class="dropdown-divider" />
{% endif %}
<a class="dropdown-item" href="{% url "logout" %}" data-force-reload="1">
{% translate "Disconnect" %}
</a>
</div>
</div>
</div>

View File

@ -0,0 +1,38 @@
{% extends widget_template %}
{% load i18n aircox %}
{% block outer %}
{% with cover|default:object.cover_url as cover %}
{% with url|default:object.get_absolute_url as url %}
{{ block.super }}
{% endwith %}
{% endwith %}
{% endblock %}
{% block title %}
{% if title %}
{{ block.super }}
{% elif object %}
{{ object.display_title }}
{% endif %}
{% endblock %}
{% block content %}
{% if not content and object %}
{% with object.display_headline as content %}
{{ block.super }}
{% endwith %}
{% else %}
{{ block.super }}
{% endif %}
{% endblock %}
{% block actions %}
{% if url and "card" not in widget_template %}
<a href="{{ url }}">{% translate "Show" %}</a>
{% endif %}
{{ block.super }}
{% endblock %}

View File

@ -0,0 +1,33 @@
{% load aircox i18n %}
{% block user-actions-container %}
{% if user.is_authenticated %}
{{ object.get_status_display }}
{% if object.pub_date %}
({{ object.pub_date|date:"d/m/Y H:i" }})
{% endif %}
{% endif %}
{% if user.is_authenticated and can_edit %}
{% with request.resolver_match.view_name as view_name %}
&nbsp;
{% if "-edit" in view_name %}
<a href="{% url view_name|detail_view page.slug %}" target="_self" title="{% translate 'View' %} {{ page }}">
<span class="icon">
<i class="fa-regular fa-eye"></i>
</span>
<span>{% translate 'View' %} </span>
</a>
{% else %}
<a href="{% url view_name|edit_view page.pk %}" target="_self" title="{% translate 'Edit' %} {{ page }}">
<span class="icon">
<i class="fa-solid fa-pencil"></i>
</span>
<span>{% translate 'Edit' %} </span>
</a>
{% endif %}
{% endwith %}
{% endif %}
{% endblock %}

View File

@ -0,0 +1,13 @@
{% extends widget|default:"./card.html" %}
{% block outer %}
{% if object %}
{% with content=object.get_display_excerpt() %}
{% with title=object.get_display_title() %}
{{ block.super }}
{% endwith %}
{% endwith %}
{% else %}
{{ block.super }}
{% endif %}
{% endblock %}

View File

@ -3,3 +3,11 @@
{% block card_title %}
{% block title %}{{ block.super }}{% endblock %}
{% endblock %}
{% block card_subtitle %}
{% block subtitle %}{{ block.super }}{% endblock %}
{% endblock %}
{% block card_class %}
{% block class %}{{ block.super }}{% endblock %}
{% endblock %}

View File

@ -5,10 +5,10 @@ Context:
- object_list: object list
- list_url: url to complete list page
{% endcomment %}
{% load i18n %}
{% load i18n aircox %}
{% for object in object_list %}
{% include object.item_template_name %}
{% page_widget "item" object %}
{% endfor %}
{% if list_url %}

View File

@ -5,7 +5,7 @@ The audio player
<br>
<div class="box is-fullwidth is-fixed-bottom is-paddingless player"
<div class="is-fullwidth is-fixed-bottom is-paddingless player-container"
role="{% translate "player" %}"
aria-description="{% translate "Audio player used to listen to the radio and podcasts" %}">
<noscript>
@ -20,26 +20,32 @@ The audio player
<a-player ref="player"
:live-args="{% player_live_attr %}"
:playlists="{pin: ['{% translate "Bookmarks" %}', 'fa fa-star'], queue: ['{% translate 'Playlist' %}', 'fa fa-list']}"
button-title="{% translate "Play or pause audio" %}">
<template v-slot:content="{ loaded, live, current }">
<h4 v-if="loaded" class="title is-4">
[[ loaded.name ]]
<h4 v-if="loaded" class="title">
<a v-if="current?.data?.page_url" :href="current.data.page_url">
[[ loaded.name ]]
</a>
<template v-else>[[ loaded.name ]]</template>
</h4>
<h4 v-else-if="current && current.data.type == 'track'"
class="title is-4" aria-description="{% translate "Track currently on air" %}">
<span class="has-text-info is-size-3">&#9836;</span>
class="title" aria-description="{% translate "Track currently on air" %}">
<span class="icon secondary-color mr-3">
<i class="fas fa-music"></i>
</span>
<span>[[ current.data.title ]]</span>
<span class="has-text-grey-dark has-text-weight-light">
&mdash; [[ current.data.artist ]]
<i v-if="current.data.info">([[ current.data.info ]])</i>
</span>
</h4>
<div v-else-if="live && current && current.data.type == 'diffusion'">
<h4 class="title is-4" aria-description="{% translate "Diffusion currently on air" %}">
<a :href="current.data.url">[[ current.data.title ]]</a>
<h4 v-else-if="live && current && current.data.type == 'diffusion'"
class="title"
aria-description="{% translate "Diffusion currently on air" %}">
<a :href="current.data.url" v-if="current.data.url">[[ current.data.title ]]</a>
<template v-else>[[ current.data.title ]]</template>
</h4>
<div class="">[[ current.data.info ]]</div>
</div>
<h4 v-else class="title is-4" aria-description="{% translate "Currently playing" %}">
{{ request.station.name }}
</h4>

View File

@ -0,0 +1,70 @@
{% load i18n %}
{% comment %}
Content related context:
- object: object to display
- cover: cover
- title: title
- subtitle: subtitle
- content: content to display
Components:
- no_cover: don't show cover
- no_content: don't show content
Styling related context:
- is_active: add "active" css class
- is_small: add "small" css class
- is_tiny: add "tiny" css class
- tag
- tag_class: css class to set to main tag
- tag_extra: extra tag attributes
{% endcomment %}
{% load aircox %}
{% block outer %}
<{{ tag|default:"article" }} id="{{ object|object_id }}" class="preview {% if not cover %}no-cover {% endif %}{% if is_active %}active {% endif %}{% if is_tiny %}tiny{% elif is_small %}small{% endif %}{% block tag-class %}{{ tag_class|default:"" }} {% endblock %}" {% block tag-extra %}{% endblock %}>
{% block inner %}
{% block headings-container %}
<header class="headings{% block headings-class %}{% endblock %}"{% block headings-tag-extra %}{% endblock %}>
{% block headings %}
{% block title-container %}
<a href="{{ url|escape }}" class="heading title {% block title-class %}{% endblock %}"{% if title %} title="{{ title|escape }}"{% endif %}>
{% block title %}{{ title|default:"" }}{% endblock %}
</a>
{% endblock %}
{% block subtitle-container %}
<span class="heading subtitle {% block subtitle-class %}{% endblock %}">
{% block subtitle %}{{ subtitle|default:"" }}{% endblock %}
</span>
{% endblock %}
{% endblock %}
</header>
{% endblock %}
{% block content-container %}
<section class="content headings-container">
{% block content %}
{% if content and not no_content %}
{% autoescape off %}
{{ content|striptags|linebreaks }}
{% endautoescape %}
{% endif %}
{% endblock %}
</section>
{% endblock %}
{% block actions-container %}
{% spaceless %}
<div class="actions">
{% block actions %}
{% if admin and object.edit_url_name %}
<a href="{% url object.edit_url_name pk=object.pk %}">{% translate "Edit" %}</a>
{% endif %}
{% endblock %}
</div>
{% endspaceless %}
{% endblock %}
{% endblock %}
</{{ tag|default:"article" }}>
{% endblock %}

View File

@ -5,9 +5,20 @@ Context:
- object: track to render
{% endcomment %}
<span class="has-text-info is-size-5">&#9836;</span>
<span>{{ object.title }}</span>
<span class="has-text-grey-dark has-text-weight-light">
&mdash; {{ object.artist }}
{% if object.info %}(<i>{{ object.info }}</i>){% endif %}
<span class="track">
<span class="icon secondary-color">
<i class="fas fa-music"></i>
</span>
<label>
{% if log %}
<span>{{ log.date|date:"H:i" }} &mdash; </span>
{% endif %}
<span class="has-text-weight-boldk">{{ object.title }}</span>
{% if object.artist and object.artist != object.title %}
<span>
&mdash; {{ object.artist }}
{% if object.info %}(<i>{{ object.info }}</i>){% endif %}
</span>
{% endif %}
</label>
</span>

View File

@ -0,0 +1,43 @@
{% extends "./preview.html" %}
{% load i18n aircox %}
{% block tag-class %}{{ block.super }} list-item wide is-fullwidth{% endblock %}
{% block headings %}
<a href="{{ url|escape }}" class="heading title {% block title-class %}{% endblock %}">
{% block title %}{{ title|default:"" }}{% endblock %}
</a>
<span class="heading subtitle {% block subtitle-class %}{% endblock %}">
{% block subtitle %}{{ subtitle|default:"" }}{% endblock %}
</span>
{% endblock %}
{% block inner %}
{% block content-container %}
<div class="media">
{% if object.cover %}
<a href="{{ object.get_absolute_url }}"
class="media-left preview-cover"
style="background-image: url({{ object.cover.url }})">
</a>
{% endif %}
<div class="media-content">
{% block headings-container %}{{ block.super }}{% endblock %}
<section class="content">
{% block content %}
{% if content and with_content %}
{% autoescape off %}
{{ content|striptags|linebreaks }}
{% endautoescape %}
{% endif %}
{% endblock %}
</section>
{% block actions-container %}
{{ block.super }}
{% endblock %}
</div>
{% endblock %}
{% endblock %}

View File

@ -0,0 +1,18 @@
{% extends "aircox/base.html" %}
{% load i18n aircox %}
{% block content-container %}
<div class="container content page-content">
<h2>{% trans "Log in" %}</h2>
<br/>
<form method="post" action="{% url 'login' %}">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<br/>
<button class="button" type="submit">{% trans "Log in" %}</button>
<input type="hidden" name="next" value="{{ next }}">
</form>
</div>
{% endblock %}