class GlobalStreamBlock(StreamBlock): paragraph = RichTextBlock(icon="pilcrow", template="blocks/paragraph.html") header = StructBlock([ ('header_text', CharBlock(blank=True, required=False, label='Header')), ('size', ChoiceBlock(choices=[('', 'Select a header size'), ('h2', 'H2'), ('h3', 'H3'), ('h4', 'H4')], blank=True, required=False)) ], classname="title", icon="title", template="blocks/header.html") image = StructBlock([('image', ImageChooserBlock()), ('caption', CharBlock(blank=True, required=False)), ('style', ChoiceBlock(choices=[('', 'Select an image size'), ('full', 'Full-width'), ('contain', 'Contained-width'), ('half', 'Half-width')], required=False))], icon="image", template="blocks/image.html") blockquote = StructBlock([ ('text', TextBlock()), ('attribute_name', CharBlock(blank=True, required=False, label='e.g. Guy Picciotto')), ('attribute_group', CharBlock(blank=True, required=False, label='e.g. Fugazi')), ], icon="openquote", template="blocks/blockquote.html") embed = EmbedBlock( help_text= 'Insert an embed URL e.g https://www.youtube.com/embed/SGJFWirQ3ks')
class DefinitionListBlock(ContentBlock): definitions = ListBlock( StructBlock([('title', CharBlock()), ('definition', CharBlock())])) class Meta: icon = 'tag' template = 'blocks/definition_list.html'
class NavigationSettings(BaseSetting, ClusterableModel): header_links = StreamField([ ('item', OverrideablePageChooserBlock()), ], blank=True) footer_links = StreamField([ ('column', StructBlock([ ('column_heading', TextBlock()), ('subitems', ListBlock(OverrideablePageChooserBlock(label="Sub-item"))), ])), ], blank=True) footer_secondary_links = StreamField([ ('item', OverrideablePageChooserBlock()), ], blank=True) panels = [ StreamFieldPanel('header_links'), StreamFieldPanel('footer_links'), StreamFieldPanel('footer_secondary_links'), ] def save(self, *args, **kwargs): super().save(*args, **kwargs) purge_esi() def delete(self, *args, **kwargs): super().delete(*args, **kwargs) purge_esi()
class SkillbarBlock(ContentBlock): skills = ListBlock( StructBlock([('skill_percentage', CharBlock()), ('skill_name', CharBlock())])) class Meta: icon = 'form' template = 'blocks/skillbar.html'
class OrderedListBlock(StructBlock): title = CharBlock(required=False) items = ListBlock( StructBlock([('title', CharBlock()), ('description', TextBlock())])) label = CharBlock(required=False) page = PageChooserBlock(required=False) class Meta: template = 'cms/blocks/ordered_list_block.html'
class ImageListBlock(StructBlock): title = CharBlock(required=False) items = ListBlock( StructBlock([('title', CharBlock()), ('subtitle', CharBlock()), ('description', RichTextBlock()), ('image', ImageChooserBlock()), ('page', PageChooserBlock(required=False))])) class Meta: template = 'cms/blocks/image_list_block.html'
class Header(BaseSetting): links = StreamField([ ('links', StructBlock([ ('link', PageChooserBlock(required=True)), ('text', CharBlock(max_length=255)), ], blank=True, null=True)) ], null=True) panels = [ StreamFieldPanel('links') ]
class ImageGridBlock(StructBlock): title = CharBlock(required=False) items = ListBlock( StructBlock([('image', ImageChooserBlock()), ('url', URLBlock(required=False)), ('page', PageChooserBlock(required=False))])) class Meta: help_text = ''' Use either URL or page, if both are filled in URL takes precedence.''' template = 'cms/blocks/image_grid_block.html'
class AccordionBlock(StructBlock): class Meta: icon = "arrow-down-big" template = "blocks/accordion_block.html" accordion_group_title = TextBlock(required=False, blank=True, default="") accordion_items = ListBlock( StructBlock([ ('accordion_title', CharBlock(required=True)), ('accordion_content', RichTextBlock(required=True)), ]))
class SimplifiedBlock(StreamBlock): paragraph = RichTextBlock(icon="pilcrow", template="blocks/paragraph.html") header = CharBlock(classname="title", icon="fa-header", template="blocks/h3.html") image = StructBlock([('image', ImageChooserBlock()), ('caption', CharBlock(blank=True, required=False)), ('style', ChoiceBlock(choices=[('', 'Select an image size'), ('full', 'Full-width'), ('half', 'Half-width')], required=False))], icon="image", template="blocks/image.html") blockquote = StructBlock([ ('text', TextBlock()), ('attribute_name', CharBlock(blank=True, required=False, label='e.g. Guy Picciotto')), ('attribute_group', CharBlock(blank=True, required=False, label='e.g. Fugazi')), ], icon="openquote", template="blocks/blockquote.html")
class HeadingWithTextBlock(StructBlock): block_heading = CharBlock(required=True) style = ChoiceBlock(choices=[('white', 'White background'), ('community', 'Community background')], required=True) items = ListBlock( StructBlock([ ('heading', CharBlock(required=True)), ('text', RichTextBlock(required=True)), ])) read_more_button = ReadMoreButtonBlock(required=False) class Meta: image = 'pages/images/streamfield_blocks/heading_with_text.jpg' template = 'pages/streamfield_blocks/heading_with_text.html'
class Stakeholders(StructBlock): block_heading = CharBlock(required=True) personas = ListBlock( StructBlock([ ('image', ImageChooserBlock(required=True)), ('name', CharBlock(required=True)), ('position', CharBlock(required=True)), ('excerpt', TextBlock(required=True)), ])) file = DocumentChooserBlock(required=False) label = CharBlock(default="Find out more", required=False, label="Download button label") class Meta: image = 'pages/images/streamfield_blocks/team.jpg' template = 'pages/streamfield_blocks/stakeholders.html'
class CustomPage(Page, index.Indexed): footer = ForeignKey('common.Footer', default=DEFAULT_FOOTER_ID, null=True, blank=True, on_delete=SET_NULL, related_name='+') content = StreamField([ ('appeal', StructBlock([ ('icon', ChoiceBlock(required=True, choices=[ ('none', 'none'), ('flask', 'flask'), ('group', 'group'), ('laptop', 'laptop'), ('sitemap', 'sitemap'), ('user', 'user'), ('book', 'book'), ('download', 'download'), ])), ('topic', CharBlock(required=True, max_length=35)), ('content', RichTextBlock(required=True)), ], classname='appeal', icon='tick', template='common/blocks/appeal.html')), ('heading', CharBlock(classname="full title")), ('statement', CharBlock()), ('paragraph', RichTextBlock()), ('imagechooser', ImageChooserBlock()), ('column', RowBlock()), ('tabs', TabsBlock()), ('image', ImageBlock()), ('customImage', CustomImageBlock()), ('rich_text', RichTextBlock()), ('raw_html', RawHTMLBlock( help_text= 'With great power comes great responsibility. This HTML is unescaped. Be careful!' )), ('people_block', PeopleBlock()), ('centered_text', CenteredTextBlock()), ('hero_block', HeroBlock()), ('spotlight_block', SpotlightBlock()), ('job_whole_block', JobsWholeBlock()), ('embed_block', EmbedBlock()), ('whitespaceblock', WhitespaceBlock()), ('clear_fixblock', ClearfixBlock()), ('code_block', CodeBlock()), ('table_block', CustomTableBlock()), ('calender_block', GoogleCalendarBlock()), ('journal_block', JournalsTabBlock()), ('render_file', MfrBlock()), ('sponsor_partner_block', SponsorPartnerBlock()), ('collapse_block', CollapseBoxBlock()), ('button', ButtonBlock()), ], null=True, blank=True) custom_url = CharField(max_length=256, default='', null=True, blank=True) menu_order = IntegerField( blank=True, default=1, help_text=( 'The order this page should appear in the menu. ' 'The lower the number, the more left the page will appear. ' 'This is required for all pages where "Show in menus" is checked.' )) search_fields = [ index.SearchField('content', partial_match=True), ] content_panels = Page.content_panels + [ StreamFieldPanel('content'), SnippetChooserPanel('footer'), ] promote_panels = Page.promote_panels + [ FieldPanel('custom_url'), FieldPanel('menu_order'), InlinePanel('versioned_redirects', label='URL Versioning'), ] def serve(self, request): return render( request, self.template, { 'page': self, 'people': Person.objects.all(), 'jobs': Job.objects.all(), 'journals': Journal.objects.all(), 'organizations': Organization.objects.all(), 'donations': Donation.objects.all(), 'inkinddonations': InkindDonation.objects.all(), }) @transaction.atomic # only commit when all descendants are properly updated def move(self, target, pos=None): """ Extension to the treebeard 'move' method to ensure that url_path is updated too. """ old_url_path = Page.objects.get(id=self.id).url_path super(Page, self).move(target, pos=pos) # treebeard's move method doesn't actually update the in-memory instance, so we need to work # with a freshly loaded one now new_self = Page.objects.get(id=self.id) new_url_path = new_self.set_url_path(new_self.get_parent()) new_self.save() new_self._update_descendant_url_paths(old_url_path, new_url_path) new_redirect = new_self.versioned_redirects.create() redirect_url = ('/' + '/'.join(old_url_path.split('/')[2:]))[:-1] new_redirect.old_path = redirect_url new_redirect.redirect_page = new_self new_redirect.site = new_self.get_site() new_redirect.save() new_self.redirect_set.add(new_redirect) # Log logger.info("Page moved: \"%s\" id=%d path=%s", self.title, self.id, new_url_path) @transaction.atomic # ensure that changes are only committed when we have updated all descendant URL paths, to preserve consistency def save(self, *args, **kwargs): self.full_clean() update_descendant_url_paths = False is_new = self.id is None if is_new: # we are creating a record. If we're doing things properly, this should happen # through a treebeard method like add_child, in which case the 'path' field # has been set and so we can safely call get_parent self.set_url_path(self.get_parent()) else: # Check that we are committing the slug to the database # Basically: If update_fields has been specified, and slug is not included, skip this step if not ('update_fields' in kwargs and 'slug' not in kwargs['update_fields']): # see if the slug has changed from the record in the db, in which case we need to # update url_path of self and all descendants old_record = Page.objects.get(id=self.id) if old_record.slug != self.slug: self.set_url_path(self.get_parent()) update_descendant_url_paths = True old_url_path = old_record.url_path new_url_path = self.url_path new_redirect = self.versioned_redirects.create() redirect_url = ('/' + '/'.join(old_url_path.split('/')[2:]))[:-1] new_redirect.old_path = redirect_url new_redirect.redirect_page = self new_redirect.site = self.get_site() new_redirect.save() self.redirect_set.add(new_redirect) for redirect in self.versioned_redirects.all(): redirect.versioned_redirect_page = self redirect.redirect_page = self redirect.save() result = super(Page, self).save(*args, **kwargs) if update_descendant_url_paths: self._update_descendant_url_paths(old_url_path, new_url_path) # Check if this is a root page of any sites and clear the 'wagtail_site_root_paths' key if so if Site.objects.filter(root_page=self).exists(): cache.delete('wagtail_site_root_paths') # Log if is_new: cls = type(self) logger.info( "Page created: \"%s\" id=%d content_type=%s.%s path=%s", self.title, self.id, cls._meta.app_label, cls.__name__, self.url_path) return result