Example #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')
    ]
Example #2
0
class BlogPage(AbstractFilterPage):
    content = StreamField([
        ('full_width_text', organisms.FullWidthText()),
        ('info_unit_group', organisms.InfoUnitGroup()),
        ('expandable', organisms.Expandable()),
        ('well', organisms.Well()),
        ('video_player', organisms.VideoPlayer()),
        ('email_signup', organisms.EmailSignUp()),
        ('feedback', v1_blocks.Feedback()),
    ])
    edit_handler = AbstractFilterPage.generate_edit_handler(
        content_panel=StreamFieldPanel('content'))
    template = 'blog/blog_page.html'

    objects = PageManager()
    search_fields = AbstractFilterPage.search_fields + [
        index.SearchField('content')
    ]

    def get_context(self, request, *args, **kwargs):
        context = super(BlogPage, self).get_context(request, *args, **kwargs)

        context['rss_feed'] = get_appropriate_rss_feed_url_for_page(
            self, request=request)

        return context
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()
Example #4
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()
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')
    ]
Example #6
0
class LegacyBlogPage(AbstractFilterPage):
    content = StreamField([
        ('content',
         blocks.RawHTMLBlock(help_text='Content from WordPress unescaped.')),
        ('feedback', v1_blocks.Feedback()),
    ])
    objects = CFGOVPageManager()
    edit_handler = AbstractFilterPage.generate_edit_handler(
        content_panel=StreamFieldPanel('content'))
    template = 'blog/blog_page.html'
Example #7
0
class BlogPage(AbstractFilterPage):
    content = StreamField([
        ('full_width_text', organisms.FullWidthText()),
        ('image_text_50_50_group', organisms.ImageText5050Group()),
        ('feedback', v1_blocks.Feedback()),
        ('email_signup', organisms.EmailSignUp()),
        ('expandable', organisms.Expandable()),
    ])
    edit_handler = AbstractFilterPage.generate_edit_handler(
        content_panel=StreamFieldPanel('content'))
    template = 'blog/blog_page.html'

    objects = PageManager()
class BrowseFilterableContent(StreamBlock):
    """Defines the StreamField blocks for BrowseFilterablePage content.

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

    class Meta:
        block_counts = {
            'filter_controls': {'max_num': 1},
        }
Example #9
0
class FormExplainerContent(StreamBlock):
    """Defines the StreamField blocks for FormExplainer page's content.
    Pages can have at most one Explainer block.
    """
    explainer = Explainer()
    well = organisms.Well()
    info_unit_group = organisms.InfoUnitGroup()
    feedback = v1_blocks.Feedback()

    class Meta:
        block_counts = {
            'explainer': {
                'max_num': 1
            },
        }
Example #10
0
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 LegacyBlogPage(AbstractFilterPage):
    content = StreamField([
        ('content',
         blocks.RawHTMLBlock(help_text='Content from WordPress unescaped.')),
        ('feedback', v1_blocks.Feedback()),
        ('reusable_text',
         v1_blocks.ReusableTextChooserBlock('v1.ReusableText')),
    ])
    objects = CFGOVPageManager()
    edit_handler = AbstractFilterPage.generate_edit_handler(
        content_panel=StreamFieldPanel('content'))
    template = 'blog/blog_page.html'

    search_fields = AbstractFilterPage.search_fields + [
        index.SearchField('content')
    ]
Example #12
0
class BlogPage(AbstractFilterPage):
    content = StreamField([
        ('full_width_text', organisms.FullWidthText()),
        ('info_unit_group', organisms.InfoUnitGroup()),
        ('expandable', organisms.Expandable()),
        ('well', organisms.Well()),
        ('email_signup', organisms.EmailSignUp()),
        ('feedback', v1_blocks.Feedback()),
    ])
    edit_handler = AbstractFilterPage.generate_edit_handler(
        content_panel=StreamFieldPanel('content'))
    template = 'blog/blog_page.html'

    objects = PageManager()
    search_fields = AbstractFilterPage.search_fields + [
        index.SearchField('content')
    ]
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', 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()

    @property
    def page_js(self):
        return (
            super(BrowseFilterablePage, self).page_js
            + ['secondary-navigation.js']
        )
Example #15
0
class AnswerPage(CFGOVPage):
    """Page type for Ask CFPB answers."""
    from ask_cfpb.models import Answer
    last_edited = models.DateField(
        blank=True,
        null=True,
        help_text="Change the date to today if you make a significant change.")
    question = models.TextField(blank=True)
    statement = models.TextField(
        blank=True,
        help_text=(
            "(Optional) Use this field to rephrase the question title as "
            "a statement. Use only if this answer has been chosen to appear "
            "on a money topic portal (e.g. /consumer-tools/debt-collection)."))
    short_answer = RichTextField(blank=True,
                                 features=['link', 'document-link'],
                                 help_text='Optional answer intro')
    answer_content = StreamField(ask_blocks.AskAnswerContent(),
                                 blank=True,
                                 verbose_name='Answer')
    answer_base = models.ForeignKey(Answer,
                                    blank=True,
                                    null=True,
                                    related_name='answer_pages',
                                    on_delete=models.SET_NULL)
    redirect_to_page = models.ForeignKey(
        'self',
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        related_name='redirect_to_pages',
        help_text="Choose another AnswerPage to redirect this page to")
    featured = models.BooleanField(
        default=False,
        help_text=("Check to make this one of two featured answers "
                   "on the landing page."))
    featured_rank = models.IntegerField(blank=True, null=True)
    category = models.ManyToManyField(
        'Category',
        blank=True,
        help_text=("Categorize this answer. "
                   "Avoid putting into more than one category."))
    search_tags = models.CharField(
        max_length=1000,
        blank=True,
        help_text="Search words or phrases, separated by commas")
    related_resource = models.ForeignKey(RelatedResource,
                                         blank=True,
                                         null=True,
                                         on_delete=models.SET_NULL)
    related_questions = ParentalManyToManyField(
        'self',
        symmetrical=False,
        blank=True,
        related_name='related_question',
        help_text='Maximum of 3 related questions')
    portal_topic = ParentalManyToManyField(
        PortalTopic,
        blank=True,
        help_text='Limit to 1 portal topic if possible')
    primary_portal_topic = ParentalKey(
        PortalTopic,
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        related_name='primary_portal_topic',
        help_text=("Use only if assigning more than one portal topic, "
                   "to control which topic is used as a breadcrumb."))
    portal_category = ParentalManyToManyField(PortalCategory, blank=True)

    user_feedback = StreamField([
        ('feedback', v1_blocks.Feedback()),
    ],
                                blank=True)

    content_panels = CFGOVPage.content_panels + [
        MultiFieldPanel([
            FieldPanel('last_edited'),
            FieldPanel('question'),
            FieldPanel('statement'),
            FieldPanel('short_answer')
        ],
                        heading="Page content",
                        classname="collapsible"),
        StreamFieldPanel('answer_content'),
        MultiFieldPanel([
            SnippetChooserPanel('related_resource'),
            AutocompletePanel('related_questions',
                              page_type='ask_cfpb.AnswerPage',
                              is_single=False)
        ],
                        heading="Related resources",
                        classname="collapsible"),
        MultiFieldPanel([
            FieldPanel('portal_topic', widget=forms.CheckboxSelectMultiple),
            FieldPanel('primary_portal_topic'),
            FieldPanel('portal_category', widget=forms.CheckboxSelectMultiple)
        ],
                        heading="Portal tags",
                        classname="collapsible"),
        MultiFieldPanel([FieldPanel('featured')],
                        heading="Featured answer on Ask landing page",
                        classname="collapsible"),
        MultiFieldPanel([
            AutocompletePanel('redirect_to_page',
                              page_type='ask_cfpb.AnswerPage')
        ],
                        heading="Redirect to another answer",
                        classname="collapsible"),
        MultiFieldPanel([StreamFieldPanel('user_feedback')],
                        heading="User feedback",
                        classname="collapsible collapsed"),
    ]

    sidebar = StreamField([
        ('call_to_action', molecules.CallToAction()),
        ('related_links', molecules.RelatedLinks()),
        ('related_metadata', molecules.RelatedMetadata()),
        ('email_signup', organisms.EmailSignUp()),
        ('sidebar_contact', organisms.SidebarContactInfo()),
        ('rss_feed', molecules.RSSFeed()),
        ('social_media', molecules.SocialMedia()),
        ('reusable_text', v1_blocks.ReusableTextChooserBlock(ReusableText)),
    ],
                          blank=True)

    sidebar_panels = [
        StreamFieldPanel('sidebar'),
    ]

    search_fields = Page.search_fields + [
        index.SearchField('answer_content'),
        index.SearchField('short_answer')
    ]

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

    template = 'ask-cfpb/answer-page.html'

    objects = CFGOVPageManager()

    def get_sibling_url(self):
        if self.answer_base:
            if self.language == 'es':
                sibling = self.answer_base.english_page
            else:
                sibling = self.answer_base.spanish_page
            if sibling and sibling.live and not sibling.redirect_to_page:
                return sibling.url

    def get_context(self, request, *args, **kwargs):
        portal_topic = self.primary_portal_topic or self.portal_topic.first()
        context = super(AnswerPage, self).get_context(request)
        context['related_questions'] = self.related_questions.all()
        context['description'] = (self.short_answer if self.short_answer else
                                  Truncator(self.answer_content).words(
                                      40, truncate=' ...'))
        context['last_edited'] = self.last_edited
        context['portal_page'] = get_portal_or_portal_search_page(
            portal_topic, language=self.language)
        context['breadcrumb_items'] = get_ask_breadcrumbs(
            language=self.language,
            portal_topic=portal_topic,
        )
        context['about_us'] = get_standard_text(self.language, 'about_us')
        context['disclaimer'] = get_standard_text(self.language, 'disclaimer')
        context['sibling_url'] = self.get_sibling_url()
        return context

    def __str__(self):
        if self.answer_base:
            return '{}: {}'.format(self.answer_base.id, self.title)
        else:
            return self.title

    @property
    def clean_search_tags(self):
        return [tag.strip() for tag in self.search_tags.split(',')]

    @property
    def status_string(self):
        if self.redirect_to_page:
            if not self.live:
                return ("redirected but not live")
            else:
                return ("redirected")
        else:
            return super(AnswerPage, self).status_string

    # Returns an image for the page's meta Open Graph tag
    @property
    def meta_image(self):
        if self.social_sharing_image:
            return self.social_sharing_image

        if not self.category.exists():
            return None

        return self.category.first().category_image

    # Overrides the default of page.id for comparing against split testing
    # clusters. See: core.feature_flags.in_split_testing_cluster
    @property
    def split_test_id(self):
        return self.answer_base.id
Example #16
0
class AnswerPage(CFGOVPage):
    """
    Page type for Ask CFPB answers.
    """
    from ask_cfpb.models import Answer
    last_edited = models.DateField(
        blank=True,
        null=True,
        help_text="Change the date to today if you make a significant change.")
    question = models.TextField(blank=True)
    statement = models.TextField(
        blank=True,
        help_text=(
            "(Optional) Use this field to rephrase the question title as "
            "a statement. Use only if this answer has been chosen to appear "
            "on a money topic portal (e.g. /consumer-tools/debt-collection)."))
    short_answer = RichTextField(blank=True, help_text='Optional answer intro')
    answer = RichTextField(
        blank=True,
        features=[
            'bold', 'italic', 'h2', 'h3', 'h4', 'link', 'ol', 'ul',
            'document-link', 'image', 'embed', 'ask-tips', 'edit-html'
        ],
        help_text=(
            "Do not use H2 or H3 to style text. Only use the HTML Editor "
            "for troubleshooting. To style tips, warnings and notes, "
            "select the content that will go inside the rule lines "
            "(so, title + paragraph) and click the Pencil button "
            "to style it. Re-select the content and click the button "
            "again to unstyle the tip."))
    answer_base = models.ForeignKey(Answer,
                                    blank=True,
                                    null=True,
                                    related_name='answer_pages',
                                    on_delete=models.SET_NULL)
    redirect_to = models.ForeignKey(
        Answer,
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        related_name='redirected_pages',
        help_text="Choose another Answer to redirect this page to")
    redirect_to_page = models.ForeignKey(
        'self',
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        related_name='redirect_to_pages',
        help_text="Choose another AnswerPage to redirect this page to")
    featured = models.BooleanField(
        default=False,
        help_text=("Check to make this one of two featured answers "
                   "on the landing page."))
    featured_rank = models.IntegerField(blank=True, null=True)
    category = models.ManyToManyField(
        'Category',
        blank=True,
        help_text=("Categorize this answer. "
                   "Avoid putting into more than one category."))
    subcategory = models.ManyToManyField(
        'SubCategory',
        blank=True,
        help_text=("Choose only subcategories that belong "
                   "to one of the categories checked above."))
    search_tags = models.CharField(
        max_length=1000,
        blank=True,
        help_text="Search words or phrases, separated by commas")
    related_resource = models.ForeignKey(RelatedResource,
                                         blank=True,
                                         null=True,
                                         on_delete=models.SET_NULL)
    related_questions = ParentalManyToManyField(
        'self',
        symmetrical=False,
        blank=True,
        related_name='related_question',
        help_text='Maximum of 3 related questions')
    answer_id = models.IntegerField(default=0)
    portal_topic = ParentalManyToManyField(
        PortalTopic,
        blank=True,
        help_text='Limit to 1 portal topic if possible')
    primary_portal_topic = ParentalKey(
        PortalTopic,
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        related_name='primary_portal_topic',
        help_text=("Use only if assigning more than one portal topic, "
                   "to control which topic is used as a breadcrumb."))
    portal_category = ParentalManyToManyField(PortalCategory, blank=True)

    user_feedback = StreamField([
        ('feedback', v1_blocks.Feedback()),
    ],
                                blank=True)

    content_panels = CFGOVPage.content_panels + [
        MultiFieldPanel([
            FieldPanel('last_edited'),
            FieldPanel('question'),
            FieldPanel('statement'),
            FieldPanel('short_answer'),
            FieldPanel('answer')
        ],
                        heading="Page content",
                        classname="collapsible"),
        MultiFieldPanel([
            SnippetChooserPanel('related_resource'),
            AutocompletePanel('related_questions',
                              page_type='ask_cfpb.AnswerPage',
                              is_single=False)
        ],
                        heading="Related resources",
                        classname="collapsible"),
        MultiFieldPanel([
            FieldPanel('portal_topic', widget=forms.CheckboxSelectMultiple),
            FieldPanel('primary_portal_topic'),
            FieldPanel('portal_category', widget=forms.CheckboxSelectMultiple)
        ],
                        heading="Portal tags",
                        classname="collapsible"),
        MultiFieldPanel([
            AutocompletePanel('redirect_to_page',
                              page_type='ask_cfpb.AnswerPage')
        ],
                        heading="Redirect to another answer",
                        classname="collapsible"),
        MultiFieldPanel([StreamFieldPanel('user_feedback')],
                        heading="User feedback",
                        classname="collapsible collapsed"),
    ]

    sidebar = StreamField([
        ('call_to_action', molecules.CallToAction()),
        ('related_links', molecules.RelatedLinks()),
        ('related_metadata', molecules.RelatedMetadata()),
        ('email_signup', organisms.EmailSignUp()),
        ('sidebar_contact', organisms.SidebarContactInfo()),
        ('rss_feed', molecules.RSSFeed()),
        ('social_media', molecules.SocialMedia()),
        ('reusable_text', v1_blocks.ReusableTextChooserBlock(ReusableText)),
    ],
                          blank=True)

    sidebar_panels = [
        StreamFieldPanel('sidebar'),
    ]

    search_fields = Page.search_fields + [
        index.SearchField('answer'),
        index.SearchField('short_answer')
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
        ObjectList(sidebar_panels, heading='Sidebar (English only)'),
        ObjectList(CFGOVPage.settings_panels, heading='Configuration'),
    ])

    objects = CFGOVPageManager()

    def get_context(self, request, *args, **kwargs):
        context = super(AnswerPage, self).get_context(request)
        context['related_questions'] = self.related_questions.all()
        context['description'] = self.short_answer if self.short_answer \
            else Truncator(self.answer).words(40, truncate=' ...')
        context['answer_id'] = self.answer_base.id
        if self.language == 'es':
            context['search_tags'] = self.clean_search_tags
            context['tweet_text'] = Truncator(self.question).chars(
                100, truncate=' ...')
            context['disclaimer'] = get_reusable_text_snippet(
                SPANISH_DISCLAIMER_SNIPPET_TITLE)
            context['category'] = self.category.first()
        elif self.language == 'en':
            context['about_us'] = get_reusable_text_snippet(
                ABOUT_US_SNIPPET_TITLE)
            context['disclaimer'] = get_reusable_text_snippet(
                ENGLISH_DISCLAIMER_SNIPPET_TITLE)
            context['last_edited'] = self.last_edited
            # breadcrumbs and/or category should reflect
            # the referrer if it is a consumer tools portal or
            # ask category page
            context['category'], context['breadcrumb_items'] = \
                get_question_referrer_data(
                    request, self.category.all())
            subcategories = []
            for subcat in self.subcategory.all():
                if subcat.parent == context['category']:
                    subcategories.append(subcat)
                for related in subcat.related_subcategories.all():
                    if related.parent == context['category']:
                        subcategories.append(related)
            context['subcategories'] = set(subcategories)

        return context

    def get_template(self, request):
        printable = request.GET.get('print', False)
        if self.language == 'es':
            if printable == 'true':
                return 'ask-cfpb/answer-page-spanish-printable.html'

            return 'ask-cfpb/answer-page-spanish.html'

        return 'ask-cfpb/answer-page.html'

    def __str__(self):
        if self.answer_base:
            return '{}: {}'.format(self.answer_base.id, self.title)
        else:
            return self.title

    @property
    def clean_search_tags(self):
        return [tag.strip() for tag in self.search_tags.split(',')]

    @property
    def status_string(self):
        if self.redirect_to_page:
            if not self.live:
                return ("redirected but not live")
            else:
                return ("redirected")
        else:
            return super(AnswerPage, self).status_string

    # Returns an image for the page's meta Open Graph tag
    @property
    def meta_image(self):
        if self.social_sharing_image:
            return self.social_sharing_image

        if not self.category.exists():
            return None

        return self.category.first().category_image

    # Overrides the default of page.id for comparing against split testing
    # clusters. See: core.feature_flags.in_split_testing_cluster
    @property
    def split_test_id(self):
        return self.answer_base.id
Example #17
0
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
class AnswerPage(CFGOVPage):
    """Page type for Ask CFPB answers."""

    from ask_cfpb.models.django import Answer
    last_edited = models.DateField(
        blank=True,
        null=True,
        help_text="Change the date to today if you make a significant change.")
    question = models.TextField(blank=True)
    statement = models.TextField(
        blank=True,
        help_text=(
            "(Optional) Use this field to rephrase the question title as "
            "a statement. Use only if this answer has been chosen to appear "
            "on a money topic portal (e.g. /consumer-tools/debt-collection)."))
    short_answer = RichTextField(blank=True,
                                 features=['link', 'document-link'],
                                 help_text='Optional answer intro')
    answer_content = StreamField(ask_blocks.AskAnswerContent(),
                                 blank=True,
                                 verbose_name='Answer')
    answer_base = models.ForeignKey(Answer,
                                    blank=True,
                                    null=True,
                                    related_name='answer_pages',
                                    on_delete=models.SET_NULL)
    redirect_to_page = models.ForeignKey(
        'self',
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        related_name='redirect_to_pages',
        help_text="Choose another AnswerPage to redirect this page to")
    featured = models.BooleanField(
        default=False,
        help_text=("Check to make this one of two featured answers "
                   "on the landing page."))
    featured_rank = models.IntegerField(blank=True, null=True)
    category = models.ManyToManyField(
        'Category',
        blank=True,
        help_text=("Categorize this answer. "
                   "Avoid putting into more than one category."))
    search_tags = models.CharField(
        max_length=1000,
        blank=True,
        help_text="Search words or phrases, separated by commas")
    related_resource = models.ForeignKey(RelatedResource,
                                         blank=True,
                                         null=True,
                                         on_delete=models.SET_NULL)
    related_questions = ParentalManyToManyField(
        'self',
        symmetrical=False,
        blank=True,
        related_name='related_question',
        help_text='Maximum of 3 related questions')
    portal_topic = ParentalManyToManyField(
        PortalTopic,
        blank=True,
        help_text='Limit to 1 portal topic if possible')
    primary_portal_topic = ParentalKey(
        PortalTopic,
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        related_name='primary_portal_topic',
        help_text=("Use only if assigning more than one portal topic, "
                   "to control which topic is used as a breadcrumb."))
    portal_category = ParentalManyToManyField(PortalCategory, blank=True)

    user_feedback = StreamField([
        ('feedback', v1_blocks.Feedback()),
    ],
                                blank=True)

    share_and_print = models.BooleanField(
        default=False,
        help_text="Include share and print buttons above answer.")

    content_panels = CFGOVPage.content_panels + [
        MultiFieldPanel([
            FieldPanel('last_edited'),
            FieldPanel('question'),
            FieldPanel('statement'),
            FieldPanel('short_answer')
        ],
                        heading="Page content",
                        classname="collapsible"),
        FieldPanel('share_and_print'),
        StreamFieldPanel('answer_content'),
        MultiFieldPanel([
            SnippetChooserPanel('related_resource'),
            AutocompletePanel('related_questions',
                              target_model='ask_cfpb.AnswerPage')
        ],
                        heading="Related resources",
                        classname="collapsible"),
        MultiFieldPanel([
            FieldPanel('portal_topic', widget=forms.CheckboxSelectMultiple),
            FieldPanel('primary_portal_topic'),
            FieldPanel('portal_category', widget=forms.CheckboxSelectMultiple)
        ],
                        heading="Portal tags",
                        classname="collapsible"),
        MultiFieldPanel([FieldPanel('featured')],
                        heading="Featured answer on Ask landing page",
                        classname="collapsible"),
        MultiFieldPanel([
            AutocompletePanel('redirect_to_page',
                              target_model='ask_cfpb.AnswerPage')
        ],
                        heading="Redirect to another answer",
                        classname="collapsible"),
        MultiFieldPanel([StreamFieldPanel('user_feedback')],
                        heading="User feedback",
                        classname="collapsible collapsed"),
    ]

    sidebar = StreamField([
        ('call_to_action', molecules.CallToAction()),
        ('related_links', molecules.RelatedLinks()),
        ('related_metadata', molecules.RelatedMetadata()),
        ('email_signup', organisms.EmailSignUp()),
        ('sidebar_contact', organisms.SidebarContactInfo()),
        ('rss_feed', molecules.RSSFeed()),
        ('social_media', molecules.SocialMedia()),
        ('reusable_text', v1_blocks.ReusableTextChooserBlock(ReusableText)),
    ],
                          blank=True)

    sidebar_panels = [
        StreamFieldPanel('sidebar'),
    ]

    search_fields = Page.search_fields + [
        index.SearchField('answer_content'),
        index.SearchField('short_answer')
    ]

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

    template = 'ask-cfpb/answer-page.html'

    objects = CFGOVPageManager()

    def get_sibling_url(self):
        if self.answer_base:
            if self.language == 'es':
                sibling = self.answer_base.english_page
            else:
                sibling = self.answer_base.spanish_page
            if sibling and sibling.live and not sibling.redirect_to_page:
                return sibling.url

    def get_meta_description(self):
        """Determine what the page's meta and OpenGraph description should be

        Checks several different possible fields in order of preference.
        If none are found, returns an empty string, which is preferable to a
        generic description repeated on many pages.

        This method is overriding the standard one on CFGOVPage to factor in
        Ask CFPB AnswerPage-specific fields.
        """

        preference_order = [
            'search_description',
            'short_answer',
            'first_text',
        ]
        candidates = {}

        if self.search_description:
            candidates['search_description'] = self.search_description
        if self.short_answer:
            candidates['short_answer'] = strip_tags(self.short_answer)
        if hasattr(self, 'answer_content'):
            for block in self.answer_content:
                if block.block_type == 'text':
                    candidates['first_text'] = truncate_by_words_and_chars(
                        strip_tags(block.value['content'].source),
                        word_limit=35,
                        char_limit=160)
                    break

        for entry in preference_order:
            if candidates.get(entry):
                return candidates[entry]

        return ''

    def get_context(self, request, *args, **kwargs):
        # self.get_meta_description() is not called here because it is called
        # and added to the context by CFGOVPage's get_context() method.
        portal_topic = self.primary_portal_topic or self.portal_topic.first()
        context = super(AnswerPage, self).get_context(request)
        context['related_questions'] = self.related_questions.all()
        context['last_edited'] = self.last_edited
        context['portal_page'] = get_portal_or_portal_search_page(
            portal_topic, language=self.language)
        context['breadcrumb_items'] = get_ask_breadcrumbs(
            language=self.language,
            portal_topic=portal_topic,
        )
        context['about_us'] = get_standard_text(self.language, 'about_us')
        context['disclaimer'] = get_standard_text(self.language, 'disclaimer')
        context['sibling_url'] = self.get_sibling_url()
        return context

    def answer_content_text(self):
        raw_text = extract_raw_text(self.answer_content.stream_data)
        return strip_tags(" ".join([self.short_answer, raw_text]))

    def answer_content_data(self):
        return truncate_by_words_and_chars(self.answer_content_text())

    def short_answer_data(self):
        return ' '.join(
            RichTextField.get_searchable_content(self, self.short_answer))

    def text(self):
        short_answer = self.short_answer_data()
        answer_text = self.answer_content_text()
        full_text = f"{short_answer}\n\n{answer_text}\n\n{self.question}"
        return full_text

    def __str__(self):
        if self.answer_base:
            return f"{self.answer_base.id}: {self.title}"
        else:
            return self.title

    @property
    def clean_search_tags(self):
        return [tag.strip() for tag in self.search_tags.split(",")]

    @property
    def status_string(self):
        if self.redirect_to_page:
            if not self.live:
                return ("redirected but not live")
            else:
                return ("redirected")
        else:
            return super(AnswerPage, self).status_string

    # Returns an image for the page's meta Open Graph tag
    @property
    def meta_image(self):
        if self.social_sharing_image:
            return self.social_sharing_image

        if not self.category.exists():
            return None

        return self.category.first().category_image

    # Overrides the default of page.id for comparing against split testing
    # clusters. See: core.feature_flags.in_split_testing_cluster
    @property
    def split_test_id(self):
        return self.answer_base.id
Example #19
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
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]
Example #21
0
class AnswerPage(CFGOVPage):
    """
    Page type for Ask CFPB answers.
    """
    from ask_cfpb.models import Answer
    question = RichTextField(blank=True, editable=False)
    answer = RichTextField(blank=True, editable=False)
    snippet = RichTextField(
        blank=True, help_text='Optional answer intro', editable=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    publish_date = models.DateTimeField(default=timezone.now)
    answer_base = models.ForeignKey(
        Answer,
        blank=True,
        null=True,
        related_name='answer_pages',
        on_delete=models.SET_NULL)
    redirect_to = models.ForeignKey(
        Answer,
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        related_name='redirected_pages',
        help_text="Choose another Answer to redirect this page to")

    content = StreamField([
        ('feedback', v1_blocks.Feedback()),
    ], blank=True)

    content_panels = CFGOVPage.content_panels + [
        FieldPanel('redirect_to'),
    ]

    sidebar = StreamField([
        ('call_to_action', molecules.CallToAction()),
        ('related_links', molecules.RelatedLinks()),
        ('related_metadata', molecules.RelatedMetadata()),
        ('email_signup', organisms.EmailSignUp()),
        ('sidebar_contact', organisms.SidebarContactInfo()),
        ('rss_feed', molecules.RSSFeed()),
        ('social_media', molecules.SocialMedia()),
        ('reusable_text', v1_blocks.ReusableTextChooserBlock(ReusableText)),
    ], blank=True)

    sidebar_panels = [StreamFieldPanel('sidebar'), ]

    search_fields = Page.search_fields + [
        index.SearchField('question'),
        index.SearchField('answer'),
        index.SearchField('answer_base'),
        index.FilterField('language')
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
        ObjectList(sidebar_panels, heading='Sidebar (English only)'),
        ObjectList(CFGOVPage.settings_panels, heading='Configuration'),
    ])

    objects = CFGOVPageManager()

    def get_context(self, request, *args, **kwargs):
        context = super(AnswerPage, self).get_context(request)
        context['answer_id'] = self.answer_base.id
        context['related_questions'] = self.answer_base.related_questions.all()
        context['description'] = self.snippet if self.snippet \
            else Truncator(self.answer).words(40, truncate=' ...')
        context['audiences'] = [
            {'text': audience.name,
             'url': '/ask-cfpb/audience-{}'.format(
                    slugify(audience.name))}
            for audience in self.answer_base.audiences.all()]
        if self.language == 'es':
            tag_dict = self.Answer.valid_tags(language='es')
            context['tags_es'] = [tag for tag in self.answer_base.tags_es
                                  if tag in tag_dict['valid_tags']]
            context['tweet_text'] = Truncator(self.question).chars(
                100, truncate=' ...')
            context['disclaimer'] = get_reusable_text_snippet(
                SPANISH_DISCLAIMER_SNIPPET_TITLE)
            context['category'] = self.answer_base.category.first()
        elif self.language == 'en':
            # we're not using tags on English pages yet, so cut the overhead
            # tag_dict = self.Answer.valid_tags()
            # context['tags'] = [tag for tag in self.answer_base.tags
            #                    if tag in tag_dict['valid_tags']]
            context['about_us'] = get_reusable_text_snippet(
                ABOUT_US_SNIPPET_TITLE)
            context['disclaimer'] = get_reusable_text_snippet(
                ENGLISH_DISCLAIMER_SNIPPET_TITLE)
            context['last_edited'] = self.answer_base.last_edited
            # breadcrumbs and/or category should reflect
            # the referrer if it is a consumer tools portal or
            # ask category page
            context['category'], context['breadcrumb_items'] = \
                get_question_referrer_data(
                    request, self.answer_base.category.all())
            subcategories = []
            for subcat in self.answer_base.subcategory.all():
                if subcat.parent == context['category']:
                    subcategories.append(subcat)
                for related in subcat.related_subcategories.all():
                    if related.parent == context['category']:
                        subcategories.append(related)
            context['subcategories'] = set(subcategories)

        return context

    def get_template(self, request):
        printable = request.GET.get('print', False)
        if self.language == 'es':
            if printable == 'true':
                return 'ask-cfpb/answer-page-spanish-printable.html'

            return 'ask-cfpb/answer-page-spanish.html'

        return 'ask-cfpb/answer-page.html'

    def __str__(self):
        if self.answer_base:
            return '{}: {}'.format(self.answer_base.id, self.title)
        else:
            return self.title

    @property
    def status_string(self):
        if self.redirect_to:
            if not self.live:
                return _("redirected but not live")
            else:
                return _("redirected")
        else:
            return super(AnswerPage, self).status_string

    # Returns an image for the page's meta Open Graph tag
    @property
    def meta_image(self):
        if self.answer_base.social_sharing_image:
            return self.answer_base.social_sharing_image

        if not self.answer_base.category.exists():
            return None

        return self.answer_base.category.first().category_image
Example #22
0
class AnswerPage(CFGOVPage):
    """
    Page type for Ask CFPB answers.
    """
    from ask_cfpb.models import Answer
    last_edited = models.DateField(
        blank=True,
        null=True,
        help_text="Change the date to today if you make a significant change.")
    question = models.TextField(blank=True)
    statement = models.TextField(
        blank=True,
        help_text=(
            "(Optional) Use this field to rephrase the question title as "
            "a statement. Use only if this answer has been chosen to appear "
            "on a money topic portal (e.g. /consumer-tools/debt-collection)."))
    answer = RichTextField(
        blank=True,
        features=[
            'bold', 'italic', 'h2', 'h3', 'h4', 'link', 'ol', 'ul',
            'document-link', 'image', 'embed', 'ask-tips', 'edit-html'
        ],
        help_text=(
            "Do not use H2 or H3 to style text. Only use the HTML Editor "
            "for troubleshooting. To style tips, warnings and notes, "
            "select the content that will go inside the rule lines "
            "(so, title + paragraph) and click the Pencil button "
            "to style it. Re-select the content and click the button "
            "again to unstyle the tip."))
    snippet = RichTextField(blank=True, help_text='Optional answer intro')
    search_tags = models.CharField(
        max_length=1000,
        blank=True,
        help_text="PLEASE DON'T USE. THIS FIELD IS NOT YET ACTIVATED.")
    answer_base = models.ForeignKey(Answer,
                                    blank=True,
                                    null=True,
                                    related_name='answer_pages',
                                    on_delete=models.SET_NULL)
    redirect_to = models.ForeignKey(
        Answer,
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        related_name='redirected_pages',
        help_text="Choose another Answer to redirect this page to")

    content = StreamField([
        ('feedback', v1_blocks.Feedback()),
    ], blank=True)

    content_panels = CFGOVPage.content_panels + [
        MultiFieldPanel([FieldRowPanel([FieldPanel('last_edited')])],
                        heading="Visible time stamp"),
        FieldPanel('question'),
        FieldPanel('statement'),
        FieldPanel('snippet'),
        FieldPanel('answer'),
        FieldPanel('search_tags'),
        FieldPanel('redirect_to'),
    ]

    sidebar = StreamField([
        ('call_to_action', molecules.CallToAction()),
        ('related_links', molecules.RelatedLinks()),
        ('related_metadata', molecules.RelatedMetadata()),
        ('email_signup', organisms.EmailSignUp()),
        ('sidebar_contact', organisms.SidebarContactInfo()),
        ('rss_feed', molecules.RSSFeed()),
        ('social_media', molecules.SocialMedia()),
        ('reusable_text', v1_blocks.ReusableTextChooserBlock(ReusableText)),
    ],
                          blank=True)

    sidebar_panels = [
        StreamFieldPanel('sidebar'),
    ]

    search_fields = Page.search_fields + [
        index.SearchField('answer'),
        index.SearchField('snippet')
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
        ObjectList(sidebar_panels, heading='Sidebar (English only)'),
        ObjectList(CFGOVPage.settings_panels, heading='Configuration'),
    ])

    objects = CFGOVPageManager()

    def get_context(self, request, *args, **kwargs):
        context = super(AnswerPage, self).get_context(request)
        context['answer_id'] = self.answer_base.id
        context['related_questions'] = self.answer_base.related_questions.all()
        context['description'] = self.snippet if self.snippet \
            else Truncator(self.answer).words(40, truncate=' ...')
        context['audiences'] = [{
            'text':
            audience.name,
            'url':
            '/ask-cfpb/audience-{}'.format(slugify(audience.name))
        } for audience in self.answer_base.audiences.all()]
        if self.language == 'es':
            tag_dict = self.Answer.valid_tags(language='es')
            context['tags_es'] = [
                tag for tag in self.answer_base.clean_tags_es
                if tag in tag_dict['valid_tags']
            ]
            context['tweet_text'] = Truncator(self.question).chars(
                100, truncate=' ...')
            context['disclaimer'] = get_reusable_text_snippet(
                SPANISH_DISCLAIMER_SNIPPET_TITLE)
            context['category'] = self.answer_base.category.first()
        elif self.language == 'en':
            # we're not using tags on English pages yet, so cut the overhead
            # tag_dict = self.Answer.valid_tags()
            # context['tags'] = [tag for tag in self.answer_base.clean_tags
            #                    if tag in tag_dict['valid_tags']]
            context['about_us'] = get_reusable_text_snippet(
                ABOUT_US_SNIPPET_TITLE)
            context['disclaimer'] = get_reusable_text_snippet(
                ENGLISH_DISCLAIMER_SNIPPET_TITLE)
            context['last_edited'] = (self.last_edited
                                      or self.answer_base.last_edited)
            # breadcrumbs and/or category should reflect
            # the referrer if it is a consumer tools portal or
            # ask category page
            context['category'], context['breadcrumb_items'] = \
                get_question_referrer_data(
                    request, self.answer_base.category.all())
            subcategories = []
            for subcat in self.answer_base.subcategory.all():
                if subcat.parent == context['category']:
                    subcategories.append(subcat)
                for related in subcat.related_subcategories.all():
                    if related.parent == context['category']:
                        subcategories.append(related)
            context['subcategories'] = set(subcategories)

        return context

    def get_template(self, request):
        printable = request.GET.get('print', False)
        if self.language == 'es':
            if printable == 'true':
                return 'ask-cfpb/answer-page-spanish-printable.html'

            return 'ask-cfpb/answer-page-spanish.html'

        return 'ask-cfpb/answer-page.html'

    def __str__(self):
        if self.answer_base:
            return '{}: {}'.format(self.answer_base.id, self.title)
        else:
            return self.title

    @property
    def status_string(self):
        if self.redirect_to:
            if not self.live:
                return _("redirected but not live")
            else:
                return _("redirected")
        else:
            return super(AnswerPage, self).status_string

    # Returns an image for the page's meta Open Graph tag
    @property
    def meta_image(self):
        if self.answer_base.social_sharing_image:
            return self.answer_base.social_sharing_image

        if not self.answer_base.category.exists():
            return None

        return self.answer_base.category.first().category_image

    # Overrides the default of page.id for comparing against split testing
    # clusters. See: core.feature_flags.in_split_testing_cluster
    @property
    def split_test_id(self):
        return self.answer_base.id
Example #23
0
class AnswerPage(CFGOVPage):
    """
    Page type for Ask CFPB answers.
    """
    from .django import Answer
    question = RichTextField(blank=True, editable=False)
    answer = RichTextField(blank=True, editable=False)
    snippet = RichTextField(blank=True,
                            help_text='Optional answer intro',
                            editable=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    publish_date = models.DateTimeField(default=timezone.now)
    answer_base = models.ForeignKey(Answer,
                                    blank=True,
                                    null=True,
                                    related_name='answer_pages',
                                    on_delete=models.SET_NULL)
    redirect_to = models.ForeignKey(
        Answer,
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        related_name='redirected_pages',
        help_text="Choose another Answer to redirect this page to")

    content = StreamField([
        ('feedback', v1_blocks.Feedback()),
    ], blank=True)

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

    search_fields = Page.search_fields + [
        index.SearchField('question'),
        index.SearchField('answer'),
        index.SearchField('answer_base'),
        index.FilterField('language')
    ]
    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
        ObjectList(CFGOVPage.settings_panels, heading='Configuration'),
    ])

    template = 'ask-cfpb/answer-page.html'
    objects = PageManager()

    def __str__(self):
        if self.answer_base:
            return '{}: {}'.format(self.answer_base.id, self.title)
        else:
            return self.title

    @property
    def status_string(self):
        if self.redirect_to:
            if not self.live:
                return _("redirected but not live")
            else:
                return _("redirected")
        else:
            return super(AnswerPage, self).status_string
Example #24
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]
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')
    ]