kopia lustrzana https://github.com/rtts/django-simplecms
A new templatetag 'includesection' now renders a section with its own
context, as provided by the polymorphic subsection's registered view. Also, I'm trying to move all the website-related cruft from cms into the example project, so that only the Page and Section models with their own "admin" views will remain.readwriteweb
rodzic
9e1baf6ee1
commit
5f5f303187
20
cms/forms.py
20
cms/forms.py
|
@ -1,5 +1,4 @@
|
|||
from django import forms
|
||||
from django.core.mail import EmailMessage
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
|
@ -7,25 +6,6 @@ import swapper
|
|||
Page = swapper.load_model('cms', 'Page')
|
||||
Section = swapper.load_model('cms', 'Section')
|
||||
|
||||
class ContactForm(forms.Form):
|
||||
sender = forms.EmailField(label=_('Your email address'))
|
||||
spam_protection = forms.CharField(label=_('Your message'), widget=forms.Textarea())
|
||||
message = forms.CharField(label=_('Your message'), widget=forms.Textarea(), initial='Hi there!')
|
||||
|
||||
def save(self, request):
|
||||
hostname = request.get_host()
|
||||
body = self.cleaned_data.get('spam_protection') # MUHAHA
|
||||
if len(body.split()) < 7:
|
||||
return
|
||||
email = EmailMessage(
|
||||
to = ['info@' + hostname],
|
||||
from_email = 'noreply@' + hostname,
|
||||
body = body,
|
||||
subject = _('Contact form at %(hostname)s.') % {'hostname': hostname},
|
||||
headers = {'Reply-To': self.cleaned_data.get('sender')},
|
||||
)
|
||||
email.send()
|
||||
|
||||
class PageForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Page
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
# Generated by Django 3.0.1 on 2020-01-02 20:41
|
||||
# Generated by Django 3.0.2 on 2020-01-04 22:55
|
||||
|
||||
import cms.models
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import embed_video.fields
|
||||
import markdownfield.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
@ -21,9 +20,9 @@ class Migration(migrations.Migration):
|
|||
name='Page',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('position', models.PositiveIntegerField(blank=True, verbose_name='position')),
|
||||
('title', cms.models.VarCharField(verbose_name='title')),
|
||||
('slug', models.SlugField(blank=True, help_text='A short identifier to use in URLs', unique=True, verbose_name='slug')),
|
||||
('title', cms.models.VarCharField(verbose_name='title')),
|
||||
('position', models.PositiveIntegerField(blank=True, verbose_name='position')),
|
||||
('menu', models.BooleanField(default=True, verbose_name='visible in menu')),
|
||||
],
|
||||
options={
|
||||
|
@ -38,12 +37,10 @@ class Migration(migrations.Migration):
|
|||
name='Section',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('type', cms.models.VarCharChoiceField(choices=[('textsection', 'Tekst'), ('imagesection', 'Afbeelding'), ('contactsection', 'Contact')], default='', verbose_name='section type')),
|
||||
('position', models.PositiveIntegerField(blank=True, verbose_name='position')),
|
||||
('type', cms.models.VarCharChoiceField(choices=[('textsection', 'Tekst'), ('imagesection', 'Afbeelding'), ('contactsection', 'Contact')], default='', verbose_name='type')),
|
||||
('title', cms.models.VarCharField(blank=True, verbose_name='title')),
|
||||
('color', models.PositiveIntegerField(choices=[(1, 'Wit')], default=1, verbose_name='color')),
|
||||
('content', markdownfield.models.MarkdownField(blank=True, verbose_name='content')),
|
||||
('content_rendered', markdownfield.models.RenderedMarkdownField(editable=False)),
|
||||
('position', models.PositiveIntegerField(blank=True, verbose_name='position')),
|
||||
('content', models.TextField(blank=True, verbose_name='content')),
|
||||
('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')),
|
||||
('button_text', cms.models.VarCharField(blank=True, verbose_name='button text')),
|
||||
|
|
|
@ -4,13 +4,10 @@ import swapper
|
|||
|
||||
from django.db import models
|
||||
from django.urls import reverse
|
||||
from django.conf import settings
|
||||
from django.forms import TextInput, Select
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from embed_video.fields import EmbedVideoField
|
||||
from polymorphic.models import PolymorphicModel
|
||||
from markdownfield.models import MarkdownField, RenderedMarkdownField
|
||||
from markdownfield.validators import VALIDATOR_NULL
|
||||
|
||||
from numberedmodel.models import NumberedModel
|
||||
|
||||
|
@ -56,9 +53,7 @@ class BaseSection(NumberedModel, PolymorphicModel):
|
|||
type = VarCharChoiceField(_('type'), default='', choices=TYPES)
|
||||
title = VarCharField(_('title'), blank=True)
|
||||
position = models.PositiveIntegerField(_('position'), blank=True)
|
||||
color = models.PositiveIntegerField(_('color'), default=1, choices=settings.SECTION_COLORS)
|
||||
content = MarkdownField(_('content'), rendered_field='content_rendered', validator=VALIDATOR_NULL, use_admin_editor=False, blank=True)
|
||||
content_rendered = RenderedMarkdownField()
|
||||
content = models.TextField(_('content'), blank=True)
|
||||
image = models.ImageField(_('image'), blank=True)
|
||||
video = EmbedVideoField(_('video'), blank=True, help_text=_('Paste a YouTube, Vimeo, or SoundCloud link'))
|
||||
button_text = VarCharField(_('button text'), blank=True)
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
{% load i18n %}
|
||||
|
||||
{% if request.user.is_staff %}
|
||||
<div class="edit">
|
||||
<a href="{% url 'cms:updatesection' section.pk %}">{% trans 'edit this section' %}</a>
|
||||
</div>
|
||||
{% endif %}
|
|
@ -1,13 +1,12 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% load cms %}
|
||||
|
||||
{% block title %}{{block.super}} - {{page.title}}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% for section in sections %}
|
||||
<section class="{{section.type}} color{{section.color}}">
|
||||
{% include 'cms/sections/'|add:section.type|lower|add:'.html' %}
|
||||
</section>
|
||||
{% include_section section %}
|
||||
{% endfor %}
|
||||
|
||||
<div class="edit page">
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
{% load i18n thumbnail embed_video_tags %}
|
||||
{% load markdown %}
|
||||
|
||||
{% block section %}
|
||||
<section class="{{section.type}} color{{section.color}}">
|
||||
<div class="wrapper">
|
||||
|
||||
{% if section.image %}
|
||||
|
@ -17,9 +18,9 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if section.content_rendered %}
|
||||
{% if section.content %}
|
||||
<div class="content">
|
||||
{{section.content_rendered|safe}}
|
||||
{{section.content|markdown}}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
@ -31,32 +32,12 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if section.subsections.exists %}
|
||||
<div class="subsections">
|
||||
{% for sub in section.subsections.all %}
|
||||
<div class="subsection color{{sub.color}}">
|
||||
{% with section=sub user=None %}
|
||||
{% include 'cms/sections/base.html' %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if section.button_text %}
|
||||
<div class="button">
|
||||
<a class="button" href="{{section.button_link}}">{{section.button_text}}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extracontent %}{% endblock %}
|
||||
|
||||
{% if user.is_staff %}
|
||||
<div class="wrapper">
|
||||
<div class="edit">
|
||||
<a href="{% url 'cms:updatesection' section.pk %}">{% trans 'edit this section' %}</a>
|
||||
</div>
|
||||
{% include 'cms/editlink.html' %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</section>
|
|
@ -0,0 +1,29 @@
|
|||
from django import template
|
||||
|
||||
register = template.Library()
|
||||
|
||||
@register.tag('include_section')
|
||||
def do_include(parser, token):
|
||||
'''Renders the section with its own context
|
||||
|
||||
'''
|
||||
_, section = token.split_contents()
|
||||
return IncludeSectionNode(section)
|
||||
|
||||
class IncludeSectionNode(template.Node):
|
||||
def __init__(self, section):
|
||||
self.section = template.Variable(section)
|
||||
self.csrf_token = template.Variable('csrf_token')
|
||||
super().__init__()
|
||||
|
||||
def render(self, context):
|
||||
section = self.section.resolve(context)
|
||||
template_name = section.view.template_name
|
||||
if template_name is None:
|
||||
raise ValueError(f'{section} view has no template_name attribute')
|
||||
csrf_token = self.csrf_token.resolve(context)
|
||||
if not hasattr(section, 'context'):
|
||||
raise ValueError(dir(section))
|
||||
section.context.update({'csrf_token': csrf_token})
|
||||
t = context.template.engine.get_template(template_name)
|
||||
return t.render(template.Context(section.context))
|
|
@ -0,0 +1,15 @@
|
|||
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))
|
|
@ -9,8 +9,6 @@ urlpatterns = [
|
|||
path('updatesection/<int:pk>/', UpdateSection.as_view(), name='updatesection'),
|
||||
path('createpage/', CreatePage.as_view(), name='createpage'),
|
||||
path('createsection/<int:pk>', CreateSection.as_view(), name='createsection'),
|
||||
|
||||
# Feel free to copy the following into your root URL conf!
|
||||
path('', PageView.as_view(), name='page'),
|
||||
path('<slug:slug>/', PageView.as_view(), name='page'),
|
||||
]
|
||||
|
|
71
cms/views.py
71
cms/views.py
|
@ -4,6 +4,7 @@ import json
|
|||
import swapper
|
||||
|
||||
from django.views import generic
|
||||
from django.views.generic.edit import FormMixin
|
||||
from django.shortcuts import redirect
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
@ -19,43 +20,27 @@ Section = swapper.load_model('cms', 'Section')
|
|||
@register_view(Section)
|
||||
class SectionView:
|
||||
'''Generic section view'''
|
||||
def get(self, request, section):
|
||||
'''Override this to add custom attributes to a section'''
|
||||
return section
|
||||
template_name = 'cms/sections/section.html'
|
||||
|
||||
class SectionWithFormView(SectionView):
|
||||
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(FormMixin, SectionView):
|
||||
'''Generic section with associated form'''
|
||||
form_class = None
|
||||
success_url = None
|
||||
|
||||
def get_form_class(self):
|
||||
'''Return the form class to use in this view.'''
|
||||
if self.form_class:
|
||||
return self.form_class
|
||||
raise ImproperlyConfigured(
|
||||
'Either specify formclass attribute or override get_form_class()')
|
||||
|
||||
def get_success_url(self):
|
||||
'''Return the URL to redirect to after processing a valid form.'''
|
||||
if self.success_url:
|
||||
return self.success_url
|
||||
raise ImproperlyConfigured(
|
||||
'Either specify success_url attribute or override get_success_url()')
|
||||
|
||||
def get(self, request, section):
|
||||
'''Add form to section'''
|
||||
form = self.get_form_class()()
|
||||
section.form = form
|
||||
return section
|
||||
|
||||
def post(self, request, section):
|
||||
def post(self, request):
|
||||
'''Process form'''
|
||||
form = self.get_form_class()(request.POST, request.FILES)
|
||||
form = self.get_form()
|
||||
if form.is_valid():
|
||||
form.save(request)
|
||||
return redirect(self.get_success_url())
|
||||
section.form = form
|
||||
return section
|
||||
return form
|
||||
|
||||
class MenuMixin:
|
||||
'''Add pages to template context'''
|
||||
|
@ -83,14 +68,21 @@ class PageView(MenuMixin, MemoryMixin, generic.DetailView):
|
|||
'''Supply a default argument for slug'''
|
||||
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):
|
||||
'''Call each sections's get() view before rendering final response'''
|
||||
'''Initialize sections and render final response'''
|
||||
page = self.object = self.get_object()
|
||||
context = self.get_context_data(**kwargs)
|
||||
sections = page.sections.all()
|
||||
for section in sections:
|
||||
view = section.__class__.view_class()
|
||||
view.get(request, section)
|
||||
self.initialize_section(section)
|
||||
context.update({
|
||||
'page': page,
|
||||
'sections': sections,
|
||||
|
@ -98,7 +90,8 @@ class PageView(MenuMixin, MemoryMixin, generic.DetailView):
|
|||
return self.render_to_response(context)
|
||||
|
||||
def post(self, request, **kwargs):
|
||||
'''Call the post() function of the correct section view'''
|
||||
'''Initialize sections and call the post() function of the correct
|
||||
section view'''
|
||||
try:
|
||||
pk = int(self.request.POST.get('section'))
|
||||
except:
|
||||
|
@ -108,20 +101,20 @@ class PageView(MenuMixin, MemoryMixin, generic.DetailView):
|
|||
context = self.get_context_data(**kwargs)
|
||||
sections = page.sections.all()
|
||||
for section in sections:
|
||||
view = section.__class__.view_class()
|
||||
self.initialize_section(section)
|
||||
if section.pk == pk:
|
||||
result = view.post(request, section)
|
||||
result = section.view.post(request)
|
||||
if isinstance(result, HttpResponseRedirect):
|
||||
return result
|
||||
else:
|
||||
view.get(request, section)
|
||||
section.context['form'] = result
|
||||
|
||||
context.update({
|
||||
'page': page,
|
||||
'sections': sections,
|
||||
})
|
||||
return self.render_to_response(context)
|
||||
|
||||
# The following views all require a logged-in staff member
|
||||
# The following views require a logged-in staff member
|
||||
|
||||
class StaffRequiredMixin(UserPassesTestMixin):
|
||||
def test_func(self):
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
from django import forms
|
||||
from django.core.mail import EmailMessage
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
class ContactForm(forms.Form):
|
||||
sender = forms.EmailField(label=_('Your email address'))
|
||||
spam_protection = forms.CharField(label=_('Your message'), widget=forms.Textarea())
|
||||
message = forms.CharField(label=_('Your message'), widget=forms.Textarea(), initial='Hi there!')
|
||||
|
||||
def save(self, request):
|
||||
hostname = request.get_host()
|
||||
body = self.cleaned_data.get('spam_protection') # MUHAHA
|
||||
if len(body.split()) < 7:
|
||||
return
|
||||
email = EmailMessage(
|
||||
to = ['info@' + hostname],
|
||||
from_email = 'noreply@' + hostname,
|
||||
body = body,
|
||||
subject = _('Contact form at %(hostname)s.') % {'hostname': hostname},
|
||||
headers = {'Reply-To': self.cleaned_data.get('sender')},
|
||||
)
|
||||
email.send()
|
|
@ -1,11 +1,10 @@
|
|||
# Generated by Django 3.0.1 on 2020-01-02 20:42
|
||||
# Generated by Django 3.0.2 on 2020-01-05 02:29
|
||||
|
||||
import cms.models
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import embed_video.fields
|
||||
import markdownfield.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
@ -13,8 +12,8 @@ class Migration(migrations.Migration):
|
|||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.CMS_PAGE_MODEL),
|
||||
('contenttypes', '0002_remove_content_type_name'),
|
||||
migrations.swappable_dependency(settings.CMS_PAGE_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
@ -22,9 +21,9 @@ class Migration(migrations.Migration):
|
|||
name='Page',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('position', models.PositiveIntegerField(blank=True, verbose_name='position')),
|
||||
('title', cms.models.VarCharField(verbose_name='title')),
|
||||
('slug', models.SlugField(blank=True, help_text='A short identifier to use in URLs', unique=True, verbose_name='slug')),
|
||||
('title', cms.models.VarCharField(verbose_name='title')),
|
||||
('position', models.PositiveIntegerField(blank=True, verbose_name='position')),
|
||||
('menu', models.BooleanField(default=True, verbose_name='visible in menu')),
|
||||
],
|
||||
options={
|
||||
|
@ -38,16 +37,15 @@ class Migration(migrations.Migration):
|
|||
name='Section',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('type', cms.models.VarCharChoiceField(choices=[('textsection', 'Tekst'), ('imagesection', 'Afbeelding'), ('contactsection', 'Contact')], default='', verbose_name='section type')),
|
||||
('position', models.PositiveIntegerField(blank=True, verbose_name='position')),
|
||||
('type', cms.models.VarCharChoiceField(choices=[('textsection', 'Tekst'), ('buttonsection', 'Button'), ('imagesection', 'Afbeelding'), ('videosection', 'Video'), ('contactsection', 'Contact')], default='', verbose_name='type')),
|
||||
('title', cms.models.VarCharField(blank=True, verbose_name='title')),
|
||||
('color', models.PositiveIntegerField(choices=[(1, 'Wit')], default=1, verbose_name='color')),
|
||||
('content', markdownfield.models.MarkdownField(blank=True, verbose_name='content')),
|
||||
('content_rendered', markdownfield.models.RenderedMarkdownField(editable=False)),
|
||||
('position', models.PositiveIntegerField(blank=True, verbose_name='position')),
|
||||
('content', models.TextField(blank=True, verbose_name='content')),
|
||||
('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')),
|
||||
('button_text', cms.models.VarCharField(blank=True, verbose_name='button text')),
|
||||
('button_link', cms.models.VarCharField(blank=True, verbose_name='button link')),
|
||||
('color', models.PositiveIntegerField(choices=[(1, 'Wit')], default=1, verbose_name='kleur')),
|
||||
('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')),
|
||||
],
|
||||
|
@ -58,6 +56,17 @@ class Migration(migrations.Migration):
|
|||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ButtonSection',
|
||||
fields=[
|
||||
],
|
||||
options={
|
||||
'proxy': True,
|
||||
'indexes': [],
|
||||
'constraints': [],
|
||||
},
|
||||
bases=('app.section',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ContactSection',
|
||||
fields=[
|
||||
|
@ -91,4 +100,15 @@ class Migration(migrations.Migration):
|
|||
},
|
||||
bases=('app.section',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='VideoSection',
|
||||
fields=[
|
||||
],
|
||||
options={
|
||||
'proxy': True,
|
||||
'indexes': [],
|
||||
'constraints': [],
|
||||
},
|
||||
bases=('app.section',),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
from django.db import models
|
||||
from django.conf import settings
|
||||
from cms.models import BasePage, BaseSection
|
||||
from cms.decorators import register_model
|
||||
|
||||
|
@ -9,13 +11,20 @@ class Page(BasePage):
|
|||
|
||||
class Section(BaseSection):
|
||||
'''Add custom fields here. Already existing fields: type, position,
|
||||
title, color, content, image, video, button_text, button_link
|
||||
title, content, image, video, button_text, button_link
|
||||
|
||||
'''
|
||||
color = models.PositiveIntegerField('kleur', default=1, choices=settings.SECTION_COLORS)
|
||||
|
||||
@register_model('Tekst')
|
||||
class TextSection(Section):
|
||||
fields = ['type', 'position', 'title', 'content']
|
||||
fields = ['type', 'position', 'title', 'color', 'content']
|
||||
class Meta:
|
||||
proxy = True
|
||||
|
||||
@register_model('Button')
|
||||
class ButtonSection(Section):
|
||||
fields = ['type', 'position', 'title', 'button_text', 'button_link']
|
||||
class Meta:
|
||||
proxy = True
|
||||
|
||||
|
@ -25,6 +34,12 @@ class ImageSection(Section):
|
|||
class Meta:
|
||||
proxy = True
|
||||
|
||||
@register_model('Video')
|
||||
class VideoSection(Section):
|
||||
fields = ['type', 'position', 'title', 'video']
|
||||
class Meta:
|
||||
proxy = True
|
||||
|
||||
@register_model('Contact')
|
||||
class ContactSection(Section):
|
||||
fields = ['type', 'position', 'title']
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<section class="{{section.type}} color{{section.color}}">
|
||||
<div class="wrapper">
|
||||
{% if section.button_text %}
|
||||
<div class="button">
|
||||
<a class="button" href="{{section.button_link}}">{{section.button_text}}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% include 'cms/editlink.html' %}
|
||||
</div>
|
||||
</section>
|
|
@ -1,7 +1,6 @@
|
|||
{% extends 'cms/sections/base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block section %}
|
||||
<section class="{{section.type}} color{{section.color}}">
|
||||
<div class="wrapper">
|
||||
<div class="title">
|
||||
<h1>{{section.title}}</h1>
|
||||
|
@ -10,12 +9,13 @@
|
|||
<div class="form">
|
||||
<form method="post" class="cms">
|
||||
{% csrf_token %}
|
||||
{% for field in section.form %}
|
||||
{% for field in form %}
|
||||
{% include 'cms/formfield.html' with field=field %}
|
||||
{% endfor %}
|
||||
<button class="button" name="section" value="{{section.pk}}">{% trans 'Send' %}</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% include 'cms/editlink.html' %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
</section>
|
|
@ -0,0 +1,14 @@
|
|||
{% load thumbnail %}
|
||||
|
||||
<section class="{{section.type}} color{{section.color}}">
|
||||
<div class="wrapper">
|
||||
|
||||
{% if section.image %}
|
||||
<div class="image">
|
||||
<img alt="{{section.title}}" src="{% thumbnail section.image 800x800 %}">
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% include 'cms/editlink.html' %}
|
||||
</div>
|
||||
</section>
|
|
@ -0,0 +1,21 @@
|
|||
{% load markdown %}
|
||||
|
||||
<section class="{{section.type}} color{{section.color}}">
|
||||
<div class="wrapper">
|
||||
{% if section.title %}
|
||||
<div class="title">
|
||||
<h1>
|
||||
{{section.title}}
|
||||
</h1>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if section.content %}
|
||||
<div class="content">
|
||||
{{section.content|markdown}}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% include 'cms/editlink.html' %}
|
||||
</div>
|
||||
</section>
|
|
@ -0,0 +1,16 @@
|
|||
{% load embed_video_tags %}
|
||||
|
||||
<section class="{{section.type}} color{{section.color}}">
|
||||
<div class="wrapper">
|
||||
|
||||
{% if section.video %}
|
||||
<div class="video">
|
||||
<div class="iframe">
|
||||
{% video section.video '800x600' %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% include 'cms/editlink.html' %}
|
||||
</div>
|
||||
</section>
|
|
@ -1 +0,0 @@
|
|||
{% extends 'cms/sections/base.html' %}
|
|
@ -1 +0,0 @@
|
|||
{% extends 'cms/sections/base.html' %}
|
|
@ -1,10 +1,27 @@
|
|||
from cms.forms import ContactForm
|
||||
from cms.views import SectionWithFormView
|
||||
from cms.views import SectionView, SectionFormView
|
||||
from cms.decorators import register_view
|
||||
|
||||
from .models import *
|
||||
from .forms import ContactForm
|
||||
|
||||
@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(SectionWithFormView):
|
||||
class ContactFormView(SectionFormView):
|
||||
form_class = ContactForm
|
||||
success_url = '/thanks/'
|
||||
template_name = 'app/sections/contact.html'
|
||||
|
|
|
@ -7,7 +7,6 @@ except ImportError:
|
|||
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
||||
|
||||
PROJECT_NAME = 'example'
|
||||
SITE_URL = 'https://example.com/'
|
||||
KEYFILE = f'/tmp/{PROJECT_NAME}.secret'
|
||||
ADMINS = [('JJ Vens', 'jj@rtts.eu')]
|
||||
ALLOWED_HOSTS = ['*']
|
||||
|
@ -25,7 +24,6 @@ MEDIA_ROOT = '/srv/' + PROJECT_NAME + '/media'
|
|||
LOGIN_REDIRECT_URL = '/'
|
||||
CMS_SECTION_MODEL = 'app.Section'
|
||||
MARKDOWN_EXTENSIONS = ['extra', 'smarty']
|
||||
MARKDOWN_EXTENSION_CONFIGS = {'extra': {}, 'smarty': {}}
|
||||
|
||||
def read(file):
|
||||
with open(file) as f:
|
||||
|
|
4
setup.py
4
setup.py
|
@ -3,7 +3,7 @@ from setuptools import setup, find_packages
|
|||
|
||||
setup(
|
||||
name = 'django-simplecms',
|
||||
version = '2.2.0',
|
||||
version = '2.3.0',
|
||||
url = 'https://github.com/rtts/django-simplecms',
|
||||
author = 'Jaap Joris Vens',
|
||||
author_email = 'jj@rtts.eu',
|
||||
|
@ -16,11 +16,9 @@ setup(
|
|||
'django-extensions',
|
||||
'django-embed-video',
|
||||
'django-polymorphic',
|
||||
'django-markdownfield',
|
||||
'easy-thumbnails',
|
||||
'psycopg2',
|
||||
'markdown',
|
||||
'bleach',
|
||||
'libsass',
|
||||
'swapper',
|
||||
],
|
||||
|
|
Ładowanie…
Reference in New Issue