work on admin ui
This commit is contained in:
		@ -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,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 }}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										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):
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user