def test_initialise_with_instance(self): child_block = blocks.CharBlock() block = blocks.ListBlock(child_block) self.assertEqual(block.child_block, child_block)
class KeynotesBlock(blocks.StructBlock): class Meta: template = "cms_pages/home_page_blocks/keynotes.html" heading = blocks.CharBlock(required=True) speakers = blocks.ListBlock(KeynoteSpeakerBlock)
def test_initialise_with_class(self): block = blocks.ListBlock(blocks.CharBlock) # Child block should be initialised for us self.assertIsInstance(block.child_block, blocks.CharBlock)
class ProductBlock(blocks.StructBlock): title = blocks.CharBlock() subtitle = blocks.CharBlock() products = blocks.ListBlock(ProductChooserBlock)
class InfoUnitGroup(blocks.StructBlock): format = blocks.ChoiceBlock( choices=[ ('50-50', '50/50'), ('33-33-33', '33/33/33'), ('25-75', '25/75'), ], default='50-50', label='Format', help_text='Choose the number and width of info unit columns.', ) heading = v1_blocks.HeadingBlock(required=False) intro = blocks.RichTextBlock(required=False, help_text='If this field is not empty, ' 'the Heading field must also be set.') link_image_and_heading = blocks.BooleanBlock( default=True, required=False, help_text=( 'Check this to link all images and headings to the URL of ' 'the first link in their unit\'s list, if there is a link.')) has_top_rule_line = blocks.BooleanBlock( default=False, required=False, help_text=('Check this to add a horizontal rule line to top of ' 'info unit group.')) lines_between_items = blocks.BooleanBlock( default=False, required=False, label='Show rule lines between items', help_text=('Check this to show horizontal rule lines between info ' 'units.')) info_units = blocks.ListBlock(molecules.InfoUnit()) sharing = blocks.StructBlock([ ('shareable', blocks.BooleanBlock(label='Include sharing links?', help_text='If checked, share links ' 'will be included below ' 'the items.', required=False)), ('share_blurb', blocks.CharBlock(help_text='Sets the tweet text, ' 'email subject line, and ' 'LinkedIn post text.', required=False)), ]) def clean(self, value): cleaned = super(InfoUnitGroup, self).clean(value) # Intro paragraph may only be specified with a heading. if cleaned.get('intro') and not cleaned.get('heading'): raise ValidationError( 'Validation error in InfoUnitGroup: intro with no heading', params={ 'heading': ErrorList([ 'Required if paragraph is not empty. (If it looks empty, ' 'click into it and hit the delete key a bunch of times.)' ]) }) # If 25/75, info units must have images. if cleaned.get('format') == '25-75': for unit in cleaned.get('info_units'): if not unit['image']['upload']: raise ValidationError( ('Validation error in InfoUnitGroup: ' '25-75 with no image'), params={ 'format': ErrorList([ 'Info units must include images when using the ' '25/75 format. Search for an "FPO" image if you ' 'need a temporary placeholder.' ]) }) return cleaned class Meta: icon = 'list-ul' template = '_includes/organisms/info-unit-group-2.html'
class ArticlePage(CFGOVPage): """ General article page type. """ category = models.CharField( choices=[ ('basics', 'Basics'), ('common_issues', 'Common issues'), ('howto', 'How to'), ('know_your_rights', 'Know your rights'), ], max_length=255, ) heading = models.CharField( max_length=255, blank=False, ) intro = models.TextField(blank=False) inset_heading = models.CharField(max_length=255, blank=True, verbose_name="Heading") sections = StreamField([ ('section', blocks.StructBlock([ ('heading', blocks.CharBlock(max_length=255, required=True, label='Section heading')), ('summary', blocks.TextBlock(required=False, blank=True, label='Section summary')), ('link_text', blocks.CharBlock(required=False, blank=True, label="Section link text")), ('url', blocks.CharBlock( required=False, blank=True, label='Section link URL', max_length=255, )), ('subsections', blocks.ListBlock( blocks.StructBlock([ ('heading', blocks.CharBlock(max_length=255, required=False, blank=True, label='Subsection heading')), ('summary', blocks.TextBlock(required=False, blank=True, label='Subsection summary')), ('link_text', blocks.CharBlock(required=True, label='Subsection link text')), ('url', blocks.CharBlock(required=True, label='Subsection link URL')) ]))) ])) ]) content_panels = CFGOVPage.content_panels + [ MultiFieldPanel([ FieldPanel('category'), FieldPanel('heading'), FieldPanel('intro') ], heading="Heading", classname="collapsible"), MultiFieldPanel([ FieldPanel('inset_heading'), InlinePanel('article_links', label='Inset link', max_num=2), ], heading="Inset links", classname="collapsible"), StreamFieldPanel('sections'), ] sidebar = StreamField([ ('call_to_action', molecules.CallToAction()), ('related_links', molecules.RelatedLinks()), ('related_metadata', molecules.RelatedMetadata()), ('email_signup', organisms.EmailSignUp()), ('reusable_text', v1_blocks.ReusableTextChooserBlock(ReusableText)), ], blank=True) sidebar_panels = [ StreamFieldPanel('sidebar'), ] search_fields = Page.search_fields + [ index.SearchField('title'), ] edit_handler = TabbedInterface([ ObjectList(content_panels, heading='Content'), ObjectList(sidebar_panels, heading='Sidebar'), ObjectList(CFGOVPage.settings_panels, heading='Configuration'), ]) template = 'ask-cfpb/article-page.html' objects = CFGOVPageManager() def get_context(self, request, *args, **kwargs): context = super(ArticlePage, self).get_context(request) context['about_us'] = get_standard_text(self.language, 'about_us') return context def __str__(self): return self.title
class ResourcePage(Page): # Class for pages that include a side nav, multiple sections and citations date = models.DateField(default=datetime.date.today) intro = StreamField([ ('paragraph', blocks.RichTextBlock()) ], null=True) sidebar_title = models.CharField(max_length=255, null=True, blank=True) related_pages = StreamField([ ('related_pages', blocks.ListBlock(blocks.PageChooserBlock())) ], null=True, blank=True) sections = StreamField([ ('sections', ResourceBlock()) ], null=True) citations = StreamField([ ('citations', blocks.ListBlock(CitationsBlock())) ], null=True) related_topics = StreamField([ ('related_topics', blocks.ListBlock( blocks.PageChooserBlock(label="Related topic") )) ], null=True) category = models.CharField(max_length=255, choices=constants.report_child_categories.items(), help_text='If this is a report, add a category', blank=True, null=True) breadcrumb_style = models.CharField(max_length=255, choices=[('primary', 'Blue'), ('secondary', 'Red')], default='primary') show_contact_card = models.BooleanField( max_length=255, default=False, null=False, blank=False, choices=[ (True, 'Show contact card'), (False, 'Do not show contact card') ]) content_panels = Page.content_panels + [ StreamFieldPanel('intro'), FieldPanel('sidebar_title'), StreamFieldPanel('related_pages'), StreamFieldPanel('sections'), StreamFieldPanel('citations'), StreamFieldPanel('related_topics'), FieldPanel('show_contact_card') ] promote_panels = Page.promote_panels + [ FieldPanel('breadcrumb_style'), FieldPanel('category'), FieldPanel('date') ] # Adds a settings field for making a custom title that displays in the Wagtail page explorer menu_title = models.CharField(max_length=255, null=True) settings_panels = Page.settings_panels + [ FieldPanel('menu_title') ] def get_admin_display_title(self): return self.menu_title if self.menu_title else self.title @property def display_date(self): return self.date.strftime('%B %Y') @property def content_section(self): return get_content_section(self)
class HomePageStreamBlock(blocks.StreamBlock): carousel = blocks.ListBlock(ImageWithCaptionBlock(), template='home/blocks/carousel.html', icon='image')
class LinkBlobGroup(blocks.StructBlock): heading = blocks.CharBlock(icon='title', required=False) has_top_border = blocks.BooleanBlock(required=False) has_bottom_border = blocks.BooleanBlock(required=False) link_blobs = blocks.ListBlock(molecules.HalfWidthLinkBlob())
class ProductBlock(blocks.StructBlock): products = blocks.ListBlock(ProductChooserBlock)
class BadgetBlock(blocks.StructBlock): content = blocks.ListBlock(SingleBadgetBlock(), label='Items') class Meta: template = 'wagtail_blocks/badget.html' icon = "list-ul"
label = 'Hoogte', help_text = 'Hoogte van het blok in pixels.', min_value = 0, max_value = 999, default = 250, ) grid_array = \ [('tabellen', TableStructBlock( label='Tabellen', template = 'streamfields/table.html', icon='fa-table')) ,('citaten', blocks.ListBlock( QuoteBlock(), template = 'streamfields/quotes.html', icon="openquote",)) ,('koppen', blocks.ListBlock( HeaderBlock(), template = 'streamfields/header.html', icon="title",)) ,('tekst_velden', blocks.ListBlock( TextFieldBlock(), template = 'streamfields/text_field.html', icon="fa-align-justify",)) ,('lijst', blocks.ListBlock( UnorderedListBlock(), template = 'streamfields/list.html', icon="list-ul")) ,('accordions', blocks.ListBlock( AccordionBlock(),
class RoadmapSection(blocks.StructBlock): title = blocks.CharBlock() pages = blocks.ListBlock(blocks.PageChooserBlock()) image = ImageChooserBlock(required=False)
class MultiPage(Page): """ Multipurpose page model, allowing pretty much all types to be combined onto one page From Page: title - Char(255) slug - Slug/Char(255) content_type - FK(content_type) live - Boolean has_unpublished_changes - Boolean url_path - Text owner - FK(auth_user) seo_title - Char(255) show_in_menus - Boolean search_description - Text go_live_at - DateTime expire_at - DateTime expired - Boolean locked - Boolean first_published_at - DateTime latest_revision_created_at - DateTime """ # Database fields subtitle = models.CharField(max_length=255, null=True, blank=True) author = models.CharField(max_length=255) # Because the author may not be the person entering it on the website body = StreamField([ ('heading', blocks.CharBlock(classname="full title")), ('text', blocks.RichTextBlock()), ('image', ImageChooserBlock()), ('table', TableBlock(template="website/blocks/table.html")), ('publications_list', blocks.ListBlock(SnippetChooserBlock(Publication, label="publication"), template="website/blocks/publications_list.html")), ('projects_list', blocks.ListBlock(SnippetChooserBlock(Project, label="project"))), ('qa_list', blocks.ListBlock(QandABlock(label="entry"), template="website/blocks/qa_list.html")), ]) date = models.DateField("Post date") feed_image = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) # Search index configuration search_fields = Page.search_fields + [ index.SearchField('body'), index.SearchField('author'), index.FilterField('date'), ] # Editor panels configuration content_panels = Page.content_panels + [ FieldPanel('subtitle'), FieldPanel('author'), FieldPanel('date'), StreamFieldPanel('body'), InlinePanel('related_links', label="Related links"), ] promote_panels = [ MultiFieldPanel(Page.promote_panels, "Common page configuration"), ImageChooserPanel('feed_image'), ] # Parent page / subpage type rules parent_page_types = ['HomePage', 'MultiPage']
def test_media_inheritance(self): class ScriptedCharBlock(blocks.CharBlock): media = forms.Media(js=['scripted_char_block.js']) block = blocks.ListBlock(ScriptedCharBlock()) self.assertIn('scripted_char_block.js', ''.join(block.all_media().render_js()))
class ContentStreamField(blocks.StreamBlock): h2 = blocks.CharBlock(icon="title", classname="title", label='H2', template='content/stream_fields/h2.html') embed = EmbedBlock(label='Эмбед', icon="link") image = ImageBlock(icon="image", label='Фотография', template='content/stream_fields/image.html') wide_image = ImageBlock(icon="image", label='Широкая фотография', template='content/stream_fields/wide_image.html') left_image = ImageNotesBlock( icon="image", label='Фотография слева', template='content/stream_fields/left_image.html') left_text = LeftText(template='content/stream_fields/left_text.html', label='Текст слева') left_text_gray = LeftText( template='content/stream_fields/left_text_gray.html', label='Текст слева на сером фоне') paragraph = blocks.RichTextBlock( editor='tinymce', language='ru', label='Параграф', template='content/stream_fields/paragraph.html') read_also = ReadAlso() gallery = blocks.ListBlock(ImageBlock, label='Галерея', icon="image", template='content/stream_fields/gallery.html') notes = blocks.ListBlock( NoteBlock(), label='Подписи', template='content/stream_fields/notes.html', ) html_field = blocks.TextBlock( label='html врезка', help_text= 'Поле для html, css и js (может использоваться для сложных embed-кодов)', template='content/stream_fields/html_field.html', icon='code') anchor_field = blocks.StaticBlock( admin_text='Якорь (передергивает скрипты аналитики и баннеры)', icon="repeat", label="Якорь", template='content/stream_fields/anchor_field.html', ) box_field = BoxBlock() blockquote = blocks.CharBlock( label='Цитата', template='content/stream_fields/blockquote.html', icon='openquote') poll = PollBlock() giphy_block = GiphyChooserBlock( help_text='Поиск гифок', template='content/stream_fields/giphy.html') big_side_image = BigSideImageBlock() simple_quote_1 = blocks.TextBlock( required=True, label="Простая цитата 1", template="content/stream_fields/simple_quote_1.html", icon="openquote") simple_quote_2 = blocks.TextBlock( required=True, label="Простая цитата 2", template="content/stream_fields/simple_quote_2.html", icon="openquote")
class MyBlock(blocks.StructBlock): title = blocks.CharBlock(max_length=100) item = MyBlockItem() items = blocks.ListBlock(MyBlockItem) image = ImageChooserBlock()
class Track(Page): page_body = RichTextField( blank=True, help_text= "The main content of the page, above the list of steps and form") form_submission_message = models.CharField( max_length=255, help_text= "The text that is shown to the user after they make a form submissiom", default="Based on your choices we suggest looking at the following:") question = models.CharField( max_length=255, blank=True, help_text="The question for the form on the page (optional)") choices = StreamField([ ('label', blocks.CharBlock(required=True)), ], blank=True, null=True) has_strict_rules = models.BooleanField( default=False, help_text= "If the rule definitions are strict it will ONLY display results that match the exact answers (instead of the union of the answers)" ) rules = StreamField( [('rule', blocks.StructBlock([ ('name', ChoiceRulesBlock()), ('pages', blocks.ListBlock(blocks.PageChooserBlock())), ('override', blocks.BooleanBlock(default=False, required=False)) ]))], default=[], blank=True) default_steps = StreamField( [('page', blocks.PageChooserBlock())], blank=True, default=[], help_text= "The steps to show if someone submits a form with answers that are not covered by the rules" ) content_panels = Page.content_panels + [ FieldPanel('page_body', classname='full'), MultiFieldPanel( [ MultiFieldPanel([ FieldPanel('question'), FieldPanel('form_submission_message'), StreamFieldPanel('choices') ]), FieldPanel('has_strict_rules'), StreamFieldPanel('rules'), StreamFieldPanel('default_steps'), ], heading="Options form for the page to help narrow down choices", classname="collapsible") ] template = 'roadmap/track/base.html' def steps(self): # Get list of all step pages that are descendants of this page events = Step.objects.live().descendant_of(self) return events # Directs people to the walk through or self service routes # Walk through path uses the choices model to filter steps to take def route(self, request, path_components): if len(request.GET): return RouteResult(self, kwargs={'template': 'roadmap/track/base.html'}) else: return super(Track, self).route(request, path_components) def serve(self, request, template=''): if template == '': template = self.template #Kind of hacky but intercept request if it has 'submit-choice' in the slug #Serve the rules for the selected choices if len(request.GET): #Get selected checkbox values from params in request selected_choices = list(request.GET.values()) #Sort the choices so we have them in the same order as the admin defined rules selected_choices.sort() pages = [] #list of pages that will be presented to the user default_pages = [ ] #default pages if there isn't a rule defined for the choices the user selected all_selected_choices = ','.join(selected_choices) #loop through each admin defined rule to see if we have a defined rule for the selected choices if self.has_strict_rules: #Find the one rule that matches the selected choices and only suggest those steps for rule in self.rules: if rule.value['override'] and re.search( rule.value['name'], all_selected_choices): pages = rule.value['pages'] break if rule.value['name'] == all_selected_choices: pages = rule.value['pages'] else: #Union all the pages that match with a rule for rule in self.rules: if rule.value['override'] and re.search( rule.value['name'], all_selected_choices): pages = rule.value['pages'] break if rule.value['name'] in selected_choices: for page in rule.value['pages']: if page not in pages: pages.append(page) for page in self.default_steps: #if the user defines default pages in the admin then create a list of pages #otherwise the default default_pages list is all the steps in the track default_pages.append(Page.objects.get(id=page.value.id)) if not pages: if not default_pages: default_pages = Step.objects.live().descendant_of(self) pages = default_pages request.path = '/'.join(request.path.split('/')[:3]) return render( request, template, { 'steps': list(map((lambda page: page.specific), pages)), 'page': self, 'selected_choices': ','.join(map(str, selected_choices)), 'default_pages': default_pages, 'showMessage': True }) #Otherwise just render the track page with the appropriate template return render(request, template, {'page': self, 'steps': self.steps()})
class CustomPage(Page): """Flexible customizable page.""" author = models.CharField(max_length=255) date = models.DateField('Post date') body = StreamField([ ('heading', blocks.CharBlock(classname='full title')), ('paragraph', blocks.RichTextBlock()), ('html', blocks.RawHTMLBlock()), ('image', ImageChooserBlock()), ('table', TableBlock()), ('example_paragraph', ExampleParagraph()), ('example_forms', ExampleForms()), ('reporting_example_cards', ReportingExampleCards()) ]) sidebar = stream_factory(null=True, blank=True) citations = StreamField([('citations', blocks.ListBlock(CitationsBlock()))], null=True) record_articles = StreamField([ ('record_articles', blocks.ListBlock( blocks.PageChooserBlock(target_model=RecordPage) )) ], null=True) continue_learning = StreamField([ ('continue_learning', blocks.ListBlock(ThumbnailBlock(), icon='doc-empty')), ], null=True) show_contact_link = models.BooleanField( max_length=255, default=True, null=False, blank=False, choices=[ (True, 'Show contact link'), (False, 'Do not show contact link') ]) content_panels = Page.content_panels + [ FieldPanel('author'), FieldPanel('date'), StreamFieldPanel('body'), StreamFieldPanel('citations'), StreamFieldPanel('continue_learning'), MultiFieldPanel([ StreamFieldPanel('sidebar'), StreamFieldPanel('record_articles'), FieldPanel('show_contact_link'), ], heading="Sidebar", classname="collapsible" ) ] # Adds a settings field for making a custom title that displays in the Wagtail page explorer menu_title = models.CharField(max_length=255, null=True) settings_panels = Page.settings_panels + [ FieldPanel('menu_title') ] def get_admin_display_title(self): return self.menu_title if self.menu_title else self.title @property def content_section(self): return get_content_section(self)
class BureauStructureOffice(BureauStructurePosition): offices = blocks.ListBlock(BureauStructurePosition(required=False))
class RelatedPosts(blocks.StructBlock): limit = blocks.CharBlock( default='3', help_text=('This limit applies to EACH TYPE of post this module ' 'retrieves, not the total number of retrieved posts.')) show_heading = blocks.BooleanBlock( required=False, default=True, label='Show Heading and Icon?', help_text=('This toggles the heading and ' 'icon for the related types.')) header_title = blocks.CharBlock(default='Further reading', label='Slug Title') relate_posts = blocks.BooleanBlock(required=False, default=True, label='Blog Posts', editable=False) relate_newsroom = blocks.BooleanBlock(required=False, default=True, label='Newsroom', editable=False) relate_events = blocks.BooleanBlock(required=False, default=True, label='Events') specific_categories = blocks.ListBlock(blocks.ChoiceBlock( choices=ref.related_posts_categories, required=False), required=False) and_filtering = blocks.BooleanBlock( required=False, default=False, label='Match all topic tags', help_text=('If checked, related posts will only be pulled in if they ' 'match ALL topic tags set on this page. Otherwise, related ' 'posts can match any one topic tag.')) alternate_view_more_url = blocks.CharBlock( required=False, label='Alternate "View more" URL', help_text=('By default, the "View more" link will go to the Activity ' 'Log, filtered based on the above parameters. Enter a URL ' 'in this field to override that link destination.')) def get_context(self, value, parent_context=None): context = super(RelatedPosts, self).get_context(value, parent_context=parent_context) page = context['page'] request = context['request'] context.update({ 'posts': self.related_posts(page, value), 'view_more_url': (value['alternate_view_more_url'] or self.view_more_url(page, request)), }) return context @staticmethod def related_posts(page, value): from v1.models.learn_page import AbstractFilterPage def tag_set(related_page): return set([tag.pk for tag in related_page.tags.all()]) def match_all_topic_tags(queryset, page_tags): """Return pages that share every one of the current page's tags.""" current_tag_set = set([tag.pk for tag in page_tags]) return [ page for page in queryset if current_tag_set.issubset(tag_set(page)) ] related_types = [] related_items = {} if value.get('relate_posts'): related_types.append('blog') if value.get('relate_newsroom'): related_types.append('newsroom') if value.get('relate_events'): related_types.append('events') if not related_types: return related_items tags = page.tags.all() and_filtering = value['and_filtering'] specific_categories = value['specific_categories'] limit = int(value['limit']) queryset = AbstractFilterPage.objects.live().exclude( id=page.id).order_by('-date_published').distinct().specific() for parent in related_types: # blog, newsroom or events # Include children of this slug that match at least 1 tag children = Page.objects.child_of_q(Page.objects.get(slug=parent)) filters = children & Q(('tags__in', tags)) if parent == 'events': # Include archived events matches archive = Page.objects.get(slug='archive-past-events') children = Page.objects.child_of_q(archive) filters |= children & Q(('tags__in', tags)) if specific_categories: # Filter by any additional categories specified categories = ref.get_appropriate_categories( specific_categories=specific_categories, page_type=parent) if categories: filters &= Q(('categories__name__in', categories)) related_queryset = queryset.filter(filters) if and_filtering: # By default, we need to match at least one tag # If specified in the admin, change this to match ALL tags related_queryset = match_all_topic_tags(related_queryset, tags) related_items[parent.title()] = related_queryset[:limit] # Return items in the dictionary that have non-empty querysets return {key: value for key, value in related_items.items() if value} @staticmethod def view_more_url(page, request): """Generate a URL to see more pages like this one. This method generates a link to the Activity Log page (which must exist and must have a unique site-wide slug of "activity-log") with filters set by the tags assigned to this page, like this: /activity-log/?topics=foo&topics=bar&topics=baz If for some reason a page with slug "activity-log" does not exist, this method will raise Page.DoesNotExist. """ activity_log = Page.objects.get(slug='activity-log') url = activity_log.get_url(request) tags = urlencode([('topics', tag) for tag in page.tags.slugs()]) if tags: url += '?' + tags return url class Meta: icon = 'link' template = '_includes/molecules/related-posts.html'
class AddressBlock(blocks.StructBlock): lines = blocks.ListBlock(blocks.CharBlock(label="Line", required=False)) class Meta: template = 'wagtail_extensions/blocks/address.html'