You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
646 lines
23 KiB
646 lines
23 KiB
from datetime import date
|
|
|
|
from crispy_forms.utils import flatatt
|
|
from django.db import models
|
|
import django.db.models.options as options
|
|
from django.utils.html import format_html, format_html_join
|
|
from wagtail.embeds.blocks import EmbedBlock
|
|
|
|
options.DEFAULT_NAMES = options.DEFAULT_NAMES + ('description',)
|
|
from django.urls import reverse
|
|
from django.contrib import messages
|
|
|
|
from wagtailcaptcha.models import WagtailCaptchaEmailForm
|
|
from crispy_forms.helper import FormHelper
|
|
from django_extensions.db.models import TimeStampedModel
|
|
|
|
from modelcluster.fields import ParentalKey
|
|
from wagtail import blocks
|
|
from wagtail.documents import get_document_model
|
|
from wagtail.images import get_image_model
|
|
from wagtail.models import Page
|
|
from wagtail.fields import RichTextField, StreamField
|
|
from wagtail.admin.panels import (
|
|
MultiFieldPanel, PageChooserPanel, InlinePanel, FieldPanel)
|
|
from wagtail.images.blocks import ImageChooserBlock
|
|
from wagtail.documents.blocks import DocumentChooserBlock
|
|
from wagtail.contrib.forms.models import AbstractEmailForm, AbstractFormField
|
|
from wagtailmedia.edit_handlers import MediaChooserPanel
|
|
|
|
from wagtailmedia.blocks import AbstractMediaChooserBlock
|
|
|
|
|
|
class MediaBlock(AbstractMediaChooserBlock):
|
|
def render_basic(self, value, context=None):
|
|
if not value:
|
|
return ''
|
|
|
|
if value.type == 'video':
|
|
player_code = '''
|
|
<div>
|
|
<video width="720" controls>
|
|
{0}
|
|
Your browser does not support the video tag.
|
|
</video>
|
|
</div>
|
|
'''
|
|
else:
|
|
player_code = '''
|
|
<div>
|
|
<audio controls>
|
|
{0}
|
|
Your browser does not support the audio element.
|
|
</audio>
|
|
</div>
|
|
'''
|
|
|
|
return format_html(player_code, format_html_join(
|
|
'\n', "<source{0}>",
|
|
[[flatatt(s)] for s in value.sources]
|
|
))
|
|
|
|
|
|
class HomePage(Page):
|
|
block1 = RichTextField(blank=True)
|
|
|
|
# revolution slider
|
|
slide1_img = models.ForeignKey(
|
|
get_image_model(),
|
|
null=True,
|
|
blank=True,
|
|
on_delete=models.SET_NULL,
|
|
related_name='+'
|
|
)
|
|
slide1_headline = models.CharField(max_length=512, blank=True)
|
|
slide1_subline = models.CharField(max_length=512, blank=True)
|
|
slide1_link_url = models.URLField(blank=True)
|
|
slide1_link_text = models.CharField(max_length=64, blank=True)
|
|
slide2_img = models.ForeignKey(
|
|
get_image_model(),
|
|
null=True,
|
|
blank=True,
|
|
on_delete=models.SET_NULL,
|
|
related_name='+'
|
|
)
|
|
slide2_headline = models.CharField(max_length=512, blank=True)
|
|
slide2_subline = models.CharField(max_length=512, blank=True)
|
|
slide2_link_url = models.URLField(blank=True)
|
|
slide2_link_text = models.CharField(max_length=64, blank=True)
|
|
slide3_img = models.ForeignKey(
|
|
get_image_model(),
|
|
null=True,
|
|
blank=True,
|
|
on_delete=models.SET_NULL,
|
|
related_name='+'
|
|
)
|
|
slide3_headline = models.CharField(max_length=512, blank=True)
|
|
slide3_subline = models.CharField(max_length=512, blank=True)
|
|
slide3_link_url = models.URLField(blank=True)
|
|
slide3_link_text = models.CharField(max_length=64, blank=True)
|
|
|
|
thumbnail1_img = models.ForeignKey(
|
|
get_image_model(),
|
|
null=True,
|
|
blank=True,
|
|
on_delete=models.SET_NULL,
|
|
related_name='+'
|
|
)
|
|
thumbnail1_more_page = models.ForeignKey(
|
|
'wagtailcore.Page',
|
|
null=True,
|
|
blank=True,
|
|
on_delete=models.SET_NULL,
|
|
related_name='+',
|
|
)
|
|
thumbnail1_more_text = models.CharField(max_length=64, blank=True)
|
|
thumbnail1_title = models.CharField(max_length=128, blank=True)
|
|
thumbnail1_text = RichTextField(blank=True)
|
|
|
|
thumbnail2_img = models.ForeignKey(
|
|
get_image_model(),
|
|
null=True,
|
|
blank=True,
|
|
on_delete=models.SET_NULL,
|
|
related_name='+'
|
|
)
|
|
thumbnail2_more_page = models.ForeignKey(
|
|
'wagtailcore.Page',
|
|
null=True,
|
|
blank=True,
|
|
on_delete=models.SET_NULL,
|
|
related_name='+',
|
|
)
|
|
thumbnail2_more_text = models.CharField(max_length=64, blank=True)
|
|
thumbnail2_title = models.CharField(max_length=128, blank=True)
|
|
thumbnail2_text = RichTextField(blank=True)
|
|
|
|
thumbnail3_img = models.ForeignKey(
|
|
get_image_model(),
|
|
null=True,
|
|
blank=True,
|
|
on_delete=models.SET_NULL,
|
|
related_name='+'
|
|
)
|
|
thumbnail3_more_page = models.ForeignKey(
|
|
'wagtailcore.Page',
|
|
null=True,
|
|
blank=True,
|
|
on_delete=models.SET_NULL,
|
|
related_name='+',
|
|
)
|
|
thumbnail3_more_text = models.CharField(max_length=64, blank=True)
|
|
thumbnail3_title = models.CharField(max_length=128, blank=True)
|
|
thumbnail3_text = RichTextField(blank=True)
|
|
|
|
|
|
REVOLUTION_SLIDER_FIELDS = [
|
|
MultiFieldPanel([
|
|
FieldPanel('slide1_img'),
|
|
FieldPanel('slide1_headline'),
|
|
FieldPanel('slide1_subline'),
|
|
FieldPanel('slide1_link_url'),
|
|
FieldPanel('slide1_link_text'),
|
|
], heading='Slide 1', classname="collapsible"),
|
|
|
|
MultiFieldPanel([
|
|
FieldPanel('slide2_img'),
|
|
FieldPanel('slide2_headline'),
|
|
FieldPanel('slide2_subline'),
|
|
FieldPanel('slide2_link_url'),
|
|
FieldPanel('slide2_link_text'),
|
|
], heading='Slide 2', classname="collapsible"),
|
|
|
|
MultiFieldPanel([
|
|
FieldPanel('slide3_img'),
|
|
FieldPanel('slide3_headline'),
|
|
FieldPanel('slide3_subline'),
|
|
FieldPanel('slide3_link_url'),
|
|
FieldPanel('slide3_link_text'),
|
|
], heading='Slide 3'),
|
|
]
|
|
|
|
HOME_THUMBNAIL_FIELDS = [
|
|
MultiFieldPanel([
|
|
FieldPanel('thumbnail1_img'),
|
|
PageChooserPanel('thumbnail1_more_page'),
|
|
FieldPanel('thumbnail1_more_text'),
|
|
FieldPanel('thumbnail1_title'),
|
|
FieldPanel('thumbnail1_text'),
|
|
], heading='Thumbnail 1', classname="collapsible"),
|
|
MultiFieldPanel([
|
|
FieldPanel('thumbnail2_img'),
|
|
PageChooserPanel('thumbnail2_more_page'),
|
|
FieldPanel('thumbnail2_more_text'),
|
|
FieldPanel('thumbnail2_title'),
|
|
FieldPanel('thumbnail2_text'),
|
|
], heading='Thumbnail 2', classname="collapsible"),
|
|
MultiFieldPanel([
|
|
FieldPanel('thumbnail3_img'),
|
|
PageChooserPanel('thumbnail3_more_page'),
|
|
FieldPanel('thumbnail3_more_text'),
|
|
FieldPanel('thumbnail3_title'),
|
|
FieldPanel('thumbnail3_text'),
|
|
], heading='Thumbnail 3', classname="collapsible"),
|
|
]
|
|
|
|
HomePage.content_panels = [
|
|
FieldPanel('title', classname="full title"),
|
|
MultiFieldPanel(REVOLUTION_SLIDER_FIELDS, heading='Slider elements', classname="collapsible collapsed"),
|
|
FieldPanel('block1'),
|
|
MultiFieldPanel(HOME_THUMBNAIL_FIELDS, heading='Thumbnail elements', classname="collapsible collapsed"),
|
|
]
|
|
|
|
|
|
class SimpleOneColumnPage(Page):
|
|
featured_media = models.ForeignKey(
|
|
'wagtailmedia.Media',
|
|
null=True,
|
|
blank=True,
|
|
on_delete=models.SET_NULL,
|
|
related_name='+'
|
|
)
|
|
content = RichTextField(blank=True)
|
|
|
|
|
|
SimpleOneColumnPage.content_panels = [
|
|
FieldPanel('title', classname="full title"),
|
|
FieldPanel('featured_media'),
|
|
FieldPanel('content'),
|
|
]
|
|
|
|
|
|
class BlogPage(Page):
|
|
subtitle = models.CharField(
|
|
max_length=512,
|
|
default="Aktuelle Neuigkeiten, Einblicke und Ankündigungen",
|
|
blank=True
|
|
)
|
|
posts = StreamField([
|
|
('post', blocks.StructBlock([
|
|
('images', blocks.ListBlock(ImageChooserBlock())),
|
|
('title', blocks.CharBlock(max_length=200)),
|
|
('author', blocks.CharBlock(max_length=64, default="Marion Schauf", required=False)),
|
|
('date', blocks.DateBlock()),
|
|
('text', blocks.RichTextBlock()),
|
|
('url', blocks.URLBlock(required=False)),
|
|
])),
|
|
], null=True, blank=True, use_json_field=True)
|
|
|
|
|
|
BlogPage.content_panels = [
|
|
FieldPanel('title', classname="full title"),
|
|
FieldPanel('subtitle'),
|
|
FieldPanel('posts'),
|
|
]
|
|
|
|
|
|
class EventIndexPage(Page):
|
|
subpage_types = ['core.EventPage']
|
|
|
|
def get_events(self):
|
|
return EventPage.objects.live().filter(show_in_event_calendar=True, path__startswith=self.path).order_by('start_date')
|
|
|
|
class Meta:
|
|
description = "Displays all events on EventPages below in an overview. Use this as parent page for events."
|
|
|
|
|
|
class EventPage(Page):
|
|
parent_page_types = ['core.EventIndexPage', 'core.EventHistoryPage']
|
|
subtitle = models.CharField(max_length=512, null=True, blank=True)
|
|
description = RichTextField(blank=True)
|
|
|
|
# revolution slider
|
|
slide1_img = models.ForeignKey(
|
|
get_image_model(),
|
|
null=True,
|
|
blank=True,
|
|
on_delete=models.SET_NULL,
|
|
related_name='+'
|
|
)
|
|
slide1_headline = models.CharField(max_length=512, blank=True)
|
|
slide1_subline = models.CharField(max_length=512, blank=True)
|
|
slide1_link_url = models.URLField(blank=True)
|
|
slide1_link_text = models.CharField(max_length=64, blank=True)
|
|
slide2_img = models.ForeignKey(
|
|
get_image_model(),
|
|
null=True,
|
|
blank=True,
|
|
on_delete=models.SET_NULL,
|
|
related_name='+'
|
|
)
|
|
slide2_headline = models.CharField(max_length=512, blank=True)
|
|
slide2_subline = models.CharField(max_length=512, blank=True)
|
|
slide2_link_url = models.URLField(blank=True)
|
|
slide2_link_text = models.CharField(max_length=64, blank=True)
|
|
slide3_img = models.ForeignKey(
|
|
get_image_model(),
|
|
null=True,
|
|
blank=True,
|
|
on_delete=models.SET_NULL,
|
|
related_name='+'
|
|
)
|
|
slide3_headline = models.CharField(max_length=512, blank=True)
|
|
slide3_subline = models.CharField(max_length=512, blank=True)
|
|
slide3_link_url = models.URLField(blank=True)
|
|
slide3_link_text = models.CharField(max_length=64, blank=True)
|
|
|
|
img1_img = models.ForeignKey(
|
|
get_image_model(),
|
|
null=True,
|
|
blank=True,
|
|
on_delete=models.SET_NULL,
|
|
related_name='+'
|
|
)
|
|
img1_caption = models.CharField(max_length=64, blank=True)
|
|
img2_img = models.ForeignKey(
|
|
get_image_model(),
|
|
null=True,
|
|
blank=True,
|
|
on_delete=models.SET_NULL,
|
|
related_name='+'
|
|
)
|
|
img2_caption = models.CharField(max_length=64, blank=True)
|
|
|
|
start_date = models.DateField()
|
|
end_date = models.DateField(null=True, blank=True)
|
|
location_name = models.CharField(
|
|
max_length=256, help_text="Name of the location (first line of address)", blank=True)
|
|
location_street = models.CharField(max_length=256, help_text="Street and house number", blank=True)
|
|
location_city = models.CharField(max_length=256, help_text="Zip code and city", blank=True)
|
|
flyer = models.ForeignKey(
|
|
get_document_model(),
|
|
null=True,
|
|
blank=True,
|
|
on_delete=models.SET_NULL,
|
|
related_name='+'
|
|
)
|
|
downloads = StreamField([
|
|
('download', blocks.StructBlock([
|
|
('title', blocks.CharBlock(max_length=128, label='Name (Button-Beschriftung)')),
|
|
('file', DocumentChooserBlock(label='Datei')),
|
|
])),
|
|
], null=True, blank=True, use_json_field=True)
|
|
tabs = StreamField([
|
|
('sponsor_tab', blocks.StructBlock([
|
|
('title', blocks.CharBlock(max_length=64)),
|
|
('sponsors', blocks.StreamBlock([
|
|
('sponsor', blocks.StructBlock([
|
|
('name', blocks.CharBlock(max_length=64)),
|
|
('logo', ImageChooserBlock(required=False)),
|
|
('url', blocks.URLBlock(required=False)),
|
|
])),
|
|
('headline', blocks.CharBlock(max_length=64)),
|
|
])),
|
|
])),
|
|
('richtext_tab', blocks.StructBlock([
|
|
('title', blocks.CharBlock(max_length=64)),
|
|
('content', blocks.RichTextBlock()),
|
|
])),
|
|
('media_tab', blocks.StructBlock([
|
|
('title', blocks.CharBlock(max_length=64)),
|
|
('pre_content', blocks.RichTextBlock(required=False)),
|
|
('media_file', MediaBlock(template='core/blocks/media_block.html')),
|
|
('autoplay', blocks.BooleanBlock(label='Automatisch abspielen', required=False)),
|
|
('muted', blocks.BooleanBlock(label='Stummgeschaltet starten', required=False)),
|
|
('post_content', blocks.RichTextBlock(required=False)),
|
|
])),
|
|
('image_tab', blocks.StructBlock([
|
|
('title', blocks.CharBlock(max_length=64)),
|
|
('image', ImageChooserBlock()),
|
|
('caption', blocks.CharBlock(max_length=256, required=False)),
|
|
('size', blocks.ChoiceBlock(choices=[
|
|
('small', 'Small (25% width)'),
|
|
('medium', 'Medium (50% width)'),
|
|
('large', 'Large (75% width)'),
|
|
('full', 'Full width (100%)'),
|
|
], default='large')),
|
|
('alignment', blocks.ChoiceBlock(choices=[
|
|
('left', 'Left'),
|
|
('center', 'Center'),
|
|
('right', 'Right'),
|
|
], default='center')),
|
|
])),
|
|
], null=True, blank=True, use_json_field=True)
|
|
|
|
registration_url = models.URLField(max_length=512, blank=True, help_text="URL der Anmeldeseite (leer lassen um klassische Anmeldung zu verwenden)")
|
|
pretix_slug = models.CharField(max_length=512, blank=True, help_text="Slug von Pretix (leer lassen, um klassische Anmeldung zu verwenden)")
|
|
registration_start_date = models.DateField(
|
|
null=True, blank=True, help_text="When does the registration open? Leave empty to disable registration")
|
|
registration_end_date = models.DateField(
|
|
null=True, blank=True,
|
|
help_text="Last day where the registration will be available. Leave empty to enable registration permanently"
|
|
"after start date")
|
|
show_in_event_calendar = models.BooleanField(default=True, help_text='Event in Eventliste und im Kalender anzeigen?')
|
|
is_external = models.BooleanField(default=False, help_text='Externes Event? Externe Events werden nicht im Upcoming Events Banner auf der Startseite angezeigt.')
|
|
is_free = models.BooleanField(default=False, help_text='If this is checked the send button under the registration'
|
|
'form will be labeled "verbindlich anmelden" instead of "kostenpflichtig anmelden"')
|
|
|
|
vimeo_id = models.TextField(blank=True, help_text='Vimeo Event ID (aus Vimeo-Link: https://vimeo.com/<hier-ist-die-id> )')
|
|
|
|
def is_registration_active(self):
|
|
return self.registration_start_date and self.registration_start_date <= date.today() and\
|
|
(not self.registration_end_date or self.registration_end_date >= date.today())
|
|
|
|
@property
|
|
def pretix_url(self):
|
|
if self.pretix_slug:
|
|
return 'https://tickets.feo.gmbh/' + self.pretix_slug + '/'
|
|
|
|
return None
|
|
|
|
def get_form_helper(self):
|
|
helper = FormHelper()
|
|
helper.label_class = 'col-lg-3'
|
|
helper.field_class = 'col-lg-9'
|
|
helper.form_tag = False
|
|
return helper
|
|
|
|
class Meta:
|
|
ordering = ['-start_date']
|
|
|
|
|
|
class EventRegistrationField(AbstractFormField):
|
|
page = ParentalKey('EventPage', related_name='form_fields')
|
|
|
|
|
|
EventPage.content_panels = [
|
|
MultiFieldPanel(REVOLUTION_SLIDER_FIELDS, heading='Slider elements', classname="collapsible collapsed"),
|
|
FieldPanel('title', classname="full title"),
|
|
FieldPanel('subtitle'),
|
|
FieldPanel('description'),
|
|
FieldPanel('img1_img'),
|
|
FieldPanel('img1_caption'),
|
|
FieldPanel('img2_img'),
|
|
FieldPanel('img2_caption'),
|
|
FieldPanel('start_date'),
|
|
FieldPanel('end_date'),
|
|
FieldPanel('tabs'),
|
|
FieldPanel('location_name'),
|
|
FieldPanel('location_street'),
|
|
FieldPanel('location_city'),
|
|
FieldPanel('flyer'),
|
|
FieldPanel('downloads'),
|
|
MultiFieldPanel([
|
|
FieldPanel('show_in_event_calendar'),
|
|
FieldPanel('is_external'),
|
|
FieldPanel('vimeo_id'),
|
|
], heading="Settings", classname="collapsible collapsed"),
|
|
MultiFieldPanel([
|
|
FieldPanel('is_free'),
|
|
FieldPanel('pretix_slug'),
|
|
FieldPanel('registration_url'),
|
|
FieldPanel('registration_start_date'),
|
|
FieldPanel('registration_end_date'),
|
|
], heading="Registration Settings", classname="collapsible collapsed"),
|
|
]
|
|
|
|
|
|
class EventHistoryPage(Page):
|
|
events = StreamField([
|
|
('event', blocks.StructBlock([
|
|
('title', blocks.CharBlock(max_length=512, required=False)),
|
|
('subtitle', blocks.CharBlock(max_length=512, required=False)),
|
|
('start_date', blocks.DateBlock()),
|
|
('end_date', blocks.DateBlock(required=False, help_text="Leave empty for single day events")),
|
|
('location_name', blocks.CharBlock(
|
|
max_length=256, help_text="Name of the location (first line of address)", required=False)),
|
|
('location_city', blocks.CharBlock(max_length=256, help_text="Zip code and city", required=False)),
|
|
('image', ImageChooserBlock(required=False)),
|
|
('flyer', DocumentChooserBlock(required=False)),
|
|
('additional_info_btn_label', blocks.CharBlock(max_length=128, required=False, label='Zusatzinfo Button Text')),
|
|
('additional_info_target', blocks.PageChooserBlock(required=False, label='Zusatzinfo Zielseite')),
|
|
])),
|
|
], null=True, blank=True, use_json_field=True)
|
|
|
|
def get_all_events(self):
|
|
"""
|
|
Returns a list of all events, including both manual entries from the StreamField
|
|
and child EventPage objects, sorted by start_date.
|
|
"""
|
|
# Get manual entries from StreamField
|
|
manual_events = []
|
|
for event_block in self.events:
|
|
manual_events.append({
|
|
'type': 'manual',
|
|
'value': event_block.value
|
|
})
|
|
|
|
# Get child EventPage objects
|
|
from core.models import EventPage
|
|
child_events = []
|
|
for child in self.get_children().specific():
|
|
if isinstance(child, EventPage):
|
|
child_events.append({
|
|
'type': 'page',
|
|
'value': {
|
|
'title': child.title,
|
|
'subtitle': child.subtitle,
|
|
'start_date': child.start_date,
|
|
'end_date': child.end_date,
|
|
'location_name': child.location_name,
|
|
'location_city': child.location_city,
|
|
'image': child.img1_img,
|
|
'flyer': child.flyer,
|
|
'additional_info_btn_label': '',
|
|
'additional_info_target': None,
|
|
},
|
|
'page': child
|
|
})
|
|
|
|
# Combine and sort all events by start_date
|
|
all_events = manual_events + child_events
|
|
return sorted(all_events, key=lambda x: x['value']['start_date'], reverse=True)
|
|
|
|
|
|
EventHistoryPage.content_panels = [
|
|
FieldPanel('title', classname="full title"),
|
|
FieldPanel('events'),
|
|
]
|
|
|
|
|
|
class ContactFormField(AbstractFormField):
|
|
page = ParentalKey('ContactFormPage', related_name='form_fields')
|
|
|
|
|
|
class ContactFormPage(WagtailCaptchaEmailForm):
|
|
form_title = models.CharField(max_length=128, blank=True)
|
|
person_img = models.ForeignKey(
|
|
get_image_model(),
|
|
null=True,
|
|
blank=True,
|
|
on_delete=models.SET_NULL,
|
|
related_name='+'
|
|
)
|
|
person_name = models.CharField(max_length=128, blank=True)
|
|
person_position = models.CharField(max_length=128, blank=True)
|
|
address = models.CharField(max_length=128, blank=True)
|
|
mail_address = models.CharField(max_length=128, blank=True)
|
|
phone = models.CharField(max_length=128, blank=True)
|
|
web_address = models.URLField(blank=True)
|
|
thank_you_text = RichTextField(blank=True)
|
|
|
|
|
|
ContactFormPage.content_panels = [
|
|
FieldPanel('title', classname="full title"),
|
|
FieldPanel('form_title'),
|
|
FieldPanel('person_img'),
|
|
FieldPanel('person_name'),
|
|
FieldPanel('person_position'),
|
|
FieldPanel('address'),
|
|
FieldPanel('mail_address'),
|
|
FieldPanel('phone'),
|
|
FieldPanel('web_address'),
|
|
InlinePanel('form_fields', label="Form fields"),
|
|
FieldPanel('thank_you_text'),
|
|
MultiFieldPanel([
|
|
FieldPanel('to_address'),
|
|
FieldPanel('from_address'),
|
|
FieldPanel('subject'),
|
|
], "Email")
|
|
]
|
|
|
|
|
|
class UploadedScript(TimeStampedModel):
|
|
name = models.CharField(max_length=512, verbose_name='Ihr Name')
|
|
script = models.FileField(upload_to='scripts', verbose_name='Skript')
|
|
|
|
def get_delete_url(self):
|
|
return reverse('delete_script', kwargs={'pk': self.pk})
|
|
|
|
def delete(self, *args, **kwargs):
|
|
storage, path = self.script.storage, self.script.path
|
|
out = super().delete(*args, **kwargs)
|
|
storage.delete(path)
|
|
return out
|
|
|
|
|
|
class AboutUsPage(Page):
|
|
"""
|
|
A page for displaying company information, team members, mission statements, etc.
|
|
"""
|
|
# Company information
|
|
company_intro = RichTextField(
|
|
blank=True,
|
|
help_text="Introductory text about the company"
|
|
)
|
|
|
|
# Mission and vision
|
|
mission_title = models.CharField(max_length=255, blank=True)
|
|
mission_text = RichTextField(blank=True)
|
|
vision_title = models.CharField(max_length=255, blank=True)
|
|
vision_text = RichTextField(blank=True)
|
|
|
|
# Company history
|
|
history_title = models.CharField(max_length=255, blank=True)
|
|
history_text = RichTextField(blank=True)
|
|
|
|
# Team members section
|
|
team_title = models.CharField(max_length=255, blank=True)
|
|
team_intro = RichTextField(blank=True)
|
|
|
|
# Team members as a StreamField to allow adding multiple team members
|
|
team_members = StreamField([
|
|
('team_member', blocks.StructBlock([
|
|
('name', blocks.CharBlock(max_length=255)),
|
|
('position', blocks.CharBlock(max_length=255)),
|
|
('photo', ImageChooserBlock(required=False)),
|
|
('bio', blocks.RichTextBlock(required=False)),
|
|
('email', blocks.EmailBlock(required=False)),
|
|
('phone', blocks.CharBlock(max_length=128, required=False)),
|
|
('linkedin', blocks.URLBlock(required=False)),
|
|
('xing', blocks.URLBlock(required=False)),
|
|
])),
|
|
], null=True, blank=True, use_json_field=True)
|
|
|
|
# Additional content
|
|
additional_content = StreamField([
|
|
('heading', blocks.CharBlock(form_classname="full title")),
|
|
('paragraph', blocks.RichTextBlock()),
|
|
('image', ImageChooserBlock()),
|
|
('quote', blocks.StructBlock([
|
|
('quote', blocks.TextBlock()),
|
|
('attribution', blocks.CharBlock(required=False)),
|
|
])),
|
|
], null=True, blank=True, use_json_field=True)
|
|
|
|
|
|
AboutUsPage.content_panels = [
|
|
FieldPanel('title', classname="full title"),
|
|
FieldPanel('company_intro'),
|
|
MultiFieldPanel([
|
|
FieldPanel('mission_title'),
|
|
FieldPanel('mission_text'),
|
|
FieldPanel('vision_title'),
|
|
FieldPanel('vision_text'),
|
|
], heading="Mission and Vision", classname="collapsible"),
|
|
MultiFieldPanel([
|
|
FieldPanel('history_title'),
|
|
FieldPanel('history_text'),
|
|
], heading="Company History", classname="collapsible"),
|
|
MultiFieldPanel([
|
|
FieldPanel('team_title'),
|
|
FieldPanel('team_intro'),
|
|
FieldPanel('team_members'),
|
|
], heading="Team Members", classname="collapsible"),
|
|
FieldPanel('additional_content'),
|
|
]
|
|
|