kopia lustrzana https://github.com/rtts/django-simplecms
New version! Modelled after Django's admin application, simplecms now allows
you to define everything related to it in the file 'cms.py'. No more inherited proxy models and no more migrations every time you add/remove a section type! In fact, the dependency on django-polymorphic has completely been removed! The example project has been updated and should get you started. Documentation will be coming soon!main
rodzic
09f03b6866
commit
3cc1f9ec08
|
@ -1 +1,4 @@
|
||||||
|
from .decorators import register
|
||||||
|
from .cms import SectionView, SectionFormView
|
||||||
|
|
||||||
default_app_config = 'cms.apps.CmsConfig'
|
default_app_config = 'cms.apps.CmsConfig'
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
from django.views.generic import edit
|
||||||
|
from django.http import HttpResponseRedirect
|
||||||
|
|
||||||
|
class SectionView:
|
||||||
|
'''Generic section view'''
|
||||||
|
template_name = 'cms/sections/section.html'
|
||||||
|
|
||||||
|
def __init__(self, request):
|
||||||
|
'''Initialize request attribute'''
|
||||||
|
self.request = request
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
'''Override this to customize a section's context'''
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
class SectionFormView(edit.FormMixin, SectionView):
|
||||||
|
'''Generic section with associated form'''
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
'''Process form'''
|
||||||
|
form = self.get_form()
|
||||||
|
if form.is_valid():
|
||||||
|
form.save(request)
|
||||||
|
return HttpResponseRedirect(self.get_success_url())
|
||||||
|
return form
|
|
@ -1,14 +1,25 @@
|
||||||
def register_model(verbose_name):
|
def register(verbose_name):
|
||||||
'''Decorator to register a section subclass'''
|
import swapper
|
||||||
def wrapper(model):
|
Section = swapper.load_model('cms', 'Section')
|
||||||
parent_model = model.__bases__[-1]
|
|
||||||
parent_model.TYPES.append((model.__name__.lower(), verbose_name))
|
'''Decorator to register a specific section type'''
|
||||||
return model
|
def wrapper(view):
|
||||||
|
Section._cms_views[view.__name__.lower()] = view
|
||||||
|
Section.TYPES.append((view.__name__.lower(), verbose_name))
|
||||||
|
return view
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
def register_view(section_class):
|
# def register_model(verbose_name):
|
||||||
'''Decorator to connect a section model to a view class'''
|
# '''Decorator to register a section subclass'''
|
||||||
def wrapper(model):
|
# def wrapper(model):
|
||||||
section_class.view_class = model
|
# parent_model = model.__bases__[-1]
|
||||||
return model
|
# parent_model.TYPES.append((model.__name__.lower(), verbose_name))
|
||||||
return wrapper
|
# return model
|
||||||
|
# return wrapper
|
||||||
|
|
||||||
|
# def register_view(section_class):
|
||||||
|
# '''Decorator to connect a section model to a view class'''
|
||||||
|
# def wrapper(model):
|
||||||
|
# section_class.view_class = model
|
||||||
|
# return model
|
||||||
|
# return wrapper
|
||||||
|
|
24
cms/forms.py
24
cms/forms.py
|
@ -1,6 +1,7 @@
|
||||||
import swapper
|
import swapper
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.db.models import Prefetch
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.core.mail import EmailMessage
|
from django.core.mail import EmailMessage
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
@ -13,7 +14,6 @@ class PageForm(forms.ModelForm):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.label_suffix = ''
|
self.label_suffix = ''
|
||||||
extra = 1 if self.instance.pk else 2
|
extra = 1 if self.instance.pk else 2
|
||||||
|
|
||||||
self.formsets = [forms.inlineformset_factory(
|
self.formsets = [forms.inlineformset_factory(
|
||||||
parent_model = Page,
|
parent_model = Page,
|
||||||
model = Section,
|
model = Section,
|
||||||
|
@ -37,7 +37,7 @@ class PageForm(forms.ModelForm):
|
||||||
def clean(self):
|
def clean(self):
|
||||||
super().clean()
|
super().clean()
|
||||||
if not self.formsets[0].is_valid():
|
if not self.formsets[0].is_valid():
|
||||||
self.add_error(None, _('There’s a problem saving one of the sections'))
|
self.add_error(None, repr(self.formsets[0].errors))
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
page = super().save()
|
page = super().save()
|
||||||
|
@ -60,21 +60,21 @@ class SectionForm(forms.ModelForm):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.label_suffix = ''
|
self.label_suffix = ''
|
||||||
self.fields['DELETE'] = forms.BooleanField(label=_('Delete'), required=False)
|
self.fields['DELETE'] = forms.BooleanField(label=_('Delete'), required=False)
|
||||||
|
extra = 1 if self.instance.pk else 2
|
||||||
|
|
||||||
# Repopulate the 'choices' attribute of the type field from
|
# Repopulate the 'choices' attribute of the type field from
|
||||||
# the child model.
|
# the child model.
|
||||||
self.fields['type'].choices = self._meta.model.TYPES
|
self.fields['type'].choices = self._meta.model.TYPES
|
||||||
self.fields['type'].initial = self._meta.model.TYPES[0][0]
|
self.fields['type'].initial = self._meta.model.TYPES[0][0]
|
||||||
|
|
||||||
|
|
||||||
# Populate the 'formsets' attribute if the Section was
|
# Populate the 'formsets' attribute if the Section was
|
||||||
# extendend with one_to_many fields
|
# extendend with one_to_many fields
|
||||||
self.formsets = []
|
self.formsets = []
|
||||||
for field in self.instance._meta.get_fields():
|
for field in self.instance._meta.get_fields():
|
||||||
if field.one_to_many:
|
if field.one_to_many:
|
||||||
extra = 1 if getattr(self.instance, field.name).exists() else 2
|
|
||||||
formset = forms.inlineformset_factory(
|
formset = forms.inlineformset_factory(
|
||||||
Section, field.related_model,
|
parent_model=Section,
|
||||||
|
model=field.related_model,
|
||||||
fields='__all__',
|
fields='__all__',
|
||||||
extra=extra,
|
extra=extra,
|
||||||
)(
|
)(
|
||||||
|
@ -107,19 +107,9 @@ class SectionForm(forms.ModelForm):
|
||||||
if section.page.slug and not section.page.sections.exists():
|
if section.page.slug and not section.page.sections.exists():
|
||||||
section.page.delete()
|
section.page.delete()
|
||||||
return
|
return
|
||||||
|
elif commit:
|
||||||
# Explanation: get the content type of the model that the user
|
|
||||||
# supplied when filling in this form, and save it's id to the
|
|
||||||
# 'polymorphic_ctype_id' field. The next time the object is
|
|
||||||
# requested from the database, django-polymorphic will convert
|
|
||||||
# it to the correct subclass.
|
|
||||||
section.polymorphic_ctype = ContentType.objects.get(
|
|
||||||
app_label=section._meta.app_label,
|
|
||||||
model=section.type.lower(),
|
|
||||||
)
|
|
||||||
|
|
||||||
if commit:
|
|
||||||
section.save()
|
section.save()
|
||||||
|
|
||||||
for formset in self.formsets:
|
for formset in self.formsets:
|
||||||
formset.save(commit=commit)
|
formset.save(commit=commit)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Generated by Django 3.0.3 on 2020-03-21 10:23
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('cms', '0002_initial_homepage'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='section',
|
||||||
|
name='polymorphic_ctype',
|
||||||
|
),
|
||||||
|
]
|
|
@ -2,11 +2,12 @@ import swapper
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.forms import TextInput, Select
|
|
||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
|
from django.forms import TextInput, Select
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
|
|
||||||
from embed_video.fields import EmbedVideoField
|
from embed_video.fields import EmbedVideoField
|
||||||
from polymorphic.models import PolymorphicModel
|
|
||||||
|
|
||||||
class VarCharField(models.TextField):
|
class VarCharField(models.TextField):
|
||||||
'''Variable width CharField'''
|
'''Variable width CharField'''
|
||||||
|
@ -86,9 +87,13 @@ class BasePage(Numbered, models.Model):
|
||||||
verbose_name_plural = _('Pages')
|
verbose_name_plural = _('Pages')
|
||||||
ordering = ['number']
|
ordering = ['number']
|
||||||
|
|
||||||
class BaseSection(Numbered, PolymorphicModel):
|
class BaseSection(Numbered, models.Model):
|
||||||
'''Abstract base model for sections'''
|
'''Abstract base model for sections'''
|
||||||
TYPES = [] # Will be populated by @register_model()
|
|
||||||
|
# These will be populated by @register
|
||||||
|
TYPES = []
|
||||||
|
_cms_views = {}
|
||||||
|
|
||||||
page = models.ForeignKey(swapper.get_model_name('cms', 'Page'), verbose_name=_('page'), related_name='sections', on_delete=models.PROTECT)
|
page = models.ForeignKey(swapper.get_model_name('cms', 'Page'), verbose_name=_('page'), related_name='sections', on_delete=models.PROTECT)
|
||||||
title = VarCharField(_('section'))
|
title = VarCharField(_('section'))
|
||||||
type = VarCharField(_('type'))
|
type = VarCharField(_('type'))
|
||||||
|
@ -113,6 +118,21 @@ class BaseSection(Numbered, PolymorphicModel):
|
||||||
else:
|
else:
|
||||||
return self.title
|
return self.title
|
||||||
|
|
||||||
|
def get_view(self, request):
|
||||||
|
'''Try to instantiate the registered view for this section'''
|
||||||
|
try:
|
||||||
|
return self.__class__._cms_views[self.type](request)
|
||||||
|
except:
|
||||||
|
raise ImproperlyConfigured(
|
||||||
|
f'No view registered for sections of type {self.type}!')
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_fields_per_type(cls):
|
||||||
|
fields_per_type = {}
|
||||||
|
for name, view in cls._cms_views.items():
|
||||||
|
fields_per_type[name] = ['type', 'number'] + view.fields
|
||||||
|
return fields_per_type
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
verbose_name = _('section')
|
verbose_name = _('section')
|
||||||
|
|
|
@ -7,6 +7,24 @@ html, body {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.edit {
|
||||||
|
position: fixed;
|
||||||
|
right: 1em;
|
||||||
|
bottom: 1em;
|
||||||
|
z-index: 1000;
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 0;
|
||||||
|
outline: none;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
width: 75px !important;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Form elements */
|
/* Form elements */
|
||||||
|
|
||||||
form.cms {
|
form.cms {
|
||||||
|
|
|
@ -4,6 +4,20 @@ html, body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0; }
|
padding: 0; }
|
||||||
|
|
||||||
|
div.edit {
|
||||||
|
position: fixed;
|
||||||
|
right: 1em;
|
||||||
|
bottom: 1em;
|
||||||
|
z-index: 1000; }
|
||||||
|
div.edit button {
|
||||||
|
padding: 0;
|
||||||
|
outline: none;
|
||||||
|
background: none;
|
||||||
|
border: none; }
|
||||||
|
div.edit img {
|
||||||
|
width: 75px !important;
|
||||||
|
height: auto; }
|
||||||
|
|
||||||
/* Form elements */
|
/* Form elements */
|
||||||
form.cms {
|
form.cms {
|
||||||
background: #bfb;
|
background: #bfb;
|
||||||
|
|
|
@ -5,5 +5,5 @@
|
||||||
"admin.scss"
|
"admin.scss"
|
||||||
],
|
],
|
||||||
"names": [],
|
"names": [],
|
||||||
"mappings": "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"
|
"mappings": "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"
|
||||||
}
|
}
|
|
@ -1,246 +0,0 @@
|
||||||
$font: sans-serif;
|
|
||||||
$small: 500px;
|
|
||||||
$medium: 800px;
|
|
||||||
$blue: #3573a8;
|
|
||||||
|
|
||||||
html, body {
|
|
||||||
font-family: $font;
|
|
||||||
line-height: 1.33;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: $blue;
|
|
||||||
text-decoration: none;
|
|
||||||
&:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
th, td {
|
|
||||||
padding: 1em;
|
|
||||||
}
|
|
||||||
th {
|
|
||||||
border-bottom: 2px solid black;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
div.wrapper {
|
|
||||||
box-sizing: border-box;
|
|
||||||
max-width: 700px;
|
|
||||||
margin: auto;
|
|
||||||
padding: 0 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.spacer {
|
|
||||||
height: 1rem;
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav {
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
button#hamburger {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 1;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
|
|
||||||
.hamburger-inner, .hamburger-inner:before, .hamburger-inner:after {
|
|
||||||
background: $blue;
|
|
||||||
}
|
|
||||||
&.is-active {
|
|
||||||
position: fixed;
|
|
||||||
}
|
|
||||||
&:hover {
|
|
||||||
opacity: 1 !important;
|
|
||||||
}
|
|
||||||
&:focus {
|
|
||||||
outline: none !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
border-top: 2px solid $blue;
|
|
||||||
border-bottom: 2px solid $blue;
|
|
||||||
list-style: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
text-align: center;
|
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
align-content: start;
|
|
||||||
|
|
||||||
li {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
display: inline-block;
|
|
||||||
|
|
||||||
a {
|
|
||||||
font-size: 1.25rem;
|
|
||||||
padding: 5px .5em;
|
|
||||||
transition: .1s ease;
|
|
||||||
display: inline-block;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media(min-width: $small) {
|
|
||||||
a:hover:not(.edit), a.current {
|
|
||||||
text-decoration: none;
|
|
||||||
color: white;
|
|
||||||
background: $blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
button#hamburger {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media(max-width: $small) {
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
button#hamburger {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
ul#menu {
|
|
||||||
position: fixed;
|
|
||||||
overflow-y: auto;
|
|
||||||
z-index: 1;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
padding-top: 2em;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background: white;
|
|
||||||
list-style: none;
|
|
||||||
|
|
||||||
li {
|
|
||||||
width: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 1em;
|
|
||||||
margin: 0 1em;
|
|
||||||
border-bottom: 1px solid #ddd;
|
|
||||||
line-height: 1.5;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
transition: .5s ease;
|
|
||||||
transform: translatex(100%);
|
|
||||||
&.visible {
|
|
||||||
transform: translatex(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
div.edit {
|
|
||||||
text-align: center;
|
|
||||||
&.page {
|
|
||||||
position: fixed;
|
|
||||||
right: 1em;
|
|
||||||
bottom: 1em;
|
|
||||||
z-index: 1000;
|
|
||||||
button {
|
|
||||||
padding: 0;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
img {
|
|
||||||
width: 75px;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
a:before, button:before, a:after, button:after {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
div.edit a, div.edit button, a.edit{
|
|
||||||
font-family: inherit;
|
|
||||||
font-size: 1rem !important;
|
|
||||||
font-weight: normal !important;
|
|
||||||
color: red !important;
|
|
||||||
text-transform: none !important;
|
|
||||||
text-decoration: none;
|
|
||||||
color: inherit;
|
|
||||||
border: none;
|
|
||||||
display: inline;
|
|
||||||
background: none;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
content: '[ ';
|
|
||||||
}
|
|
||||||
&:after {
|
|
||||||
content: ' ]';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
section {
|
|
||||||
clear: both;
|
|
||||||
|
|
||||||
div.image {
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
div.title {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.content {
|
|
||||||
}
|
|
||||||
|
|
||||||
div.video {
|
|
||||||
div.iframe {
|
|
||||||
width: 100%;
|
|
||||||
padding-bottom: 56%;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
iframe {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
div.button {
|
|
||||||
text-align: center;
|
|
||||||
padding: 1em 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
section.contact {
|
|
||||||
div.message {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
div.formfield {
|
|
||||||
padding: 0.5em 0;
|
|
||||||
}
|
|
||||||
form input, form textarea {
|
|
||||||
box-sizing: border-box;
|
|
||||||
font-family: inherit;
|
|
||||||
font-size: inherit;
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
padding: 0.5em;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,175 +1,2 @@
|
||||||
html, body {
|
|
||||||
font-family: sans-serif;
|
|
||||||
line-height: 1.33;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0; }
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #3573a8;
|
|
||||||
text-decoration: none; }
|
|
||||||
a:hover {
|
|
||||||
text-decoration: underline; }
|
|
||||||
|
|
||||||
table {
|
|
||||||
border-collapse: collapse; }
|
|
||||||
table th, table td {
|
|
||||||
padding: 1em; }
|
|
||||||
table th {
|
|
||||||
border-bottom: 2px solid black; }
|
|
||||||
|
|
||||||
div.wrapper {
|
|
||||||
box-sizing: border-box;
|
|
||||||
max-width: 700px;
|
|
||||||
margin: auto;
|
|
||||||
padding: 0 1rem; }
|
|
||||||
|
|
||||||
div.spacer {
|
|
||||||
height: 1rem;
|
|
||||||
clear: both; }
|
|
||||||
|
|
||||||
nav {
|
|
||||||
padding: 0; }
|
|
||||||
nav button#hamburger {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 1;
|
|
||||||
top: 0;
|
|
||||||
right: 0; }
|
|
||||||
nav button#hamburger .hamburger-inner, nav button#hamburger .hamburger-inner:before, nav button#hamburger .hamburger-inner:after {
|
|
||||||
background: #3573a8; }
|
|
||||||
nav button#hamburger.is-active {
|
|
||||||
position: fixed; }
|
|
||||||
nav button#hamburger:hover {
|
|
||||||
opacity: 1 !important; }
|
|
||||||
nav button#hamburger:focus {
|
|
||||||
outline: none !important; }
|
|
||||||
nav ul {
|
|
||||||
border-top: 2px solid #3573a8;
|
|
||||||
border-bottom: 2px solid #3573a8;
|
|
||||||
list-style: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
text-align: center;
|
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
align-content: start; }
|
|
||||||
nav ul li {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
display: inline-block; }
|
|
||||||
nav ul li a {
|
|
||||||
font-size: 1.25rem;
|
|
||||||
padding: 5px .5em;
|
|
||||||
transition: .1s ease;
|
|
||||||
display: inline-block;
|
|
||||||
font-weight: bold; }
|
|
||||||
@media (min-width: 500px) {
|
|
||||||
nav a:hover:not(.edit), nav a.current {
|
|
||||||
text-decoration: none;
|
|
||||||
color: white;
|
|
||||||
background: #3573a8; }
|
|
||||||
nav button#hamburger {
|
|
||||||
display: none; } }
|
|
||||||
@media (max-width: 500px) {
|
|
||||||
nav {
|
|
||||||
padding: 0; }
|
|
||||||
nav button#hamburger {
|
|
||||||
display: block; }
|
|
||||||
nav ul#menu {
|
|
||||||
position: fixed;
|
|
||||||
overflow-y: auto;
|
|
||||||
z-index: 1;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
padding-top: 2em;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background: white;
|
|
||||||
list-style: none;
|
|
||||||
transition: .5s ease;
|
|
||||||
transform: translatex(100%); }
|
|
||||||
nav ul#menu li {
|
|
||||||
width: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 1em;
|
|
||||||
margin: 0 1em;
|
|
||||||
border-bottom: 1px solid #ddd;
|
|
||||||
line-height: 1.5;
|
|
||||||
text-align: center; }
|
|
||||||
nav ul#menu li a {
|
|
||||||
text-decoration: none; }
|
|
||||||
nav ul#menu.visible {
|
|
||||||
transform: translatex(0); } }
|
|
||||||
div.edit {
|
|
||||||
text-align: center; }
|
|
||||||
div.edit.page {
|
|
||||||
position: fixed;
|
|
||||||
right: 1em;
|
|
||||||
bottom: 1em;
|
|
||||||
z-index: 1000; }
|
|
||||||
div.edit.page button {
|
|
||||||
padding: 0;
|
|
||||||
outline: none; }
|
|
||||||
div.edit.page img {
|
|
||||||
width: 75px;
|
|
||||||
height: auto; }
|
|
||||||
div.edit.page a:before, div.edit.page button:before, div.edit.page a:after, div.edit.page button:after {
|
|
||||||
display: none; }
|
|
||||||
|
|
||||||
div.edit a, div.edit button, a.edit {
|
|
||||||
font-family: inherit;
|
|
||||||
font-size: 1rem !important;
|
|
||||||
font-weight: normal !important;
|
|
||||||
color: red !important;
|
|
||||||
text-transform: none !important;
|
|
||||||
text-decoration: none;
|
|
||||||
color: inherit;
|
|
||||||
border: none;
|
|
||||||
display: inline;
|
|
||||||
background: none;
|
|
||||||
cursor: pointer; }
|
|
||||||
div.edit a:before, div.edit button:before, a.edit:before {
|
|
||||||
content: '[ '; }
|
|
||||||
div.edit a:after, div.edit button:after, a.edit:after {
|
|
||||||
content: ' ]'; }
|
|
||||||
|
|
||||||
section {
|
|
||||||
clear: both; }
|
|
||||||
section div.image img {
|
|
||||||
width: 100%; }
|
|
||||||
section div.title {
|
|
||||||
text-align: center; }
|
|
||||||
section div.video div.iframe {
|
|
||||||
width: 100%;
|
|
||||||
padding-bottom: 56%;
|
|
||||||
position: relative; }
|
|
||||||
section div.video div.iframe iframe {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
left: 0;
|
|
||||||
top: 0; }
|
|
||||||
section div.button {
|
|
||||||
text-align: center;
|
|
||||||
padding: 1em 0; }
|
|
||||||
|
|
||||||
section.contact div.message {
|
|
||||||
display: none; }
|
|
||||||
|
|
||||||
section.contact div.formfield {
|
|
||||||
padding: 0.5em 0; }
|
|
||||||
|
|
||||||
section.contact form input, section.contact form textarea {
|
|
||||||
box-sizing: border-box;
|
|
||||||
font-family: inherit;
|
|
||||||
font-size: inherit;
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
padding: 0.5em;
|
|
||||||
margin: 0; }
|
|
||||||
|
|
||||||
/*# sourceMappingURL=cms.scss.css.map */
|
/*# sourceMappingURL=cms.scss.css.map */
|
File diff suppressed because one or more lines are too long
|
@ -1,8 +1,8 @@
|
||||||
{% load static %}
|
{% load static i18n %}
|
||||||
{% load i18n %}
|
{% get_current_language as lang%}
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="{% get_current_language as lang%}{{lang}}">
|
<html lang="{{lang}}">
|
||||||
<head>
|
<head>
|
||||||
<title>{% block title %}{% endblock %}</title>
|
<title>{% block title %}{% endblock %}</title>
|
||||||
<meta charset="utf-8"/>
|
<meta charset="utf-8"/>
|
||||||
|
@ -49,7 +49,6 @@
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
{% block footer %}
|
{% block footer %}
|
||||||
{{footer|safe}}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,15 @@
|
||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load static %}
|
{% load static cms %}
|
||||||
{% load cms %}
|
|
||||||
|
|
||||||
{% block title %}{{block.super}} - {{page.title}}{% endblock %}
|
{% block title %}{{block.super}} - {{page.title}}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% for section in sections %}
|
{% for section in sections %}
|
||||||
<div id="{{section.title|slugify}}"></div>
|
|
||||||
{% include_section section %}
|
{% include_section section %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
{% if perms.cms_page.change %}
|
{% if perms.cms_page.change %}
|
||||||
<div class="edit page">
|
<div class="edit">
|
||||||
<a href="{% if page.slug %}{% url 'cms:updatepage' page.slug %}{% else %}{% url 'cms:updatepage' %}{% endif %}"><img src="{% static 'cms/edit.png' %}"></a>
|
<a href="{% if page.slug %}{% url 'cms:updatepage' page.slug %}{% else %}{% url 'cms:updatepage' %}{% endif %}"><img src="{% static 'cms/edit.png' %}"></a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -14,17 +14,21 @@ class IncludeSectionNode(template.Node):
|
||||||
def __init__(self, section):
|
def __init__(self, section):
|
||||||
self.section = template.Variable(section)
|
self.section = template.Variable(section)
|
||||||
self.csrf_token = template.Variable('csrf_token')
|
self.csrf_token = template.Variable('csrf_token')
|
||||||
|
self.request = template.Variable('request')
|
||||||
self.perms = template.Variable('perms')
|
self.perms = template.Variable('perms')
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
section = self.section.resolve(context)
|
section = self.section.resolve(context)
|
||||||
template_name = section.view.template_name
|
|
||||||
csrf_token = self.csrf_token.resolve(context)
|
csrf_token = self.csrf_token.resolve(context)
|
||||||
|
request = self.request.resolve(context)
|
||||||
perms = self.perms.resolve(context)
|
perms = self.perms.resolve(context)
|
||||||
section.context.update({
|
view = section.get_view(request)
|
||||||
'csrf_token': csrf_token,
|
section_context = view.get_context_data(
|
||||||
'perms': perms,
|
csrf_token=csrf_token,
|
||||||
})
|
section=section,
|
||||||
t = context.template.engine.get_template(template_name)
|
request=request,
|
||||||
return t.render(template.Context(section.context))
|
perms=perms,
|
||||||
|
)
|
||||||
|
t = context.template.engine.get_template(view.template_name)
|
||||||
|
return t.render(template.Context(section_context))
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
from markdown import markdown as md
|
||||||
|
from django import template
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
|
MARKDOWN_EXTENSIONS = ['extra', 'smarty']
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
@register.simple_tag(takes_context=True)
|
||||||
|
def eval(context, expr):
|
||||||
|
'''USE WITH CAUTION!!!
|
||||||
|
|
||||||
|
This template tag runs its argument through Django's templating
|
||||||
|
system using the current context, placing all power into the
|
||||||
|
hands of the content editors.
|
||||||
|
|
||||||
|
Also, it applies Markdown.
|
||||||
|
|
||||||
|
'''
|
||||||
|
result = template.Template(expr).render(context)
|
||||||
|
return mark_safe(md(result, extensions=MARKDOWN_EXTENSIONS))
|
|
@ -1,15 +0,0 @@
|
||||||
from markdown import markdown as md
|
|
||||||
from django import template
|
|
||||||
from django.conf import settings
|
|
||||||
from django.utils.safestring import mark_safe
|
|
||||||
|
|
||||||
EXTENSIONS = getattr(settings, 'MARKDOWN_EXTENSIONS', [])
|
|
||||||
|
|
||||||
register = template.Library()
|
|
||||||
|
|
||||||
@register.filter(is_safe=True)
|
|
||||||
def markdown(value):
|
|
||||||
'''Runs Markdown over a given value
|
|
||||||
|
|
||||||
'''
|
|
||||||
return mark_safe(md(value, extensions=EXTENSIONS))
|
|
112
cms/views.py
112
cms/views.py
|
@ -3,45 +3,16 @@ import swapper
|
||||||
|
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.views.generic import base, detail, edit
|
from django.views.generic import base, detail, edit
|
||||||
from django.http import Http404, HttpResponse, HttpResponseRedirect
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
|
||||||
from django.contrib.auth.mixins import UserPassesTestMixin
|
from django.contrib.auth.mixins import UserPassesTestMixin
|
||||||
|
from django.http import Http404, HttpResponse, HttpResponseRedirect, HttpResponseBadRequest
|
||||||
|
|
||||||
from .decorators import register_view
|
|
||||||
from .forms import PageForm, SectionForm
|
from .forms import PageForm, SectionForm
|
||||||
|
|
||||||
Page = swapper.load_model('cms', 'Page')
|
Page = swapper.load_model('cms', 'Page')
|
||||||
Section = swapper.load_model('cms', 'Section')
|
Section = swapper.load_model('cms', 'Section')
|
||||||
|
|
||||||
@register_view(Section)
|
|
||||||
class SectionView:
|
|
||||||
'''Generic section view'''
|
|
||||||
template_name = 'cms/sections/section.html'
|
|
||||||
|
|
||||||
def setup(self, request, section):
|
|
||||||
'''Initialize request and section attributes'''
|
|
||||||
self.request = request
|
|
||||||
self.section = section
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
'''Override this to customize a section's context'''
|
|
||||||
return kwargs
|
|
||||||
|
|
||||||
class SectionFormView(edit.FormMixin, SectionView):
|
|
||||||
'''Generic section with associated form'''
|
|
||||||
|
|
||||||
def post(self, request):
|
|
||||||
'''Process form'''
|
|
||||||
form = self.get_form()
|
|
||||||
if form.is_valid():
|
|
||||||
form.save(request)
|
|
||||||
return HttpResponseRedirect(self.get_success_url())
|
|
||||||
return form
|
|
||||||
|
|
||||||
class PageView(detail.DetailView):
|
class PageView(detail.DetailView):
|
||||||
'''View of a page with heterogeneous (polymorphic) sections'''
|
'''View of a page with heterogeneous sections'''
|
||||||
model = Page
|
model = Page
|
||||||
template_name = 'cms/page.html'
|
template_name = 'cms/page.html'
|
||||||
|
|
||||||
|
@ -49,27 +20,16 @@ class PageView(detail.DetailView):
|
||||||
'''Supply a default argument for slug'''
|
'''Supply a default argument for slug'''
|
||||||
super().setup(*args, slug=slug, **kwargs)
|
super().setup(*args, slug=slug, **kwargs)
|
||||||
|
|
||||||
def initialize_section(self, section):
|
|
||||||
section.view = section.__class__.view_class()
|
|
||||||
section.view.setup(self.request, section)
|
|
||||||
section.context = section.view.get_context_data(
|
|
||||||
request = self.request,
|
|
||||||
section = section,
|
|
||||||
)
|
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
'''Initialize sections and render final response'''
|
'''Instantiate section views and render final response'''
|
||||||
try:
|
try:
|
||||||
page = self.object = self.get_object()
|
page = self.object = self.get_object()
|
||||||
except Http404:
|
except Http404:
|
||||||
if self.request.user.has_perm('cms_page_create'):
|
if self.request.user.has_perm('cms_page_create'):
|
||||||
return redirect('cms:updatepage', self.kwargs['slug'])
|
return redirect('cms:updatepage', self.kwargs['slug'])
|
||||||
else:
|
raise
|
||||||
raise
|
|
||||||
context = self.get_context_data(**kwargs)
|
context = self.get_context_data(**kwargs)
|
||||||
sections = page.sections.all()
|
sections = page.sections.all()
|
||||||
for section in sections:
|
|
||||||
self.initialize_section(section)
|
|
||||||
context.update({
|
context.update({
|
||||||
'page': page,
|
'page': page,
|
||||||
'sections': sections,
|
'sections': sections,
|
||||||
|
@ -77,8 +37,7 @@ class PageView(detail.DetailView):
|
||||||
return self.render_to_response(context)
|
return self.render_to_response(context)
|
||||||
|
|
||||||
def post(self, request, **kwargs):
|
def post(self, request, **kwargs):
|
||||||
'''Initialize sections and call the post() function of the correct
|
'''Call the post() method of the correct section view'''
|
||||||
section view'''
|
|
||||||
try:
|
try:
|
||||||
pk = int(self.request.POST.get('section'))
|
pk = int(self.request.POST.get('section'))
|
||||||
except:
|
except:
|
||||||
|
@ -88,11 +47,9 @@ class PageView(detail.DetailView):
|
||||||
context = self.get_context_data(**kwargs)
|
context = self.get_context_data(**kwargs)
|
||||||
sections = page.sections.all()
|
sections = page.sections.all()
|
||||||
for section in sections:
|
for section in sections:
|
||||||
self.initialize_section(section)
|
|
||||||
if section.pk == pk:
|
if section.pk == pk:
|
||||||
result = section.view.post(request)
|
view = section.get_view(request)
|
||||||
if isinstance(result, HttpResponseRedirect):
|
result = view.post(request)
|
||||||
return result
|
|
||||||
if isinstance(result, HttpResponse):
|
if isinstance(result, HttpResponse):
|
||||||
return result
|
return result
|
||||||
section.context['form'] = result
|
section.context['form'] = result
|
||||||
|
@ -112,33 +69,26 @@ class PageView(detail.DetailView):
|
||||||
return context
|
return context
|
||||||
|
|
||||||
class EditPage(UserPassesTestMixin, edit.ModelFormMixin, base.TemplateResponseMixin, base.View):
|
class EditPage(UserPassesTestMixin, edit.ModelFormMixin, base.TemplateResponseMixin, base.View):
|
||||||
|
'''Base view with nested forms for editing the page and all its sections'''
|
||||||
model = Page
|
model = Page
|
||||||
form_class = PageForm
|
form_class = PageForm
|
||||||
template_name = 'cms/edit.html'
|
template_name = 'cms/edit.html'
|
||||||
|
|
||||||
def test_func(self):
|
def test_func(self):
|
||||||
app, model = swapper.get_model_name('cms', 'page').lower().split('.')
|
'''Only allow users with the correct permissions'''
|
||||||
return self.request.user.has_perm('f{app}_{model}_change')
|
return self.request.user.has_perm('cms_page_change')
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
|
'''Set the default slug to the current URL for new pages'''
|
||||||
kwargs = super().get_form_kwargs()
|
kwargs = super().get_form_kwargs()
|
||||||
if 'slug' in self.kwargs:
|
if 'slug' in self.kwargs:
|
||||||
kwargs.update({'initial': {'slug': self.kwargs['slug']}})
|
kwargs.update({'initial': {'slug': self.kwargs['slug']}})
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
|
'''Populate the fields_per_type dict for use in javascript'''
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
fields_per_type = {}
|
context['fields_per_type'] = json.dumps(Section.get_fields_per_type())
|
||||||
for model, _ in Section.TYPES:
|
|
||||||
ctype = ContentType.objects.get(
|
|
||||||
app_label = Section._meta.app_label,
|
|
||||||
model = model.lower(),
|
|
||||||
)
|
|
||||||
fields_per_type[ctype.model] = ['type', 'number'] + ctype.model_class().fields
|
|
||||||
|
|
||||||
context.update({
|
|
||||||
'fields_per_type': json.dumps(fields_per_type),
|
|
||||||
})
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
|
@ -148,11 +98,13 @@ class EditPage(UserPassesTestMixin, edit.ModelFormMixin, base.TemplateResponseMi
|
||||||
except Http404:
|
except Http404:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, *args, **kwargs):
|
||||||
|
'''Handle GET requests'''
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
return self.render_to_response(self.get_context_data())
|
return self.render_to_response(self.get_context_data(**kwargs))
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, *args, **kwargs):
|
||||||
|
'''Handle POST requests'''
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
form = self.get_form()
|
form = self.get_form()
|
||||||
|
|
||||||
|
@ -161,14 +113,15 @@ class EditPage(UserPassesTestMixin, edit.ModelFormMixin, base.TemplateResponseMi
|
||||||
if page:
|
if page:
|
||||||
return HttpResponseRedirect(page.get_absolute_url())
|
return HttpResponseRedirect(page.get_absolute_url())
|
||||||
return HttpResponseRedirect('/')
|
return HttpResponseRedirect('/')
|
||||||
return self.render_to_response(self.get_context_data(form=form))
|
return self.render_to_response(self.get_context_data(form=form, **kwargs))
|
||||||
|
|
||||||
class CreatePage(EditPage):
|
class CreatePage(EditPage):
|
||||||
|
'''View for creating new pages'''
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
return Page()
|
return Page()
|
||||||
|
|
||||||
class UpdatePage(EditPage):
|
class UpdatePage(EditPage):
|
||||||
pass
|
'''View for editing existing pages'''
|
||||||
|
|
||||||
class EditSection(UserPassesTestMixin, edit.ModelFormMixin, base.TemplateResponseMixin, base.View):
|
class EditSection(UserPassesTestMixin, edit.ModelFormMixin, base.TemplateResponseMixin, base.View):
|
||||||
model = Section
|
model = Section
|
||||||
|
@ -176,8 +129,7 @@ class EditSection(UserPassesTestMixin, edit.ModelFormMixin, base.TemplateRespons
|
||||||
template_name = 'cms/edit.html'
|
template_name = 'cms/edit.html'
|
||||||
|
|
||||||
def test_func(self):
|
def test_func(self):
|
||||||
app, model = swapper.get_model_name('cms', 'section').lower().split('.')
|
return self.request.user.has_perm('cms_section_change')
|
||||||
return self.request.user.has_perm('f{app}_{model}_change')
|
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
kwargs = super().get_form_kwargs()
|
kwargs = super().get_form_kwargs()
|
||||||
|
@ -188,17 +140,7 @@ class EditSection(UserPassesTestMixin, edit.ModelFormMixin, base.TemplateRespons
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
fields_per_type = {}
|
context['fields_per_type'] = json.dumps(Section.get_fields_per_type())
|
||||||
for model, _ in Section.TYPES:
|
|
||||||
ctype = ContentType.objects.get(
|
|
||||||
app_label = Section._meta.app_label,
|
|
||||||
model = model.lower(),
|
|
||||||
)
|
|
||||||
fields_per_type[ctype.model] = ['type', 'number'] + ctype.model_class().fields
|
|
||||||
|
|
||||||
context.update({
|
|
||||||
'fields_per_type': json.dumps(fields_per_type),
|
|
||||||
})
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def get_object(self, queryset=None):
|
def get_object(self, queryset=None):
|
||||||
|
@ -215,11 +157,11 @@ class EditSection(UserPassesTestMixin, edit.ModelFormMixin, base.TemplateRespons
|
||||||
raise Http404()
|
raise Http404()
|
||||||
return section
|
return section
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, *args, **kwargs):
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
return self.render_to_response(self.get_context_data())
|
return self.render_to_response(self.get_context_data(**kwargs))
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, *args, **kwargs):
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
form = self.get_form()
|
form = self.get_form()
|
||||||
|
|
||||||
|
@ -231,7 +173,7 @@ class EditSection(UserPassesTestMixin, edit.ModelFormMixin, base.TemplateRespons
|
||||||
return HttpResponseRedirect(self.page.get_absolute_url())
|
return HttpResponseRedirect(self.page.get_absolute_url())
|
||||||
else:
|
else:
|
||||||
return HttpResponseRedirect('/')
|
return HttpResponseRedirect('/')
|
||||||
return self.render_to_response(self.get_context_data(form=form))
|
return self.render_to_response(self.get_context_data(form=form, **kwargs))
|
||||||
|
|
||||||
class CreateSection(EditSection):
|
class CreateSection(EditSection):
|
||||||
def get_section(self):
|
def get_section(self):
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
default_app_config = 'app.apps.Config'
|
|
|
@ -1,4 +0,0 @@
|
||||||
from django.apps import AppConfig
|
|
||||||
class Config(AppConfig):
|
|
||||||
name = 'app'
|
|
||||||
verbose_name = 'app'
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
import cms
|
||||||
|
from cms.forms import ContactForm
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
@cms.register(_('Text'))
|
||||||
|
class Text(cms.SectionView):
|
||||||
|
fields = ['title', 'content']
|
||||||
|
template_name = 'app/sections/text.html'
|
||||||
|
|
||||||
|
@cms.register(_('Images'))
|
||||||
|
class Images(cms.SectionView):
|
||||||
|
fields = ['title', 'images']
|
||||||
|
template_name = 'app/sections/images.html'
|
||||||
|
|
||||||
|
@cms.register(_('Video'))
|
||||||
|
class Video(cms.SectionView):
|
||||||
|
fields = ['title', 'video']
|
||||||
|
template_name = 'app/sections/video.html'
|
||||||
|
|
||||||
|
@cms.register(_('Contact'))
|
||||||
|
class Contact(cms.SectionFormView):
|
||||||
|
fields = ['title']
|
||||||
|
form_class = ContactForm
|
||||||
|
success_url = '/thanks/'
|
||||||
|
template_name = 'app/sections/contact.html'
|
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 3.0.2 on 2020-02-16 14:27
|
# Generated by Django 3.0.2 on 2020-03-21 16:44
|
||||||
|
|
||||||
import cms.models
|
import cms.models
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
@ -12,7 +12,6 @@ class Migration(migrations.Migration):
|
||||||
initial = True
|
initial = True
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('contenttypes', '0002_remove_content_type_name'),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
|
@ -20,9 +19,9 @@ class Migration(migrations.Migration):
|
||||||
name='Page',
|
name='Page',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
('number', models.PositiveIntegerField(blank=True, verbose_name='number')),
|
('title', cms.models.VarCharField(verbose_name='page')),
|
||||||
('title', cms.models.VarCharField(verbose_name='title')),
|
|
||||||
('slug', models.SlugField(blank=True, unique=True, verbose_name='slug')),
|
('slug', models.SlugField(blank=True, unique=True, verbose_name='slug')),
|
||||||
|
('number', models.PositiveIntegerField(blank=True, verbose_name='number')),
|
||||||
('menu', models.BooleanField(default=True, verbose_name='visible in menu')),
|
('menu', models.BooleanField(default=True, verbose_name='visible in menu')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
|
@ -37,15 +36,14 @@ class Migration(migrations.Migration):
|
||||||
name='Section',
|
name='Section',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
('type', cms.models.VarCharField(blank=True, verbose_name='type')),
|
('title', cms.models.VarCharField(verbose_name='section')),
|
||||||
|
('type', cms.models.VarCharField(verbose_name='type')),
|
||||||
('number', models.PositiveIntegerField(blank=True, verbose_name='number')),
|
('number', models.PositiveIntegerField(blank=True, verbose_name='number')),
|
||||||
('title', cms.models.VarCharField(blank=True, verbose_name='title')),
|
|
||||||
('content', models.TextField(blank=True, verbose_name='content')),
|
('content', models.TextField(blank=True, verbose_name='content')),
|
||||||
('image', models.ImageField(blank=True, upload_to='', verbose_name='image')),
|
('image', models.ImageField(blank=True, upload_to='', verbose_name='image')),
|
||||||
('video', embed_video.fields.EmbedVideoField(blank=True, help_text='Paste a YouTube, Vimeo, or SoundCloud link', verbose_name='video')),
|
('video', embed_video.fields.EmbedVideoField(blank=True, help_text='Paste a YouTube, Vimeo, or SoundCloud link', verbose_name='video')),
|
||||||
('href', cms.models.VarCharField(blank=True, verbose_name='button link')),
|
('href', cms.models.VarCharField(blank=True, verbose_name='link')),
|
||||||
('page', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='sections', to=settings.CMS_PAGE_MODEL, verbose_name='page')),
|
('page', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='sections', to=settings.CMS_PAGE_MODEL, verbose_name='page')),
|
||||||
('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_app.section_set+', to='contenttypes.ContentType')),
|
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'section',
|
'verbose_name': 'section',
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
# Generated by Django 3.0.2 on 2020-03-21 16:58
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('app', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='SectionImage',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('image', models.ImageField(upload_to='', verbose_name='Image')),
|
||||||
|
('section', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='images', to=settings.CMS_SECTION_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ['pk'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
|
@ -1,46 +1,22 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.conf import settings
|
|
||||||
from cms.models import BasePage, BaseSection
|
from cms.models import BasePage, BaseSection
|
||||||
from cms.decorators import register_model
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
class Page(BasePage):
|
class Page(BasePage):
|
||||||
'''Add custom fields here. Already existing fields: position, title,
|
'''Add custom fields here. Already existing fields: title, slug,
|
||||||
slug, menu
|
number, menu
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class Section(BaseSection):
|
class Section(BaseSection):
|
||||||
'''Add custom fields here. Already existing fields: type, position,
|
'''Add custom fields here. Already existing fields: title, type,
|
||||||
title, content, image, video, href
|
number, content, image, video, href
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
@register_model('Tekst')
|
class SectionImage(models.Model):
|
||||||
class TextSection(Section):
|
section = models.ForeignKey(Section, related_name='images', on_delete=models.CASCADE)
|
||||||
fields = ['title', 'content']
|
image = models.ImageField(_('Image'))
|
||||||
class Meta:
|
|
||||||
proxy = True
|
|
||||||
|
|
||||||
@register_model('Button')
|
|
||||||
class ButtonSection(Section):
|
|
||||||
fields = ['title', 'href']
|
|
||||||
class Meta:
|
class Meta:
|
||||||
proxy = True
|
ordering = ['pk']
|
||||||
|
|
||||||
@register_model('Afbeelding')
|
|
||||||
class ImageSection(Section):
|
|
||||||
fields = ['title', 'image']
|
|
||||||
class Meta:
|
|
||||||
proxy = True
|
|
||||||
|
|
||||||
@register_model('Video')
|
|
||||||
class VideoSection(Section):
|
|
||||||
fields = ['title', 'video']
|
|
||||||
class Meta:
|
|
||||||
proxy = True
|
|
||||||
|
|
||||||
@register_model('Contact')
|
|
||||||
class ContactSection(Section):
|
|
||||||
fields = ['title']
|
|
||||||
class Meta:
|
|
||||||
proxy = True
|
|
||||||
|
|
|
@ -1,10 +1,43 @@
|
||||||
$small: 500px;
|
|
||||||
$medium: 800px;
|
|
||||||
$font: sans-serif;
|
$font: sans-serif;
|
||||||
$titlefont: sans-serif;
|
$titlefont: sans-serif;
|
||||||
|
$small: 500px;
|
||||||
|
$medium: 800px;
|
||||||
|
$max-width: 700px;
|
||||||
|
$color1: #3573a8;
|
||||||
|
|
||||||
body {
|
html, body {
|
||||||
font-family: $font;
|
font-family: $font;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: $color1;
|
||||||
|
text-decoration: none;
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a.button, button.button {
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: $titlefont
|
||||||
|
font-size: 1.5em;
|
||||||
|
line-height: 1.25;
|
||||||
|
border-radius: 5px;
|
||||||
|
display: inline-block;
|
||||||
|
text-decoration: none;
|
||||||
|
border: none;
|
||||||
|
padding: 5px 20px;
|
||||||
|
background: $color1;
|
||||||
|
color: white;
|
||||||
|
box-sizing: border-box;
|
||||||
|
&:hover {
|
||||||
|
color: $color1;
|
||||||
|
background: white;
|
||||||
|
box-shadow: inset 0 0 0 2px $color1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
@ -16,32 +49,232 @@ h2 { font-size: 1.5em }
|
||||||
h3 { font-size: 1.25em }
|
h3 { font-size: 1.25em }
|
||||||
h4, h5, h6 { font-size: 1em }
|
h4, h5, h6 { font-size: 1em }
|
||||||
|
|
||||||
a {
|
a.edit {
|
||||||
&:hover {
|
color: red;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: normal;
|
||||||
|
&:before { content: '[ ' }
|
||||||
|
&:after { content: ' ]' }
|
||||||
|
}
|
||||||
|
section a.edit {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
th, td {
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
border-bottom: 2px solid $color1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a.button {
|
div.wrapper {
|
||||||
&:hover {
|
box-sizing: border-box;
|
||||||
}
|
max-width: $max-width;
|
||||||
|
margin: auto;
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.spacer {
|
||||||
|
height: 1rem;
|
||||||
|
clear: both;
|
||||||
}
|
}
|
||||||
|
|
||||||
header {
|
header {
|
||||||
h1 {
|
text-align: center;
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
img {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 400px;
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
|
button#hamburger {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
|
||||||
|
.hamburger-inner, .hamburger-inner:before, .hamburger-inner:after {
|
||||||
|
background: $color1;
|
||||||
|
}
|
||||||
|
&.is-active {
|
||||||
|
position: fixed;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
opacity: 1 !important;
|
||||||
|
}
|
||||||
|
&:focus {
|
||||||
|
outline: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
border-top: 2px solid $color1;
|
||||||
|
border-bottom: 2px solid $color1;
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
text-align: center;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
align-content: start;
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
a {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
padding: 5px .5em;
|
||||||
|
transition: .1s ease;
|
||||||
|
display: inline-block;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(min-width: $small) {
|
||||||
|
a:hover:not(.edit), a.current {
|
||||||
|
text-decoration: none;
|
||||||
|
color: white;
|
||||||
|
background: $color1;
|
||||||
|
}
|
||||||
|
|
||||||
|
button#hamburger {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(max-width: $small) {
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
button#hamburger {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
ul#menu {
|
||||||
|
position: fixed;
|
||||||
|
overflow-y: auto;
|
||||||
|
z-index: 1;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
padding-top: 2em;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: white;
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
li {
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 1em;
|
||||||
|
margin: 0 1em;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
line-height: 1.5;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
transition: .5s ease;
|
||||||
|
transform: translatex(100%);
|
||||||
|
&.visible {
|
||||||
|
transform: translatex(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div.edit {
|
||||||
|
position: fixed;
|
||||||
|
right: 1em;
|
||||||
|
bottom: 1em;
|
||||||
|
z-index: 1000;
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 0;
|
||||||
|
outline: none;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
width: 75px;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
section {
|
||||||
|
clear: both;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
div.title {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.video {
|
||||||
|
div.iframe {
|
||||||
|
width: 100%;
|
||||||
|
padding-bottom: 56%;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
iframe {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
section.images {
|
||||||
|
div.images {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin: 0.25em;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
div.image {
|
||||||
|
flex: 1 1 100px;
|
||||||
|
max-width: $max-width;
|
||||||
|
margin: 0.5em;
|
||||||
|
|
||||||
|
img {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
section.contact {
|
||||||
|
div.message {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
div.formfield {
|
||||||
|
padding: 0.5em 0;
|
||||||
|
}
|
||||||
|
form input, form textarea {
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.5em;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
margin-top: 4em;
|
min-height: 10em;
|
||||||
min-height: 400px;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,14 @@
|
||||||
body {
|
html, body {
|
||||||
font-family: sans-serif; }
|
font-family: sans-serif;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0; }
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #3573a8;
|
||||||
|
text-decoration: none; }
|
||||||
|
a:hover {
|
||||||
|
text-decoration: underline; }
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
h1, h2, h3, h4, h5, h6 {
|
||||||
margin: .5em 0;
|
margin: .5em 0;
|
||||||
|
@ -17,17 +26,179 @@ h3 {
|
||||||
h4, h5, h6 {
|
h4, h5, h6 {
|
||||||
font-size: 1em; }
|
font-size: 1em; }
|
||||||
|
|
||||||
header h1 {
|
a.edit {
|
||||||
|
color: red;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: normal; }
|
||||||
|
a.edit:before {
|
||||||
|
content: '[ '; }
|
||||||
|
a.edit:after {
|
||||||
|
content: ' ]'; }
|
||||||
|
|
||||||
|
section a.edit {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
right: 1em; }
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-collapse: collapse; }
|
||||||
|
table th, table td {
|
||||||
|
padding: 1em; }
|
||||||
|
table th {
|
||||||
|
border-bottom: 2px solid #3573a8; }
|
||||||
|
|
||||||
|
div.wrapper {
|
||||||
|
box-sizing: border-box;
|
||||||
|
max-width: 700px;
|
||||||
|
margin: auto;
|
||||||
|
padding: 0 1rem; }
|
||||||
|
|
||||||
|
div.spacer {
|
||||||
|
height: 1rem;
|
||||||
|
clear: both; }
|
||||||
|
|
||||||
|
header {
|
||||||
text-align: center; }
|
text-align: center; }
|
||||||
|
|
||||||
header img {
|
nav button#hamburger {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
top: 0;
|
||||||
|
right: 0; }
|
||||||
|
nav button#hamburger .hamburger-inner, nav button#hamburger .hamburger-inner:before, nav button#hamburger .hamburger-inner:after {
|
||||||
|
background: #3573a8; }
|
||||||
|
nav button#hamburger.is-active {
|
||||||
|
position: fixed; }
|
||||||
|
nav button#hamburger:hover {
|
||||||
|
opacity: 1 !important; }
|
||||||
|
nav button#hamburger:focus {
|
||||||
|
outline: none !important; }
|
||||||
|
|
||||||
|
nav ul {
|
||||||
|
border-top: 2px solid #3573a8;
|
||||||
|
border-bottom: 2px solid #3573a8;
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
text-align: center;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
align-content: start; }
|
||||||
|
nav ul li {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: inline-block; }
|
||||||
|
nav ul li a {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
padding: 5px .5em;
|
||||||
|
transition: .1s ease;
|
||||||
|
display: inline-block;
|
||||||
|
font-weight: bold; }
|
||||||
|
|
||||||
|
@media (min-width: 500px) {
|
||||||
|
nav a:hover:not(.edit), nav a.current {
|
||||||
|
text-decoration: none;
|
||||||
|
color: white;
|
||||||
|
background: #3573a8; }
|
||||||
|
nav button#hamburger {
|
||||||
|
display: none; } }
|
||||||
|
|
||||||
|
@media (max-width: 500px) {
|
||||||
|
nav {
|
||||||
|
padding: 0; }
|
||||||
|
nav button#hamburger {
|
||||||
|
display: block; }
|
||||||
|
nav ul#menu {
|
||||||
|
position: fixed;
|
||||||
|
overflow-y: auto;
|
||||||
|
z-index: 1;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
padding-top: 2em;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: white;
|
||||||
|
list-style: none;
|
||||||
|
transition: .5s ease;
|
||||||
|
transform: translatex(100%); }
|
||||||
|
nav ul#menu li {
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 1em;
|
||||||
|
margin: 0 1em;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
line-height: 1.5;
|
||||||
|
text-align: center; }
|
||||||
|
nav ul#menu li a {
|
||||||
|
text-decoration: none; }
|
||||||
|
nav ul#menu.visible {
|
||||||
|
transform: translatex(0); } }
|
||||||
|
|
||||||
|
div.edit {
|
||||||
|
position: fixed;
|
||||||
|
right: 1em;
|
||||||
|
bottom: 1em;
|
||||||
|
z-index: 1000; }
|
||||||
|
div.edit button {
|
||||||
|
padding: 0;
|
||||||
|
outline: none;
|
||||||
|
background: none;
|
||||||
|
border: none; }
|
||||||
|
div.edit img {
|
||||||
|
width: 75px;
|
||||||
|
height: auto; }
|
||||||
|
|
||||||
|
section {
|
||||||
|
clear: both;
|
||||||
|
position: relative; }
|
||||||
|
section div.title {
|
||||||
|
text-align: center; }
|
||||||
|
section div.video div.iframe {
|
||||||
|
width: 100%;
|
||||||
|
padding-bottom: 56%;
|
||||||
|
position: relative; }
|
||||||
|
section div.video div.iframe iframe {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
left: 0;
|
||||||
|
top: 0; }
|
||||||
|
|
||||||
|
section.images div.images {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin: 0.25em;
|
||||||
|
justify-content: center; }
|
||||||
|
section.images div.images div.image {
|
||||||
|
flex: 1 1 100px;
|
||||||
|
max-width: 700px;
|
||||||
|
margin: 0.5em; }
|
||||||
|
section.images div.images div.image img {
|
||||||
|
display: block;
|
||||||
|
width: 100%; }
|
||||||
|
|
||||||
|
section.contact div.message {
|
||||||
|
display: none; }
|
||||||
|
|
||||||
|
section.contact div.formfield {
|
||||||
|
padding: 0.5em 0; }
|
||||||
|
|
||||||
|
section.contact form input, section.contact form textarea {
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: inherit;
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 400px;
|
padding: 0.5em;
|
||||||
margin: auto; }
|
margin: 0; }
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
margin-top: 4em;
|
min-height: 10em; }
|
||||||
min-height: 400px; }
|
|
||||||
|
|
||||||
/*# sourceMappingURL=main1.scss.css.map */
|
/*# sourceMappingURL=main1.scss.css.map */
|
File diff suppressed because one or more lines are too long
|
@ -1,7 +0,0 @@
|
||||||
<section class="button">
|
|
||||||
<div class="wrapper">
|
|
||||||
<div class="button">
|
|
||||||
<a class="button" href="{{section.href}}">{{section.title}}</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
|
@ -15,4 +15,8 @@
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% if perms.cms_section_change %}
|
||||||
|
<a class="edit" href="{% url 'cms:updatesection' section.number %}">{% trans 'edit' %}</a><br>
|
||||||
|
{% endif %}
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
{% load thumbnail %}
|
|
||||||
|
|
||||||
<section class="image">
|
|
||||||
<div class="wrapper">
|
|
||||||
{% if section.image %}
|
|
||||||
<div class="image">
|
|
||||||
<img alt="{{section.title}}" src="{% thumbnail section.image 800x800 %}">
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
{% load thumbnail i18n %}
|
||||||
|
|
||||||
|
<section class="images">
|
||||||
|
<div class="images">
|
||||||
|
{% for image in section.images.all %}
|
||||||
|
<div class="image">
|
||||||
|
<img src="{% thumbnail image.image 700x700 %}">
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if perms.cms_section_change %}
|
||||||
|
<a class="edit" href="{% url 'cms:updatesection' section.number %}">{% trans 'edit' %}</a><br>
|
||||||
|
{% endif %}
|
||||||
|
</section>
|
|
@ -1,4 +1,4 @@
|
||||||
{% load markdown %}
|
{% load eval i18n %}
|
||||||
|
|
||||||
<section class="text">
|
<section class="text">
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
|
@ -9,7 +9,11 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
{{section.content|markdown}}
|
{% eval section.content %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% if perms.cms_section_change %}
|
||||||
|
<a class="edit" href="{% url 'cms:updatesection' section.number %}">{% trans 'edit' %}</a><br>
|
||||||
|
{% endif %}
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
{% load embed_video_tags %}
|
{% load embed_video_tags i18n %}
|
||||||
|
|
||||||
<section class="video">
|
<section class="video">
|
||||||
<div class="wrapper">
|
{% if section.video %}
|
||||||
{% if section.video %}
|
<div class="video">
|
||||||
<div class="video">
|
<div class="iframe">
|
||||||
<div class="iframe">
|
{% video section.video '800x600' %}
|
||||||
{% video section.video '800x600' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
</div>
|
||||||
</div>
|
{% endif %}
|
||||||
|
|
||||||
|
{% if perms.cms_section_change %}
|
||||||
|
<a class="edit" href="{% url 'cms:updatesection' section.number %}">{% trans 'edit' %}</a><br>
|
||||||
|
{% endif %}
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
{% extends 'cms/base.html' %}
|
{% extends 'cms/base.html' %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% block title %}app{% endblock %}
|
|
||||||
|
{% block title %}Awesome Website{% endblock %}
|
||||||
|
|
||||||
{% block extrahead %}
|
{% block extrahead %}
|
||||||
<link rel="stylesheet" href="{% static 'app/main1.scss.css' %}">
|
<link rel="stylesheet" href="{% static 'app/main1.scss.css' %}">
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
from cms.forms import ContactForm
|
|
||||||
from cms.views import SectionView, SectionFormView
|
|
||||||
from cms.decorators import register_view
|
|
||||||
|
|
||||||
from .models import *
|
|
||||||
|
|
||||||
@register_view(TextSection)
|
|
||||||
class TextView(SectionView):
|
|
||||||
template_name = 'app/sections/text.html'
|
|
||||||
|
|
||||||
@register_view(ButtonSection)
|
|
||||||
class ButtonView(SectionView):
|
|
||||||
template_name = 'app/sections/button.html'
|
|
||||||
|
|
||||||
@register_view(ImageSection)
|
|
||||||
class ImageView(SectionView):
|
|
||||||
template_name = 'app/sections/image.html'
|
|
||||||
|
|
||||||
@register_view(VideoSection)
|
|
||||||
class VideoView(SectionView):
|
|
||||||
template_name = 'app/sections/video.html'
|
|
||||||
|
|
||||||
@register_view(ContactSection)
|
|
||||||
class ContactFormView(SectionFormView):
|
|
||||||
form_class = ContactForm
|
|
||||||
success_url = '/thanks/'
|
|
||||||
template_name = 'app/sections/contact.html'
|
|
|
@ -9,6 +9,7 @@ except ImportError:
|
||||||
PROJECT_NAME = 'example'
|
PROJECT_NAME = 'example'
|
||||||
KEYFILE = f'/tmp/{PROJECT_NAME}.secret'
|
KEYFILE = f'/tmp/{PROJECT_NAME}.secret'
|
||||||
ADMINS = [('JJ Vens', 'jj@rtts.eu')]
|
ADMINS = [('JJ Vens', 'jj@rtts.eu')]
|
||||||
|
DEFAULT_FROM_EMAIL = 'noreply@rtts.eu'
|
||||||
DEFAULT_TO_EMAIL = 'jj@rtts.eu'
|
DEFAULT_TO_EMAIL = 'jj@rtts.eu'
|
||||||
ALLOWED_HOSTS = ['*']
|
ALLOWED_HOSTS = ['*']
|
||||||
ROOT_URLCONF = 'project.urls'
|
ROOT_URLCONF = 'project.urls'
|
||||||
|
@ -24,8 +25,7 @@ MEDIA_URL = '/media/'
|
||||||
MEDIA_ROOT = '/srv/' + PROJECT_NAME + '/media'
|
MEDIA_ROOT = '/srv/' + PROJECT_NAME + '/media'
|
||||||
LOGIN_REDIRECT_URL = '/'
|
LOGIN_REDIRECT_URL = '/'
|
||||||
CMS_SECTION_MODEL = 'app.Section'
|
CMS_SECTION_MODEL = 'app.Section'
|
||||||
CMS_PAGE_MODEL = 'app.Page' # https://github.com/wq/django-swappable-models/issues/18#issuecomment-514039164
|
CMS_PAGE_MODEL = 'app.Page'
|
||||||
MARKDOWN_EXTENSIONS = ['extra', 'smarty']
|
|
||||||
|
|
||||||
def read(file):
|
def read(file):
|
||||||
with open(file) as f:
|
with open(file) as f:
|
||||||
|
@ -48,7 +48,6 @@ INSTALLED_APPS = [
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
'cms',
|
'cms',
|
||||||
'polymorphic',
|
|
||||||
'embed_video',
|
'embed_video',
|
||||||
'easy_thumbnails',
|
'easy_thumbnails',
|
||||||
'django_extensions',
|
'django_extensions',
|
||||||
|
|
Ładowanie…
Reference in New Issue