123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468 |
- """
- Snippets are for content that is re-usable in nature.
- """
- from django.db import models
- from django.utils.text import slugify
- from django.utils.translation import gettext_lazy as _
- from modelcluster.fields import ParentalKey
- from modelcluster.models import ClusterableModel
- from wagtail.admin.panels import (
- FieldPanel,
- InlinePanel,
- MultiFieldPanel,
- )
- from wagtail.models import Orderable
- from wagtail.snippets.models import register_snippet
- from wagtail.images import get_image_model_string
- from wagtailcrx.blocks import HTML_STREAMBLOCKS, LAYOUT_STREAMBLOCKS, NAVIGATION_STREAMBLOCKS
- from wagtailcrx.fields import CoderedStreamField
- from wagtailcrx.settings import crx_settings
- @register_snippet
- class Carousel(ClusterableModel):
- """
- Model that represents a Carousel. Can be modified through the snippets UI.
- Selected through Page StreamField bodies by the CarouselSnippetChooser in
- snippet_choosers.py
- """
- class Meta:
- verbose_name = _('Carousel')
- if crx_settings.CRX_DB_TABLE_CODEREDCMS:
- db_table = "coderedcms_carousel"
- name = models.CharField(
- max_length=255,
- verbose_name=_('Name'),
- )
- show_controls = models.BooleanField(
- default=True,
- verbose_name=_('Show controls'),
- help_text=_('Shows arrows on the left and right of the carousel to advance next or previous slides.'), # noqa
- )
- show_indicators = models.BooleanField(
- default=True,
- verbose_name=_('Show indicators'),
- help_text=_('Shows small indicators at the bottom of the carousel based on the number of slides.'), # noqa
- )
- animation = models.CharField(
- blank=True,
- max_length=20,
- choices=None,
- default='',
- verbose_name=_('Animation'),
- help_text=_('The animation when transitioning between slides.'),
- )
- panels = (
- [
- MultiFieldPanel(
- heading=_('Slider'),
- children=[
- FieldPanel('name'),
- FieldPanel('show_controls'),
- FieldPanel('show_indicators'),
- FieldPanel('animation'),
- ]
- ),
- InlinePanel('carousel_slides', label=_('Slides'))
- ]
- )
- def __str__(self):
- return self.name
- def __init__(self, *args, **kwargs):
- """
- Inject custom choices and defaults into the form fields
- to enable customization of settings without causing migration issues.
- """
- super().__init__(*args, **kwargs)
- # Set choices dynamically.
- self._meta.get_field('animation').choices = (
- crx_settings.CRX_FRONTEND_CAROUSEL_FX_CHOICES
- )
- # Set default dynamically.
- if not self.id:
- self.animation = crx_settings.CRX_FRONTEND_CAROUSEL_FX_DEFAULT
- class CarouselSlide(Orderable, models.Model):
- """
- Represents a slide for the Carousel model. Can be modified through the
- snippets UI.
- """
- class Meta(Orderable.Meta):
- verbose_name = _('Carousel Slide')
- if crx_settings.CRX_DB_TABLE_CODEREDCMS:
- db_table = "coderedcms_carouselslide"
- carousel = ParentalKey(
- Carousel,
- related_name='carousel_slides',
- verbose_name=_('Carousel'),
- )
- image = models.ForeignKey(
- get_image_model_string(),
- null=True,
- blank=True,
- on_delete=models.SET_NULL,
- related_name='+',
- verbose_name=_('Image'),
- )
- background_color = models.CharField(
- max_length=255,
- blank=True,
- verbose_name=_('Background color'),
- help_text=_('Hexadecimal, rgba, or CSS color notation (e.g. #ff0011)'),
- )
- custom_css_class = models.CharField(
- max_length=255,
- blank=True,
- verbose_name=_('Custom CSS class'),
- )
- custom_id = models.CharField(
- max_length=255,
- blank=True,
- verbose_name=_('Custom ID'),
- )
- content = CoderedStreamField(
- HTML_STREAMBLOCKS,
- blank=True,
- use_json_field=True,
- )
- panels = (
- [
- FieldPanel('image'),
- FieldPanel('background_color'),
- FieldPanel('custom_css_class'),
- FieldPanel('custom_id'),
- FieldPanel('content'),
- ]
- )
- @register_snippet
- class Classifier(ClusterableModel):
- """
- Simple and generic model to organize/categorize/group pages.
- """
- class Meta:
- verbose_name = _('Classifier')
- verbose_name_plural = _('Classifiers')
- ordering = ['name']
- if crx_settings.CRX_DB_TABLE_CODEREDCMS:
- db_table = "coderedcms_classifier"
- slug = models.SlugField(
- allow_unicode=True,
- unique=True,
- verbose_name=_('Slug'),
- )
- name = models.CharField(
- max_length=255,
- verbose_name=_('Name'),
- )
- panels = [
- FieldPanel('name'),
- InlinePanel('terms', label=_('Classifier Terms'))
- ]
- def save(self, *args, **kwargs):
- if not self.slug:
- # Make a slug and suffix a number if it already exists to ensure uniqueness
- newslug = slugify(self.name, allow_unicode=True)
- tmpslug = newslug
- suffix = 1
- while True:
- if not Classifier.objects.filter(slug=tmpslug).exists():
- self.slug = tmpslug
- break
- tmpslug = newslug + "-" + str(suffix)
- suffix += 1
- return super().save(*args, **kwargs)
- def __str__(self):
- return self.name
- class ClassifierTerm(Orderable, models.Model):
- """
- Term used to categorize a page.
- """
- class Meta(Orderable.Meta):
- verbose_name = _('Classifier Term')
- verbose_name_plural = _('Classifier Terms')
- if crx_settings.CRX_DB_TABLE_CODEREDCMS:
- db_table = "coderedcms_classifierterm"
- classifier = ParentalKey(
- Classifier,
- related_name='terms',
- verbose_name=_('Classifier'),
- )
- slug = models.SlugField(
- allow_unicode=True,
- unique=True,
- verbose_name=_('Slug'),
- )
- name = models.CharField(
- max_length=255,
- verbose_name=_('Name'),
- )
- panels = [
- FieldPanel('name'),
- ]
- def save(self, *args, **kwargs):
- if not self.slug:
- # Make a slug and suffix a number if it already exists to ensure uniqueness
- newslug = slugify(self.name, allow_unicode=True)
- tmpslug = newslug
- suffix = 1
- while True:
- if not ClassifierTerm.objects.filter(slug=tmpslug).exists():
- self.slug = tmpslug
- break
- tmpslug = newslug + "-" + str(suffix)
- suffix += 1
- return super().save(*args, **kwargs)
- def __str__(self):
- return "{0} > {1}".format(self.classifier.name, self.name)
- @register_snippet
- class Navbar(models.Model):
- """
- Snippet for site navigation bars (header, main menu, etc.)
- """
- class Meta:
- verbose_name = _('Navigation Bar')
- name = models.CharField(
- max_length=255,
- verbose_name=_('Name'),
- )
- custom_css_class = models.CharField(
- max_length=255,
- blank=True,
- verbose_name=_('Custom CSS Class'),
- )
- custom_id = models.CharField(
- max_length=255,
- blank=True,
- verbose_name=_('Custom ID'),
- )
- menu_items = CoderedStreamField(
- NAVIGATION_STREAMBLOCKS,
- verbose_name=_('Navigation links'),
- blank=True,
- use_json_field=True,
- )
- panels = [
- FieldPanel('name'),
- MultiFieldPanel(
- [
- FieldPanel('custom_css_class'),
- FieldPanel('custom_id'),
- ],
- heading=_('Attributes')
- ),
- FieldPanel('menu_items')
- ]
- def __str__(self):
- return self.name
- @register_snippet
- class Footer(models.Model):
- """
- Snippet for website footer content.
- """
- class Meta:
- verbose_name = _('Footer')
- if crx_settings.CRX_DB_TABLE_CODEREDCMS:
- db_table = "coderedcms_footer"
- name = models.CharField(
- max_length=255,
- verbose_name=_('Name'),
- )
- custom_css_class = models.CharField(
- max_length=255,
- blank=True,
- verbose_name=_('Custom CSS Class'),
- )
- custom_id = models.CharField(
- max_length=255,
- blank=True,
- verbose_name=_('Custom ID'),
- )
- content = CoderedStreamField(
- LAYOUT_STREAMBLOCKS,
- verbose_name=_('Content'),
- blank=True,
- use_json_field=True,
- )
- panels = [
- FieldPanel('name'),
- MultiFieldPanel(
- [
- FieldPanel('custom_css_class'),
- FieldPanel('custom_id'),
- ],
- heading=_('Attributes')
- ),
- FieldPanel('content')
- ]
- def __str__(self):
- return self.name
- @register_snippet
- class ReusableContent(models.Model):
- """
- Snippet for resusable content in streamfields.
- """
- class Meta:
- verbose_name = _('Reusable Content')
- verbose_name_plural = _('Reusable Content')
- if crx_settings.CRX_DB_TABLE_CODEREDCMS:
- db_table = "coderedcms_reusablecontent"
- name = models.CharField(
- max_length=255,
- verbose_name=_('Name'),
- )
- content = CoderedStreamField(
- LAYOUT_STREAMBLOCKS,
- verbose_name=_('content'),
- blank=True,
- use_json_field=True,
- )
- panels = [
- FieldPanel('name'),
- FieldPanel('content')
- ]
- def __str__(self):
- return self.name
- @register_snippet
- class ContentWall(models.Model):
- """
- Snippet that restricts access to a page with a modal.
- """
- class Meta:
- verbose_name = _('Content Wall')
- if crx_settings.CRX_DB_TABLE_CODEREDCMS:
- db_table = "coderedcms_contentwall"
- name = models.CharField(
- max_length=255,
- verbose_name=_('Name'),
- )
- content = CoderedStreamField(
- LAYOUT_STREAMBLOCKS,
- verbose_name=_('Content'),
- blank=True,
- use_json_field=True,
- )
- is_dismissible = models.BooleanField(
- default=True,
- verbose_name=_('Dismissible'),
- )
- show_once = models.BooleanField(
- default=True,
- verbose_name=_('Show once'),
- help_text=_('Do not show the content wall to the same user again after it has been closed.')
- )
- panels = [
- MultiFieldPanel(
- [
- FieldPanel('name'),
- FieldPanel('is_dismissible'),
- FieldPanel('show_once'),
- ],
- heading=_('Content Wall')
- ),
- FieldPanel('content'),
- ]
- def __str__(self):
- return self.name
- class CoderedEmail(ClusterableModel):
- """
- General purpose abstract clusterable model used for holding email information.
- Most likely this should be subclassed with addition of a ParentalKey.
- """
- class Meta:
- abstract = True
- verbose_name = _('CodeRed Email')
- to_address = models.CharField(
- max_length=255,
- blank=True,
- verbose_name=_('To Addresses'),
- help_text=_('Separate multiple email addresses with commas.')
- )
- from_address = models.CharField(
- max_length=255,
- blank=True,
- verbose_name=_('From Address'),
- help_text=_('For example: "sender@example.com" or "Sender Name <sender@example.com>" (without quotes).') # noqa
- )
- reply_address = models.CharField(
- max_length=255,
- blank=True,
- verbose_name=_('Reply-To Address'),
- help_text=_('Separate multiple email addresses with commas.')
- )
- cc_address = models.CharField(
- max_length=255,
- blank=True,
- verbose_name=_('CC'),
- help_text=_('Separate multiple email addresses with commas.')
- )
- bcc_address = models.CharField(
- max_length=255,
- blank=True,
- verbose_name=_('BCC'),
- help_text=_('Separate multiple email addresses with commas.')
- )
- subject = models.CharField(max_length=255, blank=True, verbose_name=_('Subject'))
- body = models.TextField(blank=True, verbose_name=_('Body'))
- panels = (
- [
- MultiFieldPanel(
- [
- FieldPanel('to_address'),
- FieldPanel('from_address'),
- FieldPanel('cc_address'),
- FieldPanel('bcc_address'),
- FieldPanel('subject'),
- FieldPanel('body'),
- ],
- _('Email Message')
- ),
- ])
- def __str__(self):
- return self.subject
|