示例#1
0
class LandingPage(CFGOVPage):
    header = StreamField([
        ('hero', molecules.Hero()),
        ('text_introduction', molecules.TextIntroduction()),
    ],
                         blank=True)

    content = StreamField([
        ('info_unit_group', organisms.InfoUnitGroup()),
        ('well', organisms.Well()),
        ('feedback', v1_blocks.Feedback()),
    ],
                          blank=True)

    # General content tab
    content_panels = CFGOVPage.content_panels + [
        StreamFieldPanel('header'),
        StreamFieldPanel('content'),
    ]

    # Tab handler interface
    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='General Content'),
        ObjectList(CFGOVPage.sidefoot_panels, heading='Sidebar'),
        ObjectList(CFGOVPage.settings_panels, heading='Configuration'),
    ])

    template = 'landing-page/index.html'

    objects = PageManager()

    search_fields = CFGOVPage.search_fields + [
        index.SearchField('content'),
        index.SearchField('header')
    ]
class SublandingFilterablePage(FilterableFeedPageMixin,
                               FilterableListMixin,
                               CFGOVPage):
    header = StreamField([
        ('hero', molecules.Hero()),
    ], blank=True)
    content = StreamField([
        ('text_introduction', molecules.TextIntroduction()),
        ('full_width_text', organisms.FullWidthText()),
        ('filter_controls', organisms.FilterControls()),
        ('featured_content', molecules.FeaturedContent()),
        ('feedback', v1_blocks.Feedback()),
    ])

    # General content tab
    content_panels = CFGOVPage.content_panels + [
        StreamFieldPanel('header'),
        StreamFieldPanel('content'),
    ]

    # Tab handler interface
    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='General Content'),
        ObjectList(CFGOVPage.sidefoot_panels, heading='Sidebar'),
        ObjectList(CFGOVPage.settings_panels, heading='Configuration'),
    ])

    template = 'sublanding-page/index.html'

    objects = PageManager()
示例#3
0
class LandingPage(CFGOVPage):
    header = StreamField([
        ('hero', molecules.Hero()),
        ('text_introduction', molecules.TextIntroduction()),
    ], blank=True)

    content = StreamField([
        ('info_unit_group', organisms.InfoUnitGroup()),
        ('image_text_25_75_group', organisms.ImageText2575Group()),
        ('image_text_50_50_group', organisms.ImageText5050Group()),
        ('half_width_link_blob_group', organisms.HalfWidthLinkBlobGroup()),
        ('third_width_link_blob_group', organisms.ThirdWidthLinkBlobGroup()),
        ('well', organisms.Well()),
        ('feedback', v1_blocks.Feedback()),
    ], blank=True)

    # General content tab
    content_panels = CFGOVPage.content_panels + [
        StreamFieldPanel('header'),
        StreamFieldPanel('content'),
    ]

    # Tab handler interface
    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='General Content'),
        ObjectList(CFGOVPage.sidefoot_panels, heading='Sidebar'),
        ObjectList(CFGOVPage.settings_panels, heading='Configuration'),
    ])

    template = 'landing-page/index.html'

    objects = PageManager()
示例#4
0
class FormExplainerPage(CFGOVPage):
    header = StreamField([
        ('hero', molecules.Hero()),
        ('text_introduction', molecules.TextIntroduction()),
    ],
                         blank=True)

    content = StreamField(FormExplainerContent)

    # General content tab
    content_panels = CFGOVPage.content_panels + [
        StreamFieldPanel('header'),
        StreamFieldPanel('content'),
    ]

    # Tab handler interface
    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='General Content'),
        ObjectList(CFGOVPage.sidefoot_panels, heading='Sidebar'),
        ObjectList(CFGOVPage.settings_panels, heading='Configuration'),
    ])

    template = 'form-explainer/index.html'

    objects = CFGOVPageManager()

    search_fields = CFGOVPage.search_fields + [index.SearchField('header')]
class SublandingFilterableContent(StreamBlock):
    """Defines the StreamField blocks for SublandingFilterablePage content.

    Pages can have at most one filterable list.
    """
    text_introduction = molecules.TextIntroduction()
    full_width_text = organisms.FullWidthText()
    filter_controls = organisms.FilterableList()
    featured_content = organisms.FeaturedContent()
    feedback = v1_blocks.Feedback()

    class Meta:
        block_counts = {
            'filter_controls': {
                'max_num': 1
            },
        }
class BrowseFilterablePage(FilterableFeedPageMixin,
                           FilterableListMixin,
                           CFGOVPage):
    header = StreamField([
        ('text_introduction', molecules.TextIntroduction()),
        ('featured_content', organisms.FeaturedContent()),
    ])
    content = StreamField(BrowseFilterableContent)

    secondary_nav_exclude_sibling_pages = models.BooleanField(default=False)

    # General content tab
    content_panels = CFGOVPage.content_panels + [
        StreamFieldPanel('header'),
        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='SideFoot'),
        ObjectList(CFGOVPage.settings_panels, heading='Configuration'),
    ])

    template = 'browse-filterable/index.html'

    objects = PageManager()

    search_fields = CFGOVPage.search_fields + [
        index.SearchField('content'),
        index.SearchField('header')
    ]

    @property
    def page_js(self):
        return (
            super(BrowseFilterablePage, self).page_js
            + ['secondary-navigation.js']
        )
示例#7
0
class PayingForCollegePage(CFGOVPage):
    """A base class for our suite of PFC pages."""
    header = StreamField([
        ('text_introduction', molecules.TextIntroduction()),
        ('featured_content', organisms.FeaturedContent()),
    ], blank=True)

    content_panels = CFGOVPage.content_panels + [
        StreamFieldPanel('header'),
        StreamFieldPanel('content'),
    ]
    # Tab handler interface
    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='General Content'),
        ObjectList(CFGOVPage.sidefoot_panels, heading='Sidebar'),
        ObjectList(CFGOVPage.settings_panels, heading='Configuration'),
    ])
    objects = CFGOVPageManager()

    class Meta:
        abstract = True
示例#8
0
class BrowseFilterablePage(FilterableFeedPageMixin, FilterableListMixin,
                           CFGOVPage):
    header = StreamField([
        ('text_introduction', molecules.TextIntroduction()),
        ('featured_content', molecules.FeaturedContent()),
    ])
    content = StreamField([
        ('full_width_text', organisms.FullWidthText()),
        ('filter_controls', organisms.FilterControls()),
        ('feedback', v1_blocks.Feedback()),
    ])

    secondary_nav_exclude_sibling_pages = models.BooleanField(default=False)

    # General content tab
    content_panels = CFGOVPage.content_panels + [
        StreamFieldPanel('header'),
        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='SideFoot'),
        ObjectList(CFGOVPage.settings_panels, heading='Configuration'),
    ])

    template = 'browse-filterable/index.html'

    objects = PageManager()

    def add_page_js(self, js):
        super(BrowseFilterablePage, self).add_page_js(js)
        js['template'] += ['secondary-navigation.js']
示例#9
0
class CollegeCostsPage(PayingForCollegePage):
    """Breaking down financial aid and loans for prospective students."""
    header = StreamField([
        ('hero', molecules.Hero()),
        ('text_introduction', molecules.TextIntroduction()),
        ('featured_content', organisms.FeaturedContent()),
    ],
                         blank=True)

    content_panels = CFGOVPage.content_panels + [
        StreamFieldPanel('header'),
        StreamFieldPanel('content'),
    ]

    # Tab handler interface
    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='General Content'),
        # ObjectList(, heading='School and living situation'),
        ObjectList(CFGOVPage.settings_panels, heading='Configuration'),
    ])
    objects = CFGOVPageManager()
    content = StreamField(PayingForCollegeContent, blank=True)
    template = 'paying-for-college/college-costs.html'
示例#10
0
class ActivityIndexPage(CFGOVPage):
    """
    A model for the Activity Search page.
    """

    subpage_types = ['teachers_digital_platform.ActivityPage']

    objects = CFGOVPageManager()

    header = StreamField([
        ('text_introduction', molecules.TextIntroduction()),
    ],
                         blank=True)

    results = {}
    content_panels = CFGOVPage.content_panels + [
        StreamFieldPanel('header'),
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='General Content'),
        ObjectList(CFGOVPage.sidefoot_panels, heading='Sidebar/Footer'),
        ObjectList(CFGOVPage.settings_panels, heading='Configuration'),
    ])

    @classmethod
    def can_create_at(cls, parent):
        # You can only create one of these!
        return super(ActivityIndexPage, cls).can_create_at(parent) \
            and not cls.objects.exists()

    def get_template(self, request):
        template = 'teachers_digital_platform/activity_index_page.html'
        if 'partial' in request.GET:
            template = 'teachers_digital_platform/activity_search_facets_and_results.html'  # noqa: E501
        return template

    def get_context(self, request, *args, **kwargs):
        facet_map = (
            ('building_block', (ActivityBuildingBlock, False, 10)),
            ('school_subject', (ActivitySchoolSubject, False, 25)),
            ('topic', (ActivityTopic, True, 25)),
            ('grade_level', (ActivityGradeLevel, False, 10)),
            ('age_range', (ActivityAgeRange, False, 10)),
            ('student_characteristics', (ActivityStudentCharacteristics, False,
                                         10)),  # noqa: E501
            ('activity_type', (ActivityType, False, 10)),
            ('teaching_strategy', (ActivityTeachingStrategy, False, 25)),
            ('blooms_taxonomy_level', (ActivityBloomsTaxonomyLevel, False,
                                       25)),  # noqa: E501
            ('activity_duration', (ActivityDuration, False, 10)),
            ('jump_start_coalition', (ActivityJumpStartCoalition, False, 25)),
            ('council_for_economic_education', (ActivityCouncilForEconEd,
                                                False, 25)),  # noqa: E501
        )
        search_query = request.GET.get('q', '')  # haystack cleans this string
        sqs = SearchQuerySet().models(ActivityPage).filter(live=True)
        total_activities = sqs.count()
        # Load selected facets
        selected_facets = {}
        facet_queries = {}

        for facet, facet_config in facet_map:
            sqs = sqs.facet(str(facet), size=facet_config[2])
            if facet in request.GET and request.GET.get(facet):
                selected_facets[facet] = [
                    int(value) for value in request.GET.getlist(facet)
                    if value.isdigit()
                ]
                facet_queries[facet] = facet + '_exact:' + (
                    " OR " + facet + "_exact:").join(
                        [str(value) for value in selected_facets[facet]])

        payload = {
            'search_query': search_query,
            'results': [],
            'total_results': 0,
            'total_activities': total_activities,
            'selected_facets': selected_facets,
            'facet_queries': facet_queries,
            'all_facets': {},
        }

        # Apply search query if it exists, but don't apply facets
        if search_query:
            sqs = sqs.filter(content=search_query).order_by(
                '-_score', '-date')  # noqa: E501
        else:
            sqs = sqs.order_by('-date')

        # Get all facets and their counts
        facet_counts = sqs.facet_counts()
        all_facets = self.get_all_facets(facet_map, sqs, facet_counts,
                                         facet_queries,
                                         selected_facets)  # noqa: E501

        # List all facet blocks that need to be expanded
        always_expanded = {'building_block', 'topic', 'school_subject'}
        conditionally_expanded = {
            facet_name
            for facet_name, facet_items in all_facets.items()
            if any(facet['selected'] is True for facet in facet_items)
        }
        expanded_facets = always_expanded.union(set(conditionally_expanded))

        payload.update({
            'facet_counts': facet_counts,
            'all_facets': all_facets,
            'expanded_facets': expanded_facets,
        })

        # Apply all the active facet values to our search results
        for facet_narrow_query in facet_queries.values():
            sqs = sqs.narrow(facet_narrow_query)

        results = [activity.object for activity in sqs]
        total_results = sqs.count()

        payload.update({
            'results': results,
            'total_results': total_results,
        })
        self.results = payload
        results_per_page = validate_results_per_page(request)
        paginator = Paginator(payload['results'], results_per_page)
        current_page = validate_page_number(request, paginator)
        paginated_page = paginator.page(current_page)

        context = super(ActivityIndexPage, self).get_context(request)
        context.update({
            'facet_counts': facet_counts,
            'facets': all_facets,
            'activities': paginated_page,
            'total_results': total_results,
            'results_per_page': results_per_page,
            'current_page': current_page,
            'paginator': paginator,
            'show_filters': bool(facet_queries),
        })
        return context

    def get_all_facets(self, facet_map, sqs, facet_counts, facet_queries,
                       selected_facets):  # noqa: E501
        all_facets = {}
        if 'fields' in facet_counts:
            for facet, facet_config in facet_map:
                class_object, is_nested, max_facet_count = facet_config
                all_facets_sqs = sqs
                other_facet_queries = [
                    facet_query for facet_query_name, facet_query in
                    facet_queries.items()  # noqa: E501
                    if facet != facet_query_name
                ]
                for other_facet_query in other_facet_queries:
                    all_facets_sqs = all_facets_sqs.narrow(
                        str(other_facet_query))  # noqa: E501
                narrowed_facet_counts = all_facets_sqs.facet_counts()
                if 'fields' in narrowed_facet_counts and facet in narrowed_facet_counts[
                        'fields']:  # noqa: E501
                    narrowed_facets = [
                        value[0]
                        for value in narrowed_facet_counts['fields'][facet]
                    ]  # noqa: E501
                    narrowed_selected_facets = selected_facets[
                        facet] if facet in selected_facets else [
                        ]  # noqa: E501
                    if is_nested:
                        all_facets[facet] = self.get_nested_facets(
                            class_object, narrowed_facets,
                            narrowed_selected_facets)
                    else:
                        all_facets[facet] = self.get_flat_facets(
                            class_object, narrowed_facets,
                            narrowed_selected_facets)
        return all_facets

    def get_flat_facets(self, class_object, narrowed_facets, selected_facets):
        final_facets = [{
            'selected': result['id'] in selected_facets,
            'id': result['id'],
            'title': result['title'],
        } for result in class_object.objects.filter(
            pk__in=narrowed_facets).values('id', 'title')]  # noqa: E501
        return final_facets

    def get_nested_facets(self,
                          class_object,
                          narrowed_facets,
                          selected_facets,
                          parent=None):  # noqa: E501
        if not parent:
            flat_final_facets = [{
                'selected': result['id'] in selected_facets,
                'id': result['id'],
                'title': result['title'],
                'parent': result['parent'],
            } for result in class_object.objects.filter(
                pk__in=narrowed_facets).get_ancestors(True).values(
                    'id', 'title', 'parent')]  # noqa: E501
            final_facets = []
            root_facets = [
                root_facet for root_facet in flat_final_facets
                if root_facet['parent'] == None
            ]  # noqa: E501
            for root_facet in root_facets:
                children_list = self.get_nested_facets(
                    class_object, narrowed_facets, selected_facets,
                    root_facet['id'])  # noqa: E501
                child_selected = any(child['selected'] is True
                                     or child['child_selected'] is True
                                     for child in children_list  # noqa: E501
                                     )
                final_facets.append({
                    'selected': root_facet['selected'],
                    'child_selected': child_selected,
                    'id': root_facet['id'],
                    'title': root_facet['title'],
                    'parent': root_facet['parent'],
                    'children': children_list
                })
            return final_facets
        else:
            children = [
                {
                    'selected':
                    result['id'] in selected_facets
                    or result['parent'] in selected_facets,  # noqa: E501
                    'id':
                    result['id'],
                    'title':
                    result['title'],
                    'parent':
                    result['parent'],
                    'children':
                    self.get_nested_facets(class_object, narrowed_facets,
                                           selected_facets,
                                           result['id']),  # noqa: E501
                    'child_selected':
                    any(child['selected'] is True
                        or child['child_selected'] is True
                        for child in  # noqa: E501
                        self.get_nested_facets(class_object, narrowed_facets,
                                               selected_facets,
                                               result['id'])  # noqa: E501
                        )
                } for result in class_object.objects.filter(
                    pk__in=narrowed_facets).filter(
                        parent_id=parent).values('id', 'title', 'parent')
            ]  # noqa: E501
            return children

    class Meta:
        verbose_name = "TDP Activity search page"
示例#11
0
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
示例#12
0
class RegulationPage(RoutablePageMixin, SecondaryNavigationJSMixin, CFGOVPage):
    """A routable page for serving an eregulations page by Section ID."""

    objects = PageManager()
    parent_page_types = ['regulations3k.RegulationLandingPage']
    subpage_types = []

    template = 'regulations3k/browse-regulation.html'

    header = StreamField([
        ('text_introduction', molecules.TextIntroduction()),
    ],
                         blank=True)

    content = StreamField([], null=True)
    regulation = models.ForeignKey(Part,
                                   blank=True,
                                   null=True,
                                   on_delete=models.PROTECT,
                                   related_name='eregs3k_page')

    content_panels = CFGOVPage.content_panels + [
        StreamFieldPanel('header'),
        FieldPanel('regulation', Part),
    ]

    secondary_nav_exclude_sibling_pages = models.BooleanField(default=False)

    sidefoot_panels = CFGOVPage.sidefoot_panels + [
        FieldPanel('secondary_nav_exclude_sibling_pages'),
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='General Content'),
        ObjectList(sidefoot_panels, heading='Sidebar'),
        ObjectList(CFGOVPage.settings_panels, heading='Configuration'),
    ])

    @cached_property
    def section_query(self):
        """Query set for Sections in this regulation's effective version."""
        return Section.objects.filter(
            subpart__version=self.regulation.effective_version, )

    @cached_property
    def sections(self):
        return list(self.section_query.all())

    def get_context(self, request, *args, **kwargs):
        context = super(CFGOVPage, self).get_context(request, *args, **kwargs)
        context.update({
            'get_secondary_nav_items': get_reg_nav_items,
            'regulation': self.regulation,
            'section': None,
            'breadcrumb_items': self.get_breadcrumbs(request)
        })
        return context

    def get_breadcrumbs(self, request, section=None):
        landing_page = self.get_parent()
        crumbs = [{
            'href': landing_page.url,
            'title': landing_page.title,
        }]

        if section is not None:
            crumbs = crumbs + [
                {
                    'href': self.url,
                    'title': str(section.subpart.version.part),
                },
                {
                    'title': section.subpart.title,
                },
            ]

        return crumbs

    @route(r'^(?P<section_label>[0-9A-Za-z-]+)/$', name="section")
    def section_page(self, request, section_label):
        section = self.section_query.get(label=section_label)
        current_index = self.sections.index(section)
        context = self.get_context(request)

        content = regdown(section.contents,
                          url_resolver=get_url_resolver(self),
                          contents_resolver=get_contents_resolver(self),
                          render_block_reference=partial(
                              self.render_interp, context))

        context.update({
            'version':
            self.regulation.effective_version,
            'content':
            content,
            'get_secondary_nav_items':
            get_reg_nav_items,
            'next_section':
            get_next_section(self.sections, current_index),
            'previous_section':
            get_previous_section(self.sections, current_index),
            'section':
            section,
            'breadcrumb_items':
            self.get_breadcrumbs(request, section),
        })

        return TemplateResponse(request, self.template, context)

    def render_interp(self, context, raw_contents, **kwargs):
        template = get_template('regulations3k/inline_interps.html')

        # Extract the title from the raw regdown
        section_title_match = re.search(r'#+\s?(?P<section_title>.*)\s',
                                        raw_contents)
        if section_title_match is not None:
            context.update({'section_title': section_title_match.group(1)})
            span = section_title_match.span()
            raw_contents = raw_contents[:span[0]] + raw_contents[span[1]:]

        context.update({'contents': regdown(raw_contents)})
        context.update(kwargs)

        return template.render(context)
示例#13
0
class RegulationPage(RoutablePageMixin, SecondaryNavigationJSMixin, CFGOVPage):
    """A routable page for serving an eregulations page by Section ID."""

    objects = PageManager()
    parent_page_types = ['regulations3k.RegulationLandingPage']
    subpage_types = []

    template = 'regulations3k/browse-regulation.html'

    header = StreamField([
        ('text_introduction', molecules.TextIntroduction()),
    ],
                         blank=True)

    content = StreamField([
        ('info_unit_group', organisms.InfoUnitGroup()),
        ('full_width_text', organisms.FullWidthText()),
    ],
                          null=True,
                          blank=True)

    regulation = models.ForeignKey(Part,
                                   blank=True,
                                   null=True,
                                   on_delete=models.PROTECT,
                                   related_name='page')

    content_panels = CFGOVPage.content_panels + [
        StreamFieldPanel('header'),
        StreamFieldPanel('content'),
        FieldPanel('regulation', Part),
    ]

    secondary_nav_exclude_sibling_pages = models.BooleanField(default=False)

    sidefoot_panels = CFGOVPage.sidefoot_panels + [
        FieldPanel('secondary_nav_exclude_sibling_pages'),
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='General Content'),
        ObjectList(sidefoot_panels, heading='Sidebar'),
        ObjectList(CFGOVPage.settings_panels, heading='Configuration'),
    ])

    def get_effective_version(self, request, date_str):
        """ Get the requested effective version if the user has permission """
        effective_version = self.regulation.versions.get(
            effective_date=date_str)

        if effective_version.draft:
            page_perms = self.permissions_for_user(request.user)
            if not page_perms.can_edit():
                raise PermissionDenied

        return effective_version

    def get_section_query(self, effective_version=None):
        """Query set for Sections in this regulation's effective version."""
        if effective_version is None:
            effective_version = self.regulation.effective_version
        return Section.objects.filter(subpart__version=effective_version)

    def get_context(self, request, *args, **kwargs):
        context = super(RegulationPage,
                        self).get_context(request, *args, **kwargs)
        context.update({
            'regulation': self.regulation,
            'breadcrumb_items': self.get_breadcrumbs(request)
        })
        return context

    def get_breadcrumbs(self, request, section=None):
        crumbs = super(RegulationPage, self).get_breadcrumbs(request)

        if section is not None:
            crumbs = crumbs + [
                {
                    'href': self.url,
                    'title': str(section.subpart.version.part),
                },
            ]

        return crumbs

    def render_interp(self, context, raw_contents, **kwargs):
        template = get_template('regulations3k/inline_interps.html')

        # Extract the title from the raw regdown
        section_title_match = re.search(r'#+\s?(?P<section_title>.*)\s',
                                        raw_contents)
        if section_title_match is not None:
            context.update({'section_title': section_title_match.group(1)})
            span = section_title_match.span()
            raw_contents = raw_contents[:span[0]] + raw_contents[span[1]:]

        context.update({'contents': regdown(raw_contents)})
        context.update(kwargs)

        return template.render(context)

    @route(r'^((?P<date_str>[0-9]{4}-[0-9]{2}-[0-9]{2})/)?$')
    def index_route(self, request, date_str=None, *args, **kwargs):
        request.is_preview = getattr(request, 'is_preview', False)

        if date_str is not None:
            effective_version = self.get_effective_version(request, date_str)
            section_query = self.get_section_query(
                effective_version=effective_version)
        else:
            section_query = self.get_section_query()

        sections = list(section_query.all())

        context = self.get_context(request, *args, **kwargs)
        context.update({
            'get_secondary_nav_items':
            partial(get_reg_nav_items, sections=sections, date_str=date_str),
        })

        return TemplateResponse(request,
                                self.get_template(request, *args, **kwargs),
                                context)

    @route(r'^(?:(?P<date_str>[0-9]{4}-[0-9]{2}-[0-9]{2})/)?'
           r'(?P<section_label>[0-9A-Za-z-]+)/$',
           name="section")
    def section_page(self, request, date_str=None, section_label=None):
        """ Render a section of the currently effective regulation """

        if date_str is not None:
            effective_version = self.get_effective_version(request, date_str)
            section_query = self.get_section_query(
                effective_version=effective_version)
        else:
            section_query = self.get_section_query()

        sections = list(section_query.all())
        section = section_query.get(label=section_label)
        current_index = sections.index(section)
        context = self.get_context(request, sections=sections)

        content = regdown(section.contents,
                          url_resolver=get_url_resolver(self),
                          contents_resolver=get_contents_resolver(self),
                          render_block_reference=partial(
                              self.render_interp, context))

        context.update({
            'version':
            self.regulation.effective_version,
            'content':
            content,
            'get_secondary_nav_items':
            partial(get_reg_nav_items, sections=sections, date_str=date_str),
            'next_section':
            get_next_section(sections, current_index),
            'previous_section':
            get_previous_section(sections, current_index),
            'section':
            section,
            'breadcrumb_items':
            self.get_breadcrumbs(request, section),
            'search_url':
            (self.get_parent().url + 'search-regulations/results/?regs=' +
             self.regulation.part_number)
        })

        return TemplateResponse(request, self.template, context)
示例#14
0
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]
示例#15
0
class AbstractFilterPage(CFGOVPage):
    header = StreamField([
        ('article_subheader', blocks.RichTextBlock(icon='form')),
        ('text_introduction', molecules.TextIntroduction()),
        ('item_introduction', organisms.ItemIntroduction()),
    ],
                         blank=True)
    preview_title = models.CharField(max_length=255, null=True, blank=True)
    preview_subheading = models.CharField(max_length=255,
                                          null=True,
                                          blank=True)
    preview_description = RichTextField(null=True, blank=True)
    secondary_link_url = models.CharField(max_length=500,
                                          null=True,
                                          blank=True)
    secondary_link_text = models.CharField(max_length=255,
                                           null=True,
                                           blank=True)
    preview_image = models.ForeignKey('v1.CFGOVImage',
                                      null=True,
                                      blank=True,
                                      on_delete=models.SET_NULL,
                                      related_name='+')
    date_published = models.DateField(default=date.today)
    date_filed = models.DateField(null=True, blank=True)
    comments_close_by = models.DateField(null=True, blank=True)

    # Configuration tab panels
    settings_panels = [
        MultiFieldPanel(CFGOVPage.promote_panels, 'Settings'),
        InlinePanel('categories', label="Categories", max_num=2),
        FieldPanel('tags', 'Tags'),
        MultiFieldPanel([
            FieldPanel('preview_title', classname="full"),
            FieldPanel('preview_subheading', classname="full"),
            FieldPanel('preview_description', classname="full"),
            FieldPanel('secondary_link_url', classname="full"),
            FieldPanel('secondary_link_text', classname="full"),
            ImageChooserPanel('preview_image'),
        ],
                        heading='Page Preview Fields',
                        classname='collapsible'),
        FieldPanel('authors', 'Authors'),
        MultiFieldPanel([
            FieldPanel('date_published'),
            FieldPanel('date_filed'),
            FieldPanel('comments_close_by'),
        ],
                        'Relevant Dates',
                        classname='collapsible'),
        MultiFieldPanel(Page.settings_panels, 'Scheduled Publishing'),
    ]

    # This page class cannot be created.
    is_creatable = False

    objects = CFGOVPageManager()

    @classmethod
    def generate_edit_handler(self, content_panel):
        content_panels = [
            StreamFieldPanel('header'),
            content_panel,
        ]
        return TabbedInterface([
            ObjectList(self.content_panels + content_panels,
                       heading='General Content'),
            ObjectList(CFGOVPage.sidefoot_panels, heading='Sidebar'),
            ObjectList(self.settings_panels, heading='Configuration'),
        ])

    # Returns an image for the page's meta Open Graph tag
    @property
    def meta_image(self):
        parent_meta = super(AbstractFilterPage, self).meta_image
        return parent_meta or self.preview_image
示例#16
0
class RegulationPage(RoutablePageMixin, SecondaryNavigationJSMixin, CFGOVPage):
    """A routable page for serving an eregulations page by Section ID."""

    objects = PageManager()
    parent_page_types = ['regulations3k.RegulationLandingPage']
    subpage_types = []

    template = 'regulations3k/browse-regulation.html'

    header = StreamField([
        ('text_introduction', molecules.TextIntroduction()),
    ],
                         blank=True)

    content = StreamField([
        ('info_unit_group', organisms.InfoUnitGroup()),
        ('full_width_text', organisms.FullWidthText()),
    ],
                          null=True,
                          blank=True)

    regulation = models.ForeignKey(Part,
                                   blank=True,
                                   null=True,
                                   on_delete=models.PROTECT,
                                   related_name='page')

    content_panels = CFGOVPage.content_panels + [
        StreamFieldPanel('header'),
        StreamFieldPanel('content'),
        FieldPanel('regulation', Part),
    ]

    secondary_nav_exclude_sibling_pages = models.BooleanField(default=False)

    sidefoot_panels = CFGOVPage.sidefoot_panels + [
        FieldPanel('secondary_nav_exclude_sibling_pages'),
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='General Content'),
        ObjectList(sidefoot_panels, heading='Sidebar'),
        ObjectList(CFGOVPage.settings_panels, heading='Configuration'),
    ])

    def can_serve_draft_versions(self, request):
        perms = request.user.get_all_permissions()
        if (request.user.is_superuser
                or getattr(request, 'served_by_wagtail_sharing', False)
                or 'regulations3k.change_section' in perms):
            return True
        return False

    def get_versions_query(self, request):
        versions = self.regulation.versions

        if not self.can_serve_draft_versions(request):
            versions = versions.filter(draft=False)

        return versions

    def get_effective_version(self, request, date_str=None):
        """ Get the requested effective version if the user has permission """
        query_filter = {}

        if date_str is None:
            query_filter['effective_date__lte'] = date.today()
        else:
            query_filter['effective_date'] = date_str

        draft_permission = self.can_serve_draft_versions(request)
        if not draft_permission:
            query_filter['draft'] = False

        effective_version = self.regulation.versions.filter(
            **query_filter).order_by('-effective_date').first()

        if effective_version is None:
            raise Http404

        return effective_version

    def get_section_query(self, request=None, effective_version=None):
        """Query set for Sections in this regulation's effective version."""
        if effective_version is None:
            effective_version = self.get_effective_version(request)
        return Section.objects.filter(subpart__version=effective_version)

    def get_context(self, request, *args, **kwargs):
        context = super(RegulationPage,
                        self).get_context(request, *args, **kwargs)
        context.update({
            'regulation':
            self.regulation,
            'current_version':
            self.get_effective_version(request),
            'breadcrumb_items':
            self.get_breadcrumbs(request, *args, **kwargs),
            'search_url':
            (self.get_parent().url + 'search-regulations/results/?regs=' +
             self.regulation.part_number),
            'num_versions':
            self.get_versions_query(request).count(),
        })
        return context

    def get_breadcrumbs(self, request, section=None, **kwargs):
        crumbs = super(RegulationPage, self).get_breadcrumbs(request)

        if section is not None:
            crumbs = crumbs + [
                {
                    'href':
                    self.url + self.reverse_subpage(
                        'index',
                        kwargs={
                            k: v
                            for k, v in kwargs.items() if k == 'date_str'
                        }),
                    'title':
                    str(section.subpart.version.part),
                },
            ]

        return crumbs

    def get_urls_for_version(self, effective_version, section=None):
        base_url = self.get_full_url()
        versions_url = urljoin(base_url, 'versions') + '/'

        if effective_version.live_version:
            # This is the current version
            version_url = base_url
        else:
            # It's a past or future version, URLs have the date str
            date_str = str(effective_version.effective_date)
            version_url = urljoin(base_url, date_str) + '/'
            yield version_url

        if section is not None:
            yield urljoin(version_url, section.label) + '/'
        else:
            sections = self.get_section_query(
                effective_version=effective_version)
            yield version_url
            yield versions_url
            for section in sections.all():
                yield urljoin(version_url, section.label) + '/'

    def render_interp(self, context, raw_contents, **kwargs):
        template = get_template('regulations3k/inline_interps.html')

        # Extract the title from the raw regdown
        section_title_match = re.search(r'#+\s?(?P<section_title>.*)\s',
                                        raw_contents)
        if section_title_match is not None:
            context.update({'section_title': section_title_match.group(1)})
            span = section_title_match.span()
            raw_contents = raw_contents[:span[0]] + raw_contents[span[1]:]

        context.update({'contents': regdown(raw_contents)})
        context.update(kwargs)

        return template.render(context)

    @route(r'^(?:(?P<date_str>[0-9]{4}-[0-9]{2}-[0-9]{2})/)?$', name="index")
    def index_route(self, request, date_str=None):
        request.is_preview = getattr(request, 'is_preview', False)

        effective_version = self.get_effective_version(request,
                                                       date_str=date_str)
        section_query = self.get_section_query(
            effective_version=effective_version)
        sections = list(section_query.all())

        context = self.get_context(request)
        context.update({
            'requested_version':
            effective_version,
            'sections':
            sections,
            'get_secondary_nav_items':
            partial(get_secondary_nav_items,
                    sections=sections,
                    date_str=date_str),
        })

        if date_str is not None:
            context['date_str'] = date_str

        return TemplateResponse(request, self.get_template(request), context)

    @route(r'^versions/(?:(?P<section_label>' + label_re_str + r')/)?$',
           name="versions")
    def versions_page(self, request, section_label=None):
        section_query = self.get_section_query(request=request)
        sections = list(section_query.all())
        context = self.get_context(request, sections=sections)

        versions = [{
            'effective_date':
            v.effective_date,
            'date_str':
            str(v.effective_date),
            'sections':
            self.get_section_query(effective_version=v).all(),
            'draft':
            v.draft
        } for v in self.get_versions_query(request).order_by('-effective_date')
                    ]

        context.update({
            'versions':
            versions,
            'section_label':
            section_label,
            'get_secondary_nav_items':
            partial(get_secondary_nav_items, sections=sections),
        })

        return TemplateResponse(request, self.template, context)

    @route(r'^(?:(?P<date_str>[0-9]{4}-[0-9]{2}-[0-9]{2})/)?'
           r'(?P<section_label>' + label_re_str + r')/$',
           name="section")
    def section_page(self, request, date_str=None, section_label=None):
        """ Render a section of the currently effective regulation """

        effective_version = self.get_effective_version(request,
                                                       date_str=date_str)
        section_query = self.get_section_query(
            effective_version=effective_version)

        next_version = self.get_versions_query(request).filter(
            effective_date__gt=effective_version.effective_date).first()

        kwargs = {}
        if date_str is not None:
            kwargs['date_str'] = date_str

        try:
            section = section_query.get(label=section_label)
        except Section.DoesNotExist:
            return redirect(self.url +
                            self.reverse_subpage("index", kwargs=kwargs))

        sections = list(section_query.all())
        current_index = sections.index(section)
        context = self.get_context(request,
                                   section,
                                   sections=sections,
                                   **kwargs)

        content = regdown(
            section.contents,
            url_resolver=get_url_resolver(self, date_str=date_str),
            contents_resolver=get_contents_resolver(effective_version),
            render_block_reference=partial(self.render_interp, context))

        next_section = get_next_section(sections, current_index)
        previous_section = get_previous_section(sections, current_index)

        context.update({
            'requested_version':
            effective_version,
            'next_version':
            next_version,
            'section':
            section,
            'content':
            content,
            'get_secondary_nav_items':
            partial(get_secondary_nav_items,
                    sections=sections,
                    date_str=date_str),
            'next_section':
            next_section,
            'next_url':
            get_section_url(self, next_section, date_str=date_str),
            'previous_section':
            previous_section,
            'previous_url':
            get_section_url(self, previous_section, date_str=date_str),
        })

        return TemplateResponse(request, self.template, context)
class ActivityIndexPage(CFGOVPage):
    """A model for the Activity Search page."""

    subpage_types = ['teachers_digital_platform.ActivityPage']

    objects = CFGOVPageManager()

    header = StreamField([
        ('text_introduction', molecules.TextIntroduction()),
        ('notification', molecules.Notification()),
    ], blank=True)

    header_sidebar = StreamField([
        ('image', TdpSearchHeroImage()),
    ], blank=True)

    results = {}
    activity_setups = None
    content_panels = CFGOVPage.content_panels + [
        StreamFieldPanel('header'),
        StreamFieldPanel('header_sidebar'),
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='General Content'),
        ObjectList(CFGOVPage.sidefoot_panels, heading='Sidebar/Footer'),
        ObjectList(CFGOVPage.settings_panels, heading='Configuration'),
    ])

    @classmethod
    def can_create_at(cls, parent):
        # You can only create one of these!
        return super(ActivityIndexPage, cls).can_create_at(parent) \
            and not cls.objects.exists()

    def get_template(self, request):
        template = 'teachers_digital_platform/activity_index_page.html'
        if 'partial' in request.GET:
            template = 'teachers_digital_platform/activity_search_facets_and_results.html'  # noqa: E501
        return template

    def dsl_search(self, request, *args, **kwargs):
        """Search using Elasticsearch 7 and django-elasticsearch-dsl."""
        all_facets = copy.copy(self.activity_setups.facet_setup)
        selected_facets = {}
        card_setup = self.activity_setups.ordered_cards
        total_activities = len(card_setup)
        search_query = request.GET.get('q', '')
        facet_called = any(
            [request.GET.get(facet, '') for facet in FACET_LIST]
        )
        # If there's no query or facet request, we can return cached setups:
        if not search_query and not facet_called:
            payload = {
                'search_query': search_query,
                'results': list(card_setup.values()),
                'total_results': total_activities,
                'total_activities': total_activities,
                'selected_facets': selected_facets,
                'all_facets': all_facets,
                'expanded_facets': ALWAYS_EXPANDED,
            }
            self.results = payload
            results_per_page = validate_results_per_page(request)
            paginator = Paginator(payload['results'], results_per_page)
            current_page = validate_page_number(request, paginator)
            paginated_page = paginator.page(current_page)
            context_update = {
                'facets': all_facets,
                'activities': paginated_page,
                'total_results': total_activities,
                'results_per_page': results_per_page,
                'current_page': current_page,
                'paginator': paginator,
                'show_filters': bool(selected_facets),
            }
            return context_update

        dsl_search = ActivityPageDocument().search()
        if search_query:
            terms = search_query.split()
            for term in terms:
                dsl_search = dsl_search.query(
                    "bool",
                    must=Q("multi_match", query=term, fields=SEARCH_FIELDS)
                )
        else:
            dsl_search = dsl_search.sort('-date')
        for facet, facet_config in FACET_MAP:
            if facet in request.GET and request.GET.get(facet):
                facet_ids = [
                    value for value in request.GET.getlist(facet)
                    if value.isdigit()
                ]
                selected_facets[facet] = facet_ids
        for facet, pks in selected_facets.items():
            dsl_search = dsl_search.query(
                "bool",
                should=[Q("match", **{facet: pk}) for pk in pks]
            )
        facet_search = dsl_search.update_from_dict(FACET_DICT)
        total_results = dsl_search.count()
        dsl_search = dsl_search[:total_results]
        response = dsl_search.execute()
        results = [
            card_setup[str(hit.id)] for hit in response[:total_results]
        ]
        facet_response = facet_search.execute()
        facet_counts = {facet: getattr(
            facet_response.aggregations, f"{facet}_terms").buckets
            for facet in FACET_LIST}
        all_facets = parse_dsl_facets(
            all_facets, facet_counts, selected_facets
        )
        payload = {
            'search_query': search_query,
            'results': results,
            'total_results': total_results,
            'total_activities': total_activities,
            'selected_facets': selected_facets,
            'all_facets': all_facets,
        }
        # List all facet blocks that need to be expanded
        conditionally_expanded = {
            facet_name for facet_name, facet_items in all_facets.items()
            if any(
                facet['selected'] is True for facet in facet_items
            )
        }
        expanded_facets = ALWAYS_EXPANDED.union(set(conditionally_expanded))
        payload.update({
            'expanded_facets': expanded_facets,
        })
        self.results = payload
        results_per_page = validate_results_per_page(request)
        paginator = Paginator(payload['results'], results_per_page)
        current_page = validate_page_number(request, paginator)
        paginated_page = paginator.page(current_page)
        context_update = {
            'facets': all_facets,
            'activities': paginated_page,
            'total_results': total_results,
            'results_per_page': results_per_page,
            'current_page': current_page,
            'paginator': paginator,
            'show_filters': bool(selected_facets),
        }
        return context_update

    def get_context(self, request, *args, **kwargs):
        if not self.activity_setups:
            self.activity_setups = get_activity_setup()
        context_update = self.dsl_search(request, *args, **kwargs)
        context = super(ActivityIndexPage, self).get_context(request)
        context.update(context_update)
        return context

    class Meta:
        verbose_name = "TDP Activity search page"
示例#18
0
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]