work hard on this

This commit is contained in:
bkfox
2019-06-29 18:13:25 +02:00
parent a951d7a319
commit 74dbc620ed
31 changed files with 1191 additions and 833 deletions

0
aircox_web/__init__.py Normal file
View File

68
aircox_web/admin.py Normal file
View File

@ -0,0 +1,68 @@
import copy
from django.contrib import admin
from django.utils.translation import ugettext_lazy as _
from content_editor.admin import ContentEditor
from feincms3 import plugins
from feincms3.admin import TreeAdmin
from aircox import models as aircox
from . import models
from aircox.admin.playlist import TracksInline
from aircox.admin.mixins import UnrelatedInlineMixin
@admin.register(models.SiteSettings)
class SettingsAdmin(admin.ModelAdmin):
pass
class PageDiffusionPlaylist(UnrelatedInlineMixin, TracksInline):
parent_model = aircox.Diffusion
fields = list(TracksInline.fields)
fields.remove('timestamp')
def get_parent(self, view_obj):
return view_obj and view_obj.diffusion
def save_parent(self, parent, view_obj):
parent.save()
view_obj.diffusion = parent
view_obj.save()
@admin.register(models.Page)
class PageAdmin(ContentEditor, TreeAdmin):
list_display = ["indented_title", "move_column", "is_active"]
prepopulated_fields = {"slug": ("title",)}
# readonly_fields = ('diffusion',)
fieldsets = (
(_('Main'), {
'fields': ['title', 'slug', 'by_program', 'summary'],
'classes': ('tabbed', 'uncollapse')
}),
(_('Settings'), {
'fields': ['show_author', 'featured', 'allow_comments',
'status', 'static_path', 'path'],
'classes': ('tabbed',)
}),
(_('Infos'), {
'fields': ['diffusion'],
'classes': ('tabbed',)
}),
)
inlines = [
plugins.richtext.RichTextInline.create(models.RichText),
plugins.image.ImageInline.create(models.Image),
]
def get_inline_instances(self, request, obj=None):
inlines = super().get_inline_instances(request, obj)
if obj and obj.diffusion:
inlines.insert(0, PageDiffusionPlaylist(self.model, self.admin_site))
return inlines

5
aircox_web/apps.py Normal file
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class AircoxWebConfig(AppConfig):
name = 'aircox_web'

View File

@ -0,0 +1 @@
import './js';

View File

@ -0,0 +1,12 @@
import Vue from 'vue';
import Buefy from 'buefy';
import 'buefy/dist/buefy.css';
Vue.use(Buefy);
var app = new Vue({
el: '#app',
})

117
aircox_web/models.py Normal file
View File

@ -0,0 +1,117 @@
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth import models as auth
from content_editor.models import Region, create_plugin_base
from feincms3 import plugins
from feincms3.pages import AbstractPage
from model_utils.models import TimeStampedModel, StatusModel
from model_utils import Choices
from filer.fields.image import FilerImageField
from aircox import models as aircox
class SiteSettings(models.Model):
station = models.ForeignKey(
aircox.Station, on_delete=models.SET_NULL, null=True,
)
# main settings
title = models.CharField(
_('Title'), max_length=32,
help_text=_('Website title used at various places'),
)
logo = FilerImageField(
on_delete=models.SET_NULL, null=True, blank=True,
verbose_name=_('Logo'),
related_name='+',
)
favicon = FilerImageField(
on_delete=models.SET_NULL, null=True, blank=True,
verbose_name=_('Favicon'),
related_name='+',
)
# meta descriptors
description = models.CharField(
_('Description'), max_length=128,
blank=True, null=True,
)
tags = models.CharField(
_('Tags'), max_length=128,
blank=True, null=True,
)
class Page(AbstractPage, TimeStampedModel, StatusModel):
STATUS = Choices('draft', 'published')
regions = [
Region(key="main", title=_("Content")),
Region(key="sidebar", title=_("Sidebar")),
]
# metadata
by = models.ForeignKey(
auth.User, models.SET_NULL, blank=True, null=True,
verbose_name=_('Author'),
)
by_program = models.ForeignKey(
aircox.Program, models.SET_NULL, blank=True, null=True,
related_name='authored_pages',
limit_choices_to={'schedule__isnull': False},
verbose_name=_('Show program as author'),
help_text=_("If nothing is selected, display user's name"),
)
# options
show_author = models.BooleanField(
_('Show author'), default=True,
)
featured = models.BooleanField(
_('featured'), default=False,
)
allow_comments = models.BooleanField(
_('allow comments'), default=True,
)
# content
title = models.CharField(
_('title'), max_length=64,
)
summary = models.TextField(
_('Summary'),
max_length=128, blank=True, null=True,
)
cover = FilerImageField(
on_delete=models.SET_NULL, null=True, blank=True,
verbose_name=_('Cover'),
)
diffusion = models.OneToOneField(
aircox.Diffusion, models.CASCADE,
blank=True, null=True,
)
PagePlugin = create_plugin_base(Page)
class RichText(plugins.richtext.RichText, PagePlugin):
pass
class Image(plugins.image.Image, PagePlugin):
caption = models.CharField(_("caption"), max_length=200, blank=True)
class ProgramPage(Page):
program = models.OneToOneField(
aircox.Program, models.CASCADE,
)

26
aircox_web/package.json Normal file
View File

@ -0,0 +1,26 @@
{
"name": "aircox-web-assets",
"version": "0.0.0",
"description": "Assets for Aircox Web",
"main": "index.js",
"author": "bkfox",
"license": "AGPL",
"devDependencies": {
"@fortawesome/fontawesome-free": "^5.8.2",
"mini-css-extract-plugin": "^0.5.0",
"css-loader": "^2.1.1",
"file-loader": "^3.0.1",
"ttf-loader": "^1.0.2",
"vue-loader": "^15.7.0",
"vue-style-loader": "^4.1.2",
"webpack": "^4.32.2",
"webpack-bundle-analyzer": "^3.3.2",
"webpack-bundle-tracker": "^0.4.2-beta",
"webpack-cli": "^3.3.2"
},
"dependencies": {
"bootstrap": "^4.3.1",
"buefy": "^0.7.8",
"vue": "^2.6.10"
}
}

20
aircox_web/renderer.py Normal file
View File

@ -0,0 +1,20 @@
from django.utils.html import format_html, mark_safe
from feincms3.renderer import TemplatePluginRenderer
from .models import Page, RichText, Image
renderer = TemplatePluginRenderer()
renderer.register_string_renderer(
RichText,
lambda plugin: mark_safe(plugin.text),
)
renderer.register_string_renderer(
Image,
lambda plugin: format_html(
'<figure><img src="{}" alt=""/><figcaption>{}</figcaption></figure>',
plugin.image.url,
plugin.caption,
),
)

View File

@ -0,0 +1,34 @@
{% load static thumbnail %}
<html>
<head>
<meta charset="utf-8">
<meta name="application-name" content="aircox">
<meta name="description" content="{{ site_settings.description }}">
<meta name="keywords" content="{{ site_settings.tags }}">
<link rel="icon" href="{% thumbnail site_settings.favicon 32x32 crop %}" />
{% block assets %}
<link rel="stylesheet" type="text/css" href="{% static "aircox_web/assets/main.css" %}"/>
<link rel="stylesheet" type="text/css" href="{% static "aircox_web/assets/vendor.css" %}"/>
<script src="{% static "aircox_web/assets/main.js" %}"></script>
<script src="{% static "aircox_web/assets/vendor.js" %}"></script>
{% endblock %}
<title>{% block title %}{{ site_settings.title }}{% endblock %}</title>
{% block extra_head %}{% endblock %}
</head>
<body id="app">
<nav class="navbar" role="navigation" aria-label="main navigation">
</nav>
<main>
{% block main %}
{% endblock main %}
</main>
</body>
</html>

View File

@ -0,0 +1,8 @@
{% extends "aircox_web/base.html" %}
{% block title %}{{ page.title }} -- {{ block.super }}{% endblock %}
{% block main %}
<h1 class="title">{{ page.title }}</h1>
{% endblock %}

3
aircox_web/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

9
aircox_web/urls.py Normal file
View File

@ -0,0 +1,9 @@
from django.conf.urls import url
from . import views
urlpatterns = [
url(r"^(?P<path>[-\w/]+)/$", views.page_detail, name="page"),
url(r"^$", views.page_detail, name="root"),
]

22
aircox_web/views.py Normal file
View File

@ -0,0 +1,22 @@
from django.shortcuts import get_object_or_404, render
from feincms3.regions import Regions
from .models import SiteSettings, Page
from .renderer import renderer
def page_detail(request, path=None):
page = get_object_or_404(
# TODO: published
Page.objects.all(),
path="/{}/".format(path) if path else "/",
)
return render(request, "aircox_web/page.html", {
'site_settings': SiteSettings.objects.all().first(),
"page": page,
"regions": Regions.from_item(
page, renderer=renderer, timeout=60
),
})

View File

@ -0,0 +1,87 @@
const path = require('path');
const webpack = require('webpack');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
// const { createLodashAliases } = require('lodash-loader');
const { VueLoaderPlugin } = require('vue-loader');
module.exports = (env, argv) => Object({
context: __dirname,
entry: './assets/index',
output: {
path: path.resolve('static/aircox_web/assets'),
filename: '[name].js',
chunkFilename: '[name].js',
},
optimization: {
usedExports: true,
concatenateModules: argv.mode == 'production' ? true : false,
splitChunks: {
cacheGroups: {
vendor: {
name: 'vendor',
chunks: 'initial',
enforce: true,
test: /[\\/]node_modules[\\/]/,
},
}
}
},
plugins: [
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
}),
new VueLoaderPlugin(),
],
module: {
rules: [
{
test: /\/node_modules\//,
sideEffects: false
},
{
test: /\.css$/,
use: [ { loader: MiniCssExtractPlugin.loader },
'css-loader' ]
},
{
// TODO: remove ttf eot svg
test: /\.(ttf|eot|svg|woff2?)$/,
use: [{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'fonts/',
}
}],
},
{ test: /\.vue$/, use: 'vue-loader' },
],
},
resolve: {
alias: {
js: path.resolve(__dirname, 'assets/js'),
vue: path.resolve(__dirname, 'assets/vue'),
css: path.resolve(__dirname, 'assets/css'),
vue: 'vue/dist/vue.esm.browser.js',
// buefy: 'buefy/dist/buefy.js',
},
modules: [
'assets/css',
'assets/js',
'assets/vue',
'./node_modules',
],
extensions: ['.js', '.vue', '.css', '.styl', '.ttf']
},
})