forked from rc/aircox
		
	work hard on this
This commit is contained in:
		
							
								
								
									
										0
									
								
								aircox_web/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								aircox_web/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										68
									
								
								aircox_web/admin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								aircox_web/admin.py
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										5
									
								
								aircox_web/apps.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
			
		||||
from django.apps import AppConfig
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AircoxWebConfig(AppConfig):
 | 
			
		||||
    name = 'aircox_web'
 | 
			
		||||
							
								
								
									
										1
									
								
								aircox_web/assets/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								aircox_web/assets/index.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
import './js';
 | 
			
		||||
							
								
								
									
										12
									
								
								aircox_web/assets/js/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								aircox_web/assets/js/index.js
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										117
									
								
								aircox_web/models.py
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										26
									
								
								aircox_web/package.json
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										20
									
								
								aircox_web/renderer.py
									
									
									
									
									
										Normal 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,
 | 
			
		||||
    ),
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										34
									
								
								aircox_web/templates/aircox_web/base.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								aircox_web/templates/aircox_web/base.html
									
									
									
									
									
										Normal 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>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										8
									
								
								aircox_web/templates/aircox_web/page.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								aircox_web/templates/aircox_web/page.html
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										3
									
								
								aircox_web/tests.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
			
		||||
from django.test import TestCase
 | 
			
		||||
 | 
			
		||||
# Create your tests here.
 | 
			
		||||
							
								
								
									
										9
									
								
								aircox_web/urls.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								aircox_web/urls.py
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										22
									
								
								aircox_web/views.py
									
									
									
									
									
										Normal 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
 | 
			
		||||
        ),
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										87
									
								
								aircox_web/webpack.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								aircox_web/webpack.config.js
									
									
									
									
									
										Normal 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']
 | 
			
		||||
    },
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user