work on admin ui
This commit is contained in:
parent
b96f60e779
commit
2d21ab2434
|
@ -5,3 +5,4 @@ from .program import ProgramAdmin, ScheduleAdmin, StreamAdmin
|
||||||
from .sound import SoundAdmin, TrackAdmin
|
from .sound import SoundAdmin, TrackAdmin
|
||||||
from .station import StationAdmin
|
from .station import StationAdmin
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ from copy import deepcopy
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.utils.translation import ugettext_lazy as _
|
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
|
from .page import PageAdmin
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,7 +26,8 @@ class ProgramAdmin(PageAdmin):
|
||||||
schedule.boolean = True
|
schedule.boolean = True
|
||||||
schedule.short_description = _("Schedule")
|
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) + [
|
fieldsets = deepcopy(PageAdmin.fieldsets) + [
|
||||||
(_('Program Settings'), {
|
(_('Program Settings'), {
|
||||||
'fields': ['active', 'station', 'sync'],
|
'fields': ['active', 'station', 'sync'],
|
||||||
|
|
|
@ -3,7 +3,7 @@ from django.utils.translation import ugettext as _, ugettext_lazy
|
||||||
|
|
||||||
from adminsortable2.admin import SortableInlineAdminMixin
|
from adminsortable2.admin import SortableInlineAdminMixin
|
||||||
|
|
||||||
from aircox.models import Sound, Track
|
from ..models import Sound, Track
|
||||||
|
|
||||||
|
|
||||||
class TracksInline(SortableInlineAdminMixin, admin.TabularInline):
|
class TracksInline(SortableInlineAdminMixin, admin.TabularInline):
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
|
from django.contrib.admin.apps import AdminConfig
|
||||||
|
|
||||||
|
|
||||||
class AircoxConfig(AppConfig):
|
class AircoxConfig(AppConfig):
|
||||||
name = 'aircox'
|
name = 'aircox'
|
||||||
verbose_name = 'Aircox'
|
verbose_name = 'Aircox'
|
||||||
|
|
||||||
def ready(self):
|
|
||||||
pass
|
class AircoxAdminConfig(AdminConfig):
|
||||||
|
default_site = 'aircox.views.admin.AdminSite'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
20
aircox/forms.py
Normal file
20
aircox/forms.py
Normal 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']
|
||||||
|
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -31,6 +31,9 @@ class ProgramQuerySet(PageQuerySet):
|
||||||
# FIXME: reverse-lookup
|
# FIXME: reverse-lookup
|
||||||
return self.filter(station=station)
|
return self.filter(station=station)
|
||||||
|
|
||||||
|
def active(self):
|
||||||
|
return self.filter(active=True)
|
||||||
|
|
||||||
|
|
||||||
class Program(Page):
|
class Program(Page):
|
||||||
"""
|
"""
|
||||||
|
|
18
aircox/static/aircox/admin.css
Normal file
18
aircox/static/aircox/admin.css
Normal 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; }
|
||||||
|
|
26
aircox/static/aircox/admin.js
Normal file
26
aircox/static/aircox/admin.js
Normal 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
BIN
aircox/static/aircox/logo.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 7.1 KiB |
13
aircox/templates/admin/aircox/statistics.html
Normal file
13
aircox/templates/admin/aircox/statistics.html
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{% extends "admin/base_site.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block content %}{{ block.super }}
|
||||||
|
<table class="table">
|
||||||
|
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -30,19 +30,44 @@
|
||||||
|
|
||||||
{% if not is_popup %}
|
{% if not is_popup %}
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<nav class="navbar">
|
<nav class="navbar is-dark">
|
||||||
<div class="navbar-brand">
|
<div class="navbar-brand">
|
||||||
{% block branding %}{% endblock %}
|
{% block branding %}{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
<div class="navbar-menu">
|
<div class="navbar-menu">
|
||||||
{% block usertools %}
|
{% block usertools %}
|
||||||
<div class="navbar-start">
|
<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>
|
<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>
|
</div>
|
||||||
|
|
||||||
{% if has_permission %}
|
|
||||||
<div class="navbar-end">
|
<div class="navbar-end">
|
||||||
<div class="navbar-item has-dropdown is-hoverable">
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
<a href="{% url "admin:auth_user_change" user.pk %}" class="navbar-link">
|
<a href="{% url "admin:auth_user_change" user.pk %}" class="navbar-link">
|
||||||
|
@ -62,14 +87,14 @@
|
||||||
{% if user.has_usable_password %}
|
{% if user.has_usable_password %}
|
||||||
<a href="{% url 'admin:password_change' %}" class="navbar-item">{% trans 'Change password' %}</a>
|
<a href="{% url 'admin:password_change' %}" class="navbar-item">{% trans 'Change password' %}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<hr class="navbar-divider" />
|
||||||
<a href="{% url 'admin:logout' %}" class="navbar-item">{% trans 'Log out' %}</a>
|
<a href="{% url 'admin:logout' %}" class="navbar-item">{% trans 'Log out' %}</a>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endblock %}
|
||||||
{% endblock %}
|
|
||||||
</div>
|
</div>
|
||||||
{% block nav-global %}{% endblock %}
|
{% block nav-global %}{% endblock %}
|
||||||
</nav>
|
</nav>
|
||||||
|
@ -94,7 +119,7 @@
|
||||||
<!-- Content -->
|
<!-- Content -->
|
||||||
<div id="content" class="{% block coltype %}colM{% endblock %}">
|
<div id="content" class="{% block coltype %}colM{% endblock %}">
|
||||||
{% block pretitle %}{% 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 content %}
|
||||||
{% block object-tools %}{% endblock %}
|
{% block object-tools %}{% endblock %}
|
||||||
{{ content }}
|
{{ content }}
|
||||||
|
|
82
aircox/templates/aircox/page_detail.html
Normal file
82
aircox/templates/aircox/page_detail.html
Normal 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 %}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from django.contrib import admin
|
||||||
from django.urls import include, path, register_converter
|
from django.urls import include, path, register_converter
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
@ -24,6 +25,7 @@ api = [
|
||||||
|
|
||||||
urls = [
|
urls = [
|
||||||
path('api/', include(api)),
|
path('api/', include(api)),
|
||||||
|
|
||||||
# path('', views.PageDetailView.as_view(model=models.Article),
|
# path('', views.PageDetailView.as_view(model=models.Article),
|
||||||
# name='home'),
|
# name='home'),
|
||||||
path(_('articles/'),
|
path(_('articles/'),
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from . import api
|
from . import api, admin
|
||||||
|
|
||||||
from .article import ArticleDetailView, ArticleListView
|
from .article import ArticleDetailView, ArticleListView
|
||||||
from .base import BaseView
|
from .base import BaseView
|
||||||
|
|
64
aircox/views/admin.py
Normal file
64
aircox/views/admin.py
Normal 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()
|
||||||
|
|
|
@ -19,7 +19,6 @@ class BaseAPIView:
|
||||||
|
|
||||||
|
|
||||||
class LiveAPIView(BaseLogListView, BaseAPIView, ListAPIView):
|
class LiveAPIView(BaseLogListView, BaseAPIView, ListAPIView):
|
||||||
model = Log
|
|
||||||
serializer_class = LogInfoSerializer
|
serializer_class = LogInfoSerializer
|
||||||
min_date = None
|
min_date = None
|
||||||
queryset = Log.objects.all()
|
queryset = Log.objects.all()
|
||||||
|
|
|
@ -11,6 +11,7 @@ __all__ = ['BaseLogListView', 'LogListView']
|
||||||
|
|
||||||
|
|
||||||
class BaseLogListView:
|
class BaseLogListView:
|
||||||
|
model = Log
|
||||||
date = None
|
date = None
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
|
@ -23,8 +24,6 @@ class BaseLogListView:
|
||||||
|
|
||||||
|
|
||||||
class LogListView(BaseView, BaseLogListView, ListView):
|
class LogListView(BaseView, BaseLogListView, ListView):
|
||||||
model = Log
|
|
||||||
|
|
||||||
date = None
|
date = None
|
||||||
max_age = 10
|
max_age = 10
|
||||||
min_date = None
|
min_date = None
|
||||||
|
@ -37,8 +36,6 @@ class LogListView(BaseView, BaseLogListView, ListView):
|
||||||
return super().get(request, *args, **kwargs)
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_queryset(self):
|
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)
|
return super().get_queryset().today(self.date)
|
||||||
|
|
||||||
def get_diffusions_queryset(self):
|
def get_diffusions_queryset(self):
|
||||||
|
|
Loading…
Reference in New Issue
Block a user