work on admin ui

This commit is contained in:
bkfox 2019-09-05 23:05:04 +02:00
parent b96f60e779
commit 2d21ab2434
19 changed files with 274 additions and 18 deletions

View File

@ -5,3 +5,4 @@ from .program import ProgramAdmin, ScheduleAdmin, StreamAdmin
from .sound import SoundAdmin, TrackAdmin
from .station import StationAdmin

View File

@ -3,7 +3,7 @@ from copy import deepcopy
from django.contrib import admin
from django.utils.translation import ugettext_lazy as _
from aircox.models import Program, Schedule, Stream
from ..models import Program, Schedule, Stream
from .page import PageAdmin
@ -26,7 +26,8 @@ class ProgramAdmin(PageAdmin):
schedule.boolean = True
schedule.short_description = _("Schedule")
list_display = PageAdmin.list_display + ('schedule', 'station')
list_display = PageAdmin.list_display + ('schedule', 'station', 'active')
list_filter = PageAdmin.list_filter + ('station', 'active')
fieldsets = deepcopy(PageAdmin.fieldsets) + [
(_('Program Settings'), {
'fields': ['active', 'station', 'sync'],

View File

@ -3,7 +3,7 @@ from django.utils.translation import ugettext as _, ugettext_lazy
from adminsortable2.admin import SortableInlineAdminMixin
from aircox.models import Sound, Track
from ..models import Sound, Track
class TracksInline(SortableInlineAdminMixin, admin.TabularInline):

View File

@ -1,9 +1,14 @@
from django.apps import AppConfig
from django.contrib.admin.apps import AdminConfig
class AircoxConfig(AppConfig):
name = 'aircox'
verbose_name = 'Aircox'
def ready(self):
pass
class AircoxAdminConfig(AdminConfig):
default_site = 'aircox.views.admin.AdminSite'

20
aircox/forms.py Normal file
View File

@ -0,0 +1,20 @@
from django import forms
from django.forms import ModelForm
from .models import Comment
class CommentForm(ModelForm):
nickname = forms.CharField()
email = forms.EmailField(required=False)
content = forms.CharField(widget=forms.Textarea())
nickname.widget.attrs.update({'class': 'input'})
email.widget.attrs.update({'class': 'input'})
content.widget.attrs.update({'class': 'textarea'})
class Meta:
model = Comment
fields = ['nickname', 'email', 'content']

View File

@ -31,6 +31,9 @@ class ProgramQuerySet(PageQuerySet):
# FIXME: reverse-lookup
return self.filter(station=station)
def active(self):
return self.filter(active=True)
class Program(Page):
"""

View File

@ -0,0 +1,18 @@
.navbar {
box-shadow: 0em 0em 0.6em rgba(0, 0, 0, 0.4); }
.navbar .navbar-brand {
padding-right: 1em; }
.navbar .navbar-brand img {
margin: 0em 0.4em;
margin-top: 0.3em;
max-height: 3em; }
.breadcrumbs {
margin-bottom: 1em; }
.results > #result_list {
width: 100%;
margin: 1em 0em; }

View File

@ -0,0 +1,26 @@
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([["admin"],{
/***/ "./assets/admin/admin.scss":
/*!*********************************!*\
!*** ./assets/admin/admin.scss ***!
\*********************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
eval("// extracted by mini-css-extract-plugin\n\n//# sourceURL=webpack:///./assets/admin/admin.scss?");
/***/ }),
/***/ "./assets/admin/index.js":
/*!*******************************!*\
!*** ./assets/admin/index.js ***!
\*******************************/
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("/* harmony import */ var _admin_scss__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./admin.scss */ \"./assets/admin/admin.scss\");\n/* harmony import */ var _admin_scss__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_admin_scss__WEBPACK_IMPORTED_MODULE_0__);\n\n\n\n\n\n//# sourceURL=webpack:///./assets/admin/index.js?");
/***/ })
}]);

BIN
aircox/static/aircox/logo.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@ -0,0 +1,13 @@
{% extends "admin/base_site.html" %}
{% load i18n %}
{% block content %}{{ block.super }}
<table class="table">
</table>
{% endblock %}

View File

@ -30,19 +30,44 @@
{% if not is_popup %}
<!-- Header -->
<nav class="navbar">
<nav class="navbar is-dark">
<div class="navbar-brand">
{% block branding %}{% endblock %}
</div>
<div class="navbar-menu">
{% block usertools %}
<div class="navbar-start">
<a class="navbar-item" href="{% url "admin:aircox_article_changelist" %}">{% trans "Articles" %}</a>
<a class="navbar-item" href="{% url "admin:aircox_episode_changelist" %}">{% trans "Episodes" %}</a>
<a class="navbar-item" href="{% url "admin:aircox_program_changelist" %}">{% trans "Programs" %}</a>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link" href="{% url "admin:aircox_article_changelist" %}">{% trans "Articles" %}</a>
<div class="navbar-dropdown is-boxed is-right">
{% for program in programs %}
<a class="navbar-item" href="{% url "admin:aircox_article_changelist" %}?program={{ program.pk }}">
{{ program.title }}</a>
{% endfor %}
</div>
</div>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link" href="{% url "admin:aircox_episode_changelist" %}">{% trans "Episodes" %}</a>
<div class="navbar-dropdown is-boxed is-right">
{% for program in programs %}
<a class="navbar-item" href="{% url "admin:aircox_episode_changelist" %}?program={{ program.pk }}">
{{ program.title }}</a>
{% endfor %}
</div>
</div>
<div class="navbar-item has-dropdown is-hoverable">
<a href="#" class="navbar-link">{% trans "Tools" %}</a>
<div class="navbar-dropdown is-boxed is-right">
<a href="{% url 'admin:tools-stats' %}" class="navbar-item">{% trans "Statistics" %}</a>
</div>
</div>
</div>
{% if has_permission %}
<div class="navbar-end">
<div class="navbar-item has-dropdown is-hoverable">
<a href="{% url "admin:auth_user_change" user.pk %}" class="navbar-link">
@ -62,13 +87,13 @@
{% if user.has_usable_password %}
<a href="{% url 'admin:password_change' %}" class="navbar-item">{% trans 'Change password' %}</a>
{% endif %}
<hr class="navbar-divider" />
<a href="{% url 'admin:logout' %}" class="navbar-item">{% trans 'Log out' %}</a>
{% endblock %}
</div>
</div>
</div>
{% endif %}
{% endblock %}
</div>
{% block nav-global %}{% endblock %}
@ -94,7 +119,7 @@
<!-- Content -->
<div id="content" class="{% block coltype %}colM{% endblock %}">
{% block pretitle %}{% endblock %}
{% block content_title %}{% if title %}<h1>{{ title }}</h1>{% endif %}{% endblock %}
{% block content_title %}{% if title %}<h1 class="subtitle is-3">{{ title }}</h1>{% endif %}{% endblock %}
{% block content %}
{% block object-tools %}{% endblock %}
{{ content }}

View File

@ -0,0 +1,82 @@
{% extends "aircox/page.html" %}
{% load static i18n humanize honeypot %}
{% comment %}
Context:
- title: title
- page: page
{% endcomment %}
{% block subtitle %}
{{ block.super }}
{% if page.category %}
<span class="column has-text-right">{{ object.category.title }}</span>
{% endif %}
{% endblock %}
{% block main %}
{% block content %}
{{ object.content|default_if_none:''|safe }}
{% endblock %}
{% block comments %}
{% if comments or comment_form %}
<hr>
<section>
<h4 class="title is-4">{% trans "Comments" %}</h4>
{% for comment in comments %}
<div class="media box">
<div class="media-content">
<p>
<strong>{{ comment.nickname }}</strong>
<time datetime="{{ comment.date }}" title="{{ comment.date }}">
<small>{{ comment.date|naturaltime }}</small>
</time>
<br>
{{ comment.content }}
</p>
</div>
</div>
{% endfor %}
{% if comments and comment_form %}<hr>{% endif %}
{% if comment_form %}
<form method="POST">
<h5 class="title is-5">{% trans "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>
</div>
{% endfor %}
<div class="has-text-right">
<button type="reset" class="button is-danger">{% trans "Reset" %}</button>
<button type="submit" class="button is-success">{% trans "Comment" %}</button>
</div>
</form>
{% endif %}
</section>
{% endif %}
{% endblock %}
{% endblock %}

View File

@ -1,3 +1,4 @@
from django.contrib import admin
from django.urls import include, path, register_converter
from django.utils.translation import ugettext_lazy as _
@ -24,6 +25,7 @@ api = [
urls = [
path('api/', include(api)),
# path('', views.PageDetailView.as_view(model=models.Article),
# name='home'),
path(_('articles/'),

View File

@ -1,4 +1,4 @@
from . import api
from . import api, admin
from .article import ArticleDetailView, ArticleListView
from .base import BaseView

64
aircox/views/admin.py Normal file
View File

@ -0,0 +1,64 @@
"""
Aircox admin tools and views.
"""
import datetime
from django.contrib import admin
from django.contrib.auth.mixins import LoginRequiredMixin, \
PermissionRequiredMixin, UserPassesTestMixin
from django.utils.translation import ugettext_lazy as _
from django.views.generic import ListView
from ..models import Program
from .log import BaseLogListView
class BaseAdminView(LoginRequiredMixin, UserPassesTestMixin):
title = ''
def test_func(self):
return self.request.user.is_staff
def get_context_data(self, **kwargs):
kwargs.update(admin.site.each_context(self.request))
kwargs.setdefault('title', self.title)
return super().get_context_data(**kwargs)
class StatisticsView(BaseAdminView, BaseLogListView, ListView):
template_name = 'admin/aircox/statistics.html'
title = _('Statistics')
date = None
def get_queryset(self):
return super().get_queryset().today(self.date)
def get_diffusions_queryset(self):
return super().get_diffusions_queryset().today(self.date)
def get(self, request, *args, date=None, **kwargs):
self.date = datetime.date.today() if date is None else date
return super().get(request, *args, date=date, **kwargs)
class AdminSite(admin.AdminSite):
def each_context(self, request):
context = super().each_context(request)
context.update({
'programs': Program.objects.all().active().values('pk', 'title'),
})
return context
def get_urls(self):
from django.urls import path, include
urls = super().get_urls()
urls += [
path('tools/statistics/',
self.admin_view(StatisticsView.as_view()),
name='tools-stats'),
]
return urls
admin_site = AdminSite()

View File

@ -19,7 +19,6 @@ class BaseAPIView:
class LiveAPIView(BaseLogListView, BaseAPIView, ListAPIView):
model = Log
serializer_class = LogInfoSerializer
min_date = None
queryset = Log.objects.all()

View File

@ -11,6 +11,7 @@ __all__ = ['BaseLogListView', 'LogListView']
class BaseLogListView:
model = Log
date = None
def get_queryset(self):
@ -23,8 +24,6 @@ class BaseLogListView:
class LogListView(BaseView, BaseLogListView, ListView):
model = Log
date = None
max_age = 10
min_date = None
@ -37,8 +36,6 @@ class LogListView(BaseView, BaseLogListView, ListView):
return super().get(request, *args, **kwargs)
def get_queryset(self):
# only get logs for tracks: log for diffusion will be retrieved
# by the diffusions' queryset.
return super().get_queryset().today(self.date)
def get_diffusions_queryset(self):