class TableBlockStreamPage(Page): table = StreamField([('table', TableBlock())]) content_panels = [StreamFieldPanel('table')]
class BaseIndexPage(BasePage, InlineHeroMixin): class Meta: abstract = True search_fields = BasePage.search_fields + InlineHeroMixin.extra_search_fields content_panels = [ *Page.content_panels, FieldPanel("sub_head"), ImageChooserPanel("image"), StreamFieldPanel("body"), ] def _paginator(self, request, page_num=1, tags=None): children = self._child_model.objects.exclude( id__in=self.featured_ids).live().public().order_by( '-first_published_at') if tags is not None: children = children.filter(tags__slug__in=tags).distinct() p = Paginator(children, settings.PAGINATION_ITEMS_PER_PAGE) result_set = p.page(page_num) return result_set def _make_pagination_link(self, tags, page): base = f"{self.get_url()}?" if tags: base += f"tag={tags}&" base += f"page={page}" return base def get_context(self, request): ctx = super().get_context(request) request_tags = request.GET.get('tag', None) tags = None page = 1 if request_tags: tags = request_tags.split(',') if request.GET.get('page', None): page = int(request.GET.get('page', 1)) children = self._paginator(request, page, tags) ctx.update({'children': children}) if children.has_next(): ctx.update({ 'next_link': self._make_pagination_link(request_tags, children.next_page_number()) }) if children.has_previous(): ctx.update({ 'previous_link': self._make_pagination_link(request_tags, children.previous_page_number()) }) return ctx @property def featured_ids(self): rv = [] if hasattr(self, 'featured_posts'): for item in self.featured_posts: rv.append(item.value.id) return rv @cached_classmethod def get_admin_tabs(cls): tabs = super().get_admin_tabs() return tabs
class PrimaryPage(FoundationMetadataPageMixin, Page): """ Basically a straight copy of modular page, but with restrictions on what can live 'under it'. Ideally this is just PrimaryPage(ModularPage) but setting that up as a migration seems to be causing problems. """ header = models.CharField( max_length=250, blank=True ) banner = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='primary_banner', verbose_name='Hero Image', help_text='Choose an image that\'s bigger than 4032px x 1152px with aspect ratio 3.5:1', ) intro = models.CharField( max_length=250, blank=True, help_text='Intro paragraph to show in hero cutout box' ) narrowed_page_content = models.BooleanField( default=False, help_text='For text-heavy pages, turn this on to reduce the overall width of the content on the page.' ) zen_nav = models.BooleanField( default=False, help_text='For secondary nav pages, use this to collapse the primary nav under a toggle hamburger.' ) body = StreamField(base_fields) settings_panels = Page.settings_panels + [ MultiFieldPanel([ FieldPanel('narrowed_page_content'), ]), MultiFieldPanel([ FieldPanel('zen_nav'), ]) ] content_panels = Page.content_panels + [ FieldPanel('header'), ImageChooserPanel('banner'), FieldPanel('intro'), StreamFieldPanel('body'), ] subpage_types = [ 'PrimaryPage', 'RedirectingPage' ] show_in_menus_default = True def get_context(self, request): context = super(PrimaryPage, self).get_context(request) return get_page_tree_information(self, context)
class HomePage(Page): body = StreamField(blocks.BodyBlock) content_panels = Page.content_panels + [StreamFieldPanel("body")]
class Event(Page): resource_type = 'event' parent_page_types = ['events.Events'] subpage_types = [] template = 'event.html' # Content fields description = TextField( blank=True, default='', help_text='Optional short text description, max. 400 characters', max_length=400, ) image = ForeignKey( 'mozimages.MozImage', null=True, blank=True, on_delete=SET_NULL, related_name='+', ) body = CustomStreamField( blank=True, null=True, help_text= ('Optional body content. Supports rich text, images, embed via URL, embed via HTML, and inline code snippets' )) agenda = StreamField( StreamBlock([ ('agenda_item', AgendaItemBlock()), ], required=False), blank=True, null=True, help_text='Optional list of agenda items for this event', ) speakers = StreamField( StreamBlock([ ('speaker', PageChooserBlock(target_model='people.Person')), ('external_speaker', ExternalSpeakerBlock()), ], required=False), blank=True, null=True, help_text='Optional list of speakers for this event', ) # Card fields card_title = CharField('Title', max_length=140, blank=True, default='') card_description = TextField('Description', max_length=400, blank=True, default='') card_image = ForeignKey( 'mozimages.MozImage', null=True, blank=True, on_delete=SET_NULL, related_name='+', verbose_name='Image', ) # Meta fields start_date = DateField(default=datetime.date.today) end_date = DateField(blank=True, null=True) latitude = FloatField(blank=True, null=True) longitude = FloatField(blank=True, null=True) register_url = URLField('Register URL', blank=True, null=True) venue_name = CharField(max_length=100, blank=True, default='') venue_url = URLField('Venue URL', max_length=100, blank=True, default='') address_line_1 = CharField(max_length=100, blank=True, default='') address_line_2 = CharField(max_length=100, blank=True, default='') address_line_3 = CharField(max_length=100, blank=True, default='') city = CharField(max_length=100, blank=True, default='') state = CharField('State/Province/Region', max_length=100, blank=True, default='') zip_code = CharField('Zip/Postal code', max_length=100, blank=True, default='') country = CountryField(blank=True, default='') keywords = ClusterTaggableManager(through=EventTag, blank=True) # Content panels content_panels = Page.content_panels + [ FieldPanel('description'), MultiFieldPanel( [ ImageChooserPanel('image'), ], heading='Image', help_text= ('Optional header image. If not specified a fallback will be used. This image is also shown when sharing ' 'this page via social media')), StreamFieldPanel('body'), StreamFieldPanel('agenda'), StreamFieldPanel('speakers'), ] # Card panels card_panels = [ FieldPanel('card_title'), FieldPanel('card_description'), ImageChooserPanel('card_image'), ] # Meta panels meta_panels = [ MultiFieldPanel( [ FieldPanel('start_date'), FieldPanel('end_date'), FieldPanel('latitude'), FieldPanel('longitude'), FieldPanel('register_url'), ], heading='Event details', classname='collapsible', help_text=mark_safe( 'Optional time and location information for this event. Latitude and longitude are used to show a map ' 'of the event’s location. For more information on finding these values for a given location, ' '<a href="https://support.google.com/maps/answer/18539">see this article</a>' )), MultiFieldPanel( [ FieldPanel('venue_name'), FieldPanel('venue_url'), FieldPanel('address_line_1'), FieldPanel('address_line_2'), FieldPanel('address_line_3'), FieldPanel('city'), FieldPanel('state'), FieldPanel('zip_code'), FieldPanel('country'), ], heading='Event address', classname='collapsible', help_text= 'Optional address fields. The city and country are also shown on event cards' ), MultiFieldPanel( [ InlinePanel('topics'), ], heading='Topics', help_text= ('These are the topic pages the event will appear on. The first topic in the list will be treated as the ' 'primary topic and will be shown in the page’s related content.' )), MultiFieldPanel( [ FieldPanel('seo_title'), FieldPanel('search_description'), FieldPanel('keywords'), ], heading='SEO', help_text= 'Optional fields to override the default title and description for SEO purposes' ), ] # Settings panels settings_panels = [ FieldPanel('slug'), ] edit_handler = TabbedInterface([ ObjectList(content_panels, heading='Content'), ObjectList(card_panels, heading='Card'), ObjectList(meta_panels, heading='Meta'), ObjectList(settings_panels, heading='Settings', classname='settings'), ]) @property def is_upcoming(self): """Returns whether an event is in the future.""" return self.start_date > datetime.date.today() @property def primary_topic(self): """Return the first (primary) topic specified for the event.""" article_topic = self.topics.first() return article_topic.topic if article_topic else None @property def month_group(self): return self.start_date.replace(day=1) @property def event_dates(self): """Return a formatted string of the event start and end dates""" event_dates = self.start_date.strftime("%b %-d") if self.end_date: event_dates += " – " start_month = self.start_date.strftime("%m") if self.end_date.strftime("%m") == start_month: event_dates += self.end_date.strftime("%-d") else: event_dates += self.end_date.strftime("%b %-d") return event_dates @property def event_dates_full(self): """Return a formatted string of the event start and end dates, including the year""" return self.event_dates + self.start_date.strftime(", %Y") def has_speaker(self, person): for speaker in self.speakers: # pylint: disable=not-an-iterable if (speaker.block_type == 'speaker' and str(speaker.value) == str(person.title)): return True return False
class HomePage(RoutablePageMixin, Page): banner_title = models.CharField(max_length=100, blank=False, null=True) banner_subtitle = RichTextField(features=['bold', 'italic']) banner_image = models.ForeignKey( 'wagtailimages.Image', null=True, blank=False, on_delete=models.SET_NULL, related_name='+', ) banner_cta = models.ForeignKey( 'wagtailcore.Page', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', ) content = StreamField([ ('cta', blocks.CTABlock()), ], null=True, blank=True) # only one on site max_count = 1 # parent_page_type = ['wagtailcore.Page'] subpage_types = [ 'blog.BlogListingPage', 'contact.ContactPage', 'flex.FlexPage', 'behance.BehanceProjectListingPage' ] # search_fields = Page.search_fields + [ # ] api_fields = [ APIField('banner_title'), APIField('banner_subtitle'), APIField('banner_image'), APIField('banner_cta'), APIField('carousel_images'), APIField('content'), ] content_panels = Page.content_panels + [ MultiFieldPanel([ InlinePanel('carousel_images', max_num=5, min_num=1, label='Image'), ], heading='Carousel Images'), StreamFieldPanel('content'), ] # promote_panels = [] # settings_panels = [] banner_panels = [ MultiFieldPanel([ FieldPanel('banner_title'), FieldPanel('banner_subtitle'), ImageChooserPanel('banner_image'), PageChooserPanel('banner_cta'), ], heading='Banners Options'), ] edit_handler = TabbedInterface([ ObjectList(content_panels, heading='Content'), ObjectList(banner_panels, heading='Banner'), ObjectList(Page.promote_panels, heading='Promote'), ObjectList(Page.settings_panels, heading='Settings'), ]) class Meta: verbose_name = 'Home Page' verbose_name_plural = 'Home Pages' @route(r'^subscribe/$') def the_subscribe_page(self, request, *args, **kwargs): context = self.get_context(request, *args, **kwargs) context['a_special_text'] = 'Hello world 123123' return render(request, 'home/subscribe.html', context)
class PublicationPage(BasePage, CanonicalMixin): # standard approach appears to be # (via https://docs.wagtail.io/en/latest/topics/streamfield.html) # author = models.Charfield(max_length=255) # which also gives subcategories for StreamField # next two lines would make the post less available but by commenting out # instead we make it available anywhere that doesn't exclude it # parent_page_types = ["PublicationIndexPage"] # subpage_types = [] updated_at = models.DateTimeField( editable=True, null=True, blank=True, verbose_name="Updated at (leave blank for initial publication)", ) history = RichTextField( blank=True, verbose_name="Version history (leave blank for initial publication)") content_panels = [ *Page.content_panels, FieldPanel("first_published_at"), FieldPanel("updated_at"), StreamFieldPanel("body"), FieldPanel("history"), ] settings_panels = CanonicalMixin.panels + BasePage.settings_panels def replace_headers(self, html): """Given a html string, return a html string where the header tags have id attributes so they can be used as anchors, and list the ids and text of those tags so we can make a table of contents.""" header_tags = ["h2"] toc = [] root = lxml.html.fromstring(html) for tag_name in header_tags: for tag in root.xpath(f"//{tag_name}"): # create a slugged name for the tag of the form tag-text-3 header_name = tag.text bare_slug = slugify(header_name, allow_unicode=True) self.slug_count[bare_slug] += 1 slug = bare_slug + slug_count_text(self.slug_count[bare_slug]) # modify the tag and record the details tag.set("id", slug) toc.append((header_name, slug)) return lxml.html.tostring(root).decode("utf-8"), toc def get_context(self, request): """Pass the html of each richtext node within the streamfield to replace_headers, creating a page-wide table of contents. We use self.slug_count to preserve the list of slugs seen so far across multiple rich_text blocks.""" context = super().get_context(request) context["toc"] = [] # table of contents self.slug_count = Counter({"contents": 1}) for i, block in enumerate(self.body._raw_data): if block["type"] == "rich_text": replacement_html, new_toc = self.replace_headers( block["value"]) context["toc"].extend(new_toc) self.body._raw_data[i]["value"] = replacement_html return context
class MovePageTag(TaggedItemBase): content_object = ParentalKey( 'moves.MovePage', related_name='tagged_items' ) class MovePage(Page): description = models.CharField(max_length=255) content = RichTextField(blank=True) tags = ClusterTaggableManager(through=MovePageTag, blank=True) image = models.ForeignKey( Image, null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) indexed_fields = ('info', 'description', 'content', 'tags') MovePage.content_panels = Page.content_panels + [ StreamFieldPanel('info'), FieldPanel('description'), FieldPanel('content'), ImageChooserPanel('image'), FieldPanel('tags'), InlinePanel('credits', label="Credits") ]
class FooterPage(Page): footer_image = models.ForeignKey('wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL) column_1 = StreamField([ ('image', ImageChooserBlock()), ('externalLink', LinkBlock(label="External Link", template="cms/external_link.html")), ('internalLink', InternalLinkBlock(label="Internal Link", template="cms/internal_link.html")), ('google_form', LinkBlock( label="Google Form Link", template="cms/google_form_footer.html", help_text= "The URL for the Google form. This will be loaded into a popup when clicked." )), ('text', RichTextBlock(features=[ 'h2', 'h3', 'h4', 'bold', 'italic', 'link', 'ol', 'ul', 'hr', 'superscript', 'subscript', 'strikethrough', 'blockquote', 'image', 'embed' ])), ]) column_2 = StreamField([ ('image', ImageChooserBlock()), ('externalLink', LinkBlock(label="External Link", template="cms/external_link.html")), ('internalLink', InternalLinkBlock(label="Internal Link", template="cms/internal_link.html")), ('google_form', LinkBlock( label="Google Form Link", template="cms/google_form_footer.html", help_text= "The URL for the Google form. This will be loaded into a popup when clicked." )), ('text', RichTextBlock(features=[ 'h2', 'h3', 'h4', 'bold', 'italic', 'link', 'ol', 'ul', 'hr', 'superscript', 'subscript', 'strikethrough', 'blockquote', 'image', 'embed' ])), ]) column_3 = StreamField([ ('image', ImageChooserBlock()), ('externalLink', LinkBlock(label="External Link", template="cms/external_link.html")), ('internalLink', InternalLinkBlock(label="Internal Link", template="cms/internal_link.html")), ('google_form', LinkBlock( label="Google Form Link", template="cms/google_form_footer.html", help_text= "The URL for the Google form. This will be loaded into a popup when clicked." )), ('text', RichTextBlock(features=[ 'h2', 'h3', 'h4', 'bold', 'italic', 'link', 'ol', 'ul', 'hr', 'superscript', 'subscript', 'strikethrough', 'blockquote', 'image', 'embed' ])), ]) content_panels = Page.content_panels + [ ImageChooserPanel('footer_image'), StreamFieldPanel('column_1'), StreamFieldPanel('column_2'), StreamFieldPanel('column_3'), ]
class WebPage(Page): # Values that help Streamfield Blocks. _rich_text_features = [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'bold', 'italic', 'ol', 'ul', 'link', 'document-link', 'image', 'embed' ] _block_list = [ ( 'CardLinks', ListBlock( CardLinkBlock( label=_BlockNames.cardlink, help_text=_StreamfieldHelpTexts.cardlink, ), template='home/list_block_card_link.html', ), ), ( 'Postcard', PostcardBlock( label=_BlockNames.postcard, help_text=_StreamfieldHelpTexts.postcard, ), ), ( 'Quote', QuoteWithAttributionBlock( label=_BlockNames.quote, help_text=_StreamfieldHelpTexts.quote, ), ), ( 'RichText', RichTextBlock( features=_rich_text_features, label=_BlockNames.rich_text, help_text=_StreamfieldHelpTexts.rich_text, ), ), ( 'Title', TitleBlock( label=_BlockNames.title, help_text=_StreamfieldHelpTexts.title_block, ), ), ( 'SectionTitle', SectionTitleBlock( label=_BlockNames.section_title, help_text=_StreamfieldHelpTexts.section_title_block, ), ), ( 'Line', LineBlock( label=_BlockNames.line, help_text=_StreamfieldHelpTexts.line_block, ), ), ( 'Chart', ChartBlock( label=_BlockNames.chart, help_text=_StreamfieldHelpTexts.chart_block, ), ), ] # Model Fields title_my = models.CharField( max_length=255, blank=True, verbose_name=_ModelVerboseNames.title, help_text=_ModelHelpTexts.title, ) hero_image = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', verbose_name=_ModelVerboseNames.hero_image, help_text=_ModelHelpTexts.hero_image, ) description_en = models.TextField( blank=True, verbose_name=_ModelVerboseNames.description, help_text=_ModelHelpTexts.description, ) description_my = models.TextField( blank=True, verbose_name=_ModelVerboseNames.description, help_text=_ModelHelpTexts.description, ) body_en = StreamField( _block_list, blank=True, help_text=_ModelHelpTexts.body, verbose_name=_ModelVerboseNames.body, ) body_my = StreamField( _block_list, blank=True, help_text=_ModelHelpTexts.body, verbose_name=_ModelVerboseNames.body, ) # Translatable fields. translated_title = TranslatedField( 'title', 'title_my', ) description = TranslatedField( 'description_en', 'description_my', ) body = TranslatedField( 'body_en', 'body_my', ) # Add fields to the search index. search_fields = Page.search_fields + [ index.SearchField('title'), index.SearchField('title_my'), index.SearchField('body_en'), index.SearchField('body_my'), index.SearchField('description_en'), index.SearchField('description_my') ] # Which fields the English and Myanmar admin tabs display. en_content_panels = Page.content_panels + [ FieldPanel('description_en'), StreamFieldPanel('body_en'), ] my_content_panels = [ FieldPanel('title_my'), FieldPanel('description_my'), StreamFieldPanel('body_my'), ] promote_panels = Page.promote_panels + [ ImageChooserPanel('hero_image'), ] # Sets the Wagtail Admin Interface's tabs. edit_handler = TabbedInterface([ ObjectList(en_content_panels, heading=_english_panel), ObjectList(my_content_panels, heading=_myanmar_panel), ObjectList(promote_panels, heading=_promote_panel), ObjectList(Page.settings_panels, heading=_settings_panel, classname="settings"), ])
class AssignmentTypes(index.Indexed, models.Model): category_type = models.CharField(max_length=100, blank=True, null=True, help_text="Select the category type.", choices=CATEGORY_LIST) title = models.CharField(max_length=100, blank=True, null=True, help_text="Select the type of feat.", choices=TITLE_LIST) verb = models.CharField(max_length=100, blank=True, null=True, help_text="Select the verb you for this feat.", choices=VERB_LIST) description = models.TextField( blank=True, null=True, help_text="Enter the description of the assignment type.") feat = models.CharField(max_length=255, blank=True, null=True, help_text="Enter the assignment feat.") topics = StreamField([ ("Topics", Topics()), ], null=True, blank=True, help_text="Enter any topics to explore") # add image carousel to panels panels = [ MultiFieldPanel( [ # FieldPanel("performance_type", classname="col12 line"), FieldPanel("category_type", classname="col6 line"), FieldPanel("title", classname="col6 line"), FieldPanel("feat", classname="col6 line"), FieldPanel("verb", classname="col6 line"), FieldPanel("description", classname="col12 line"), StreamFieldPanel("topics", classname="col12 line"), # DocumentChooserPanel("resource_link", classname="col6"), # DocumentChooserPanel("student_sample", classname="col6"), # FieldPanel("video_link", classname="col12 line-top"), ], heading="Assignment Info", ), # FieldPanel("category_type"), # FieldPanel("title"), # FieldPanel("verb"), # FieldPanel("description"), # FieldPanel("feat"), # # StreamFieldPanel("topics"), ] def __str__(self): return self.title or '' class Meta: verbose_name = "Assignment Type" verbose_name_plural = "Assignment Types" # # widgets # __all__ = ( # 'Media', 'MediaDefiningClass', 'Widget', 'TextInput', 'NumberInput', # 'EmailInput', 'URLInput', 'PasswordInput', 'HiddenInput', # 'MultipleHiddenInput', 'FileInput', 'ClearableFileInput', 'Textarea', # 'DateInput', 'DateTimeInput', 'TimeInput', 'CheckboxInput', 'Select', # 'NullBooleanSelect', 'SelectMultiple', 'RadioSelect', # 'CheckboxSelectMultiple', 'MultiWidget', 'SplitDateTimeWidget', # 'SplitHiddenDateTimeWidget', 'SelectDateWidget', # )
class ContactPage(Page): # ---- General Page information ------ title_sv = models.CharField(max_length=255) translated_title = TranslatedField('title', 'title_sv') contact_point = StreamField([('person', ContactCardBlock())], ) other_contacts = StreamField( [('contact', blocks.StructBlock([ ('person', ContactCardBlock()), ('english_groups', blocks.CharBlock( help_text=_('Comma separated list of English group names.'), required=False, )), ('swedish_groups', blocks.CharBlock( help_text=_('Comma separated list of Swedish group names.'), required=False, )), ], icon='user'))], ) map_location = models.CharField( max_length=255, verbose_name=_('Map Location'), help_text=_('Enter comma separated coordinates'), blank=True, ) location_description = RichTextField( verbose_name=_('Location Description'), help_text=_('Enter the text to show on the map'), blank=True, ) def get_context(self, request, *args, **kwargs): contacts = {} for contact in self.other_contacts: if request.LANGUAGE_CODE == 'sv': groups = contact.value.get('swedish_groups', '') else: groups = contact.value.get('english_groups', '') groups = groups.split(',') for group in groups: group = group.strip() list = contacts.get(group, []) list.append(contact.value['person']) contacts[group] = list context = super(ContactPage, self).get_context(request, *args, **kwargs) context['contacts'] = contacts return context content_panels = Page.content_panels + [ FieldPanel('title_sv', classname="full title"), FieldPanel('map_location'), FieldPanel('location_description'), StreamFieldPanel('contact_point'), StreamFieldPanel('other_contacts'), ]
class ExternalArticle(ExternalContent): class Meta: verbose_name = "External Post" verbose_name_plural = "External Posts" resource_type = "article" # if you change this, amend the related CSS, too date = DateField( "External Post date", default=datetime.date.today, help_text="The date the external post was published", ) authors = StreamField( StreamBlock( [ ("author", PageChooserBlock(target_model="people.Person")), ("external_author", ExternalAuthorBlock()), ], required=False, ), blank=True, null=True, help_text=( "Optional list of the external post's authors. " "Use ‘External author’ to add " "guest authors without creating a profile on the system" ), ) read_time = CharField( max_length=30, blank=True, help_text=( "Optional, approximate read-time for this external post, " "e.g. “2 mins”. This is shown as a small hint when the " "external post is displayed as a card." ), ) meta_panels = [ FieldPanel("date"), StreamFieldPanel("authors"), MultiFieldPanel( [InlinePanel("topics")], heading="Topics", help_text="The topic pages this external post will appear on", ), FieldPanel("read_time"), ] settings_panels = BasePage.settings_panels + [FieldPanel("slug")] edit_handler = TabbedInterface( [ ObjectList(ExternalContent.card_panels, heading="Card"), ObjectList(meta_panels, heading="Meta"), ObjectList(settings_panels, heading="Settings", classname="settings"), ] ) @property def article(self): return self @property def month_group(self): return self.date.replace(day=1) def has_author(self, person): for author in self.authors: # pylint: disable=not-an-iterable if author.block_type == "author" and str(author.value) == str(person.title): return True return False
class HomePage(RoutablePageMixin, Page): """Home page model""" template = "home/home_page.html" max_count =1 # Only one homepage instance # parent_page_type = [ # 'wagtailcore.Page' # ] banner_title = models.CharField(max_length=100, blank=False, null=True) banner_subtitle = RichTextField(features=["bold", "italic"]) banner_image = models.ForeignKey( "wagtailimages.Image", null=True, blank=False, on_delete=models.SET_NULL, related_name="+" ) banner_cta = models.ForeignKey( "wagtailcore.Page", null=True, blank=True, on_delete=models.SET_NULL, related_name="+" ) content = StreamField( [ ("cta", blocks.CTABlock()), ], null=True, blank=True ) content_panels = Page.content_panels + [ MultiFieldPanel([ FieldPanel("banner_title"), FieldPanel("banner_subtitle"), ImageChooserPanel("banner_image"), PageChooserPanel("banner_cta"), ], heading="Banner Options"), InlinePanel("carousel_images", max_num=5, min_num=1, label="Carousel Images"), StreamFieldPanel("content"), ] # # # def get_context_data(self, **kwargs): # # # context = super().get_context_data(**kwargs) # # def get_context(self, request): # # context = super(HomePage, self).get_context(request) def get_context(self, request, *args, **kwargs): context = super().get_context(request, *args, **kwargs) context['subscription_form'] = SubscriberCreateForm() return context class Meta: verbose_name = "Home Page" verbose_name_plural = "Home Pages" @route(r'^subscribe-route/$') def the_subscribe_page(self, request, *args, **kwargs): context = self.get_context(request, *args, **kwargs) context['subscription_form'] = SubscriberCreateForm() return render(request, "home/subscribe.html", context)
reference_title = TextField(null=True, blank=True) subtitle = TextField(null=True, blank=True) body = StreamField([ ('paragraph', RichTextBlock()), ('image', ImageChooserBlock()), ('html', RawHTMLBlock()), ('audio', AudioBlock()), ('video', VideoPlayerBlock()), ], blank=True) DefaultPage.content_panels = Page.content_panels + [ FieldPanel('reference_title'), FieldPanel('subtitle'), StreamFieldPanel('body'), ] DefaultPage.promote_panels = DefaultPage.promote_panels + [ FieldPanel('show_in_sitemap') ] class PageWithSidebar(Page): page_type = CharField(max_length=100, choices=PAGE_CHOICES, default='none') show_in_sitemap = BooleanField(default=True) reference_title = TextField(null=True, blank=True) subtitle = TextField(null=True, blank=True) menu_title = TextField(blank=True) is_nav_root = BooleanField(default=False) is_selectable = BooleanField(default=True) body = StreamField([('advertisement', AdvertisementInline()),
class CoursePage(Page): parent_page_type = ["CourseIndexPage"] intro = models.CharField('one line summary', max_length=250) summary = RichTextField('full summary') # start_date = models.DateTimeField(blank=False) address_details = models.CharField('address details', max_length=250, blank=True) formatted_address = models.CharField(max_length=255) # latlng_address = models.CharField(max_length=255) course_image = models.ForeignKey('wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') course_flyer = StreamField([ ('course_flyer', DocumentChooserBlock()), ], blank=True) organiser_name = models.CharField('name', max_length=250, blank=True) organiser_email = models.EmailField('email', blank=True) organiser_number = models.CharField('number', max_length=250, blank=True) course_programme = StreamField([ ('table', TableBlock()), ], blank=True) class Meta: verbose_name = "Courses & Conferences Page" search_fields = Page.search_fields + [ index.SearchField('intro'), index.SearchField('summary'), ] content_panels = Page.content_panels + [ FieldPanel('intro', classname="full"), FieldPanel('summary', classname="full"), ImageChooserPanel('course_image'), InlinePanel('related_links', label="Course Links"), InlinePanel('related_dates', label="Course Dates"), StreamFieldPanel('course_programme'), StreamFieldPanel('course_flyer'), MultiFieldPanel( [ FieldPanel('address_details', classname="full"), MapFieldPanel('formatted_address'), ], heading="Location", # classname="collapsible collapsed" ), MultiFieldPanel( [ FieldPanel('organiser_name', classname="full"), FieldPanel('organiser_email', classname="full"), FieldPanel('organiser_number', classname="full"), ], heading="Organiser Details", # classname="collapsible collapsed" ), # MapFieldPanel('latlng_address', latlng=True), ] # parent_page_types = ['CourseIndexPage'] def get_context(self, request): context = super().get_context(request) relateddates = self.related_dates.order_by('date') #the [:1] returns the first result from the list e.g. the start date. context['relateddates'] = relateddates return context
class ContentPage(BasePage): parent_page_types = [ "home.HomePage", "content.ContentPage", "topics.Topic" ] subpage_types = ["people.People", "content.ContentPage"] template = "content.html" # Content fields description = RichTextField( blank=True, default="", features=RICH_TEXT_FEATURES_SIMPLE, help_text="Optional short text description, max. 400 characters", max_length=400, ) body = CustomStreamField(help_text=( "Main page body content. Supports rich text, images, embed via URL, " "embed via HTML, and inline code snippets")) sidebar = CustomStreamField( null=True, blank=True, help_text= ("Sidebar page body content (narrower than main body). Rendered to the " "right of the main body content in desktop and below it in mobile." "Supports rich text, images, embed via URL, " "embed via HTML, and inline code snippets"), ) # Card fields card_title = CharField("Title", max_length=140, blank=True, default="") card_description = TextField("Description", max_length=140, blank=True, default="") card_image = ForeignKey( "mozimages.MozImage", null=True, blank=True, on_delete=SET_NULL, related_name="+", verbose_name="Image", help_text="An image in 16:9 aspect ratio", ) card_image = ForeignKey( "mozimages.MozImage", null=True, blank=True, on_delete=SET_NULL, related_name="+", verbose_name="Image", help_text="An image in 16:9 aspect ratio", ) card_image_3_2 = ForeignKey( "mozimages.MozImage", null=True, blank=True, on_delete=SET_NULL, related_name="+", verbose_name="Image", help_text="An image in 3:2 aspect ratio", ) # Meta fields nav_description = TextField("Navigation description", max_length=400, blank=True, default="") icon = FileField( upload_to="contentpage/icons", blank=True, default="", help_text=("MUST be a black-on-transparent SVG icon ONLY, " "with no bitmap embedded in it."), validators=[check_for_svg_file], ) keywords = ClusterTaggableManager(through=ContentPageTag, blank=True) # Editor panel configuration content_panels = BasePage.content_panels + [ FieldPanel("description"), StreamFieldPanel("body"), StreamFieldPanel("sidebar"), ] # Card panels card_panels = [ FieldPanel( "card_title", help_text=("Title displayed when this page is " "represented by a card in a list of items. " "If blank, the page's title is used."), ), FieldPanel( "card_description", help_text=("Summary text displayed when this page is " "represented by a card in a list of items. " "If blank, the page's description is used."), ), MultiFieldPanel( [ImageChooserPanel("card_image")], heading="16:9 Image", help_text=( "Image used for representing this page as a Card. " "Should be 16:9 aspect ratio. " "If not specified a fallback will be used. " "This image is also shown when sharing this page via social " "media unless a social image is specified."), ), MultiFieldPanel( [ImageChooserPanel("card_image_3_2")], heading="3:2 Image", help_text=("Image used for representing this page as a Card. " "Should be 3:2 aspect ratio. " "If not specified a fallback will be used. "), ), ] # Meta panels meta_panels = [ FieldPanel( "nav_description", help_text= "Text to display in the navigation with the title for this page.", ), MultiFieldPanel( [FieldPanel("icon")], heading="Theme", help_text= ("This icon will be used if, for example, this page is shown in a Menu" ), ), MultiFieldPanel( [ FieldPanel("seo_title"), FieldPanel("search_description"), ImageChooserPanel("social_image"), FieldPanel("keywords"), ], heading="SEO", help_text=("Optional fields to override the default title and " "description for SEO purposes"), ), ] # Settings panels settings_panels = BasePage.settings_panels + [ FieldPanel("slug"), FieldPanel("show_in_menus"), ] # Tabs edit_handler = TabbedInterface([ ObjectList(content_panels, heading="Content"), ObjectList(card_panels, heading="Card"), ObjectList(meta_panels, heading="Meta"), ObjectList(settings_panels, heading="Settings", classname="settings"), ])
class Navbar(models.Model): class Meta: abstract = True TITLE_POSITION = [ ('', 'Inherit'), ('left', 'Left'), ('center', 'Center'), ('right', 'Right'), ] title_position = models.CharField(max_length=6, choices=TITLE_POSITION, default=TITLE_POSITION[0][0], blank=True, help_text='Title position') navbar_color = models.CharField( max_length=25, default='', blank=True, help_text='Navbar Color (Uses Parent Page if blank)') navbar_links = StreamField( [('links', LinkBlock())], blank=True, help_text='Navbar navigation links (Uses Parent Page if blank)') sidebar_links = StreamField( [('links', LinkBlock())], blank=True, help_text="Sidebar navigation links (Uses Navbar links if blank).") promote_panels = [ MultiFieldPanel([ FieldRowPanel( [FieldPanel('title_position'), FieldPanel('navbar_color')]), StreamFieldPanel('navbar_links'), ], heading='Navbar Fields', classname='collapsible collapsed'), MultiFieldPanel([ StreamFieldPanel('sidebar_links'), ], heading='Sidebar (Mobile) Links', classname='collapsible collapsed'), ] def title_pos(self): if self.title_position: return self.title_position try: return self.get_parent().specific.title_pos() except: return self.title_position or 'center' def color(self): color = self.navbar_color if not color: try: color = self.get_parent().specific.color() except: color = self.navbar_color # Make the color be the last element for -text (Ex: 'cyan lighten-4' changed to 'lighten-4 cyan' for cyan-text cs = color.split(' ') if len(cs) >= 2: color = ' '.join((*cs[1:], cs[0])) return color def nav_links(self): if self.navbar_links: return self.navbar_links try: return self.get_parent().specific.nav_links() except AttributeError: return [] def sidenav_links(self): if self.sidebar_links: return self.sidebar_links try: parent = self.get_parent().specific if parent.sidebar_links: return parent.sidebar_links except AttributeError: pass return self.nav_links()
class HomePage(Page): city = models.CharField(null=True, blank=False, max_length=255) zip_code = models.CharField(null=True, blank=False, max_length=255) address = models.CharField(null=True, blank=False, max_length=255) telephone = models.CharField(null=True, blank=False, max_length=255) telefax = models.CharField(null=True, blank=False, max_length=255) vat_number = models.CharField(null=True, blank=False, max_length=255) whatsapp_telephone = models.CharField(null=True, blank=True, max_length=255) whatsapp_contactline = models.CharField(null=True, blank=True, max_length=255) tax_id = models.CharField(null=True, blank=False, max_length=255) trade_register_number = models.CharField(null=True, blank=False, max_length=255) court_of_registry = models.CharField(null=True, blank=False, max_length=255) place_of_registry = models.CharField(null=True, blank=False, max_length=255) trade_register_number = models.CharField(null=True, blank=False, max_length=255) ownership = models.CharField(null=True, blank=False, max_length=255) email = models.CharField(null=True, blank=False, max_length=255) copyrightholder = models.CharField(null=True, blank=False, max_length=255) about = RichTextField(null=True, blank=False) privacy = RichTextField(null=True, blank=False) sociallinks = StreamField([ ('link', blocks.URLBlock(help_text="Important! Format https://www.domain.tld/xyz")) ]) array = [] def sociallink_company(self): for link in self.sociallinks: self.array.append(str(link).split(".")[1]) return self.array headers = StreamField([ ('h_hero', _H_HeroBlock(null=True, blank=False, icon='image')), ('code', blocks.RawHTMLBlock(null=True, blank=True, classname="full", icon='code')) ], null=True, blank=False) sections = StreamField([ ('s_why', _S_WhyBlock(null=True, blank=False, icon='group')), ('s_about', _S_AboutBlock(null=True, blank=False, icon='fa-quote-left')), ('s_instagram', _S_InstagramBlock(null=True, blank=False, icon='fa-instagram')), ('s_steps', _S_StepsBlock(null=True, blank=False, icon='fa-list-ul')), ('s_shop', _S_ShopBlock(null=True, blank=False, icon='home')), ('s_trusted', _S_TrustedBlock(null=True, blank=False, icon='fa-list-ul')), ('s_wolf', _S_WolfBlock(null=True, blank=False, icon='fa-list-ul')), ('s_faq', _S_FAQBlock(null=True, blank=False, icon='home')), ('code', blocks.RawHTMLBlock(null=True, blank=True, classname="full", icon='code')) ], null=True, blank=False) token = models.CharField(null=True, blank=True, max_length=255) #graphql_fields = [ # GraphQLStreamfield("headers"), # GraphQLStreamfield("sections"), #] main_content_panels = [ StreamFieldPanel('headers'), StreamFieldPanel('sections') ] imprint_panels = [ MultiFieldPanel( [ FieldPanel('city'), FieldPanel('zip_code'), FieldPanel('address'), FieldPanel('telephone'), FieldPanel('telefax'), FieldPanel('whatsapp_telephone'), FieldPanel('whatsapp_contactline'), FieldPanel('email'), FieldPanel('copyrightholder') ], heading="contact", ), MultiFieldPanel( [ FieldPanel('vat_number'), FieldPanel('tax_id'), FieldPanel('trade_register_number'), FieldPanel('court_of_registry'), FieldPanel('place_of_registry'), FieldPanel('trade_register_number'), FieldPanel('ownership') ], heading="legal", ), StreamFieldPanel('sociallinks'), MultiFieldPanel( [ FieldPanel('about'), FieldPanel('privacy') ], heading="privacy", ) ] token_panel = [ FieldPanel('token') ] edit_handler = TabbedInterface([ ObjectList(Page.content_panels + main_content_panels, heading='Main'), ObjectList(imprint_panels, heading='Imprint'), ObjectList(Page.promote_panels + token_panel + Page.settings_panels, heading='Settings', classname="settings") ])
class HomePage(Page): city = models.CharField(null=True, blank=False, max_length=255) zip_code = models.CharField(null=True, blank=False, max_length=255) address = models.CharField(null=True, blank=False, max_length=255) telephone = models.CharField(blank=True, max_length=255) vat_number = models.CharField(blank=True, max_length=255) email = models.CharField(blank=True, max_length=255) copyrightholder = models.CharField(blank=True, max_length=255) about = RichTextField(blank=True) privacy = RichTextField(blank=True) shipping = RichTextField(blank=True) gtc = RichTextField(blank=True) cancellation_policy = RichTextField(blank=True) sociallinks = StreamField([ ('link', blocks.URLBlock( help_text="Important! Format https://www.domain.tld/xyz")) ]) array = [] def sociallink_company(self): for link in self.sociallinks: self.array.append(str(link).split(".")[1]) return self.array headers = StreamField([ ('h_hero', _H_HeroBlock(icon='image')), # ('code', blocks.RawHTMLBlock(null=True, blank=True, classname="full", icon='code')) ]) sections = StreamField( [('s_feature', _S_FeatureBlock(icon='fa-info')), ('s_maps', _S_MapsBlock(icon='fa-info')), ('s_partners', _S_PartnersBlock(icon='fa-info')), ('s_references', _S_ReferencesBlock(icon='fa-info')), ('s_about', _S_AboutBlock(icon='fa-info')), ('s_news', _S_NewsBlock(icon='fa-info')), ('s_projects', _S_ProjectsBlock(icon='fa-info')), ('s_contentcenter', _S_ContentCenter(icon='fa-info')), ('s_contentright', _S_ContentRight(icon='fa-info')), ('s_contentleft', _S_ContentLeft(icon='fa-info'))], null=True, blank=True) # footers = StreamField([ # ], null=True, blank=False) token = models.CharField(null=True, blank=True, max_length=255) #graphql_fields = [ # GraphQLStreamfield("headers"), # GraphQLStreamfield("sections"), #] main_content_panels = [ StreamFieldPanel('headers'), StreamFieldPanel('sections'), # StreamFieldPanel('footers') ] imprint_panels = [ MultiFieldPanel( [ FieldPanel('city'), FieldPanel('zip_code'), FieldPanel('address'), FieldPanel('telephone'), FieldPanel('email'), FieldPanel('copyrightholder') ], heading="contact", ), MultiFieldPanel( [FieldPanel('vat_number')], heading="legal", ), StreamFieldPanel('sociallinks'), MultiFieldPanel( [ FieldPanel('about'), FieldPanel('privacy'), FieldPanel('shipping'), FieldPanel('gtc'), FieldPanel('cancellation_policy'), ], heading="terms", ) ] token_panel = [FieldPanel('token')] edit_handler = TabbedInterface([ ObjectList(Page.content_panels + main_content_panels, heading='Main'), ObjectList(imprint_panels, heading='Imprint'), ObjectList(Page.promote_panels + token_panel + Page.settings_panels, heading='Settings', classname="settings") ]) preview_modes = []
class FlexPage(Page): body = StreamField(get_all_fields(), null=True) content_panels = Page.content_panels + [StreamFieldPanel('body')]
class Person(BasePage): resource_type = "person" parent_page_types = ["People"] subpage_types = [] template = "person.html" # Content fields nickname = CharField(max_length=250, null=True, blank=True) job_title = CharField(max_length=250) role = CharField(max_length=250, choices=ROLE_CHOICES, default="staff") description = RichTextField( "About", blank=True, default="", features=RICH_TEXT_FEATURES_SIMPLE, help_text="Optional ‘About me’ section content, supports rich text", ) # Card fields card_title = CharField("Title", max_length=140, blank=True, default="") card_description = TextField("Description", max_length=400, blank=True, default="") card_image = ForeignKey( "mozimages.MozImage", null=True, blank=True, on_delete=SET_NULL, related_name="+", verbose_name="Image", help_text="An image in 16:9 aspect ratio", ) # Meta city = CharField(max_length=250, blank=True, default="") country = CountryField(verbose_name="Country or Region", blank=True, default="") twitter = CharField(max_length=250, blank=True, default="") facebook = CharField(max_length=250, blank=True, default="") linkedin = CharField(max_length=250, blank=True, default="") github = CharField(max_length=250, blank=True, default="") email = CharField(max_length=250, blank=True, default="") websites = StreamField( StreamBlock([("website", PersonalWebsiteBlock())], max_num=3, required=False), null=True, blank=True, help_text="Optional links to any other personal websites", ) keywords = ClusterTaggableManager(through=PersonTag, blank=True) # Content panels content_panels = [ MultiFieldPanel( [ CustomLabelFieldPanel("title", label="Full name"), FieldPanel("nickname"), FieldPanel("job_title"), FieldPanel("role"), ], heading="Details", ), FieldPanel("description"), ] # Card panels card_panels = [ FieldPanel("card_title"), FieldPanel("card_description"), MultiFieldPanel( [ImageChooserPanel("card_image")], heading="16:9 Image", help_text=( "Image used for representing this page as a Card. " "Should be 16:9 aspect ratio. " "If not specified a fallback will be used. " "This image is also shown when sharing this page via social " "media unless a social image is specified."), ), ] # Meta panels meta_panels = [ MultiFieldPanel( [FieldPanel("city"), FieldPanel("country")], heading="Location", help_text=("Location fields. The country field is also filterable " "via the people directory page."), ), MultiFieldPanel([InlinePanel("topics")], heading="Topics this person specializes in"), MultiFieldPanel( [ FieldPanel("twitter"), FieldPanel("facebook"), FieldPanel("linkedin"), FieldPanel("github"), FieldPanel("email"), ], heading="Profiles", help_text="", ), StreamFieldPanel("websites"), MultiFieldPanel( [ FieldPanel("seo_title"), FieldPanel("search_description"), ImageChooserPanel("social_image"), FieldPanel("keywords"), ], heading="SEO", help_text=( "Optional fields to override the default title and description " "for SEO purposes"), ), ] # Settings panels settings_panels = BasePage.settings_panels + [FieldPanel("slug")] # Tabs edit_handler = TabbedInterface([ ObjectList(content_panels, heading="Content"), ObjectList(card_panels, heading="Card"), ObjectList(meta_panels, heading="Meta"), ObjectList(settings_panels, heading="Settings", classname="settings"), ]) @property def display_title(self): """ Return the display title for profile pages. Adds a nickname to the person's full name when one is provided. """ return f'{self.title} aka "{self.nickname}"' if self.nickname else self.title @property def events(self): """ Return upcoming events where this person is a speaker, ordered by start date """ from ..events.models import Event upcoming_events = Event.published_objects.filter( start_date__gte=get_past_event_cutoff()) speaker_events = Event.published_objects.none() for event in upcoming_events.all(): # add the event to the list if the current person is a speaker if event.has_speaker(self): speaker_events = speaker_events | Event.published_objects.page( event) return speaker_events.order_by("start_date") @property def articles(self): """ Return articles and external articles where this person is (one of) the authors, ordered by article date, most recent first """ from ..articles.models import Article from ..externalcontent.models import ExternalArticle articles = Article.published_objects.none() external_articles = ExternalArticle.published_objects.none() all_articles = Article.published_objects.all() all_external_articles = ExternalArticle.published_objects.all() for article in all_articles: if article.has_author(self): articles = articles | Article.published_objects.page(article) for external_article in all_external_articles: if external_article.has_author(self): external_articles = external_articles | ( ExternalArticle.published_objects.page(external_article)) return sorted(chain(articles, external_articles), key=attrgetter("date"), reverse=True) @property def videos(self): """ Return the most recent videos and external videos where this person is (one of) the speakers. """ from ..videos.models import Video from ..externalcontent.models import ExternalVideo videos = Video.published_objects.none() external_videos = ExternalVideo.published_objects.none() all_videos = Video.published_objects.all() all_external_videos = ExternalVideo.published_objects.all() for video in all_videos: if video.has_speaker(self): videos = videos | Video.published_objects.page(video) for external_video in all_external_videos: if external_video.has_speaker(self): external_videos = external_videos | ( ExternalVideo.published_objects.page(external_video)) return sorted(chain(videos, external_videos), key=attrgetter("date"), reverse=True) @property def role_group(self): return { "slug": self.role, "title": dict(ROLE_CHOICES).get(self.role, "") } @property def country_group(self): return ({ "slug": self.country.code.lower(), "title": self.country.name } if self.country else { "slug": "" }) def get_topics(self) -> List: """Return the live/published Topic pages associated with this Person""" # Note that we do this in Python because django-modelcluster won't support # `filter(topic__live=True)` when _previewing_ pages (even tho it'll work # on saved ones) topics = [pt.topic for pt in self.topics.all()] return [t for t in topics if t.live]
class BlogDetailPage(BannerPage): template = "blog/post.html" subpage_types = [] parent_page_type = [ "blog.BlogListingPage", ] author = models.ForeignKey( BlogAuthor, blank=False, null=True, on_delete=models.SET_NULL, ) notify_newsletter_subscribers = models.BooleanField( default=False, verbose_name="Newsletter", ) notify_telegram_subscribers = models.BooleanField( default=False, verbose_name="Telegram", ) content = StreamField( [ ("richtext", blocks.RichTextBlock()), ("image", blocks.FigureBlock()), ("code", blocks.CodeFragmentBlock()), ("HTML", RawHTMLBlock(icon="fa-html5")), ("Terminal", TerminalBlock()), ("latex", blocks.MathBlock()), ("blockquote", blocks.BlockQuoteBlock()), ("youtube", blocks.YouTubeBlock()), ], blank=True, ) content_panels = BannerPage.content_panels + [ SnippetChooserPanel("author"), StreamFieldPanel("content"), ] edit_handler = TabbedInterface([ ObjectList(content_panels, heading="Content"), ObjectList(BannerPage.promote_panels, heading="Promote"), ObjectList( BannerPage.settings_panels + [ MultiFieldPanel( [ FieldPanel("notify_newsletter_subscribers"), FieldPanel("notify_telegram_subscribers"), ], heading="Notification Channels", help_text= "Choose which channels to notifiy about this blog post.", ) ], heading="Settings", ), ])
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='Homepage image') hero_text = models.CharField( max_length=255, help_text='Write an introduction for the bakery') hero_cta = models.CharField(verbose_name='Hero CTA', max_length=255, help_text='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='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) # Promo section of the HomePage promo_image = models.ForeignKey('wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', help_text='Promo image') promo_title = models.CharField( null=True, blank=True, max_length=255, help_text='Title to display above the promo copy') promo_text = RichTextField(null=True, blank=True, help_text='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( null=True, blank=True, max_length=255, help_text='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='First featured section for the homepage. Will display up to ' 'three child items.', verbose_name='Featured section 1') featured_section_2_title = models.CharField( null=True, blank=True, max_length=255, help_text='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='Second featured section for the homepage. Will display up to ' 'three child items.', verbose_name='Featured section 2') featured_section_3_title = models.CharField( null=True, blank=True, max_length=255, help_text='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='Third featured section for the homepage. Will display up to ' 'six child items.', verbose_name='Featured section 3') content_panels = Page.content_panels + [ MultiFieldPanel([ ImageChooserPanel('image'), FieldPanel('hero_text', classname="full"), MultiFieldPanel([ FieldPanel('hero_cta'), PageChooserPanel('hero_cta_link'), ]), ], heading="Hero section"), MultiFieldPanel([ ImageChooserPanel('promo_image'), FieldPanel('promo_title'), FieldPanel('promo_text'), ], heading="Promo section"), StreamFieldPanel('body'), MultiFieldPanel([ MultiFieldPanel([ FieldPanel('featured_section_1_title'), PageChooserPanel('featured_section_1'), ]), MultiFieldPanel([ FieldPanel('featured_section_2_title'), PageChooserPanel('featured_section_2'), ]), MultiFieldPanel([ FieldPanel('featured_section_3_title'), PageChooserPanel('featured_section_3'), ]), ], heading="Featured homepage sections", classname="collapsible") ] api_fields = [ APIField('image'), APIField('body'), ] def __str__(self): return self.title
class Events(Page): parent_page_types = ['home.HomePage'] subpage_types = ['events.Event'] template = 'events.html' # Content fields featured = StreamField( StreamBlock([ ('event', PageChooserBlock(target_model=( 'events.Event', 'externalcontent.ExternalEvent', ))), ('external_page', FeaturedExternalBlock()), ], max_num=1, required=False), null=True, blank=True, help_text='Optional space to show a featured event', ) # Meta fields keywords = ClusterTaggableManager(through=EventsTag, blank=True) # Content panels content_panels = Page.content_panels + [StreamFieldPanel('featured')] # Meta panels meta_panels = [ MultiFieldPanel( [ FieldPanel('seo_title'), FieldPanel('search_description'), FieldPanel('keywords'), ], heading='SEO', help_text= 'Optional fields to override the default title and description for SEO purposes' ), ] # Settings panels settings_panels = [ FieldPanel('slug'), FieldPanel('show_in_menus'), ] edit_handler = TabbedInterface([ ObjectList(content_panels, heading='Content'), ObjectList(meta_panels, heading='Meta'), ObjectList(settings_panels, heading='Settings', classname='settings'), ]) class Meta: verbose_name_plural = 'Events' @classmethod def can_create_at(cls, parent): # Allow only one instance of this page type return super().can_create_at(parent) and not cls.objects.exists() def get_context(self, request): context = super().get_context(request) context['filters'] = self.get_filters() return context @property def events(self): """Return events in chronological order""" return get_combined_events(self) def get_filters(self): from ..topics.models import Topic return { 'months': True, 'topics': Topic.objects.live().public().order_by('title'), }
class ServicePage(Page, ContentPageMixin): date = models.DateField("Service date") description = wtfields.RichTextField( blank=True, default= "Please join us for our Sunday service as we worship and listen to God's word.", ) stream_link = models.URLField(default="", blank=True) public_stream_link = models.URLField(default="", blank=True) chat_enabled = models.BooleanField(default=True) weekly_theme = models.CharField(max_length=128, default="", blank=True) bulletin = wtfields.StreamField( [ ("bulletin_section", BulletinSectionBlock(name="Bulletin Section")), ], blank=True, ) # service = wtfields.StreamField([ # ('worship_section', WorshipSectionBlock(name="Worship Section")), # ('announcements_section', AnnouncementsSectionBlock(name="Announcement Section")), # ('sermon_section', SermonSectionBlock(name="Sermon Section")), # ('discussion_section', DiscussionSectionBlock(name="Discussion Section")), # # TODO # # - polls/voting? # # - feedback # # - discussion # ]) @property def getdescription(self): return self.description content_panels = Page.content_panels + [ FieldPanel("date"), FieldPanel("stream_link"), FieldPanel("public_stream_link"), FieldPanel("description"), InlinePanel("documents", label="Documents"), FieldPanel("chat_enabled"), StreamFieldPanel("bulletin"), FieldPanel("weekly_theme"), ] prayer_requests = models.ManyToManyField(pr_models.PrayerRequest, related_name="services_pages") def child_pages(self): pages = DailyReadingPage.objects.live().descendant_of(self).order_by( "-date") return pages @classmethod def current_service_page(cls): return cls.objects.all().order_by("date").last() def add_prayer_request(self, pr): self.prayer_requests.add(pr) @property def email_attachments(self): return [ doclink.document.file.file for doclink in self.documents.all() if doclink.include_in_email ] def get_context(self, request): context = super().get_context(request) context["self"] = self context["docs"] = self.documents.all() return context
query_string=query_string, # TODO blog_feed_title=feed_settings.blog_feed_title ) return context def serve_preview(self, request, mode_name): """ This is another hack to overcome the MRO issue we were seeing """ return BibliographyMixin.serve_preview(self, request, mode_name) class Meta: verbose_name = "IESG Statement Page" IESGStatementPage.content_panels = Page.content_panels + [ FieldPanel('date_published'), FieldPanel('introduction'), StreamFieldPanel('body'), InlinePanel('topics', label="Topics"), ] IESGStatementPage.promote_panels = Page.promote_panels + PromoteMixin.panels class IESGStatementIndexPage(RoutablePageMixin, Page): def get_context(self, request): context = super().get_context(request) context['statements'] = IESGStatementPage.objects.child_of(self).live().annotate( d=Coalesce('date_published', 'first_published_at') ).order_by('-d') return context
class BlogPage(RoutablePageMixin, Page): subtitle = models.CharField(max_length=255, null=True, blank=True) main_image = models.ForeignKey('wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') date = models.DateField("Post date", null=True, blank=True) intro = models.CharField(max_length=250, null=True, blank=True) body = StreamField([ ('heading', CharBlock(classname="full title", icon='title')), ('paragraph', RichTextBlock(icon='pilcrow')), ('image', ImageChooserBlock(icon='image')), ('codeblock', TextBlock(icon='cogs')), ('markdown', MarkDownBlock()), ('real_codeblock', CodeBlock()), ], blank=True, null=True) tags = ClusterTaggableManager(through=BlogPageTag, blank=True) listing_intro = RichTextField(null=True, blank=True) listing_image = models.ForeignKey('wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') search_fields = Page.search_fields + [ index.SearchField('intro'), index.SearchField('body'), ] content_panels = Page.content_panels + [ FieldPanel('subtitle'), ImageChooserPanel('main_image'), FieldPanel('date'), FieldPanel('intro'), StreamFieldPanel('body'), FieldPanel('tags'), ] promote_panels = Page.promote_panels + [ FieldPanel('listing_intro'), ImageChooserPanel('listing_image'), ] @property def home_page(self): return self.get_parent() @property def next_blog(self): blogs = BlogPage.objects.filter(live=True).order_by('-date') current_index = blogs.index(self) def get_absolute_url(self): return self.full_url @route(r'^$', name='normal_blog') def normal_blog(self, request): site_root = self.get_parent() return render(request, self.template, { 'self': self, }) @route(r'^amp/$', name='amp_blog') def amp_blog(self, request): context = self.get_context(request) context['is_amp'] = True context['base_template'] = 'base_amp.html' response = TemplateResponse(request, self.template, context) return response
class LocationPage(Page): """ Detail for a specific bakery location. """ introduction = models.TextField( help_text='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='Landscape mode only; horizontal width between 1000px and 3000px.' ) body = StreamField( BaseStreamBlock(), verbose_name="Page body", blank=True ) address = models.TextField() lat_long = models.CharField( max_length=36, help_text="Comma separated lat/long. (Ex. 64.144367, -21.939182) \ Right click Google Maps and select 'What\'s Here'", validators=[ RegexValidator( regex=r'^(\-?\d+(\.\d+)?),\s*(\-?\d+(\.\d+)?)$', message='Lat Long must be a comma-separated numeric lat and long', code='invalid_lat_long' ), ] ) # Search index configuration search_fields = Page.search_fields + [ index.SearchField('address'), index.SearchField('body'), ] # Fields to show to the editor in the admin view content_panels = [ FieldPanel('title', classname="full"), FieldPanel('introduction', classname="full"), ImageChooserPanel('image'), StreamFieldPanel('body'), FieldPanel('address', classname="full"), FieldPanel('lat_long'), InlinePanel('hours_of_operation', label="Hours of Operation"), ] def __str__(self): return self.title @property def operating_hours(self): hours = self.hours_of_operation.all() return hours # Determines if the location is currently open. It is timezone naive def is_open(self): now = datetime.now() current_time = now.time() current_day = now.strftime('%a').upper() try: self.operating_hours.get( day=current_day, opening_time__lte=current_time, closing_time__gte=current_time ) return True except LocationOperatingHours.DoesNotExist: return False # Makes additional context available to the template so that we can access # the latitude, longitude and map API key to render the map def get_context(self, request): context = super(LocationPage, self).get_context(request) context['lat'] = self.lat_long.split(",")[0] context['long'] = self.lat_long.split(",")[1] context['google_map_api_key'] = settings.GOOGLE_MAP_API_KEY return context # Can only be placed under a LocationsIndexPage object parent_page_types = ['LocationsIndexPage']
class DefaultRichBlockFieldPage(Page): body = StreamField([ ('rich_text', RichTextBlock()), ]) content_panels = Page.content_panels + [StreamFieldPanel('body')]