from __future__ import unicode_literals from django.db import models from modelcluster.fields import ParentalKey from modelcluster.models import ClusterableModel from wagtail.admin.panels import FieldPanel, FieldRowPanel, InlinePanel, MultiFieldPanel from wagtail.contrib.forms.models import AbstractEmailForm, AbstractFormField from wagtail.fields import RichTextField, StreamField from wagtail.models import Collection, Page from wagtail.search import index from wagtail.snippets.models import register_snippet from wagtail_editable_help.models import HelpText from .blocks import BaseStreamBlock @register_snippet class People(index.Indexed, ClusterableModel): """ A Django model to store People objects. It uses the `@register_snippet` decorator to allow it to be accessible via the Snippets UI (e.g. /admin/snippets/base/people/) `People` uses the `ClusterableModel`, which allows the relationship with another model to be stored locally to the 'parent' model (e.g. a PageModel) until the parent is explicitly saved. This allows the editor to use the 'Preview' button, to preview the content, without saving the relationships to the database. https://github.com/wagtail/django-modelcluster """ first_name = models.CharField("First name", max_length=254) last_name = models.CharField("Last name", max_length=254) job_title = models.CharField("Job title", max_length=254) image = models.ForeignKey( "wagtailimages.Image", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", ) panels = [ MultiFieldPanel( [ FieldRowPanel( [ FieldPanel("first_name", classname="col6"), FieldPanel("last_name", classname="col6"), ] ) ], "Name", ), FieldPanel("job_title"), FieldPanel("image"), ] search_fields = [ index.SearchField("first_name"), index.SearchField("last_name"), ] @property def thumb_image(self): # Returns an empty string if there is no profile pic or the rendition # file can't be found. try: return self.image.get_rendition("fill-50x50").img_tag() except: # noqa: E722 FIXME: remove bare 'except:' return "" def __str__(self): return "{} {}".format(self.first_name, self.last_name) class Meta: verbose_name = "Person" verbose_name_plural = "People" @register_snippet class FooterText(models.Model): """ This provides editable text for the site footer. Again it uses the decorator `register_snippet` to allow it to be accessible via the admin. It is made accessible on the template via a template tag defined in base/templatetags/ navigation_tags.py """ body = RichTextField() panels = [ FieldPanel("body"), ] def __str__(self): return "Footer text" class Meta: verbose_name_plural = "Footer Text" class StandardPage(Page): """ A generic content page. On this demo site we use it for an about page but it could be used for any type of page content that only needs a title, image, introduction and body field """ introduction = models.TextField( help_text=HelpText( "Standard page", "introduction", default="Text to describe the page" ), blank=True, ) image = models.ForeignKey( "wagtailimages.Image", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", help_text=HelpText( "Common", "hero image", default="Landscape mode only; horizontal width between 1000px and 3000px.", ), ) body = StreamField( BaseStreamBlock(), verbose_name="Page body", blank=True, use_json_field=True ) content_panels = Page.content_panels + [ FieldPanel("introduction", classname="full"), FieldPanel("body"), FieldPanel("image"), ] class HomePage(Page): """ The Home Page. This looks slightly more complicated than it is. You can see if you visit your site and edit the homepage that it is split between a: - Hero area - Body area - A promotional area - Moveable featured site sections """ # Hero section of HomePage image = models.ForeignKey( "wagtailimages.Image", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", help_text=HelpText( "Common", "hero image", default="Landscape mode only; horizontal width between 1000px and 3000px.", ), ) hero_text = models.CharField( max_length=255, help_text=HelpText( "Home page", "hero text", default="Write an introduction for the bakery" ), ) hero_cta = models.CharField( verbose_name="Hero CTA", max_length=255, help_text=HelpText( "Home page", "hero CTA", default="Text to display on Call to Action" ), ) hero_cta_link = models.ForeignKey( "wagtailcore.Page", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", verbose_name="Hero CTA link", help_text=HelpText( "Home page", "CTA link", default="Choose a page to link to for the Call to Action", ), ) # Body section of the HomePage body = StreamField( BaseStreamBlock(), verbose_name="Home content block", blank=True, use_json_field=True, ) # Promo section of the HomePage promo_image = models.ForeignKey( "wagtailimages.Image", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", help_text=HelpText("Home page", "promo image", default="Promo image"), ) promo_title = models.CharField( blank=True, max_length=255, help_text=HelpText( "Home page", "promo title", default="Title to display above the promo copy" ), ) promo_text = RichTextField( null=True, blank=True, help_text=HelpText( "Home page", "promo text", default="Write some promotional copy" ), ) # Featured sections on the HomePage # You will see on templates/base/home_page.html that these are treated # in different ways, and displayed in different areas of the page. # Each list their children items that we access via the children function # that we define on the individual Page models e.g. BlogIndexPage featured_section_1_title = models.CharField( blank=True, max_length=255, help_text=HelpText( "Home page", "featured section title 1", default="Title to display above the promo copy", ), ) featured_section_1 = models.ForeignKey( "wagtailcore.Page", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", help_text=HelpText( "Home page", "featured section 1", default="First featured section for the homepage. Will display up to three child items.", ), verbose_name="Featured section 1", ) featured_section_2_title = models.CharField( blank=True, max_length=255, help_text=HelpText( "Home page", "featured section title 2", default="Title to display above the promo copy", ), ) featured_section_2 = models.ForeignKey( "wagtailcore.Page", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", help_text=HelpText( "Home page", "featured section 2", default="Second featured section for the homepage. Will display up to three child items.", ), verbose_name="Featured section 2", ) featured_section_3_title = models.CharField( blank=True, max_length=255, help_text=HelpText( "Home page", "featured section title 3", default="Title to display above the promo copy", ), ) featured_section_3 = models.ForeignKey( "wagtailcore.Page", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", help_text=HelpText( "Home page", "featured section 3", default="Third featured section for the homepage. Will display up to six child items.", ), verbose_name="Featured section 3", ) content_panels = Page.content_panels + [ MultiFieldPanel( [ FieldPanel("image"), FieldPanel("hero_text", classname="full"), MultiFieldPanel( [ FieldPanel("hero_cta"), FieldPanel("hero_cta_link"), ] ), ], heading="Hero section", ), MultiFieldPanel( [ FieldPanel("promo_image"), FieldPanel("promo_title"), FieldPanel("promo_text"), ], heading="Promo section", ), FieldPanel("body"), MultiFieldPanel( [ MultiFieldPanel( [ FieldPanel("featured_section_1_title"), FieldPanel("featured_section_1"), ] ), MultiFieldPanel( [ FieldPanel("featured_section_2_title"), FieldPanel("featured_section_2"), ] ), MultiFieldPanel( [ FieldPanel("featured_section_3_title"), FieldPanel("featured_section_3"), ] ), ], heading="Featured homepage sections", classname="collapsible", ), ] def __str__(self): return self.title class GalleryPage(Page): """ This is a page to list locations from the selected Collection. We use a Q object to list any Collection created (/admin/collections/) even if they contain no items. In this demo we use it for a GalleryPage, and is intended to show the extensibility of this aspect of Wagtail """ introduction = models.TextField( help_text=HelpText( "Gallery page", "introduction", default="Text to describe the page" ), blank=True, ) image = models.ForeignKey( "wagtailimages.Image", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", help_text=HelpText( "Common", "hero image", default="Landscape mode only; horizontal width between 1000px and 3000px.", ), ) body = StreamField( BaseStreamBlock(), verbose_name="Page body", blank=True, use_json_field=True ) collection = models.ForeignKey( Collection, limit_choices_to=~models.Q(name__in=["Root"]), null=True, blank=True, on_delete=models.SET_NULL, help_text=HelpText( "Gallery page", "collection", default="Select the image collection for this gallery.", ), ) content_panels = Page.content_panels + [ FieldPanel("introduction", classname="full"), FieldPanel("body"), FieldPanel("image"), FieldPanel("collection"), ] # Defining what content type can sit under the parent. Since it's a blank # array no subpage can be added subpage_types = [] class FormField(AbstractFormField): """ Wagtailforms is a module to introduce simple forms on a Wagtail site. It isn't intended as a replacement to Django's form support but as a quick way to generate a general purpose data-collection form or contact form without having to write code. We use it on the site for a contact form. You can read more about Wagtail forms at: https://docs.wagtail.org/en/stable/reference/contrib/forms/index.html """ page = ParentalKey("FormPage", related_name="form_fields", on_delete=models.CASCADE) class FormPage(AbstractEmailForm): image = models.ForeignKey( "wagtailimages.Image", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", ) body = StreamField(BaseStreamBlock(), use_json_field=True) thank_you_text = RichTextField(blank=True) # Note how we include the FormField object via an InlinePanel using the # related_name value content_panels = AbstractEmailForm.content_panels + [ FieldPanel("image"), FieldPanel("body"), InlinePanel("form_fields", label="Form fields"), FieldPanel("thank_you_text", classname="full"), MultiFieldPanel( [ FieldRowPanel( [ FieldPanel("from_address", classname="col6"), FieldPanel("to_address", classname="col6"), ] ), FieldPanel("subject"), ], "Email", ), ]