def test_custom_editor_in_rich_text_block(self): block = RichTextBlock(editor='custom') form_html = block.render_form(block.to_python("<p>hello</p>"), 'body') # Check that the custom plugin options are being passed in the hallo initialiser self.assertIn('makeHalloRichTextEditable("body", {"halloheadings": {"formatBlocks": ["p", "h2"]}});', form_html)
def test_features_option_on_rich_text_block(self): # a 'features' list passed on the RichTextBlock # should override the list in OPTIONS block = RichTextBlock(features=['h2', 'embed']) form_html = block.render_form(block.to_python("<p>hello</p>"), 'body') self.assertIn('"type": "header-two"', form_html) self.assertIn('"type": "EMBED"', form_html) self.assertNotIn('"type": "IMAGE""', form_html) self.assertNotIn('"type": "ordered-list-item""', form_html)
def test_features_list_on_rich_text_block(self): block = RichTextBlock(features=['blockquote', 'embed', 'made-up-feature']) form_html = block.render_form(block.to_python("<p>hello</p>"), 'body') # Check that the custom plugin options are being passed in the hallo initialiser self.assertIn('"halloblockquote":', form_html) self.assertIn('"hallowagtailembeds":', form_html) self.assertNotIn('"hallolists":', form_html) self.assertNotIn('"hallowagtailimage":', form_html) # check that media (js/css) from the features is being imported media_html = str(block.media) self.assertIn('testapp/js/hallo-blockquote.js', media_html) self.assertIn('testapp/css/hallo-blockquote.css', media_html) # check that we're NOT importing media for the default features we're not using self.assertNotIn('wagtaildocs/js/hallo-plugins/hallo-wagtaildoclink.js', media_html)
def test_custom_features_option_on_rich_text_block(self): block = RichTextBlock(editor='custom') form_html = block.render_form(block.to_python("<p>hello</p>"), 'body') # Check that the custom plugin options are being passed in the hallo initialiser self.assertIn('"halloquotation":', form_html) self.assertIn('"hallowagtailimage":', form_html) self.assertNotIn('"hallowagtailembeds":', form_html) self.assertNotIn('"hallolists":', form_html) # a 'features' list passed on the RichTextBlock # should override the list in OPTIONS block = RichTextBlock(editor='custom', features=['quotation', 'embed']) form_html = block.render_form(block.to_python("<p>hello</p>"), 'body') self.assertIn('"halloquotation":', form_html) self.assertIn('"hallowagtailembeds":', form_html) self.assertNotIn('"hallowagtailimage":', form_html) self.assertNotIn('"hallolists":', form_html) # check that media (js/css) from the features is being imported media_html = str(block.media) self.assertIn('testapp/js/hallo-quotation.js', media_html) self.assertIn('testapp/css/hallo-quotation.css', media_html) # check that we're NOT importing media for the default features we're not using self.assertNotIn('wagtaildocs/js/hallo-plugins/hallo-wagtaildoclink.js', media_html)
class StreamPage(Page): body = StreamField([ ('text', CharBlock()), ('rich_text', RichTextBlock()), ('image', ExtendedImageChooserBlock()), ('product', StructBlock([ ('name', CharBlock()), ('price', CharBlock()), ])), ('raw_html', RawHTMLBlock()), ]) api_fields = ('body', ) content_panels = [ FieldPanel('title'), StreamFieldPanel('body'), ] preview_modes = []
class FormFieldsBlock(StreamBlock): text_markup = RichTextBlock(group=_('Custom'), label=_('Section text/header')) char = CharFieldBlock(group=_('Fields')) text = TextFieldBlock(group=_('Fields')) number = NumberFieldBlock(group=_('Fields')) checkbox = CheckboxFieldBlock(group=_('Fields')) radios = RadioButtonsFieldBlock(group=_('Fields')) dropdown = DropdownFieldBlock(group=_('Fields')) checkboxes = CheckboxesFieldBlock(group=_('Fields')) date = DateFieldBlock(group=_('Fields')) time = TimeFieldBlock(group=_('Fields')) datetime = DateTimeFieldBlock(group=_('Fields')) image = ImageFieldBlock(group=_('Fields')) file = FileFieldBlock(group=_('Fields')) multi_file = MultiFileFieldBlock(group=_('Fields')) group_toggle = GroupToggleBlock(group=_("Custom")) group_toggle_end = GroupToggleEndBlock(group=_("Custom")) class Meta: label = _('Form fields')
class BodyStreamBlock(StreamBlock): h3 = CharBlock(icon="title", classname="title", label="h3", template="website/blocks/h3.html") h4 = CharBlock(icon="title", classname="title", label="h4", template="website/blocks/h4.html") paragraph = RichTextBlock( icon="pilcrow", features=['bold', 'italic', 'ol', 'ul', 'link', 'document-link']) table = TableBlock() dl = ListBlock(DefinitionListItem, label='Definition list', icon="list-ul", template="website/blocks/dl.html") aligned_image = ImageBlock(label="Aligned image", icon="image") document = DocumentChooserBlock(icon="doc-full-inverse") video = LocalVideoBlock(icon="fa-video-camera") math = MathMLBlock(icon="fa-calculator")
class CommonStreamBlock(StreamBlock): # Simple Blocks title = CharBlock(classname="full title", blank=True, max_length=200, icon='title', template='streams/title_block.html') videoembed = EmbedBlock("Video embed (YouTube and Facebook)", label='Enter Video URL', max_length=500, icon='media', null=True, blank=True, template='streams/video_embed_block.html') image = ImageChooserBlock("Choose an image ...", label='Choose an image ...', icon='image', template='streams/image_block.html') googlemap = CharBlock("Google Calendar URL", label='Enter Google Map URL', icon='site', max_length=500, null=True, blank=True, template='streams/google_map_block.html') googlecal = CharBlock("Google Calendar URL", label='Enter Google Calendar URL', max_length=500, icon='date', null=True, blank=True, template='streams/google_cal_block.html') # Complex Blocks code = ContentStreamBlock() hero = HeroBlock() richtext = RichTextBlock() card = CardBlock() cards = CardGroupBlock() carousel = CarouselBlock() button = ButtonBlock() buttongroup = ButtonGroupBlock() testimonial = TestimonialBlock() class Meta: icon = 'cogs' null = True blank = True
class WarningCalloutBlock(FlattenValueContext, StructBlock): title = CharBlock(required=True, default='Important') visually_hidden_prefix = BooleanBlock( required=False, label='Visually hidden prefix', help_text= 'If the title doesn\'t contain the word \"Important\" select this to add a visually hidden \"Important\", to aid screen readers.' ) heading_level = IntegerBlock( required=True, min_value=2, max_value=6, default=3, help_text= 'The heading level affects users with screen readers. Default=3, Min=2, Max=6.' ) body = RichTextBlock(required=True) class Meta: icon = 'warning' template = 'wagtailnhsukfrontend/warning_callout.html'
class InformationPage(JanisBasePage): janis_url_page_type = "information" description = models.TextField(blank=True, verbose_name='Write a description of this page') options = StreamField( [ ('option', RichTextBlock( features=WYSIWYG_GENERAL, label='Option' )) ], verbose_name='Add option sections as needed.', help_text='Options are needed when the reader needs to make a choice between a few options, such as ways to fill out a form (online, by phone, in person, etc.).', blank=True ) additional_content = RichTextField( features=WYSIWYG_GENERAL, verbose_name='Write any additional content describing the service', blank=True ) # TODO: Add images array field base_form_class = InformationPageForm content_panels = [ FieldPanel('title_en', widget=countMe), FieldPanel('title_es', widget=countMe), FieldPanel('title_ar'), FieldPanel('title_vi'), InlinePanel('topics', label='Topics'), InlinePanel('related_departments', label='Related Departments'), FieldPanel('description', widget=countMeTextArea), # hidden for now, see: https://austininnovation.slack.com/archives/C8T4YD23T/p1570659780017500?thread_ts=1570659723.017100&cid=C8T4YD23T # StreamFieldPanel('options'), FieldPanel('additional_content'), InlinePanel('contacts', label='Contacts'), ]
class Table(StructBlock): class Meta: help_text = 'Displays tabular data with an optional heading.' icon = 'list-ol' label = 'Table' form_template = 'publications/block_forms/custom_struct.html' template = 'publications/blocks/table.html' heading = CharBlock(required=False) table = TableBlock() caption = RichTextBlock( required=False, features=FOOTNOTE_RICHTEXT_FEATURES, help_text='Optional: caption text to appear below the table') caption_link = URLBlock( required=False, help_text='Optional: external link to appear below the table') caption_label = CharBlock( required=False, help_text= 'Optional: label for the caption link, defaults to the link if left blank' )
class EscuelaVarillajeUbicacionLightBlock(StructBlock): nombre = CharBlock(classname="text-white mt-0", required=False) clase_css = ChoiceBlock(choices=[ ('bg-white', 'bg-white'), ('bg-transparent', 'bg-transparent'), ('bg-light', 'bg-light'), ]) titulo = CharBlock(classname="text-white mt-0", required=False) cuerpo = RichTextBlock(required=False) telefono = blocks.CharBlock(max_length=22) calle = blocks.CharBlock(max_length=255) colonia = blocks.CharBlock(max_length=255) ciudad = blocks.CharBlock(max_length=255) estado = blocks.CharBlock(max_length=255) facebook_url = blocks.URLBlock() youtube_url = blocks.URLBlock() instagram_url = blocks.URLBlock() email = blocks.EmailBlock() class Meta: template = 'escueladevarillaje/blocks/escuela_varillaje_ubicacion_light_block.html' icon = "fa-globe"
class ArticlePage(HeadlessPreviewMixin, Page): content = StreamField( [('section', SectionBlock()), ('content', RichTextBlock(default='Add your content here!')), ('media', EmbedBlock()), ('file', DocumentChooserBlock()), ('link', PageChooserBlock()), ('faqs', FAQListBlock(label='FAQs'))], blank=True) # body = RichTextField(blank=True) legacyArticleId = models.IntegerField(blank=True, null=True) search_fields = Page.search_fields + [ index.SearchField('content'), index.SearchField('legacyArticleId') ] content_panels = Page.content_panels + [StreamFieldPanel('content')] parent_page_types = ['ArticleIndexPage'] # Specifies what content types can exist as children of ArticlePage. # Empty list means that no child content types are allowed. subpage_types = [] api_fields = [APIField('content'), APIField('legacyArticleId')]
class AccordionBlock(StructBlock): compact = BooleanBlock( required=False, help_text='Display a compact accordion for use between paragraphs.') panels = ListBlock( StructBlock([ ('title', TextBlock( help_text= 'The headline to display when the accordion panel is closed.') ), ('body', RichTextBlock( help_text= 'The inner content of this accordion panel. It is initially hidden.' )) ], label='Panel')) class Meta: icon = 'emoji-scroll' template = 'blocks/accordion.html' help_text = 'Accordions are elements that help you organize and navigate multiple documents in a single container. They can be used for switching between items in the container.'
def test_that_new_article_page_should_has_all_mandatory_parameters( self, parameter_name, parameter_value): author = BossFactory() body_block = StreamBlock([(ArticleBodyBlockNames.PARAGRAPH.value, RichTextBlock())]) body = StreamValue(body_block, [ (ArticleBodyBlockNames.PARAGRAPH.value, RichText("Hello, World")) ]) test_title = "Simple Article Title" blog_article_parameter = { "title": test_title, "page_title": test_title, "date": datetime.now(), "body": body, "author": author, "read_time": 7, "views": 0, parameter_name: parameter_value, } with self.assertRaises(ValidationError): blog_article_page = BlogArticlePage(**blog_article_parameter) self.blog_index_page.add_child(instance=blog_article_page) blog_article_page.save()
class TypesetStreamBlock(StreamBlock): """ The custom blocks that can be used under an element with the typeset class (not sections) """ anchor = AnchorBlock() paragraph_block = RichTextBlock( icon='fa-paragraph', template='blocks/paragraph_block.html', features=RICHTEXT_FEATURES_NO_FOOTNOTES ) block_quote = BlockQuote() button_block = ButtonBlock() link_block = LinkBlock() image = TypeSetImageBlock() video = EmbedBlock( help_text='Insert an embed URL e.g https://www.youtube.com/embed/SGJFWirQ3ks', icon='fa-video-camera', template='blocks/embed_block.html', required=False ) cta = CallToActionBlock() required = False
class ContentStreamBlock(StreamBlock): paragraph = RichTextBlock(icon='fa-paragraph') bigger_heading = TextBlock(icon='fa-header', template='blocks/h2.html', label='H2') heading = TextBlock(icon='fa-header', template='blocks/h3.html', label='H3') smaller_heading = TextBlock(icon='fa-header', template='blocks/h4.html', label='H4') smallest_heading = TextBlock(icon='fa-header', template='blocks/h5.html', label='H5') image = CaptionedImageBlock() download = DocumentChooserBlock(icon='fa-download', template='blocks/download.html') accordion = AccordionBlock() notice = NoticeBlock() embed = EmbedBlock(icon="media") button = CallToActionButtonBlock() simple_flow_boxes = SimpleFlowBlockList() detailed_flow_boxes = DetailedFlowBlockList() periodic_boxes = PeriodicBlockList() highlight_block = HightlightBlock() slider_block = SimpleSliderBlock() hr_block = StructBlock(icon='fa-window-minimize', template='blocks/hr.html', label='Divider') cards = ListBlock(CardBlock(), icon='fa-clone', template='blocks/cards.html') graphic_link_grid_grid = GraphicLinkGridBlock() featured_content_block = FeaturedContentBlock() class Meta: template = 'blocks/streamfield.html'
class BootstrapWell(StructBlock): """ Custom 'StructBlock' that allows user to make a bootstrap well. (optimized for bootstrap 4 using card) """ message = RichTextBlock(help_text="Enter some message inside well") well_bg_color = ChoiceBlock(choices=[ ('bg-primary', 'DEFAULT'), ('bg-secondary', 'GREY'), ('bg-success', 'GREEN'), ('bg-danger', 'RED'), ('bg-warning', 'ORANGE'), ('bg-dark', 'DARK'), ('bg-light', 'LIGHT'), ], blank=True, required=False, help_text="select a background color") custom_class = TextBlock( required=False, blank=True, help_text="control this element by giving unique class names " "separated by space and styling the class in css") class Meta: icon = "fa-window-minimize" template = "blocks/bootstrap/well.html"
class CaptionedImage(StructBlock): class Meta: help_text = 'Displays an image with an optionally linked caption.' icon = 'image' label = 'Captioned image' form_template = 'publications/block_forms/custom_struct.html' template = 'publications/blocks/captioned_image.html' image = ImageChooserBlock(help_text='Optimal minimum width 800px') descriptive_text = RichTextBlock( required=False, features=FOOTNOTE_RICHTEXT_FEATURES, help_text='Optional: descriptive text to appear above the image') caption = TextBlock( required=False, help_text='Optional: caption text to appear below the image') caption_link = URLBlock( required=False, help_text='Optional: external link to appear below the image') caption_label = CharBlock( required=False, help_text= 'Optional: label for the caption link, defaults to the link if left blank' )
class ImprovisationTypePage(Page, MenuPageMixin): body = StreamField([ ('rich_text', RichTextBlock(features=[ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'bold', 'italic', 'ol', 'hr', 'link', 'document-link', 'image', 'embed' ])), ('image', ImageBlock()), ('video', VideoEmbedBlock(required=False)) ]) def get_context(self, request, *args, **kwargs): ctx = super().get_context(request, *args, **kwargs) ctx['improv_type_pages'] = pack_nav_pages( [p.specific for p in self.get_siblings()], self) return ctx class Meta: verbose_name = "Improvisation Type Page" content_panels = Page.content_panels + [StreamFieldPanel('body')] settings_panels = Page.settings_panels + [menupage_panel] parent_page_types = ['main.ImprovisationTypeIndexPage']
class ProjectPage( ArchiveablePageAbstract, BasicPageAbstract, ContentPage, FeatureablePageAbstract, ShareablePageAbstract, ): body = StreamField( BasicPageAbstract.body_default_blocks + BasicPageAbstract.body_poster_block + [ ('igc_timeline', StructBlock([ ('date', CharBlock(required=True)), ('title', CharBlock(required=False)), ('body', RichTextBlock(required=False)), ('location', CharBlock(required=False)), ('countries_represented', ImageChooserBlock(required=False)), ('outcomes', StreamBlock( [ ('outcome', StructBlock([ ('date', DateBlock(required=False)), ('text', RichTextBlock(required=False)), ])), ], required=False, )), ('witnesses', StreamBlock( [ ('witness_date', StructBlock([ ('date', DateBlock(required=False)), ('witnesses', StreamBlock([ ('witnesses_full_session', StructBlock([ ('title', CharBlock(required=False)), ('witness_transcript', URLBlock(required=False)), ('witness_video', URLBlock(required=False)), ])), ('witness', StructBlock([ ('name', CharBlock(required=False)), ('title', CharBlock(required=False)), ('witness_transcript', URLBlock(required=False)), ('witness_video', URLBlock(required=False)), ])), ], )), ])), ], required=False, )), ])), ], blank=True, ) project_contacts = StreamField( [ ('contact', PageChooserBlock(required=True, page_type='people.PersonPage')), ], blank=True, ) project_leads = StreamField( [ ('project_lead', PageChooserBlock(required=True, page_type='people.PersonPage')), ('external_project_lead', CharBlock(required=True)), ], blank=True, ) project_members = StreamField( [ ('project_member', PageChooserBlock(required=True, page_type='people.PersonPage')), ('external_project_member', CharBlock(required=True)), ], blank=True, ) project_types = ParentalManyToManyField('research.ProjectType', blank=True) related_files = StreamField( [ ('file', DocumentChooserBlock()), ], blank=True, ) # Reference field for the Drupal-Wagtail migrator. Can be removed after. drupal_node_id = models.IntegerField(blank=True, null=True) content_panels = [ BasicPageAbstract.title_panel, BasicPageAbstract.body_panel, MultiFieldPanel( [ FieldPanel('publishing_date'), FieldPanel('project_types'), ], heading='General Information', classname='collapsible', ), MultiFieldPanel( [ StreamFieldPanel('project_leads'), StreamFieldPanel('project_members'), StreamFieldPanel('project_contacts'), ], heading='People', classname='collapsible collapsed', ), MultiFieldPanel( [ ImageChooserPanel('image_hero'), ], heading='Images', classname='collapsible collapsed', ), MultiFieldPanel( [ FieldPanel('topics'), StreamFieldPanel('related_files'), ], heading='Related', classname='collapsible collapsed', ), ] promote_panels = Page.promote_panels + [ FeatureablePageAbstract.feature_panel, ShareablePageAbstract.social_panel, ] settings_panels = Page.settings_panels + [ ArchiveablePageAbstract.archive_panel, ] parent_page_types = ['core.BasicPage', 'research.ProjectListPage'] subpage_types = [] templates = 'research/project_page.html' class Meta: verbose_name = 'Project' verbose_name_plural = 'Projects'
class RichTextNoFootnotes(AbstractRichText): class Meta: template = 'publications/blocks/rich_text_no_footnotes.html' rich_text = RichTextBlock(features=RICHTEXT_FEATURES_NO_FOOTNOTES, )
class RichText(AbstractRichText): class Meta: template = 'publications/blocks/rich_text.html' rich_text = RichTextBlock(features=RICHTEXT_FEATURES, )
class DetailedBlock(BaseBlock): short_description = TextBlock(required=False) detailed_description = TextBlock(required=False) left_detailed_content = RichTextBlock(required=False) right_detailed_content = RichTextBlock(required=False)
class InfoBlock(StructBlock): title = CharBlock(required=True) photo = ImageChooserBlock(required=True) summary = RichTextBlock(required=True) action = CharBlock(required=False) url = URLBlock(required=False)
class MultimediaPage( BasicPageAbstract, ContentPage, FeatureablePageAbstract, FromTheArchivesPageAbstract, ShareablePageAbstract, ThemeablePageAbstract, ): class MultimediaTypes(models.TextChoices): AUDIO = ('audio', 'Audio') VIDEO = ('video', 'Video') image_square = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', verbose_name='Square image', ) multimedia_series = models.ForeignKey( 'wagtailcore.Page', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', ) multimedia_type = models.CharField( blank=False, max_length=8, choices=MultimediaTypes.choices, ) multimedia_url = models.URLField( blank=True, verbose_name='Multimedia URL', help_text= 'The URL of the multimedia source from YouTube or Simplecast.', ) podcast_audio_duration = models.CharField(blank=True, max_length=8) podcast_audio_file_size = models.IntegerField(blank=True, null=True) podcast_audio_url = models.URLField(blank=True) podcast_episode = models.IntegerField( blank=True, null=True, verbose_name='Episode Number', ) podcast_guests = StreamField( [ ('guest', CharBlock(required=True)), ], blank=True, ) podcast_season = models.IntegerField( blank=True, null=True, verbose_name='Season Number', ) podcast_subtitle = models.CharField(blank=True, max_length=255) podcast_video_duration = models.CharField(blank=True, max_length=8) podcast_video_file_size = models.IntegerField(blank=True, null=True) podcast_video_url = models.URLField(blank=True) projects = ParentalManyToManyField('research.ProjectPage', blank=True) speakers = StreamField( [ ('speaker', PageChooserBlock(required=True, page_type='people.PersonPage')), ('external_speaker', CharBlock(required=True)), ], blank=True, ) transcript = StreamField( [ ('accordion', StructBlock([ ('title', CharBlock(required=True)), ('text', RichTextBlock(required=True)), ])), ('read_more', StructBlock([ ('title', CharBlock(required=True)), ('text', RichTextBlock(required=True)), ])), ], blank=True, ) video_chapters = StreamField( [ ('video_chapter', StructBlock([ ('chapter_title', CharBlock(required=True)), ('location_time', IntegerBlock(required=True)), ('chapter_description', TextBlock(required=False)), ])), ], blank=True, ) youtube_id = models.CharField( blank=True, max_length=32, verbose_name='YouTube ID', help_text= 'Enter just the YouTube ID for this video. This is the series of letters and numbers found either at www.youtube.com/embed/[here], or www.youtube.com/watch?v=[here]. This is used for the video chaptering below.', ) # Reference field for the Drupal-Wagtail migrator. Can be removed after. drupal_node_id = models.IntegerField(blank=True, null=True) content_panels = [ BasicPageAbstract.title_panel, BasicPageAbstract.body_panel, MultiFieldPanel( [ FieldPanel('multimedia_type'), FieldPanel('publishing_date'), FieldPanel('multimedia_url'), ], heading='General Information', classname='collapsible', ), MultiFieldPanel( [ StreamFieldPanel('speakers'), ], heading='Speakers', classname='collapsible collapsed', ), MultiFieldPanel( [ FieldPanel('youtube_id'), StreamFieldPanel('video_chapters'), ], heading='Video Chapters', classname='collapsible collapsed', ), MultiFieldPanel( [ StreamFieldPanel('transcript'), ], heading='Transcript', classname='collapsible collapsed', ), MultiFieldPanel( [ FieldPanel('podcast_subtitle'), FieldPanel('podcast_season'), FieldPanel('podcast_episode'), StreamFieldPanel('podcast_guests'), MultiFieldPanel( [ FieldPanel('podcast_audio_url'), FieldPanel('podcast_audio_duration'), FieldPanel('podcast_audio_file_size'), ], heading='Audio', classname='collapsible collapsed', ), MultiFieldPanel( [ FieldPanel('podcast_video_url'), FieldPanel('podcast_video_duration'), FieldPanel('podcast_video_file_size'), ], heading='Video', classname='collapsible collapsed', ), ], heading='Podcast Details', classname='collapsible collapsed', ), MultiFieldPanel( [ ImageChooserPanel('image_hero'), ImageChooserPanel('image_square'), ], heading='Images', classname='collapsible collapsed', ), MultiFieldPanel( [ PageChooserPanel( 'multimedia_series', ['multimedia.MultimediaSeriesPage'], ), FieldPanel('topics'), FieldPanel('projects'), ], heading='Related', classname='collapsible collapsed', ), FromTheArchivesPageAbstract.from_the_archives_panel, ] promote_panels = Page.promote_panels + [ FeatureablePageAbstract.feature_panel, ShareablePageAbstract.social_panel, ] settings_panels = Page.settings_panels + [ ThemeablePageAbstract.theme_panel, ] parent_page_types = ['multimedia.MultimediaListPage'] subpage_typse = [] templates = 'multimedia/multimedia_page.html' class Meta: verbose_name = 'Multimedia' verbose_name_plural = 'Multimedia'
class ProgramPage(Page): """ CMS page representing the department e.g. Biology """ description = StreamField( [('paragraph', RichTextBlock(blank=True)), ('image_with_link', ImageWithLinkBlock(help_text='Upload an image with a clickable link', blank=True))], blank=True, help_text='The description shown on the program page') faculty_description = models.CharField( max_length=500, blank=True, null=True, help_text= 'The text to be shown as an introduction in the Faculty section') program = models.OneToOneField( 'courses.Program', null=True, on_delete=models.SET_NULL, help_text='The program for this page', ) program_home_page_url = models.URLField( blank=True, null=True, help_text= "A url for an external homepage. There will be a link to this url from the program page." ) title_program_home_page_url = models.TextField( blank=True, help_text='The text for the link to an external homepage.') program_contact_email = models.EmailField( blank=True, null=True, help_text="A contact email for the program.") background_image = models.ForeignKey( Image, null=True, blank=True, on_delete=models.SET_NULL, related_name='+', help_text='The hero image on the program page') title_over_image = RichTextField(blank=True) thumbnail_image = models.ForeignKey( Image, null=True, blank=True, on_delete=models.SET_NULL, related_name='+', help_text=( 'Thumbnail size must be at least 690x530 pixels. ' 'Thumbnails are cropped down to this size, preserving aspect ratio.' ), ) program_letter_logo = models.ForeignKey( Image, null=True, blank=True, on_delete=models.SET_NULL, related_name='+', help_text= ('The logo that will appear at the top of the program congratulation letter.' ), ) program_letter_header_text = RichTextField( blank=True, null=True, help_text="Header text that will appear next to the logo.") program_letter_footer = models.ForeignKey( Image, null=True, blank=True, on_delete=models.SET_NULL, related_name='+', help_text= ('The logo that will appear at the bottom of the program congratulation letter.' ), ) program_letter_text = RichTextField( blank=True, null=True, help_text="Text that will appear on the program congratulation letter." ) program_letter_footer_text = RichTextField( blank=True, null=True, help_text= "Footer text that will appear on the program congratulation letter.") program_subscribe_link = models.URLField( blank=True, null=True, help_text= "A url for I'm interested button, if there is no course(s) available.") subpage_types = ['FaqsPage', 'CourseTeamTabPage', 'ProgramTabPage'] content_panels = Page.content_panels + [ StreamFieldPanel('description'), FieldPanel('program'), FieldPanel('thumbnail_image'), FieldPanel('program_home_page_url'), FieldPanel('title_program_home_page_url'), FieldPanel('program_contact_email'), FieldPanel('background_image'), FieldPanel('title_over_image'), FieldPanel('faculty_description'), FieldPanel('program_letter_logo'), FieldPanel('program_letter_header_text'), FieldPanel('program_letter_footer'), FieldPanel('program_letter_text'), FieldPanel('program_letter_footer_text'), FieldPanel('program_subscribe_link'), InlinePanel('courses', label='Program Courses'), InlinePanel('info_links', label='More Info Links'), InlinePanel('faculty_members', label='Faculty'), InlinePanel('semester_dates', label='Future Semester Dates'), InlinePanel('course_certificate_signatories', label='Course Certificate Signatories'), InlinePanel('program_certificate_signatories', label='Program Certificate Signatories'), InlinePanel('program_letter_signatories', label='Program Letter Signatories'), ] def get_context(self, request, *args, **kwargs): context = get_program_page_context(self, request) context['active_tab'] = 'about' child_pages = self.get_children() course_team_page = child_pages.type(CourseTeamTabPage).live().first() # the course team tab should always be second, first one is about tab context['child_pages'] = [course_team_page] + [ page for page in child_pages.not_type(CourseTeamTabPage) ] context['course_team_page'] = course_team_page return context
class PrintSeries(Page): footer_colour = models.CharField(max_length=255, blank=True, null=True) """ All Series Page """ year = models.CharField(max_length=255, blank=True, null=True) series_description = RichTextField(blank=True) print_type = models.CharField(max_length=255, blank=True, null=True, help_text='E.g "Photogram Screen Print"') edition = models.CharField('Edition of', max_length=255, blank=True, null=True) dim_x = models.IntegerField(blank=True, null=True) dim_y = models.IntegerField(blank=True, null=True) items = StreamField([ ('item', StructBlock([('select_page', PageChooserBlock(blank=True))])), ], blank=True) story = StreamField( [('title', CharBlock(blank=True, classname="full title", template='home/blocks/inner_title.html', icon='title')), ('text_block', StructBlock([ ('text', RichTextBlock(blank=True)), ('size', ChoiceBlock(required=False, max_length=20, choices=( (u'6', u'Half'), (u'9', u'Full'), ))), ], template='home/blocks/text.html', icon='pilcrow')), ('image', StructBlock([ ('image', ImageChooserBlock(blank=True, required=False)), ('caption', CharBlock(required=False)), ('size', ChoiceBlock(required=False, max_length=20, choices=( (u'4', u'Small'), (u'6', u'Medium'), (u'8', u'Large'), ))), ], template='home/blocks/image.html', icon='image'))], blank=True) behind_the_scenes = StreamField( [('text_block', StructBlock([ ('text', RichTextBlock(blank=True)), ('size', ChoiceBlock(required=False, max_length=20, choices=( (u'6', u'Half'), (u'9', u'Full'), ))), ], template='home/blocks/text.html', icon='pilcrow')), ('image', StructBlock([ ('image', ImageChooserBlock(blank=True, required=False)), ('caption', CharBlock(required=False)), ('size', ChoiceBlock(required=False, max_length=20, choices=( (u'4', u'Small'), (u'6', u'Medium'), (u'8', u'Large'), ))), ], template='home/blocks/image.html', icon='image'))], blank=True) content_panels = Page.content_panels + [ FieldPanel('year'), FieldPanel('edition'), FieldPanel('print_type'), FieldPanel('dim_x'), FieldPanel('dim_y'), FieldPanel('series_description'), StreamFieldPanel('items'), StreamFieldPanel('story'), StreamFieldPanel('behind_the_scenes'), ] promote_panels = [ FieldPanel('footer_colour'), ] + Page.promote_panels
class StoryPage(Page): MAX_RELATED_STORIES = 10 # This is a leaf page subpage_types = [] parent_page_types = [ 'news.StoryIndexPage' ] author = models.ForeignKey( 'common.User', null=True, blank=True, on_delete=models.SET_NULL, related_name="+" ) date = models.DateField("post date") lede = models.CharField( max_length=1024, help_text="A short intro that appears in the story index page" ) tags = ClusterTaggableManager(through=StoryTag, blank=True) categories = ParentalManyToManyField( 'news.StoryCategory', blank=True, help_text="The set of categories this page will be served" ) # Add allowed block types to StreamPanel content = StreamField( [ ('paragraph', RichTextBlock(features=[ 'h2', 'h3', 'bold', 'italic', 'link', 'ol', 'ul' ])), ('markdown', MarkdownBlock(icon='code')), ('image', StructBlock([ ('image', ImageChooserBlock()), ('caption', CharBlock(required=False)) ])), ('carousel', ImageCarouselBlock()), ('quote', BlockQuoteBlock()), ('embedded_content', EmbedContentBlock()), ] ) def get_context(self, request): context = super().get_context(request) tags_list = list(self.tags.all()) # Collects set of stories that has the same tags as this story. related = StoryPage.objects.live().order_by('-first_published_at') related = related.filter(tags__in=tags_list) related = related.exclude(id=self.id) if related.count() > 0: related = related.distinct()[0:self.MAX_RELATED_STORIES] context["related"] = related try: context['prevstory'] = self.get_previous_by_date(tags__in=tags_list) except (StoryPage.DoesNotExist, ValueError): pass try: context['nextstory'] = self.get_next_by_date(tags__in=tags_list) except (StoryPage.DoesNotExist, ValueError): pass return context def hero_image(self): gallery_item = self.gallery_images.first() if gallery_item: return gallery_item.image else: return None def media_thumbnail_url(self): for child in self.content: if child.block.name == 'embedded_content': return child.value.thumbnail_url return None def hero_image_url(self): hero_image = self.hero_image() if hero_image: return hero_image.url return self.media_thumbnail_url() def author_name(self): if not self.author: return ANONYMOUS_AUTHOR_NAME else: return self.owner.username search_fields = Page.search_fields = [ index.SearchField('lede'), index.SearchField('body'), ] content_panels = Page.content_panels + [ MultiFieldPanel([ StoryAuthorFieldPanel('author', widget=forms.Select), FieldPanel('date'), FieldPanel('tags'), FieldPanel('categories', widget=forms.CheckboxSelectMultiple), ]), FieldPanel('lede'), InlinePanel('gallery_images', label="Gallery Images"), StreamFieldPanel('content') ]
class ArticlePage(Page): title_fr = models.CharField(max_length=255, default="") title_en = models.CharField(max_length=255, default="", blank=True) trans_title = TranslatedField( 'title', 'title_fr', 'title_en', ) intro_de = RichTextField(default='', blank=True) intro_fr = RichTextField(default='', blank=True) intro_en = RichTextField(default='', blank=True) trans_intro = TranslatedField( 'intro_de', 'intro_fr', 'intro_en', ) gallery = StreamField([ ('image', ListBlock(ImageCarouselBlock(), icon="image")), ], blank=True) # documents = StreamField([ # ('documents', ListBlock(DocumentChooserBlock(), icon="document")), # ]) body_de = StreamField( [('paragraph', RichTextBlock()), ('section', CharBlock(form_classname="full title")), ('info', InfoBlock(icon='help')), ('media', ChoiceBlock(choices=[ ('gallery', 'Image gallery'), ], icon='media'))], null=True, blank=True) body_fr = StreamField( [('paragraph', RichTextBlock()), ('section', CharBlock(form_classname="full title")), ('info', InfoBlock(icon='help')), ('media', ChoiceBlock(choices=[ ('gallery', 'Image gallery'), ], icon='media'))], null=True, blank=True) body_en = StreamField( [('paragraph', RichTextBlock()), ('section', CharBlock(form_classname="full title")), ('info', InfoBlock(icon='help')), ('media', ChoiceBlock(choices=[ ('gallery', 'Image gallery'), ], icon='media'))], null=True, blank=True) trans_body = TranslatedField( 'body_de', 'body_fr', 'body_en', ) date = models.DateField("Date", null=True, blank=True) on_homepage = models.BooleanField(default=False, verbose_name="Featured", help_text="Auf der Frontpage anzeigen") feed_image = models.ForeignKey('wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') search_fields = Page.search_fields + [ index.SearchField('title', partial_match=True, boost=10), index.SearchField('title_fr', partial_match=True, boost=10), index.SearchField('title_en', partial_match=True, boost=10), index.SearchField('body_de', partial_match=True), index.SearchField('body_fr', partial_match=True), index.SearchField('body_en', partial_match=True), index.SearchField('intro_de', partial_match=True), index.SearchField('intro_fr', partial_match=True), index.SearchField('intro_en', partial_match=True), ] content_panels = [ MultiFieldPanel([ FieldPanel('title'), FieldPanel('intro_de'), ], heading="Deutsch", classname="collapsible collapsed"), StreamFieldPanel('body_de'), MultiFieldPanel([ FieldPanel('title_fr'), FieldPanel('intro_fr'), ], heading="Français", classname="collapsible collapsed"), StreamFieldPanel('body_fr'), MultiFieldPanel([ FieldPanel('title_en'), FieldPanel('intro_en'), ], heading="English", classname="collapsible collapsed"), StreamFieldPanel('body_en'), MultiFieldPanel([ ImageChooserPanel('feed_image'), ], heading="Images"), StreamFieldPanel('gallery'), ] promote_panels = [ InlinePanel('related_links', label="Links"), MultiFieldPanel([ FieldPanel('date'), FieldPanel('on_homepage'), ], heading="Veröffentlichung"), MultiFieldPanel(Page.promote_panels, "Einstellungen"), ] parent_page_types = ['home.ArticleIndexPage', 'home.HomePage'] subpage_types = [] class Meta: verbose_name = "Artikel"
class ImageBlock(StructBlock): image = ImageChooserBlock() caption = RichTextBlock() alignment = ImageFormatChoiceBlock()
class StreamModel(models.Model): body = StreamField([ ('text', CharBlock()), ('rich_text', RichTextBlock()), ('image', ImageChooserBlock()), ])
class ImageText(StructBlock): reverse = BooleanBlock(required=False) text = RichTextBlock() image = CustomImageChooserBlock(rendition="width-800")
class BustoutBlock(StructBlock): image = ImageChooserBlock() text = RichTextBlock() class Meta: icon = "pick"