forked from rc/aircox
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 .station import StationAdmin
|
||||
|
||||
|
||||
|
|
|
@ -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'],
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
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
|
||||
return self.filter(station=station)
|
||||
|
||||
def active(self):
|
||||
return self.filter(active=True)
|
||||
|
||||
|
||||
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 %}
|
||||
<!-- 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,14 +87,14 @@
|
|||
{% 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 %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
{% block nav-global %}{% endblock %}
|
||||
</nav>
|
||||
|
@ -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 }}
|
||||
|
|
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.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/'),
|
||||
|
|
|
@ -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
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):
|
||||
model = Log
|
||||
serializer_class = LogInfoSerializer
|
||||
min_date = None
|
||||
queryset = Log.objects.all()
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue
Block a user