add support for subsections

readwriteweb
Jaap Joris Vens 2019-08-23 17:19:40 +02:00
rodzic caa04922b7
commit 7997d7af0f
14 zmienionych plików z 298 dodań i 50 usunięć

Wyświetl plik

@ -1,9 +1,20 @@
from django import forms
from .models import Page, Section
from .models import Page, Section, SubSection
class PageForm(forms.ModelForm):
class Meta:
model = Page
fields = '__all__'
SectionFormSet = forms.inlineformset_factory(Page, Section, exclude='__all__', extra=1)
class SectionForm(forms.ModelForm):
class Meta:
model = Section
exclude = ['page']
class SubSectionForm(forms.ModelForm):
class Meta:
model = SubSection
exclude = ['section']
SectionFormSet = forms.inlineformset_factory(Page, Section, exclude='__all__', extra=0)
SubSectionFormSet = forms.inlineformset_factory(Section, SubSection, exclude='__all__', extra=0)

Wyświetl plik

@ -0,0 +1,32 @@
import ckeditor.fields
import cms.models
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('cms', '0002_title'),
]
operations = [
migrations.CreateModel(
name='SubSection',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('position', models.PositiveIntegerField(blank=True, verbose_name='position')),
('title', models.CharField(blank=True, max_length=255, verbose_name='title')),
('color', models.PositiveIntegerField(choices=[(1, 'Licht'), (2, 'Donker')], default=1, verbose_name='color')),
('content', ckeditor.fields.RichTextField(blank=True, verbose_name='content')),
('image', models.ImageField(blank=True, upload_to='', verbose_name='image')),
('button_text', cms.models.VarCharField(blank=True, verbose_name='button text')),
('button_link', cms.models.VarCharField(blank=True, verbose_name='button link')),
('section', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='subsections', to='cms.Section', verbose_name='section')),
],
options={
'verbose_name': 'subsection',
'verbose_name_plural': 'subsections',
'ordering': ['position'],
},
),
]

Wyświetl plik

@ -1,11 +1,17 @@
from django.db import models
from django.urls import reverse
from django.conf import settings
from django.forms import TextInput
from django.utils.translation import gettext_lazy as _
from ckeditor.fields import RichTextField
from embed_video.fields import EmbedVideoField
class VarCharField(models.TextField):
def formfield(self, **kwargs):
kwargs.update({'widget': TextInput})
return super().formfield(**kwargs)
# From https://github.com/JaapJoris/django-numberedmodel.git
class NumberedModel(models.Model):
def number_with_respect_to(self):
@ -57,7 +63,7 @@ class Page(NumberedModel):
menu = models.BooleanField(_('visible in menu'), default=True)
def __str__(self):
return '{}. {}'.format(self.position, self.title)
return '{}: {}. {}'.format(self._meta.verbose_name.title(), self.position, self.title)
def get_absolute_url(self):
if self.slug:
@ -87,19 +93,37 @@ class Section(NumberedModel):
return self.page.sections.all()
def __str__(self):
if not self.position:
title = _('New section')
elif not self.title:
title = '{}. {}'.format(self.position, _('Untitled'))
else:
title = '{}. {}'.format(self.position, self.title)
return str(title)
title = self.title if self.title else _('Untitled')
return '{}: {}. {}'.format(self._meta.verbose_name.title(), self.position, title)
class Meta:
verbose_name = _('section')
verbose_name_plural = _('sections')
ordering = ['position']
class SubSection(NumberedModel):
section = models.ForeignKey(Section, verbose_name=_('section'), related_name='subsections', on_delete=models.PROTECT)
position = models.PositiveIntegerField(_('position'), blank=True)
title = models.CharField(_('title'), max_length=255, blank=True)
color = models.PositiveIntegerField(_('color'), default=1, choices=settings.SECTION_COLORS)
content = RichTextField(_('content'), blank=True)
image = models.ImageField(_('image'), blank=True)
button_text = VarCharField(_('button text'), blank=True)
button_link = VarCharField(_('button link'), blank=True)
def number_with_respect_to(self):
return self.section.subsections.all()
def __str__(self):
title = self.title if self.title else _('Untitled')
return '{}: {}. {}'.format(self._meta.verbose_name.title(), self.position, title)
class Meta:
verbose_name = _('subsection')
verbose_name_plural = _('subsections')
ordering = ['position']
class Config(models.Model):
TYPES = [
(10, _('Footer')),

Wyświetl plik

@ -113,12 +113,15 @@ nav {
}
div.edit {
position: fixed;
left: 1em;
top: 1em;
font-size: 0.8em;
color: red;
&.page {
position: fixed;
left: 1em;
top: 1em;
}
a, button {
color: inherit;
border: none;

Wyświetl plik

@ -37,6 +37,7 @@ header, section, footer {
nav ul#menu li a:hover, nav ul#menu li a.current {
transform: scale(1.2);
transform-origin: bottom; } }
@media (max-width: 500px) {
nav button#hamburger {
position: absolute;
@ -79,11 +80,12 @@ header, section, footer {
transform: translatex(0); } }
div.edit {
position: fixed;
left: 1em;
top: 1em;
font-size: 0.8em;
color: red; }
div.edit.page {
position: fixed;
left: 1em;
top: 1em; }
div.edit a, div.edit button {
color: inherit;
border: none;
@ -102,9 +104,11 @@ a.edit {
article section div.image img {
width: 100%; }
article section div.title {
font-size: 2em;
text-align: center; }
article section div.video div.iframe {
width: 100%;
padding-bottom: 56%;
@ -115,6 +119,7 @@ article section div.video div.iframe {
height: 100%;
left: 0;
top: 0; }
article section div.button {
text-align: center; }
article section div.button a {
@ -134,6 +139,7 @@ form div.global_error {
background: #f001;
color: red;
font-weight: bold; }
form fieldset {
padding: 2em;
margin-bottom: 2em;
@ -141,33 +147,43 @@ form fieldset {
border-radius: 3px; }
form fieldset legend {
font-size: 1.15em; }
form div.formfield {
margin: 5px 0;
padding: 10px 0;
font-size: 0; }
form div.formfield > * {
font-size: 1rem; }
form div.formfield.error {
border: 2px dotted red;
padding: 10px;
margin: 0 -10px;
background: #f001; }
form div.formfield.required div.label {
font-weight: 700; }
form div.formfield.required input, form div.formfield.required select, form div.formfield.required textarea {
border: 1px solid black; }
form div.label {
font-size: 0.8rem;
font-weight: 400; }
form div.input {
overflow: hidden;
margin: 5px 0; }
form div.helptext, form span.required {
color: #666;
font-size: 12px !important;
font-weight: 400 !important; }
form span.required {
font-style: italic; }
form input, form select, form textarea {
background: white;
color: black;
@ -178,21 +194,27 @@ form input, form select, form textarea {
box-sizing: border-box;
margin: 0;
padding: 5px; }
form div.django-ckeditor-widget {
display: block !important; }
form div.cke_chrome {
box-sizing: border-box !important;
border: 1px solid #aaa !important; }
form input[type=checkbox] {
width: auto; }
form select {
background: white; }
form div.filefield {
border: 1px solid #aaa;
background: white;
padding: 4px; }
form div.filefield input {
border: none; }
form ul.errorlist {
margin: 0;
margin-bottom: 1em;
@ -203,8 +225,9 @@ form ul.errorlist {
form ul.errorlist li {
margin: 0;
padding: 0; }
form .errors {
color: red;
font-weight: bold; }
/*# sourceMappingURL=cms.scss.css.map */
/*# sourceMappingURL=cms.scss.css.map */

File diff suppressed because one or more lines are too long

Wyświetl plik

@ -1,23 +1,29 @@
{% extends 'base.html' %}
{% load i18n %}
{% block header %}{% endblock %}
{% block nav %}{% endblock %}
{% block content %}
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{form.media}}
<div class="wrapper">
{% if form.errors or formset.errors %}
<div class="global_error">
{% trans 'Please correct the error(s) below and save again' %}
</div>
{% endif %}
{% for field in form %}
{% include 'cms/formfield.html' with field=field %}
{% endfor %}
</div>
<section>
<div class="wrapper">
<h1>{{form.instance}}</h1>
{% if form.errors or formset.errors %}
<div class="global_error">
{% trans 'Please correct the error(s) below and save again' %}
</div>
{% endif %}
{% for field in form %}
{% include 'cms/formfield.html' with field=field %}
{% endfor %}
</div>
</section>
{% block sections %}
{% block formset %}
{{formset.management_form}}
{% for form in formset %}
{{form.media}}
@ -26,16 +32,26 @@
{% endfor %}
<section>
<div class="wrapper">
<h1>{% trans 'Section' %}: {{form.instance}}</h1>
<h1>{{form.instance}}</h1>
{% for field in form.visible_fields %}
{% include 'cms/formfield.html' with field=field %}
{% endfor %}
</div>
</section>
{% endfor %}
<section>
<div class="wrapper">
<div class="button">
<a href="{{formset_form_url}}">
Add a new {{formset_description}}
</a>
</div>
</div>
</section>
{% endblock %}
<div class="edit">
<div class="edit page">
<button>{% trans 'save' %}</button>
</div>
</form>

Wyświetl plik

@ -0,0 +1,42 @@
{% extends 'base.html' %}
{% load i18n %}
{% block content %}
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{form.media}}
<div class="wrapper">
{% if form.errors or formset.errors %}
<div class="global_error">
{% trans 'Please correct the error(s) below and save again' %}
</div>
{% endif %}
{% for field in form %}
{% include 'cms/formfield.html' with field=field %}
{% endfor %}
</div>
{% block sections %}
{{formset.management_form}}
{% for form in formset %}
{{form.media}}
{% for field in form.hidden_fields %}
{{field}}
{% endfor %}
<section>
<div class="wrapper">
<h1>{% trans 'Section' %}: {{form.instance}}</h1>
{% for field in form.visible_fields %}
{% include 'cms/formfield.html' with field=field %}
{% endfor %}
</div>
</section>
{% endfor %}
{% endblock %}
<div class="edit">
<button>{% trans 'save' %}</button>
</div>
</form>
{% endblock %}

Wyświetl plik

@ -0,0 +1,43 @@
{% extends 'base.html' %}
{% load i18n %}
{% block content %}
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{form.media}}
<section>
<div class="wrapper">
<h1>{{form.model}}: {{form.object}}</h1>
{% if form.errors or formset.errors %}
<div class="global_error">
{% trans 'Please correct the error(s) below and save again' %}
</div>
{% endif %}
{% for field in form %}
{% include 'cms/formfield.html' with field=field %}
{% endfor %}
</div>
</section>
{{formset.management_form}}
{% for form in formset %}
{{form.media}}
{% for field in form.hidden_fields %}
{{field}}
{% endfor %}
<section>
<div class="wrapper">
<h1>{% trans 'Subsection' %}: {{form.instance}}</h1>
{% for field in form.visible_fields %}
{% include 'cms/formfield.html' with field=field %}
{% endfor %}
</div>
</section>
{% endfor %}
<div class="edit">
<button>{% trans 'save' %}</button>
</div>
</form>
{% endblock %}

Wyświetl plik

@ -1,3 +1,3 @@
{% extends 'cms/edit.html' %}
{% block sections %}{% endblock %}
{% block formset %}{% endblock %}

Wyświetl plik

@ -4,7 +4,7 @@
{% block title %}{{block.super}} - {{object.title}}{% endblock %}
{% block content %}
<div class="edit">
<div class="edit page">
{% if user.is_staff %}
{% if object.slug %}
<a href="{% url 'cms:updatepage' object.slug %}">{% trans 'edit this page' %}</a>

Wyświetl plik

@ -1,4 +1,4 @@
{% load thumbnail embed_video_tags %}
{% load i18n thumbnail embed_video_tags %}
<div class="wrapper">
@ -41,4 +41,10 @@
</div>
{% endif %}
{% if user.is_staff %}
<div class="edit">
<a href="{% url 'cms:updatesection' section.pk %}">{% trans 'edit this section' %}</a>
</div>
{% endif %}
</div>

Wyświetl plik

@ -1,5 +1,5 @@
from django.urls import path
from .views import PageView, UpdatePage, CreatePage
from .views import PageView, UpdatePage, CreatePage, UpdateSection, CreateSection, CreateSubSection
app_name = 'cms'
@ -7,6 +7,9 @@ urlpatterns = [
path('', PageView.as_view(), {'slug': ''}, name='homepage'),
path('<slug:slug>/', PageView.as_view(), name='page'),
path('cms/homepage/', UpdatePage.as_view(), {'slug': ''}, name='updatehomepage'),
path('cms/page/<slug:slug>/', UpdatePage.as_view(), name='updatepage'),
path('cms/page/<int:pk>/', UpdatePage.as_view(), name='updatepage'),
path('cms/section/<int:pk>/', UpdateSection.as_view(), name='updatesection'),
path('cms/newpage/', CreatePage.as_view(), name='createpage'),
path('cms/page/<int:pk>/newsection/', CreateSection.as_view(), name='createsection'),
path('cms/section/<int:pk>/newsubsection/', CreateSubSection.as_view(), name='createsubsection'),
]

Wyświetl plik

@ -1,9 +1,10 @@
from django.urls import reverse
from django.shortcuts import redirect
from django.contrib.auth.mixins import UserPassesTestMixin
from django.views.generic import DetailView, UpdateView, CreateView
from .models import Page
from .forms import PageForm, SectionFormSet
from .models import Page, Section, SubSection
from .forms import PageForm, SectionFormSet, SectionForm, SubSectionFormSet, SubSectionForm
from .utils import get_config
class StaffRequiredMixin(UserPassesTestMixin):
@ -25,20 +26,38 @@ class PageView(MenuMixin, DetailView):
model = Page
template_name = 'cms/page.html'
class CreatePage(StaffRequiredMixin, MenuMixin, CreateView):
class CreatePage(StaffRequiredMixin, CreateView):
model = Page
form_class = PageForm
template_name = 'cms/new.html'
class UpdatePage(StaffRequiredMixin, MenuMixin, UpdateView):
model = Page
form_class = PageForm
class CreateSection(StaffRequiredMixin, CreateView):
model = Section
form_class = SectionForm
template_name = 'cms/new.html'
def form_valid(self, form):
form.instance.page = Page.objects.get(pk=self.kwargs.get('pk'))
form.save()
return redirect(reverse('cms:updatepage', args=[form.instance.page.pk]))
class CreateSubSection(StaffRequiredMixin, CreateView):
model = SubSection
form_class = SubSectionForm
template_name = 'cms/new.html'
def form_valid(self, form):
form.instance.section = Section.objects.get(pk=self.kwargs.get('pk'))
form.save()
return redirect(reverse('cms:updatesection', args=[form.instance.section.pk]))
class BaseUpdateView(StaffRequiredMixin, MenuMixin, UpdateView):
template_name = 'cms/edit.html'
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
formset = SectionFormSet(request.POST, request.FILES, instance=self.object)
formset = self.formset_class(request.POST, request.FILES, instance=self.object)
if form.is_valid() and formset.is_valid():
return self.form_valid(form, formset)
else:
@ -47,7 +66,7 @@ class UpdatePage(StaffRequiredMixin, MenuMixin, UpdateView):
def form_valid(self, form, formset):
form.save()
formset.save()
return redirect(form.instance.get_absolute_url())
return redirect(self.get_absolute_url(form.instance))
def form_invalid(self, form, formset):
return self.render_to_response(self.get_context_data(form=form, formset=formset))
@ -55,8 +74,32 @@ class UpdatePage(StaffRequiredMixin, MenuMixin, UpdateView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if 'formset' not in context:
formset = SectionFormSet(instance=self.object)
formset = self.formset_class(instance=self.object)
context.update({
'formset': formset,
'formset_form_url': self.get_formset_form_url(self.object),
'formset_description': self.formset_class.model._meta.verbose_name.title(),
})
return context
class UpdatePage(BaseUpdateView):
model = Page
form_class = PageForm
formset_class = SectionFormSet
def get_formset_form_url(self, page):
return reverse('cms:createsection', args=[page.pk])
def get_absolute_url(self, instance):
return instance.get_absolute_url()
class UpdateSection(BaseUpdateView):
model = Section
form_class = SectionForm
formset_class = SubSectionFormSet
def get_formset_form_url(self, page):
return reverse('cms:createsubsection', args=[page.pk])
def get_absolute_url(self, instance):
return instance.page.get_absolute_url()