class LearnPage(AbstractFilterPage): content = StreamField([ ('full_width_text', organisms.FullWidthText()), ('info_unit_group', organisms.InfoUnitGroup()), ('expandable_group', organisms.ExpandableGroup()), ('contact_expandable_group', organisms.ContactExpandableGroup()), ('expandable', organisms.Expandable()), ('well', organisms.Well()), ('call_to_action', molecules.CallToAction()), ('email_signup', organisms.EmailSignUp()), ('video_player', organisms.VideoPlayer()), ('audio_player', organisms.AudioPlayer()), ('table_block', organisms.AtomicTableBlock( table_options={'renderer': 'html'} )), ('feedback', v1_blocks.Feedback()), ], blank=True) edit_handler = AbstractFilterPage.generate_edit_handler( content_panel=StreamFieldPanel('content') ) template = 'learn-page/index.html' objects = PageManager() search_fields = AbstractFilterPage.search_fields + [ index.SearchField('content') ]
class AskContent(blocks.StreamBlock): text = blocks.StructBlock([('content', blocks.RichTextBlock(features=[ 'bold', 'italic', 'h3', 'link', 'ol', 'ul', 'document-link', 'image', 'embed' ], label='Text'))]) table_block = organisms.AtomicTableBlock( table_options={'renderer': 'html'}) tip = Tip() video_player = organisms.VideoPlayer() class Meta: template = '_includes/ask/content-block.html'
class DocumentDetailPage(AbstractFilterPage): content = StreamField([ ('full_width_text', organisms.FullWidthText()), ('expandable', organisms.Expandable()), ('expandable_group', organisms.ExpandableGroup()), ('table_block', organisms.AtomicTableBlock(table_options={'renderer': 'html'})), ('feedback', v1_blocks.Feedback()), ], blank=True) edit_handler = AbstractFilterPage.generate_edit_handler( content_panel=StreamFieldPanel('content')) template = 'document-detail/index.html' objects = PageManager()
class BrowsePage(CFGOVPage): header = StreamField([ ('text_introduction', molecules.TextIntroduction()), ('featured_content', organisms.FeaturedContent()), ], blank=True) content = StreamField([ ('full_width_text', organisms.FullWidthText()), ('info_unit_group', organisms.InfoUnitGroup()), ('expandable_group', organisms.ExpandableGroup()), ('expandable', organisms.Expandable()), ('well', organisms.Well()), ('video_player', organisms.VideoPlayer()), ('snippet_list', organisms.ResourceList()), ('table_block', organisms.AtomicTableBlock(table_options={'renderer': 'html'})), ('feedback', v1_blocks.Feedback()), ('raw_html_block', blocks.RawHTMLBlock(label='Raw HTML block')), ('conference_registration_form', ConferenceRegistrationForm()), ('chart_block', organisms.ChartBlock()), ('mortgage_chart_block', organisms.MortgageChartBlock()), ('mortgage_map_block', organisms.MortgageMapBlock()), ('mortgage_downloads_block', MortgageDataDownloads()), ('data_snapshot', organisms.DataSnapshot()), ('job_listing_table', JobListingTable()), ('bureau_structure', organisms.BureauStructure()), ('yes_checklist', YESChecklist()), ], blank=True) secondary_nav_exclude_sibling_pages = models.BooleanField(default=False) share_and_print = models.BooleanField( default=False, help_text="Include share and print buttons above page content.") # General content tab content_panels = CFGOVPage.content_panels + [ StreamFieldPanel('header'), FieldPanel('share_and_print'), StreamFieldPanel('content'), ] sidefoot_panels = CFGOVPage.sidefoot_panels + [ FieldPanel('secondary_nav_exclude_sibling_pages'), ] # Tab handler interface edit_handler = TabbedInterface([ ObjectList(content_panels, heading='General Content'), ObjectList(sidefoot_panels, heading='Sidebar'), ObjectList(CFGOVPage.settings_panels, heading='Configuration'), ]) template = 'browse-basic/index.html' objects = PageManager() search_fields = CFGOVPage.search_fields + [ index.SearchField('content'), index.SearchField('header') ] @property def page_js(self): return (super(BrowsePage, self).page_js + ['secondary-navigation.js']) def get_context(self, request, *args, **kwargs): context = super(BrowsePage, self).get_context(request, *args, **kwargs) context.update({'get_secondary_nav_items': get_secondary_nav_items}) return context
class SublandingPage(CFGOVPage): header = StreamField([ ('hero', molecules.Hero()), ], blank=True) content = StreamField([ ('text_introduction', molecules.TextIntroduction()), ('featured_content', molecules.FeaturedContent()), ('info_unit_group', organisms.InfoUnitGroup()), ('image_text_25_75_group', organisms.ImageText2575Group()), ('image_text_50_50_group', organisms.ImageText5050Group()), ('full_width_text', organisms.FullWidthText()), ('half_width_link_blob_group', organisms.HalfWidthLinkBlobGroup()), ('third_width_link_blob_group', organisms.ThirdWidthLinkBlobGroup()), ('post_preview_snapshot', organisms.PostPreviewSnapshot()), ('well', organisms.Well()), ('table', organisms.Table(editable=False)), ('table_block', organisms.AtomicTableBlock(table_options={'renderer': 'html'})), ('contact', organisms.MainContactInfo()), ('formfield_with_button', molecules.FormFieldWithButton()), ('reg_comment', organisms.RegComment()), ('feedback', v1_blocks.Feedback()), ('snippet_list', organisms.SnippetList()), ], blank=True) sidebar_breakout = StreamField([ ('slug', blocks.CharBlock(icon='title')), ('heading', blocks.CharBlock(icon='title')), ('paragraph', blocks.RichTextBlock(icon='edit')), ('breakout_image', blocks.StructBlock([ ('image', ImageChooserBlock()), ('is_round', blocks.BooleanBlock(required=False, default=True, label='Round?')), ('icon', blocks.CharBlock(help_text='Enter icon class name.')), ('heading', blocks.CharBlock(required=False, label='Introduction Heading')), ('body', blocks.TextBlock(required=False, label='Introduction Body')), ], heading='Breakout Image', icon='image')), ('related_posts', organisms.RelatedPosts()), ('job_listing_list', JobListingList()), ], blank=True) # General content tab content_panels = CFGOVPage.content_panels + [ StreamFieldPanel('header'), StreamFieldPanel('content'), ] sidebar_panels = [ StreamFieldPanel('sidebar_breakout'), ] + CFGOVPage.sidefoot_panels # Tab handler interface edit_handler = TabbedInterface([ ObjectList(content_panels, heading='General Content'), ObjectList(sidebar_panels, heading='Sidebar'), ObjectList(CFGOVPage.settings_panels, heading='Configuration'), ]) template = 'sublanding-page/index.html' objects = PageManager() def get_browsefilterable_posts(self, limit): filter_pages = [ p.specific for p in self.get_appropriate_descendants() if 'FilterablePage' in p.specific_class.__name__ and 'archive' not in p.title.lower() ] posts_list = [] for page in filter_pages: eligible_children = AbstractFilterPage.objects.live().filter( CFGOVPage.objects.child_of_q(page)) form = FilterableListForm(filterable_pages=eligible_children) for post in form.get_page_set(): posts_list.append(post) return sorted(posts_list, key=lambda p: p.date_published, reverse=True)[:limit]
class EventPage(AbstractFilterPage): # General content fields body = RichTextField('Subheading', blank=True) archive_body = RichTextField(blank=True) live_body = RichTextField(blank=True) future_body = RichTextField(blank=True) persistent_body = StreamField([ ('content', blocks.RichTextBlock(icon='edit')), ('content_with_anchor', molecules.ContentWithAnchor()), ('heading', v1_blocks.HeadingBlock(required=False)), ('image', molecules.ContentImage()), ('table_block', organisms.AtomicTableBlock( table_options={'renderer': 'html'} )), ('reusable_text', v1_blocks.ReusableTextChooserBlock( 'v1.ReusableText' )), ], blank=True) start_dt = models.DateTimeField("Start") end_dt = models.DateTimeField("End", blank=True, null=True) future_body = RichTextField(blank=True) archive_image = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) video_transcript = models.ForeignKey( 'wagtaildocs.Document', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) speech_transcript = models.ForeignKey( 'wagtaildocs.Document', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) flickr_url = models.URLField("Flickr URL", blank=True) archive_video_id = models.CharField( 'YouTube video ID (archive)', null=True, blank=True, max_length=11, # This is a reasonable but not official regex for YouTube video IDs. # https://webapps.stackexchange.com/a/54448 validators=[RegexValidator(regex=r'^[\w-]{11}$')], help_text=organisms.VideoPlayer.YOUTUBE_ID_HELP_TEXT ) live_stream_availability = models.BooleanField( "Streaming?", default=False, blank=True, help_text='Check if this event will be streamed live. This causes the ' 'event page to show the parts necessary for live streaming.' ) live_video_id = models.CharField( 'YouTube video ID (live)', null=True, blank=True, max_length=11, # This is a reasonable but not official regex for YouTube video IDs. # https://webapps.stackexchange.com/a/54448 validators=[RegexValidator(regex=r'^[\w-]{11}$')], help_text=organisms.VideoPlayer.YOUTUBE_ID_HELP_TEXT ) live_stream_date = models.DateTimeField( "Go Live Date", blank=True, null=True, help_text='Enter the date and time that the page should switch from ' 'showing the venue image to showing the live video feed. ' 'This is typically 15 minutes prior to the event start time.' ) # Venue content fields venue_coords = models.CharField(max_length=100, blank=True) venue_name = models.CharField(max_length=100, blank=True) venue_street = models.CharField(max_length=100, blank=True) venue_suite = models.CharField(max_length=100, blank=True) venue_city = models.CharField(max_length=100, blank=True) venue_state = USStateField(blank=True) venue_zipcode = models.CharField(max_length=12, blank=True) venue_image_type = models.CharField( max_length=8, choices=( ('map', 'Map'), ('image', 'Image (selected below)'), ('none', 'No map or image'), ), default='map', help_text='If "Image" is chosen here, you must select the image you ' 'want below. It should be sized to 1416x796.', ) venue_image = models.ForeignKey( 'v1.CFGOVImage', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) post_event_image_type = models.CharField( max_length=16, choices=( ('placeholder', 'Placeholder image'), ('image', 'Unique image (selected below)'), ), default='placeholder', verbose_name='Post-event image type', help_text='Choose what to display after an event concludes. This will ' 'be overridden by embedded video if the "YouTube video ID ' '(archive)" field on the previous tab is populated. If ' '"Unique image" is chosen here, you must select the image ' 'you want below. It should be sized to 1416x796.', ) post_event_image = models.ForeignKey( 'v1.CFGOVImage', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) # Agenda content fields agenda_items = StreamField([('item', AgendaItemBlock())], blank=True) objects = CFGOVPageManager() search_fields = AbstractFilterPage.search_fields + [ index.SearchField('body'), index.SearchField('archive_body'), index.SearchField('live_video_id'), index.SearchField('flickr_url'), index.SearchField('archive_video_id'), index.SearchField('future_body'), index.SearchField('agenda_items') ] # General content tab content_panels = CFGOVPage.content_panels + [ FieldPanel('body'), FieldRowPanel([ FieldPanel('start_dt', classname="col6"), FieldPanel('end_dt', classname="col6"), ]), MultiFieldPanel([ FieldPanel('archive_body'), ImageChooserPanel('archive_image'), DocumentChooserPanel('video_transcript'), DocumentChooserPanel('speech_transcript'), FieldPanel('flickr_url'), FieldPanel('archive_video_id'), ], heading='Archive Information'), FieldPanel('live_body'), FieldPanel('future_body'), StreamFieldPanel('persistent_body'), MultiFieldPanel([ FieldPanel('live_stream_availability'), FieldPanel('live_video_id'), FieldPanel('live_stream_date'), ], heading='Live Stream Information'), ] # Venue content tab venue_panels = [ FieldPanel('venue_name'), MultiFieldPanel([ FieldPanel('venue_street'), FieldPanel('venue_suite'), FieldPanel('venue_city'), FieldPanel('venue_state'), FieldPanel('venue_zipcode'), ], heading='Venue Address'), MultiFieldPanel([ FieldPanel('venue_image_type'), ImageChooserPanel('venue_image'), ], heading='Venue Image'), MultiFieldPanel([ FieldPanel('post_event_image_type'), ImageChooserPanel('post_event_image'), ], heading='Post-event Image') ] # Agenda content tab agenda_panels = [ StreamFieldPanel('agenda_items'), ] # Promotion panels promote_panels = [ MultiFieldPanel(AbstractFilterPage.promote_panels, "Page configuration"), ] # Tab handler interface edit_handler = TabbedInterface([ ObjectList(content_panels, heading='General Content'), ObjectList(venue_panels, heading='Venue Information'), ObjectList(agenda_panels, heading='Agenda Information'), ObjectList(AbstractFilterPage.sidefoot_panels, heading='Sidebar'), ObjectList(AbstractFilterPage.settings_panels, heading='Configuration'), ]) template = 'events/event.html' @property def event_state(self): if self.end_dt: end = convert_date(self.end_dt, 'America/New_York') if end < datetime.now(timezone('America/New_York')): return 'past' if self.live_stream_date: start = convert_date( self.live_stream_date, 'America/New_York' ) else: start = convert_date(self.start_dt, 'America/New_York') if datetime.now(timezone('America/New_York')) > start: return 'present' return 'future' @property def page_js(self): if ( (self.live_stream_date and self.event_state == 'present') or (self.archive_video_id and self.event_state == 'past') ): return super(EventPage, self).page_js + ['video-player.js'] return super(EventPage, self).page_js def location_image_url(self, scale='2', size='276x155', zoom='12'): if not self.venue_coords: self.venue_coords = get_venue_coords( self.venue_city, self.venue_state ) api_url = 'https://api.mapbox.com/styles/v1/mapbox/streets-v11/static' static_map_image_url = '{}/{},{}/{}?access_token={}'.format( api_url, self.venue_coords, zoom, size, settings.MAPBOX_ACCESS_TOKEN ) return static_map_image_url def clean(self): super(EventPage, self).clean() if self.venue_image_type == 'image' and not self.venue_image: raise ValidationError({ 'venue_image': 'Required if "Venue image type" is "Image".' }) if self.post_event_image_type == 'image' and not self.post_event_image: raise ValidationError({ 'post_event_image': 'Required if "Post-event image type" is ' '"Image".' }) def save(self, *args, **kwargs): self.venue_coords = get_venue_coords(self.venue_city, self.venue_state) return super(EventPage, self).save(*args, **kwargs) def get_context(self, request): context = super(EventPage, self).get_context(request) context['event_state'] = self.event_state return context
class EnforcementActionPage(AbstractFilterPage): sidebar_header = models.CharField( default='Action details', max_length=100 ) court = models.CharField(default='', max_length=150, blank=True) institution_type = models.CharField(max_length=50, choices=[ ('Nonbank', 'Nonbank'), ('Bank', 'Bank') ]) content = StreamField([ ('full_width_text', organisms.FullWidthText()), ('expandable', organisms.Expandable()), ('expandable_group', organisms.ExpandableGroup()), ('notification', molecules.Notification()), ('table_block', organisms.AtomicTableBlock( table_options={'renderer': 'html'})), ('feedback', v1_blocks.Feedback()), ], blank=True) content_panels = [ StreamFieldPanel('header'), StreamFieldPanel('content') ] metadata_panels = [ MultiFieldPanel([ FieldPanel('sidebar_header'), FieldPanel('court'), FieldPanel('institution_type'), FieldPanel('date_filed'), FieldPanel('tags', 'Tags'), ], heading='Basic Metadata'), MultiFieldPanel([ InlinePanel( 'docket_numbers', label="Docket Number", min_num=1 ), ], heading='Docket Number'), MultiFieldPanel([ InlinePanel('statuses', label="Enforcement Status", min_num=1), ], heading='Enforcement Status'), MultiFieldPanel([ InlinePanel('categories', label="Categories", min_num=1, max_num=2), ], heading='Categories'), ] settings_panels = [ MultiFieldPanel(CFGOVPage.promote_panels, 'Settings'), MultiFieldPanel([ FieldPanel('preview_title'), FieldPanel('preview_subheading'), FieldPanel('preview_description'), FieldPanel('secondary_link_url'), FieldPanel('secondary_link_text'), ImageChooserPanel('preview_image'), ], heading='Page Preview Fields', classname='collapsible'), FieldPanel('authors', 'Authors'), MultiFieldPanel([ FieldPanel('date_published'), FieldPanel('comments_close_by'), ], 'Relevant Dates', classname='collapsible'), MultiFieldPanel(Page.settings_panels, 'Scheduled Publishing'), FieldPanel('language', 'Language'), ] edit_handler = TabbedInterface([ ObjectList( AbstractFilterPage.content_panels + content_panels, heading='General Content' ), ObjectList(metadata_panels, heading='Metadata'), ObjectList(CFGOVPage.sidefoot_panels, heading='Sidebar'), ObjectList(settings_panels, heading='Configuration') ]) template = 'enforcement-action/index.html' objects = PageManager() search_fields = AbstractFilterPage.search_fields + [ index.SearchField('content') ]
class SublandingPage(CFGOVPage): portal_topic = models.ForeignKey( 'v1.PortalTopic', blank=True, null=True, related_name='portal_pages', on_delete=models.SET_NULL, help_text='Select a topic if this is a MONEY TOPICS portal page.') header = StreamField([ ('hero', molecules.Hero()), ], blank=True) content = StreamField([ ('text_introduction', molecules.TextIntroduction()), ('notification', molecules.Notification()), ('featured_content', organisms.FeaturedContent()), ('full_width_text', organisms.FullWidthText()), ('info_unit_group', organisms.InfoUnitGroup()), ('well', organisms.Well()), ('snippet_list', organisms.ResourceList()), ('post_preview_snapshot', organisms.PostPreviewSnapshot()), ('contact', organisms.MainContactInfo()), ('table_block', organisms.AtomicTableBlock(table_options={'renderer': 'html'})), ('reg_comment', organisms.RegComment()), ('feedback', v1_blocks.Feedback()), ], blank=True) sidebar_breakout = StreamField([ ('slug', blocks.CharBlock(icon='title')), ('heading', blocks.CharBlock(icon='title')), ('paragraph', blocks.RichTextBlock(icon='edit')), ('breakout_image', blocks.StructBlock([ ('image', ImageChooserBlock()), ('is_round', blocks.BooleanBlock(required=False, default=True, label='Round?')), ('icon', blocks.CharBlock(help_text='Enter icon class name.')), ('heading', blocks.CharBlock(required=False, label='Introduction Heading')), ('body', blocks.TextBlock(required=False, label='Introduction Body')), ], heading='Breakout Image', icon='image')), ('related_posts', organisms.RelatedPosts()), ('job_listing_list', JobListingList()), ], blank=True) # General content tab content_panels = CFGOVPage.content_panels + [ StreamFieldPanel('header'), StreamFieldPanel('content'), FieldPanel('portal_topic'), ] sidebar_panels = [ StreamFieldPanel('sidebar_breakout'), ] + CFGOVPage.sidefoot_panels # Tab handler interface edit_handler = TabbedInterface([ ObjectList(content_panels, heading='General Content'), ObjectList(sidebar_panels, heading='Sidebar'), ObjectList(CFGOVPage.settings_panels, heading='Configuration'), ]) template = 'sublanding-page/index.html' objects = PageManager() search_fields = CFGOVPage.search_fields + [ index.SearchField('content'), index.SearchField('header') ] def get_browsefilterable_posts(self, limit): filter_pages = [ p.specific for p in self.get_appropriate_descendants() if 'FilterablePage' in p.specific_class.__name__ and 'archive' not in p.title.lower() ] posts_list = [] for page in filter_pages: posts_list.extend(AbstractFilterPage.objects.live().filter( CFGOVPage.objects.child_of_q(page))) return sorted(posts_list, key=lambda p: p.date_published, reverse=True)[:limit]
class EnforcementActionPage(AbstractFilterPage): public_enforcement_action = models.CharField(max_length=150, blank=True) initial_filing_date = models.DateField(null=True, blank=True) settled_or_contested_at_filing = models.CharField(max_length=10, choices=[('Settled', 'Settled'), ('Contested', 'Contested')], blank=True) court = models.CharField(default='', max_length=150, blank=True) content = StreamField([ ('full_width_text', organisms.FullWidthText()), ('expandable', organisms.Expandable()), ('expandable_group', organisms.ExpandableGroup()), ('notification', molecules.Notification()), ('table_block', organisms.AtomicTableBlock(table_options={'renderer': 'html'})), ('feedback', v1_blocks.Feedback()), ], blank=True) content_panels = [StreamFieldPanel('header'), StreamFieldPanel('content')] metadata_panels = [ FieldPanel('public_enforcement_action'), FieldPanel('initial_filing_date'), InlinePanel('defendant_types', label='Defendant/Respondent Type'), InlinePanel('categories', label="Forum", min_num=1, max_num=2), FieldPanel('court'), InlinePanel('docket_numbers', label="Docket Number", min_num=1), FieldPanel('settled_or_contested_at_filing'), InlinePanel('statuses', label="Status", min_num=1), InlinePanel('products', label="Products"), InlinePanel('at_risk_groups', label="At Risk Groups"), InlinePanel('statutes', label="Statutes/Regulations"), InlinePanel('enforcement_dispositions', label='Final Disposition'), ] settings_panels = [ MultiFieldPanel(CFGOVPage.promote_panels, 'Settings'), MultiFieldPanel([ FieldPanel('preview_title'), FieldPanel('preview_subheading'), FieldPanel('preview_description'), FieldPanel('secondary_link_url'), FieldPanel('secondary_link_text'), ImageChooserPanel('preview_image'), ], heading='Page Preview Fields', classname='collapsible'), FieldPanel('authors', 'Authors'), MultiFieldPanel([ FieldPanel('date_published'), FieldPanel('comments_close_by'), ], 'Relevant Dates', classname='collapsible'), MultiFieldPanel(Page.settings_panels, 'Scheduled Publishing'), FieldPanel('language', 'Language'), MultiFieldPanel(CFGOVPage.archive_panels, 'Archive'), ] edit_handler = TabbedInterface([ ObjectList(AbstractFilterPage.content_panels + content_panels, heading='General Content'), ObjectList(metadata_panels, heading='Metadata'), ObjectList(CFGOVPage.sidefoot_panels, heading='Sidebar'), ObjectList(settings_panels, heading='Configuration') ]) template = 'enforcement-action/index.html' objects = PageManager() search_fields = AbstractFilterPage.search_fields + [ index.SearchField('content') ] @classmethod def all_actions(cls): # Return the collection of all Enforcement Action Pages. # Exclude any pages in the Trash or otherwise not a child of the # EnforcementActionsFilterPage. try: # TODO: find a less hacky way to get only the pages in the # correct part of the page tree pg_id = 1327 parent_page = Page.objects.get(id=pg_id) query = cls.objects.child_of(parent_page) except (Page.DoesNotExist): query = cls.objects query = query.filter(initial_filing_date__isnull=False) query = query.live().order_by('-initial_filing_date') return query def get_context(self, request): context = super(EnforcementActionPage, self).get_context(request) dispositions = self.enforcement_dispositions.all() context.update({ 'total_consumer_relief': sum(disp.final_order_consumer_redress + disp.final_order_other_consumer_relief for disp in dispositions), 'total_cmp': sum(disp.final_order_civil_money_penalty for disp in dispositions), 'defendant_types': [ d.get_defendant_type_display() for d in self.defendant_types.all() ], 'statutes': [s.statute for s in self.statutes.all()], 'products': [p.product for p in self.products.all()], 'at_risk_groups': [g.at_risk_group for g in self.at_risk_groups.all()] }) return context