class NavGroup(blocks.StructBlock): draft = blocks.BooleanBlock( required=False, default=False, help_text='If checked, this block will only show ' 'on our sharing site (Content).', label='Mark block as draft' ) group_title = blocks.CharBlock( required=False, label='Column title') hide_group_title = blocks.BooleanBlock( required=False, label='Hide column title', help_text='If column shares title with previous column, ' 'enter title text above but check this box so title ' 'only shows in first column.') nav_items = blocks.ListBlock( NavItem(), required=False, label='Menu items')
class ItemIntroduction(blocks.StructBlock): show_category = blocks.BooleanBlock( required=False, default=True, help_text=( "Whether to show the category or not " "(category must be set in 'Configuration')." ) ) heading = blocks.CharBlock(required=False) paragraph = blocks.RichTextBlock(required=False) date = blocks.DateBlock(required=False) has_social = blocks.BooleanBlock( required=False, help_text="Whether to show the share icons or not.") class Meta: icon = 'form' template = '_includes/organisms/item-introduction.html' classname = 'block__flush-top'
class LandingPage(Page): body = StreamField([ ('heading', blocks.CharBlock(classname="full title")), ('paragraph', blocks.RichTextBlock()), ('image', ImageChooserBlock(icon="image")), ('two_columns', TwoColumnBlock()), ('embedded_video', EmbedBlock(icon="media")), ],null=True,blank=True) content_panels = Page.content_panels + [ StreamFieldPanel('body'), ] @property def blog_page(self): return self.get_parent().specific def get_context(self, request, *args, **kwargs): context = super(LandingPage, self).get_context(request, *args, **kwargs) context['blog_page'] = self.blog_page return context
def test_include_block_tag_with_filtered_value(self): """ The block parameter on include_block tag should support complex values including filters, e.g. {% include_block foo|default:123 %} """ block = blocks.CharBlock(template='tests/jinja2/heading_block.html') bound_block = block.bind('bonjour') result = render_to_string( 'tests/jinja2/include_block_test_with_filter.html', { 'test_block': bound_block, 'language': 'fr', }) self.assertIn('<body><h1 lang="fr">bonjour</h1></body>', result) result = render_to_string( 'tests/jinja2/include_block_test_with_filter.html', { 'test_block': None, 'language': 'fr', }) self.assertIn('<body>999</body>', result)
class YoutubePage(Page): abstract = RichTextField() body = StreamField([ ('rich_text', blocks.RichTextBlock()), ('heading', blocks.CharBlock(classname="full title")), ('paragraph', blocks.RichTextBlock()), ('raw_html', blocks.RawHTMLBlock()) ]) youtube_video_id = models.CharField(max_length=20) social_image = models.ForeignKey('wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') content_panels = Page.content_panels + [ FieldPanel('abstract', classname="full"), StreamFieldPanel('body'), FieldPanel('youtube_video_id'), ] promote_panels = Page.promote_panels + [ ImageChooserPanel('social_image') ]
class LatestNewsBlock(CountryRegionStructBlock): limit = blocks.CharBlock() class Meta: icon = 'fa fa-list' label = 'Latest news' template = 'widgets/latest-news.html' def get_context(self, value): context = super().get_context(value) queryset = BlogPage.objects.all() if self.country or self.region: tag = self.country.slug or self.region.slug filter_queryset = queryset.filter(tags__slug=tag) if filter_queryset.count() > 0: queryset = filter_queryset else: queryset = queryset.filter(tags__isnull=True) limit = value.get('limit') context['news'] = queryset[:int(limit)] return context
class LinkBlock(StructBlock): cls = blocks.ChoiceBlock(choices=[ ('btn', 'Button'), ], required=False, label='Type') url = blocks.URLBlock(label='URL') text = blocks.CharBlock() class Meta: icon = 'anchor' template = 'widgets/link.html' def get_context(self, value): context = super().get_context(value) context['href'] = value.get('url') context['text'] = value.get('text') context['class'] = value.get('cls') return context ('link', URLBlock(icon="link")),
class LinkBlock(blocks.StructBlock): title = blocks.CharBlock() url = blocks.URLBlock() def get_context(self, value): context = super(LinkBlock, self).get_context(value) context['classname'] = 'important' if value[ 'title'] == 'Torchbox' else 'normal' return context def get_form_context(self, value, prefix='', errors=None): context = super(LinkBlock, self).get_form_context(value, prefix=prefix, errors=errors) context['extra_var'] = "Hello from get_form_context!" return context class Meta: icon = "site" template = 'tests/blocks/link_block.html' form_template = 'tests/block_forms/link_block.html'
class MediaTextOverlayBlock(blocks.StructBlock): title = blocks.CharBlock(required=False, label="Title Text", max_length=255, help_text="Appears above the module.") image = ImageChooserBlock() logo = ImageChooserBlock(label="Title Logo", required=False) text = blocks.RichTextBlock(max_length=75, required=False, features=[ "bold", "italic", "ol", "ul", "link", "document-link", "justify" ]) link = LinkBlock(required=False) customisation = CustomisationBlock(required=False) def clean(self, value): if value['title'] and value['logo']: error_messages = ["Please choose only one of logo or title."] raise ValidationError( "Validation error in MediaTextOverlayBlock", params={ 'title': error_messages, 'logo': error_messages }, ) if not value['title'] and not value['logo']: error_messages = ["Please choose a logo or title."] raise ValidationError( "Validation error in MediaTextOverlayBlock", params={ 'title': error_messages, 'logo': error_messages }, ) return super().clean(value) class Meta: icon = "image" template = "blocks/media_text_overlay_block.html"
class SolutionsBlock(StructBlock): h5 = blocks.CharBlock(required=False) h2 = blocks.CharBlock(required=False) color = ChoiceBlock(choices=[ ('', 'Блок светлый или тёмный?'), ('light', 'Светлый'), ('dark', 'Тёмный') ], blank=True, required=False) btntext = blocks.CharBlock(required=False) formh3 = blocks.CharBlock(required=False) button = blocks.CharBlock(required=False) handle = blocks.CharBlock() class Meta: icon = 'doc-empty-inverse' template = "sections/solutions_block.html"
class BlogPage(Page): date = models.DateField("Post date", default=timezone.now) intro = models.CharField(max_length=250, blank=True) main_feature = models.BooleanField(default=False) body = StreamField([ ('heading', blocks.CharBlock(classname="full title")), ('paragraph', blocks.RichTextBlock()), ('image', ImageChooserBlock()), ('articleIP', blocks.URLBlock()), ]) tags = ClusterTaggableManager(through=BlogPageTag, blank=True) categories = ParentalManyToManyField('blog.BlogCategory', blank=True) authors = models.CharField(max_length=255, default="Buckanjären") def main_image(self): gallery_item = self.gallery_images.first() if gallery_item: return gallery_item.image else: print("--- no image") return None search_fields = Page.search_fields + [ index.SearchField('intro'), index.SearchField('body'), ] content_panels = Page.content_panels + [ MultiFieldPanel([ FieldPanel('date'), FieldPanel('tags'), FieldPanel('categories', widget=forms.CheckboxSelectMultiple), FieldPanel('main_feature', widget=forms.CheckboxSelectMultiple), ], heading="Blog information"), FieldPanel('intro'), FieldPanel('authors'), StreamFieldPanel('body'), InlinePanel('gallery_images', label="Gallery images"), ]
class StandardPage(Page): feature_image = models.ForeignKey('wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') feature_image_large = models.BooleanField(default=False) body = StreamField([ ('heading', blocks.CharBlock(classname='full title', template='blocks/heading.html')), ('section', SectionBlock()), ('image', ImageTextBlock(template='blocks/image_text.html')), ('quote', PullQuoteBlock(template='blocks/quote.html')), ('call_to_action', CallToActionBlock()), ]) content_panels = Page.content_panels + [ ImageChooserPanel('feature_image'), FieldPanel('feature_image_large'), StreamFieldPanel('body'), ]
class FilterControls(BaseExpandable): form_type = blocks.ChoiceBlock(choices=[ ('filterable-list', 'Filterable List'), ('pdf-generator', 'PDF Generator'), ], default='filterable-list') title = blocks.BooleanBlock(default=True, required=False, label='Filter Title') post_date_description = blocks.CharBlock(default='Published') categories = blocks.StructBlock([ ('filter_category', blocks.BooleanBlock(default=True, required=False)), ('show_preview_categories', blocks.BooleanBlock(default=True, required=False)), ('page_type', blocks.ChoiceBlock( choices=ref.filterable_list_page_types, required=False )), ]) topics = blocks.BooleanBlock(default=True, required=False, label='Filter Topics') authors = blocks.BooleanBlock(default=True, required=False, label='Filter Authors') date_range = blocks.BooleanBlock(default=True, required=False, label='Filter Date Range') output_5050 = blocks.BooleanBlock(default=False, required=False, label="Render preview items as 50-50s") link_image_and_heading = blocks.BooleanBlock( default=False, required=False, help_text='Add links to post preview images and' ' headings in filterable list results' ) class Meta: label = 'Filter Controls' icon = 'form' class Media: js = ['filterable-list-controls.js']
class BlogPage(Page): date = models.DateField("Post date") intro = models.CharField(max_length=250) body = StreamField([ ('heading', blocks.CharBlock(classname="full title")), ('paragraph', blocks.RichTextBlock()), ('image', ImageChooserBlock()), ('embed', EmbedBlock()), ('rawHtml', blocks.RawHTMLBlock()), ]) feed_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('date'), FieldPanel('intro'), StreamFieldPanel('body'), ] promote_panels = [ MultiFieldPanel(Page.promote_panels, "Common page configuration"), ImageChooserPanel('feed_image'), ] def get_fields(self): return [(field.name, field.value_to_string(self)) for field in BlogPage._meta.fields] def cur_site_id(self): return "{}".format(self.get_url_parts()[0])
def test_block_render_result_is_safe(self): """ Ensure that any results of template rendering in block.render are marked safe so that they don't get double-escaped when inserted into a parent template (#2541) """ stream_block = blocks.StreamBlock([ ('paragraph', blocks.CharBlock(template='tests/jinja2/paragraph.html')) ]) stream_value = stream_block.to_python([ { 'type': 'paragraph', 'value': 'hello world' }, ]) result = render_to_string('tests/jinja2/stream.html', { 'value': stream_value, }) self.assertIn('<p>hello world</p>', result)
class BlogPage(Page): date = models.DateField("Post date") intro = models.CharField(max_length=255) body = RichTextField(blank=True) blogElement = StreamField([ ('heading', blocks.CharBlock(classname="full title")), ('paragraph', blocks.TextBlock()), ('picture', ImageChooserBlock()), ], default=[]) search_fields = Page.search_fields + [ index.SearchField('intro'), index.SearchField('body'), ] content_panels = Page.content_panels + [ FieldPanel('date'), FieldPanel('intro'), FieldPanel('body', classname="full"), StreamFieldPanel('blogElement'), ]
class ThesisIndexPage(Page): header = StreamField([ ('rawHtml', blocks.RawHTMLBlock()), ('heading', blocks.CharBlock(classname="full title")), ('paragraph', blocks.RichTextBlock()), ('image', ImageChooserBlock()), ('embed', EmbedBlock()), ]) background_image = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) content_panels = Page.content_panels + [ StreamFieldPanel('header'), ImageChooserPanel('background_image'), ]
class BlogEntry(OpenGraphMixin, Page): """Page for a single blog entry.""" blog_date = models.DateField("Blog Entry Date") blog_author = models.CharField(max_length=255) blog_tags = ClusterTaggableManager(through=BlogEntryTag, blank=True) body = StreamField([ ('banner_image', common_blocks.BannerImage()), ('heading', blocks.CharBlock(classname="full title")), ('paragraph', blocks.RichTextBlock()), ('image', common_blocks.CaptionImageBlock()), ('h1', common_blocks.HeaderH1(classname="full title")), ('subhead', common_blocks.Subhead(classname="full title")), ('block_quote', common_blocks.BlockQuote()), ('call_to_action', common_blocks.CallToAction()), ('small_call_to_action', common_blocks.CTAButton()), ]) search_fields = Page.search_fields + [ index.SearchField('body'), ] content_panels = Page.content_panels + [ MultiFieldPanel([ FieldPanel('blog_date'), FieldPanel('blog_author'), FieldPanel('blog_tags'), ], heading="Blog information"), StreamFieldPanel('body'), ] def save(self, *args, **kwargs): """Override to have a more specific slug w/ date & title.""" self.slug = "{0}-{1}".format(self.blog_date.strftime("%Y-%m-%d"), slugify(self.title)) super().save(*args, **kwargs)
class ImageBasic(blocks.StructBlock): upload = ImageChooserBlock(required=False) alt = blocks.CharBlock( required=False, help_text='If the image is decorative (i.e., if a screenreader ' 'wouldn\'t have anything useful to say about it), leave the ' 'Alt field blank.') def __init__(self, required=True): self.is_required = required super(ImageBasic, self).__init__() @property def required(self): return self.is_required def clean(self, data): error_dict = {} try: data = super(ImageBasic, self).clean(data) except ValidationError as e: error_dict.update(e.params) if not self.required and not data['upload']: return data if not data['upload']: error_dict.update({'upload': is_required("Upload")}) if error_dict: raise ValidationError("ImageBasic validation errors", params=error_dict) else: return data class Meta: icon = 'image'
class BlogPage(Page): intro = RichTextField() body = StreamField([ ('heading', blocks.CharBlock(classname="full title")), ('paragraph', blocks.RichTextBlock()), ('image', ImageChooserBlock()), ]) tags = ClusterTaggableManager(through=BlogPageTag, blank=True) date = models.DateField("Post date") feed_image = models.ForeignKey('wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') search_fields = Page.search_fields + [ index.SearchField('body'), ] @property def blog_index(self): # Find closest ancestor which is a blog index return self.get_ancestors().type(BlogIndexPage).last()
def test_render_within_structblock(self, get_embed): """ When rendering the value of an EmbedBlock directly in a template (as happens when accessing it as a child of a StructBlock), the proper embed output should be rendered, not the URL. """ get_embed.return_value = Embed(html='<h1>Hello world!</h1>') block = blocks.StructBlock([ ('title', blocks.CharBlock()), ('embed', EmbedBlock()), ]) block_val = block.to_python({'title': 'A test', 'embed': 'http://www.example.com/foo'}) temp = template.Template('embed: {{ self.embed }}') context = template.Context({'self': block_val}) result = temp.render(context) self.assertIn('<h1>Hello world!</h1>', result) # Check that get_embed was called correctly get_embed.assert_any_call('http://www.example.com/foo')
class GeneralPage(Page): body = StreamField([ ('heading', blocks.CharBlock(classname="full title")), ('paragraph', blocks.RichTextBlock()), ('image', ImageChooserBlock()), ('html', RawHTMLBlock()), ]) api_fields = ( 'title', 'body', ) content_panels = [ FieldPanel('title'), StreamFieldPanel('body'), ] promote_panels = [ FieldPanel('slug'), FieldPanel('seo_title'), FieldPanel('search_description'), ]
class Notification(blocks.StructBlock): type = blocks.ChoiceBlock(choices=[ ('default', 'Default'), ('success', 'Success'), ('warning', 'Warning'), ('error', 'Error'), ], required=True, default='default') message = blocks.CharBlock( required=True, help_text='The main notification message to display.') explanation = blocks.TextBlock( required=False, help_text='Explanation text appears below the message in smaller type.' ) links = blocks.ListBlock( atoms.Hyperlink(required=False), required=False, help_text='Links appear on their own lines below the explanation.') class Meta: icon = 'warning' template = '_includes/molecules/notification.html'
class XBlockVideoBlock(blocks.StructBlock): """XBlockVideoBlock component""" video = VideoChooserBlock(required=True) title = blocks.CharBlock(required=False, help_text='Override video title') def get_title(self, value): return value.get('title') def get_video(self, value): return value.get(STREAM_DATA_VIDEO_FIELD) def get_searchable_content(self, value): return [self.get_title(value)] def get_api_representation(self, value, context=None): block_title = self.get_title(value) video = self.get_video(value) if video: return { 'video_id': video.id, 'title': block_title if block_title else video.display_name, 'view_url': video.view_access_url, 'transcript_url': video.transcript_url, 'span_id': get_span_id(VIDEO_BLOCK_TYPE, video.id), } log.warning( 'Missing Video: video has been deleted but still referenced in page' ) return { 'video_id': 0, 'title': block_title if block_title else "Missing Video", 'view_url': '', 'transcript_url': '', 'span_id': 'missing-video', }
class BlogPage(Page): main_image = models.ForeignKey(ExtendedImage, null=True, blank=True, on_delete=models.SET_NULL, related_name='+') date = models.DateField('Post Date') intro = models.CharField(max_length=250) body = StreamField([ ('heading', blocks.CharBlock(classname='full title')), ('paragraph', blocks.RichTextBlock()), ('image', ImageChooserBlock()), ('html', RawHTMLBlock()), ('embed', EmbedBlock()), ]) search_fields = Page.search_fields + ( index.SearchField('intro'), index.SearchField('body'), ) def get_absolute_url(self): return self.full_url @property def blog_index(self): return self.get_ancestors().type(BlogIndexPage).last() content_panels = Page.content_panels + [ FieldPanel('date'), ImageChooserPanel('main_image'), FieldPanel('intro'), StreamFieldPanel('body'), InlinePanel('related_links', label='Related Links') ]
class NavPage(Page): # Database fields nav_title = RichTextField(default='') body = StreamField([('heading', blocks.CharBlock(classname="full title")), ('paragraph', blocks.RawHTMLBlock())]) date = models.DateField("Post date") feed_image = models.ForeignKey('wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') # Search index configuraiton search_fields = Page.search_fields + ( index.SearchField('body'), index.FilterField('date'), ) # Editor panels configuration content_panels = Page.content_panels + [ FieldPanel('nav_title'), FieldPanel('date'), StreamFieldPanel('body'), ] promote_panels = [ MultiFieldPanel(Page.promote_panels, "Common page configuration"), ImageChooserPanel('feed_image'), ] # Parent page / subpage type rules parent_page_types = ['SectionPage']
class MapBlock(blocks.StructBlock): marker_title = blocks.CharBlock( max_length=120, default="Marker Title 'This will be updated after you save changes.'") marker_description = blocks.RichTextBlock() zoom_level = blocks.IntegerBlock(min_value=0, max_value=18, default='2', required=False) location_x = blocks.FloatBlock(default='35.0', required=False) location_y = blocks.FloatBlock(default='0.16', required=False) marker_x = blocks.FloatBlock(default='51.5', required=False) marker_y = blocks.FloatBlock(default='-0.09', required=False) @property def media(self): return forms.Media( js=["https://unpkg.com/[email protected]/dist/leaflet.js"], css={'all': ["https://unpkg.com/[email protected]/dist/leaflet.css"]}) class Meta: form_template = 'wagtail_blocks/admin_blocks/map.html' template = 'wagtail_blocks/map.html' icon = "fa-globe"
class DropShadowBlock(blocks.StructBlock): drop_shadow_is_on = blocks.BooleanBlock( label="Drop Shadow Toggle", help_text="Show or hide drop shadow", required=False) text_hex = blocks.CharBlock(label="Text Hex Code", max_length=7, required=False) def clean(self, value): value = super().clean(value) hex_fields = ['text_hex'] errors = { field: ['Please enter a valid hex code'] for field in hex_fields if not validate_hex(value[field]) } if errors: raise ValidationError( "Validation error in DropShadowBlock", params=errors, ) return value
class PDFBlock(blocks.StructBlock): """PDFBlock component""" doc = DocumentChooserBlock() title = blocks.CharBlock(required=False, help_text='Override document title') def get_title(self, value): return value.get('title') def get_doc(self, value): return value.get(STREAM_DATA_DOC_FIELD) def get_searchable_content(self, value): return [self.get_title(value)] def get_api_representation(self, value, context=None): block_title = self.get_title(value) document = self.get_doc(value) if document: return { 'doc_id': document.id, 'title': block_title if block_title else document.title, 'url': document.file.url, 'span_id': get_span_id(PDF_BLOCK_TYPE, document.id), } log.warning( 'Missing Document: document has been deleted but still referenced in page' ) return { 'doc_id': 0, 'title': block_title if block_title else "Missing Document", 'url': '', 'span_id': 'missing-document', }
class TemplatePage(Page): """ More of a traditional template page type with only one section with stream fileds. the top and bottom rtf blocks are required """ top = RichTextField(blank=True) middle = StreamField([ ('heading', blocks.CharBlock(classname="full title")), ('paragraph', blocks.RichTextBlock()), ('image', ImageChooserBlock()), ('halves', Halves(classname="full title")), ('person', PersonBlock()), ('list', ListItemBlock()), ('thirds', Thirds()) ]) bottom = RichTextField(blank=True) content_panels = Page.content_panels + [ FieldPanel('top'), StreamFieldPanel('middle'), FieldPanel('bottom'), ]