예제 #1
0
class ChicagoNeighborhoods(Page):

    # Database fields
    neighborhood_name = models.CharField(max_length=255,
                                         default='',
                                         null=False,
                                         blank=False)
    date = models.DateField("Updated On")
    introductory_text = models.CharField(max_length=255,
                                         default='',
                                         null=False,
                                         blank=False)
    introductory_image = models.ForeignKey('wagtailimages.Image',
                                           null=True,
                                           blank=True,
                                           on_delete=models.SET_NULL,
                                           related_name='+')
    overview = StreamField([
        ('draw', DrawMapBlock()),
        ('heading', blocks.CharBlock(classname="heading")),
        ('paragraph', blocks.RichTextBlock()),
        ('image', ImageChooserBlock()),
        ('video', EmbedBlock()),
        ('document', DocumentChooserBlock()),
    ],
                           default='')
    social_mix = StreamField([
        ('draw', DrawMapBlock()),
        ('heading', blocks.CharBlock(classname="heading")),
        ('paragraph', blocks.RichTextBlock()),
        ('image', ImageChooserBlock()),
        ('video', EmbedBlock()),
        ('document', DocumentChooserBlock()),
    ])
    public_space = StreamField([
        ('draw', DrawMapBlock()),
        ('heading', blocks.CharBlock(classname="heading")),
        ('paragraph', blocks.RichTextBlock()),
        ('image', ImageChooserBlock()),
        ('video', EmbedBlock()),
        ('document', DocumentChooserBlock()),
    ])
    amenities = StreamField([
        ('draw', DrawMapBlock()),
        ('heading', blocks.CharBlock(classname="heading")),
        ('paragraph', blocks.RichTextBlock()),
        ('image', ImageChooserBlock()),
        ('video', EmbedBlock()),
        ('document', DocumentChooserBlock()),
    ])
    connections = StreamField([
        ('draw', DrawMapBlock()),
        ('heading', blocks.CharBlock(classname="heading")),
        ('paragraph', blocks.RichTextBlock()),
        ('image', ImageChooserBlock()),
        ('video', EmbedBlock()),
        ('document', DocumentChooserBlock()),
    ])
    final_project = StreamField([
        ('draw', DrawMapBlock()),
        ('heading', blocks.CharBlock(classname="heading")),
        ('paragraph', blocks.RichTextBlock()),
        ('image', ImageChooserBlock()),
        ('video', EmbedBlock()),
        ('document', DocumentChooserBlock()),
    ])

    # Search index configuraiton
    search_fields = Page.search_fields + [
        index.SearchField('neighborhood_name'),
        index.SearchField('introductory_text'),
        index.SearchField('overview'),
        index.SearchField('social_mix'),
        index.SearchField('public_space'),
        index.SearchField('amenities'),
        index.SearchField('connections'),
        index.SearchField('final_project'),
        index.FilterField('date'),
    ]

    # Editor panels configuration
    content_panels = Page.content_panels + [
        FieldPanel('neighborhood_name'),
        FieldPanel('introductory_text'),
        ImageChooserPanel('introductory_image'),
        StreamFieldPanel('overview'),
        StreamFieldPanel('social_mix'),
        StreamFieldPanel('public_space'),
        StreamFieldPanel('amenities'),
        StreamFieldPanel('connections'),
        StreamFieldPanel('final_project'),
        FieldPanel('date'),
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
    ])

    # Parent page / subpage type rules
    parent_page_types = ['neighborhoods.HomePage']
    subpage_types = []
예제 #2
0
class ArticlePage(ThemeablePage, FeatureStyleFields, Promotable, ShareLinksMixin, PageLayoutOptions, VideoDocumentMixin):
    excerpt = RichTextField(blank=True, default="")
    body = article_fields.BodyField()
    chapters = article_fields.ChapterField(blank=True, null=True)
    table_of_contents_heading = models.TextField(blank=True, default="Table of Contents")
    citations_heading = models.TextField(blank=True, default="Works Cited")
    endnotes_heading = models.TextField(blank=True, default="End Notes")
    endnote_identifier_style = models.CharField(
        max_length=20,
        default="roman-lower",
        choices=(
            ('roman-lower', 'Roman Numerals - Lowercase'),
            ('roman-upper', 'Roman Numerals - Uppercase'),
            ('numbers', 'Numbers')
        )
    )

    main_image = models.ForeignKey(
        'images.AttributedImage',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+'
    )
    feature_image = models.ForeignKey(
        'images.AttributedImage',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+'
    )
    primary_topic = models.ForeignKey(
        'articles.Topic',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='articles'
    )
    category = models.ForeignKey(
        'articles.ArticleCategory',
        related_name='%(class)s',
        on_delete=models.SET_NULL,
        null=True,
        default=1
    )

    include_author_block = models.BooleanField(default=True)

    visualization = models.BooleanField(default=False)
    interview = models.BooleanField(default=False)
    video = models.BooleanField(default=False)
    number_of_related_articles = models.PositiveSmallIntegerField(default=6,
                                                                  verbose_name="Number of Related Articles to Show")
    json_file = article_fields.WagtailFileField(max_length=255, blank=True, null=True, verbose_name='JSON file',
                                                help_text="Only provide if you know your template will be filled with the contents of a JSON data file.")

    project = models.ForeignKey(
        "projects.ProjectPage",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
    )

    _response_to = False

    search_fields = Page.search_fields + [
        index.SearchField('excerpt', partial_match=True),
        index.SearchField('body', partial_match=True),
        index.SearchField('chapters', partial_match=True),
        index.SearchField('get_primary_topic_name', partial_match=True),
        index.SearchField('get_category_name', partial_match=True),
        index.SearchField('get_topic_names', partial_match=True),
        index.SearchField('get_author_names', partial_match=True),
    ]

    def get_primary_topic_name(self):
        if self.primary_topic:
            return self.primary_topic.name
        return ""

    def get_category_name(self):
        if self.category:
            return self.category.name
        return ""

    def get_topic_names(self):
        return '\n'.join([link.topic.name if link.topic else "" for link in self.topic_links.all()])

    def get_author_names(self):
        return '\n'.join(
            [author_link.author.full_name if author_link.author else "" for author_link in self.author_links.all()])

    @property
    def authors(self):
        author_list = []
        for link in self.author_links.all():
            if link.author:
                author_list.append((link.author))
        return author_list

    @property
    def series_articles(self):
        related_series_data = []
        for link in self.series_links.all():
            series_page = link.series
            series_articles = series_page.articles
            series_articles.remove(self)
            related_series_data.append((series_page, series_articles))
        return related_series_data

    @property
    def topics(self):
        primary_topic = self.primary_topic
        all_topics = [link.topic for link in self.topic_links.all()]
        if primary_topic:
            all_topics.append(primary_topic)
        all_topics = list(set(all_topics))
        if len(all_topics) > 0:
            all_topics.sort(key=attrgetter('name'))
        return all_topics

    @property
    def response_to(self):
        if self._response_to is False:
            response_to_count = self.response_to_links.count()
            if response_to_count > 1:
                logger.warning(
                    'ArticlePage(pk={0}) appears to be a response to multiple articles. Only the first one is being returned.'.format(
                        self.pk
                    )
                )
            if response_to_count != 0:
                self._response_to = self.response_to_links.first().response_to
            else:
                self._response_to = None

        return self._response_to

    @property
    def is_response(self):
        return self.response_to is not None

    def responses(self):
        return [link.response for link in self.response_links.all()]

    def related_articles(self, number):
        included = [self.id]
        article_list = []
        if self.primary_topic:
            articles = ArticlePage.objects.live().filter(primary_topic=self.primary_topic).exclude(
                id=self.id).distinct().order_by('-first_published_at')[:number]
            article_list.extend(articles.all())
            included.extend([article.id for article in articles.all()])

        current_total = len(article_list)

        if current_total < number:
            # still don't have enough, so pick using secondary topics
            topics = Topic.objects.filter(article_links__article=self)
            if topics:
                additional_articles = ArticlePage.objects.live().filter(primary_topic__in=topics).exclude(
                    id__in=included).distinct().order_by('-first_published_at')[:number - current_total]
                article_list.extend(additional_articles.all())
                current_total = len(article_list)
                included.extend([article.id for article in additional_articles.all()])

        if current_total < number:
            authors = ContributorPage.objects.live().filter(article_links__article=self)
            if authors:
                additional_articles = ArticlePage.objects.live().filter(author_links__author__in=authors).exclude(
                    id__in=included).distinct().order_by('-first_published_at')[:number - current_total]
                article_list.extend(additional_articles.all())
                current_total = len(article_list)
                included.extend([article.id for article in additional_articles.all()])

        if current_total < number:
            # still don't have enough, so just pick the most recent
            additional_articles = ArticlePage.objects.live().exclude(id__in=included).order_by('-first_published_at')[:number - current_total]
            article_list.extend(additional_articles.all())

        return article_list

    content_panels = Page.content_panels + [
        FieldPanel('excerpt'),
        InlinePanel('author_links', label="Authors"),
        PageChooserPanel('project'),
        ImageChooserPanel('main_image'),
        ImageChooserPanel('feature_image'),
        DocumentChooserPanel('video_document'),
        StreamFieldPanel('body'),
        SnippetChooserPanel('primary_topic'),
        InlinePanel('topic_links', label="Secondary Topics"),
        InlinePanel('response_links', label="Responses"),
    ]

    advanced_content_panels = [
        FieldPanel('json_file'),
        MultiFieldPanel(
            [
                FieldPanel('table_of_contents_heading'),
                StreamFieldPanel('chapters'),
            ],
            heading="Chapters Section"
        ),
        MultiFieldPanel(
            [
                FieldPanel('endnotes_heading'),
                FieldPanel('endnote_identifier_style'),
                InlinePanel('endnote_links', label="End Notes"),
            ],
            heading="End Notes Section"
        ),
        MultiFieldPanel(
            [
                FieldPanel('citations_heading'),
                InlinePanel('citation_links', label="Citations"),
            ],
            heading="Citations Section"
        ),
    ]

    promote_panels = Page.promote_panels + [
        MultiFieldPanel(
            [
                FieldPanel('sticky'),
                FieldPanel('sticky_for_type_section'),
                FieldPanel('slippery'),
                FieldPanel('slippery_for_type_section'),
                FieldPanel('editors_pick'),
                FieldPanel('feature_style'),
                FieldPanel('title_size'),
                FieldPanel('fullbleed_feature'),
                FieldPanel('image_overlay_opacity'),
            ],
            heading="Featuring Settings"
        ),
    ]

    style_panels = ThemeablePage.style_panels + [

        MultiFieldPanel(
            [
                FieldPanel('include_main_image'),
                FieldPanel('include_main_image_overlay'),
                FieldPanel('full_bleed_image_size'),
                FieldPanel('include_caption_in_footer'),
            ],
            heading="Main Image"
        ),
        MultiFieldPanel(
            [
                InlinePanel('background_image_links', label="Background Images"),
            ],
            heading="Background Images"
        ),
        MultiFieldPanel(
            [
                FieldPanel('include_author_block'),
                FieldPanel('number_of_related_articles')
            ],
            heading="Sections"
        ),
        MultiFieldPanel(
            [
                FieldPanel('interview'),
                FieldPanel('video'),
                FieldPanel('visualization'),
            ],
            heading="Categorization"
        )
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
        ObjectList(advanced_content_panels, heading='Advanced Content'),
        ObjectList(style_panels, heading='Page Style Options'),
        ObjectList(promote_panels, heading='Promote'),
        ObjectList(Page.settings_panels, heading='Settings', classname="settings"),
    ])
예제 #3
0
class ActivityPage(CFGOVPage):
    """
    A model for the Activity Detail page.
    """
    # Allow Activity pages to exist under the ActivityIndexPage or the Trash
    parent_page_types = [ActivityIndexPage, HomePage]
    subpage_types = []
    objects = CFGOVPageManager()

    date = models.DateField('Updated', default=timezone.now)
    summary = models.TextField('Summary', blank=False)
    big_idea = RichTextField('Big idea', blank=False)
    essential_questions = RichTextField('Essential questions', blank=False)
    objectives = RichTextField('Objectives', blank=False)
    what_students_will_do = RichTextField('What students will do',
                                          blank=False)  # noqa: E501
    activity_file = models.ForeignKey('wagtaildocs.Document',
                                      null=True,
                                      blank=False,
                                      on_delete=models.SET_NULL,
                                      related_name='+',
                                      verbose_name='Teacher guide')
    # TODO: to figure out how to use Document choosers on ManyToMany fields
    handout_file = models.ForeignKey('wagtaildocs.Document',
                                     null=True,
                                     blank=True,
                                     on_delete=models.SET_NULL,
                                     related_name='+',
                                     verbose_name='Student file 1')
    handout_file_2 = models.ForeignKey('wagtaildocs.Document',
                                       null=True,
                                       blank=True,
                                       on_delete=models.SET_NULL,
                                       related_name='+',
                                       verbose_name='Student file 2')
    handout_file_3 = models.ForeignKey('wagtaildocs.Document',
                                       null=True,
                                       blank=True,
                                       on_delete=models.SET_NULL,
                                       related_name='+',
                                       verbose_name='Student file 3')
    building_block = ParentalManyToManyField(
        'teachers_digital_platform.ActivityBuildingBlock',
        blank=False)  # noqa: E501
    school_subject = ParentalManyToManyField(
        'teachers_digital_platform.ActivitySchoolSubject',
        blank=False)  # noqa: E501
    topic = ParentalTreeManyToManyField(
        'teachers_digital_platform.ActivityTopic', blank=False)  # noqa: E501
    # Audience
    grade_level = ParentalManyToManyField(
        'teachers_digital_platform.ActivityGradeLevel',
        blank=False)  # noqa: E501
    age_range = ParentalManyToManyField(
        'teachers_digital_platform.ActivityAgeRange',
        blank=False)  # noqa: E501
    student_characteristics = ParentalManyToManyField(
        'teachers_digital_platform.ActivityStudentCharacteristics',
        blank=True)  # noqa: E501
    # Activity Characteristics
    activity_type = ParentalManyToManyField(
        'teachers_digital_platform.ActivityType', blank=False)  # noqa: E501
    teaching_strategy = ParentalManyToManyField(
        'teachers_digital_platform.ActivityTeachingStrategy',
        blank=False)  # noqa: E501
    blooms_taxonomy_level = ParentalManyToManyField(
        'teachers_digital_platform.ActivityBloomsTaxonomyLevel',
        blank=False)  # noqa: E501
    activity_duration = models.ForeignKey(
        ActivityDuration, blank=False, on_delete=models.PROTECT)  # noqa: E501
    # Standards taught
    jump_start_coalition = ParentalManyToManyField(
        'teachers_digital_platform.ActivityJumpStartCoalition',
        blank=True,
        verbose_name='Jump$tart Coalition',
    )
    council_for_economic_education = ParentalManyToManyField(
        'teachers_digital_platform.ActivityCouncilForEconEd',
        blank=True,
        verbose_name='Council for Economic Education',
    )
    content_panels = CFGOVPage.content_panels + [
        FieldPanel('date'),
        FieldPanel('summary'),
        FieldPanel('big_idea'),
        FieldPanel('essential_questions'),
        FieldPanel('objectives'),
        FieldPanel('what_students_will_do'),
        MultiFieldPanel(
            [
                DocumentChooserPanel('activity_file'),
                DocumentChooserPanel('handout_file'),
                DocumentChooserPanel('handout_file_2'),
                DocumentChooserPanel('handout_file_3'),
            ],
            heading="Download activity",
        ),
        FieldPanel('building_block', widget=forms.CheckboxSelectMultiple),
        FieldPanel('school_subject', widget=forms.CheckboxSelectMultiple),
        FieldPanel('topic', widget=forms.CheckboxSelectMultiple),
        MultiFieldPanel(
            [
                FieldPanel('grade_level',
                           widget=forms.CheckboxSelectMultiple),  # noqa: E501
                FieldPanel('age_range', widget=forms.CheckboxSelectMultiple),
                FieldPanel('student_characteristics',
                           widget=forms.CheckboxSelectMultiple),  # noqa: E501
            ],
            heading="Audience",
        ),
        MultiFieldPanel(
            [
                FieldPanel('activity_type',
                           widget=forms.CheckboxSelectMultiple),  # noqa: E501
                FieldPanel('teaching_strategy',
                           widget=forms.CheckboxSelectMultiple),  # noqa: E501
                FieldPanel('blooms_taxonomy_level',
                           widget=forms.CheckboxSelectMultiple),  # noqa: E501
                FieldPanel('activity_duration'),
            ],
            heading="Activity characteristics",
        ),
        MultiFieldPanel(
            [
                FieldPanel('council_for_economic_education',
                           widget=forms.CheckboxSelectMultiple),  # noqa: E501
                FieldPanel('jump_start_coalition',
                           widget=forms.CheckboxSelectMultiple),  # noqa: E501
            ],
            heading="National standards",
        ),
    ]

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

    # admin use only
    search_fields = Page.search_fields + [
        index.SearchField('summary'),
        index.SearchField('big_idea'),
        index.SearchField('essential_questions'),
        index.SearchField('objectives'),
        index.SearchField('what_students_will_do'),
        index.FilterField('date'),
        index.FilterField('building_block'),
        index.FilterField('school_subject'),
        index.FilterField('topic'),
        index.FilterField('grade_level'),
        index.FilterField('age_range'),
        index.FilterField('student_characteristics'),
        index.FilterField('activity_type'),
        index.FilterField('teaching_strategy'),
        index.FilterField('blooms_taxonomy_level'),
        index.FilterField('activity_duration'),
        index.FilterField('jump_start_coalition'),
        index.FilterField('council_for_economic_education'),
    ]

    def get_topics_list(self, parent=None):
        """
        Get a hierarchical list of this activity's topics.

        parent: ActivityTopic
        """
        if parent:
            descendants = set(parent.get_descendants()) & set(
                self.topic.all())  # noqa: E501
            children = parent.get_children()
            children_list = []
            # If this parent has descendants in self.topic, add its children.
            if descendants:
                for child in children:
                    if set(child.get_descendants()) & set(self.topic.all()):
                        children_list.append(self.get_topics_list(child))
                    elif child in self.topic.all():
                        children_list.append(child.title)

                if children_list:
                    return parent.title + " (" + ', '.join(
                        children_list) + ")"  # noqa: E501
            # Otherwise, just add the parent.
            else:
                return parent.title
        else:
            # Build root list of topics and recurse their children.
            topic_list = []
            topic_ids = [topic.id for topic in self.topic.all()]
            ancestors = ActivityTopic.objects.filter(
                id__in=topic_ids).get_ancestors(True)  # noqa: E501
            roots = ActivityTopic.objects.filter(parent=None) & ancestors
            for root_topic in roots:
                topic_list.append(self.get_topics_list(root_topic))

            if topic_list:
                return ', '.join(topic_list)
            else:
                return ''

    class Meta:
        verbose_name = "TDP Activity page"
예제 #4
0
class RegulationsSearchPage(RoutablePageMixin, CFGOVPage):
    """A page for the custom search interface for regulations."""

    objects = PageManager()

    parent_page_types = ['regulations3k.RegulationLandingPage']
    subpage_types = []
    results = {}
    content_panels = CFGOVPage.content_panels
    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
        ObjectList(CFGOVPage.settings_panels, heading='Configuration'),
    ])

    def get_template(self, request):
        return 'regulations3k/search-regulations.html'

    @route(r'^results/')
    def regulation_results_page(self, request):
        all_regs = Part.objects.order_by('part_number')
        regs = []
        sqs = SearchQuerySet()
        if 'regs' in request.GET:
            regs = request.GET.getlist('regs')
        if len(regs) == 1:
            sqs = sqs.filter(part=regs[0])
        elif regs:
            sqs = sqs.filter(part__in=regs)
        search_query = request.GET.get('q', '')  # haystack cleans this string
        if search_query:
            query_sqs = sqs.filter(content=search_query).models(Section)
        else:
            query_sqs = sqs.models(Section)
        payload = {
            'search_query':
            search_query,
            'results': [],
            'total_results':
            query_sqs.count(),
            'regs':
            regs,
            'all_regs': [{
                'name':
                "Regulation {}".format(reg.letter_code),
                'id':
                reg.part_number,
                'num_results':
                query_sqs.filter(part=reg.part_number).count(),
                'selected':
                reg.part_number in regs
            } for reg in all_regs]
        }
        for hit in query_sqs:
            label_bits = hit.object.label.partition('-')
            _part, _section = label_bits[0], label_bits[2]
            letter_code = LETTER_CODES.get(_part)
            snippet = Truncator(hit.text).words(40, truncate=' ...')
            snippet = snippet.replace('*', '').replace('#', '')
            hit_payload = {
                'id': hit.object.pk,
                'reg': 'Regulation {}'.format(letter_code),
                'label': hit.title,
                'snippet': snippet,
                'url': "/regulations/{}/{}/".format(_part, _section),
            }
            payload['results'].append(hit_payload)
        self.results = payload

        # if we want to paginate results:

        # def get_context(self, request, **kwargs):
        #     context = super(RegulationsSearchPage, self).get_context(
        #         request, **kwargs)
        #     context.update(**kwargs)
        #     paginator = Paginator(payload['results'], 25)
        #     page_number = validate_page_number(request, paginator)
        #     paginated_page = paginator.page(page_number)
        #     context['current_page'] = page_number
        #     context['paginator'] = paginator
        #     context['results'] = paginated_page
        #     return context

        context = self.get_context(request)
        return TemplateResponse(request, self.get_template(request), context)
예제 #5
0
파일: models.py 프로젝트: mauler/wagtail
    FieldPanel('seo_title'),
    FieldPanel('slug'),
    InlinePanel('advert_placements', label="Adverts"),
]
StandardIndex.promote_panels = []


class StandardChild(Page):
    pass


# Test overriding edit_handler with a custom one
StandardChild.edit_handler = TabbedInterface([
    ObjectList(StandardChild.content_panels, heading='Content'),
    ObjectList(StandardChild.promote_panels, heading='Promote'),
    ObjectList(StandardChild.settings_panels,
               heading='Settings',
               classname='settings'),
    ObjectList([], heading='Dinosaurs'),
])


class BusinessIndex(Page):
    """ Can be placed anywhere, can only have Business children """
    subpage_types = ['tests.BusinessChild', 'tests.BusinessSubIndex']


class BusinessSubIndex(Page):
    """ Can be placed under BusinessIndex, and have BusinessChild children """
    subpage_types = ['tests.BusinessChild']
    parent_page_types = ['tests.BusinessIndex']
예제 #6
0
class EventPage(AbstractFilterPage):
    # General content fields
    body = RichTextField('Subheading', blank=True)
    archive_body = RichTextField(blank=True)
    live_body = RichTextField(blank=True)
    future_body = RichTextField(blank=True)
    start_dt = models.DateTimeField("Start", blank=True, null=True)
    end_dt = models.DateTimeField("End", blank=True, null=True)
    future_body = RichTextField(blank=True)
    archive_image = models.ForeignKey('wagtailimages.Image',
                                      null=True,
                                      blank=True,
                                      on_delete=models.SET_NULL,
                                      related_name='+')
    video_transcript = models.ForeignKey('wagtaildocs.Document',
                                         null=True,
                                         blank=True,
                                         on_delete=models.SET_NULL,
                                         related_name='+')
    speech_transcript = models.ForeignKey('wagtaildocs.Document',
                                          null=True,
                                          blank=True,
                                          on_delete=models.SET_NULL,
                                          related_name='+')
    flickr_url = models.URLField("Flickr URL", blank=True)
    youtube_url = models.URLField(
        "Youtube URL",
        blank=True,
        help_text="Format: https://www.youtube.com/embed/video_id. "
        "It can be obtained by clicking on Share > "
        "Embed on Youtube.",
        validators=[
            RegexValidator(regex='^https?:\/\/www\.youtube\.com\/embed\/.*$')
        ])

    live_stream_availability = models.BooleanField("Streaming?",
                                                   default=False,
                                                   blank=True)
    live_stream_url = models.URLField(
        "URL",
        blank=True,
        help_text="Format: https://www.youtube.com/embed/video_id.")
    live_stream_date = models.DateTimeField("Go Live Date",
                                            blank=True,
                                            null=True)
    # Venue content fields
    venue_name = models.CharField(max_length=100, blank=True)
    venue_street = models.CharField(max_length=100, blank=True)
    venue_suite = models.CharField(max_length=100, blank=True)
    venue_city = models.CharField(max_length=100, blank=True)
    venue_state = USStateField(blank=True)
    venue_zip = models.IntegerField(blank=True, null=True)
    agenda_items = StreamField([('item', AgendaItemBlock())], blank=True)

    objects = CFGOVPageManager()

    # General content tab
    content_panels = CFGOVPage.content_panels + [
        FieldPanel('body', classname="full"),
        FieldRowPanel([
            FieldPanel('start_dt', classname="col6"),
            FieldPanel('end_dt', classname="col6"),
        ]),
        MultiFieldPanel([
            FieldPanel('archive_body', classname="full"),
            ImageChooserPanel('archive_image'),
            DocumentChooserPanel('video_transcript'),
            DocumentChooserPanel('speech_transcript'),
            FieldPanel('flickr_url'),
            FieldPanel('youtube_url'),
        ],
                        heading='Archive Information'),
        FieldPanel('live_body', classname="full"),
        FieldPanel('future_body', classname="full"),
        MultiFieldPanel([
            FieldPanel('live_stream_availability'),
            FieldPanel('live_stream_url'),
            FieldPanel('live_stream_date'),
        ],
                        heading='Live Stream Information'),
    ]
    # Venue content tab
    venue_panels = [
        FieldPanel('venue_name'),
        MultiFieldPanel([
            FieldPanel('venue_street'),
            FieldPanel('venue_suite'),
            FieldPanel('venue_city'),
            FieldPanel('venue_state'),
            FieldPanel('venue_zip'),
        ],
                        heading='Venue Address'),
    ]
    # Agenda content tab
    agenda_panels = [
        StreamFieldPanel('agenda_items'),
    ]
    # Promotion panels
    promote_panels = [
        MultiFieldPanel(AbstractFilterPage.promote_panels,
                        "Page configuration"),
    ]
    # Tab handler interface
    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='General Content'),
        ObjectList(venue_panels, heading='Venue Information'),
        ObjectList(agenda_panels, heading='Agenda Information'),
        ObjectList(AbstractFilterPage.sidefoot_panels, heading='Sidebar'),
        ObjectList(AbstractFilterPage.settings_panels,
                   heading='Configuration'),
    ])

    template = 'events/event.html'

    @property
    def page_js(self):
        return super(EventPage, self).page_js + ['video-player.js']

    def location_image_url(self, scale='2', size='276x155', zoom='12'):
        center = 'Washington, DC'
        if self.venue_city:
            center = self.venue_city
        if self.venue_state:
            center = center + ', ' + self.venue_state
        options = {
            'center': center,
            'scale': scale,
            'size': size,
            'zoom': zoom
        }
        url = 'https://maps.googleapis.com/maps/api/staticmap?'
        return '{url}{options}'.format(url=url, options=urlencode(options))
예제 #7
0
class TourPage(Page):

    search_fields = Page.search_fields + [
        index.SearchField('title'),
        index.SearchField('tour_description'),
    ]

    tour_image = models.ForeignKey('wagtailimages.Image',
                                   null=True,
                                   blank=True,
                                   on_delete=models.SET_NULL,
                                   related_name='+',
                                   help_text='Album cover image')

    tour_listing_introduction = models.TextField(
        "A listing introduction for the tour", blank=True, max_length=254)

    tour_description = RichTextField("A description for the tour")

    content_panels = Page.content_panels + [
        InlinePanel('tour_artist_relationship',
                    label="Arist(s)",
                    panels=None,
                    min_num=1),
        InlinePanel('tour_album_relationship',
                    label="Album(s)",
                    panels=None,
                    min_num=0),
        ImageChooserPanel('tour_image'),
        FieldPanel('tour_listing_introduction'),
        FieldPanel('tour_description'),
    ]

    tour_panels = [
        InlinePanel('tourdates',
                    label="Tour dates",
                    help_text="Enter your tour dates",
                    min_num=1),
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading="Tour details",
                   classname="content"),
        ObjectList(tour_panels, heading="Tour dates"),
        ObjectList(Page.promote_panels, heading="Promote"),
        ObjectList(Page.settings_panels,
                   heading="Settings",
                   classname="settings"),
    ])

    # We iterate within the model over the artists, genres and subgenres
    # so they can be accessible to the template via a for loop
    def artists(self):
        artists = [n.artists for n in self.tour_artist_relationship.all()]
        return artists

    def albums(self):
        albums = [n.albums for n in self.tour_album_relationship.all()]

        return albums

    @property
    def album_image(self):
        # fail silently if there is no profile pic or the rendition file can't
        # be found. Note @richbrennan worked out how to do this...
        try:
            return self.image.get_rendition('fill-400x400').img_tag()
        except:
            return ''

    parent_page_types = [
        'tours.TourIndexPage'
        # app.model
    ]

    subpage_types = []
예제 #8
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>[0-9A-Za-z-]+)/)?$',
           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>[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 """

        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)
예제 #9
0
class RegulationsSearchPage(RoutablePageMixin, CFGOVPage):
    """A page for the custom search interface for regulations."""

    objects = PageManager()

    parent_page_types = ['regulations3k.RegulationLandingPage']
    subpage_types = []
    results = {}
    content_panels = CFGOVPage.content_panels
    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
        ObjectList(CFGOVPage.settings_panels, heading='Configuration'),
    ])

    def get_template(self, request):
        template = 'regulations3k/search-regulations.html'
        if 'partial' in request.GET:
            template = 'regulations3k/search-regulations-results.html'
        return template

    @route(r'^results/')
    def regulation_results_page(self, request):
        all_regs = Part.objects.order_by('part_number')
        regs = validate_regs_list(request)
        order = validate_order(request)
        search_query = request.GET.get('q', '').strip()
        payload = {
            'search_query': search_query,
            'results': [],
            'total_results': 0,
            'regs': regs,
            'all_regs': [],
        }
        if not search_query or len(urllib.parse.unquote(search_query)) == 1:
            self.results = payload
            return TemplateResponse(request, self.get_template(request),
                                    self.get_context(request))
        sqs = SearchQuerySet().filter(content=search_query)
        payload.update({
            'all_regs': [{
                'short_name':
                reg.short_name,
                'id':
                reg.part_number,
                'num_results':
                sqs.filter(
                    part=reg.part_number).models(SectionParagraph).count(),
                'selected':
                reg.part_number in regs
            } for reg in all_regs]
        })
        payload.update({
            'total_count':
            sum([reg['num_results'] for reg in payload['all_regs']])
        })
        if len(regs) == 1:
            sqs = sqs.filter(part=regs[0])
        elif regs:
            sqs = sqs.filter(part__in=regs)
        if order == 'regulation':
            sqs = sqs.order_by('part', 'section_order')
        sqs = sqs.highlight(pre_tags=['<strong>'],
                            post_tags=['</strong>']).models(SectionParagraph)
        for hit in sqs:
            try:
                snippet = Markup(" ".join(hit.highlighted))
            except TypeError as e:
                logger.warning(
                    "Query string {} produced a TypeError: {}".format(
                        search_query, e))
                continue

            short_name = all_regs.get(part_number=hit.part).short_name
            hit_payload = {
                'id':
                hit.paragraph_id,
                'part':
                hit.part,
                'reg':
                short_name,
                'label':
                hit.title,
                'snippet':
                snippet,
                'url':
                "{}{}/{}/#{}".format(self.parent().url, hit.part,
                                     hit.section_label, hit.paragraph_id),
            }
            payload['results'].append(hit_payload)

        payload.update({'current_count': sqs.count()})
        self.results = payload
        context = self.get_context(request)
        num_results = validate_num_results(request)
        paginator = Paginator(payload['results'], num_results)
        page_number = validate_page_number(request, paginator)
        paginated_page = paginator.page(page_number)
        context.update({
            'current_count':
            payload['current_count'],
            'total_count':
            payload['total_count'],
            'paginator':
            paginator,
            'current_page':
            page_number,
            'num_results':
            num_results,
            'order':
            order,
            'results':
            paginated_page,
            'show_filters':
            any(reg['selected'] is True for reg in payload['all_regs'])
        })
        return TemplateResponse(request, self.get_template(request), context)
예제 #10
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
예제 #11
0
class HomePage(CFGOVPage):
    header = StreamField([
        ('info_unit', molecules.InfoUnit()),
        ('half_width_link_blob', molecules.HalfWidthLinkBlob()),
    ],
                         blank=True)

    # General content tab
    content_panels = CFGOVPage.content_panels + [
        StreamFieldPanel('header'),
        InlinePanel(
            'excluded_updates',
            label='Pages excluded from Latest Updates',
            help_text=('This block automatically displays the six most '
                       'recently published blog posts, press releases, '
                       'speeches, testimonies, events, or op-eds. If you want '
                       'to exclude a page from appearing as a recent update '
                       'in this block, add it as an excluded page.')),
    ]

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

    # Sets page to only be createable at the root
    parent_page_types = ['wagtailcore.Page']

    objects = PageManager()

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

    @property
    def page_js(self):
        return super(HomePage, self).page_js + ['home-page.js']

    def get_category_name(self, category_icon_name):
        cats = dict(ref.limited_categories)
        return cats[str(category_icon_name)]

    def get_context(self, request):
        context = super(HomePage, self).get_context(request)
        context['latest_updates'] = self.get_latest_updates(request)
        return context

    def get_latest_updates(self, request):
        # TODO: There should be a way to express this as part of the query
        # rather than evaluating it in Python.
        excluded_updates_pks = [
            e.excluded_page.pk for e in self.excluded_updates.all()
        ]

        latest_pages = CFGOVPage.objects.in_site(
            request.site).exclude(pk__in=excluded_updates_pks).filter(
                Q(content_type__app_label='v1') &
                (Q(content_type__model='blogpage')
                 | Q(content_type__model='eventpage')
                 | Q(content_type__model='newsroompage')),
                live=True,
            ).distinct().order_by('-first_published_at')[:6]

        return latest_pages

    def get_template(self, request):
        if flag_enabled('NEW_HOME_PAGE', request=request):
            return 'home-page/index_new.html'
        else:
            return 'home-page/index_%s.html' % self.language
예제 #12
0
class AnswerCategoryPage(RoutablePageMixin, SecondaryNavigationJSMixin,
                         CFGOVPage):
    """
    A routable page type for Ask CFPB category pages and their subcategories.
    """
    from ask_cfpb.models import Answer, Audience, Category, SubCategory

    objects = CFGOVPageManager()
    content = StreamField([], null=True)
    ask_category = models.ForeignKey(
        Category,
        blank=True,
        null=True,
        on_delete=models.PROTECT,
        related_name='category_page')
    ask_subcategory = models.ForeignKey(
        SubCategory,
        blank=True,
        null=True,
        on_delete=models.PROTECT,
        related_name='subcategory_page')
    content_panels = CFGOVPage.content_panels + [
        FieldPanel('ask_category', Category),
        StreamFieldPanel('content'),
    ]

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

    def get_template(self, request):
        if self.language == 'es':
            return 'ask-cfpb/category-page-spanish.html'

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

    def get_context(self, request, *args, **kwargs):
        context = super(
            AnswerCategoryPage, self).get_context(request, *args, **kwargs)
        sqs = SearchQuerySet().models(self.Category)
        if self.language == 'es':
            sqs = sqs.filter(content=self.ask_category.name_es)
        else:
            sqs = sqs.filter(content=self.ask_category.name)
        if sqs:
            facet_map = sqs[0].facet_map
        else:
            facet_map = self.ask_category.facet_map
        facet_dict = json.loads(facet_map)
        subcat_ids = facet_dict['subcategories'].keys()
        answer_ids = facet_dict['answers'].keys()
        audience_ids = facet_dict['audiences'].keys()
        subcats = self.SubCategory.objects.filter(
            pk__in=subcat_ids).values(
                'id', 'slug', 'slug_es', 'name', 'name_es')
        answers = self.Answer.objects.filter(
            pk__in=answer_ids).order_by('-pk').values(
                'id', 'question', 'question_es',
                'slug', 'slug_es', 'answer_es')
        for a in answers:
            a['answer_es'] = Truncator(a['answer_es']).words(
                40, truncate=' ...')
        audiences = self.Audience.objects.filter(
            pk__in=audience_ids).values('id', 'name')
        context.update({
            'answers': answers,
            'audiences': audiences,
            'facets': facet_dict,
            'choices': subcats,
            'results_count': answers.count(),
            'get_secondary_nav_items': get_ask_nav_items
        })

        if 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['breadcrumb_items'] = get_ask_breadcrumbs()
        elif self.language == 'es':
            context['tags'] = self.ask_category.top_tags_es
        return context

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

    @route(r'^$')
    def category_page(self, request):
        context = self.get_context(request)
        paginator = Paginator(context.get('answers'), 20)
        page_number = validate_page_number(request, paginator)
        page = paginator.page(page_number)
        context.update({
            'paginator': paginator,
            'current_page': page_number,
            'questions': page,
        })

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

    @route(r'^(?P<subcat>[^/]+)/$')
    def subcategory_page(self, request, **kwargs):
        subcat = self.SubCategory.objects.filter(
            slug=kwargs.get('subcat')).first()
        if subcat:
            self.ask_subcategory = subcat
        else:
            raise Http404
        context = self.get_context(request)
        id_key = str(subcat.pk)
        answers = context['answers'].filter(
            pk__in=context['facets']['subcategories'][id_key])
        paginator = Paginator(answers, 20)
        page_number = validate_page_number(request, paginator)
        page = paginator.page(page_number)
        context.update({
            'paginator': paginator,
            'current_page': page_number,
            'results_count': answers.count(),
            'questions': page,
            'breadcrumb_items': get_ask_breadcrumbs(
                self.ask_category)
        })

        return TemplateResponse(
            request, self.get_template(request), context)
예제 #13
0
class CFGOVPage(Page):
    authors = ClusterTaggableManager(through=CFGOVAuthoredPages,
                                     blank=True,
                                     verbose_name='Authors',
                                     help_text='A comma separated list of ' +
                                     'authors.',
                                     related_name='authored_pages')
    tags = ClusterTaggableManager(through=CFGOVTaggedPages,
                                  blank=True,
                                  related_name='tagged_pages')
    shared = models.BooleanField(default=False)
    has_unshared_changes = models.BooleanField(default=False)
    language = models.CharField(choices=ref.supported_languagues,
                                default='en',
                                max_length=2)
    social_sharing_image = models.ForeignKey(
        'v1.CFGOVImage',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        help_text=(
            'Optionally select a custom image to appear when users share this '
            'page on social media websites. Minimum size: 1200w x 630h.'))

    # This is used solely for subclassing pages we want to make at the CFPB.
    is_creatable = False

    objects = CFGOVPageManager()

    # These fields show up in either the sidebar or the footer of the page
    # depending on the page type.
    sidefoot = StreamField([
        ('call_to_action', molecules.CallToAction()),
        ('related_links', molecules.RelatedLinks()),
        ('related_posts', organisms.RelatedPosts()),
        ('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)

    # Panels
    promote_panels = Page.promote_panels + [
        ImageChooserPanel('social_sharing_image'),
    ]

    sidefoot_panels = [
        StreamFieldPanel('sidefoot'),
    ]

    settings_panels = [
        MultiFieldPanel(promote_panels, 'Settings'),
        InlinePanel('categories', label="Categories", max_num=2),
        FieldPanel('tags', 'Tags'),
        FieldPanel('authors', 'Authors'),
        MultiFieldPanel(Page.settings_panels, 'Scheduled Publishing'),
        FieldPanel('language', 'language'),
    ]

    # Tab handler interface guide because it must be repeated for each subclass
    edit_handler = TabbedInterface([
        ObjectList(Page.content_panels, heading='General Content'),
        ObjectList(sidefoot_panels, heading='Sidebar/Footer'),
        ObjectList(settings_panels, heading='Configuration'),
    ])

    def get_authors(self):
        """ Returns a sorted list of authors. Default is alphabetical """
        return self.alphabetize_authors()

    def alphabetize_authors(self):
        """
        Alphabetize authors of this page by last name,
        then first name if needed
        """
        # First sort by first name
        author_names = self.authors.order_by('name')
        # Then sort by last name
        return sorted(author_names, key=lambda x: x.name.split()[-1])

    def generate_view_more_url(self, request):
        activity_log = CFGOVPage.objects.get(slug='activity-log').specific
        tags = []
        tags = urlencode([('topics', tag) for tag in self.tags.slugs()])
        return (get_protected_url({'request': request}, activity_log) + '?' +
                tags)

    def related_posts(self, block):
        from v1.models.learn_page import AbstractFilterPage

        def tag_set(related_page):
            return set([tag.pk for tag in related_page.tags.all()])

        def match_all_topic_tags(queryset, page_tags):
            """Return pages that share every one of the current page's tags."""
            current_tag_set = set([tag.pk for tag in page_tags])
            return [
                page for page in queryset
                if current_tag_set.issubset(tag_set(page))
            ]

        related_types = []
        related_items = {}
        if block.value.get('relate_posts'):
            related_types.append('blog')
        if block.value.get('relate_newsroom'):
            related_types.append('newsroom')
        if block.value.get('relate_events'):
            related_types.append('events')
        if not related_types:
            return related_items

        tags = self.tags.all()
        and_filtering = block.value['and_filtering']
        specific_categories = block.value['specific_categories']
        limit = int(block.value['limit'])
        queryset = AbstractFilterPage.objects.live().exclude(
            id=self.id).order_by('-date_published').distinct()

        for parent in related_types:  # blog, newsroom or events
            # Include children of this slug that match at least 1 tag
            children = Page.objects.child_of_q(Page.objects.get(slug=parent))
            filters = children & Q(('tags__in', tags))

            if parent == 'events':
                # Include archived events matches
                archive = Page.objects.get(slug='archive-past-events')
                children = Page.objects.child_of_q(archive)
                filters |= children & Q(('tags__in', tags))

            if specific_categories:
                # Filter by any additional categories specified
                categories = ref.get_appropriate_categories(
                    specific_categories=specific_categories, page_type=parent)
                if categories:
                    filters &= Q(('categories__name__in', categories))

            related_queryset = queryset.filter(filters)

            if and_filtering:
                # By default, we need to match at least one tag
                # If specified in the admin, change this to match ALL tags
                related_queryset = match_all_topic_tags(related_queryset, tags)

            related_items[parent.title()] = related_queryset[:limit]

        # Return items in the dictionary that have non-empty querysets
        return {key: value for key, value in related_items.items() if value}

    def related_metadata_tags(self):
        # Set the tags to correct data format
        tags = {'links': []}
        filter_page = self.get_filter_data()
        for tag in self.specific.tags.all():
            tag_link = {'text': tag.name, 'url': ''}
            if filter_page:
                relative_url = filter_page.relative_url(filter_page.get_site())
                param = '?topics=' + tag.slug
                tag_link['url'] = relative_url + param
            tags['links'].append(tag_link)
        return tags

    def get_filter_data(self):
        for ancestor in self.get_ancestors().reverse().specific():
            if ancestor.specific_class.__name__ in [
                    'BrowseFilterablePage', 'SublandingFilterablePage',
                    'EventArchivePage', 'NewsroomLandingPage'
            ]:
                return ancestor
        return None

    def get_breadcrumbs(self, request):
        ancestors = self.get_ancestors()
        home_page_children = request.site.root_page.get_children()
        for i, ancestor in enumerate(ancestors):
            if ancestor in home_page_children:
                # Add top level parent page and `/process/` url segments
                # where necessary to OAH page breadcrumbs.
                # TODO: Remove this when OAH moves under /consumer-tools
                # and redirects are added after 2018 homebuying campaign.
                if ancestor.slug == 'owning-a-home':
                    breadcrumbs = []
                    for ancestor in ancestors[i:]:
                        ancestor_url = ancestor.relative_url(request.site)
                        if ancestor_url.startswith(
                            ('/owning-a-home/prepare',
                             '/owning-a-home/explore',
                             '/owning-a-home/compare', '/owning-a-home/close',
                             '/owning-a-home/sources')):
                            ancestor_url = ancestor_url.replace(
                                'owning-a-home', 'owning-a-home/process')
                        breadcrumbs.append({
                            'title': ancestor.title,
                            'href': ancestor_url,
                        })
                    return breadcrumbs
                # END TODO
                return [ancestor for ancestor in ancestors[i + 1:]]
        return []

    def get_appropriate_descendants(self, inclusive=True):
        return CFGOVPage.objects.live().descendant_of(self, inclusive)

    def get_appropriate_siblings(self, inclusive=True):
        return CFGOVPage.objects.live().sibling_of(self, inclusive)

    def get_context(self, request, *args, **kwargs):
        context = super(CFGOVPage, self).get_context(request, *args, **kwargs)
        for hook in hooks.get_hooks('cfgovpage_context_handlers'):
            hook(self, request, context, *args, **kwargs)
        return context

    def serve(self, request, *args, **kwargs):
        """
        If request is ajax, then return the ajax request handler response, else
        return the super.
        """
        if request.method == 'POST':
            return self.serve_post(request, *args, **kwargs)

        # Force the page's language on the request
        translation.activate(self.language)
        request.LANGUAGE_CODE = translation.get_language()
        return super(CFGOVPage, self).serve(request, *args, **kwargs)

    def _return_bad_post_response(self, request):
        if request.is_ajax():
            return JsonResponse({'result': 'error'}, status=400)

        return HttpResponseBadRequest(self.url)

    def serve_post(self, request, *args, **kwargs):
        """Handle a POST to a specific form on the page.

        Attempts to retrieve form_id from the POST request, which must be
        formatted like "form-name-index" where the "name" part is the name of a
        StreamField on the page and the "index" part refers to the index of the
        form element in the StreamField.

        If form_id is found, it returns the response from the block method
        retrieval.

        If form_id is not found, it returns an error response.
        """
        form_module = None
        form_id = request.POST.get('form_id', None)

        if form_id:
            form_id_parts = form_id.split('-')

            if len(form_id_parts) == 3:
                streamfield_name = form_id_parts[1]
                streamfield = getattr(self, streamfield_name, None)

                if streamfield is not None:
                    try:
                        streamfield_index = int(form_id_parts[2])
                    except ValueError:
                        streamfield_index = None

                    try:
                        form_module = streamfield[streamfield_index]
                    except IndexError:
                        form_module = None

        if form_module is None:
            return self._return_bad_post_response(request)

        result = form_module.block.get_result(self, request, form_module.value,
                                              True)

        if isinstance(result, HttpResponse):
            return result

        context = self.get_context(request, *args, **kwargs)
        context['form_modules'][streamfield_name].update(
            {streamfield_index: result})

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

    class Meta:
        app_label = 'v1'

    def parent(self):
        parent = self.get_ancestors(inclusive=False).reverse()[0].specific
        return parent

    # To be overriden if page type requires JS files every time
    @property
    def page_js(self):
        return []

    @property
    def streamfield_js(self):
        js = []

        block_cls_names = get_page_blocks(self)
        for block_cls_name in block_cls_names:
            block_cls = import_string(block_cls_name)
            if hasattr(block_cls, 'Media') and hasattr(block_cls.Media, 'js'):
                js.extend(block_cls.Media.js)

        return js

    # Returns the JS files required by this page and its StreamField blocks.
    @property
    def media(self):
        return sorted(set(self.page_js + self.streamfield_js))

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

    @property
    def post_preview_cache_key(self):
        return 'post_preview_{}'.format(self.id)
예제 #14
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
예제 #15
0
class BrowsePage(CFGOVPage):
    header = StreamField([
        ('text_introduction', molecules.TextIntroduction()),
        ('featured_content', molecules.FeaturedContent()),
    ],
                         blank=True)

    content = StreamField([
        ('bureau_structure', organisms.BureauStructure()),
        ('info_unit_group', organisms.InfoUnitGroup()),
        ('well', organisms.Well()),
        ('full_width_text', organisms.FullWidthText()),
        ('expandable', organisms.Expandable()),
        ('expandable_group', organisms.ExpandableGroup()),
        ('table_block',
         organisms.AtomicTableBlock(table_options={'renderer': 'html'})),
        ('job_listing_table', JobListingTable()),
        ('feedback', v1_blocks.Feedback()),
        ('conference_registration_form', ConferenceRegistrationForm()),
        ('raw_html_block', blocks.RawHTMLBlock(label='Raw HTML block')),
        ('html_block', organisms.HTMLBlock()),
        ('chart_block', organisms.ChartBlock()),
        ('mortgage_chart_block', organisms.MortgageChartBlock()),
        ('mortgage_map_block', organisms.MortgageMapBlock()),
        ('mortgage_downloads_block', MortgageDataDownloads()),
        ('snippet_list', organisms.SnippetList()),
        ('data_snapshot', organisms.DataSnapshot()),
        ('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()),
    ],
                          blank=True)

    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='Sidebar'),
        ObjectList(CFGOVPage.settings_panels, heading='Configuration'),
    ])

    template = 'browse-basic/index.html'

    objects = PageManager()

    @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
예제 #16
0
파일: models.py 프로젝트: CIGIHub/greyjay
class ContributorListPage(Page):
    subpage_types = ['ContributorPage']
    intro_text = RichTextField(blank=True)
    body = RichTextField(blank=True)

    def get_rows(self, contributors, number_of_columns=3, max_columns=4):
        rows = []
        number_of_items = len(contributors)
        number_of_rows = number_of_items // number_of_columns
        row_remainder = number_of_items % number_of_columns

        if row_remainder > number_of_rows:
            number_of_rows += 1
        elif row_remainder <= number_of_rows and row_remainder != 0:
            if number_of_columns < max_columns:
                number_of_columns += 1
            else:
                number_of_rows += 1

        for row_index in range(0, number_of_rows):
            row = contributors[(
                row_index *
                number_of_columns):(row_index * number_of_columns) +
                               number_of_columns]
            rows.append(row)
        return rows

    @property
    def recent_contributors(self):
        endtime = timezone.now()
        starttime = endtime - datetime.timedelta(days=365)
        contributors = (
            ContributorPage.objects.live().filter(  # noqa
                featured=False,
                article_links__article__isnull=False,
                article_links__isnull=False,
                article_links__article__first_published_at__range=[
                    starttime, endtime
                ]).order_by('last_name', 'first_name').distinct())

        return self.get_rows(contributors, number_of_columns=4)

    @property
    def nonfeatured_contributors(self):
        contributors = ContributorPage.objects.live().filter(
            featured=False).order_by('last_name', 'first_name')
        return self.get_rows(contributors)

    @property
    def featured_contributors(self):
        contributors = ContributorPage.objects.live().filter(
            featured=True).order_by('last_name', 'first_name')
        return self.get_rows(contributors)

    content_panels = Page.content_panels + [
        FieldPanel('intro_text'),
        FieldPanel('body'),
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
        ObjectList(Page.promote_panels, heading='Promote'),
        ObjectList(Page.settings_panels,
                   heading='Settings',
                   classname="settings"),
    ])
예제 #17
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]
예제 #18
0
파일: models.py 프로젝트: CIGIHub/greyjay
class ContributorPage(Page):
    first_name = models.CharField(max_length=255, blank=True, default="")
    last_name = models.CharField(max_length=255, blank=True, default="")
    nickname = models.CharField(max_length=1024, blank=True, default="")

    email = models.EmailField(blank=True, default="")
    twitter_handle = models.CharField(max_length=16, blank=True, default="")

    subtitle = RichTextField(blank=True, default="")
    short_bio = RichTextField(blank=True, default="")
    long_bio = RichTextField(blank=True, default="")

    headshot = models.ForeignKey('images.AttributedImage',
                                 null=True,
                                 blank=True,
                                 on_delete=models.SET_NULL,
                                 related_name='+')
    featured = models.BooleanField(default=False)

    search_fields = Page.search_fields + [
        index.SearchField('first_name', partial_match=True),
        index.SearchField('last_name', partial_match=True),
        index.SearchField('twitter_handle', partial_match=True),
        index.SearchField('subtitle', partial_match=True),
        index.SearchField('short_bio', partial_match=True),
        index.SearchField('long_bio', partial_match=True),
    ]

    def search_result_text(self):
        if self.subtitle:
            self.search_result_text = self.subtitle
        elif self.short_bio:
            self.search_result_text = self.short_bio
        else:
            self.search_result_text = self.long_bio
        return self.search_result_text

    def save(self, *args, **kwargs):
        if self.twitter_handle and not self.twitter_handle.startswith("@"):
            self.twitter_handle = "@{}".format(self.twitter_handle)
        super(ContributorPage, self).save(*args, **kwargs)

    @property
    def full_name(self):
        return "{} {}".format(self.first_name, self.last_name)

    @property
    def last_comma_first_name(self):
        return "{}, {}".format(self.last_name, self.first_name)

    @property
    def display_twitter_handle(self):
        if self.twitter_handle:
            return self.twitter_handle[1:]
        return self.twitter_handle

    def __str__(self):
        return "{} {} - {}".format(self.first_name, self.last_name, self.email)

    def get_admin_display_title(self):
        return '{} ({})'.format(self.title, self.id)

    content_panels = Page.content_panels + [
        FieldPanel('first_name'),
        FieldPanel('last_name'),
        FieldPanel('email'),
        FieldPanel('twitter_handle'),
        RichTextFieldPanel('short_bio'),
        RichTextFieldPanel('long_bio'),
        ImageChooserPanel('headshot'),
    ]

    promote_panels = Page.promote_panels + [
        MultiFieldPanel([
            FieldPanel('featured'),
        ],
                        heading="Featuring Settings")
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
        ObjectList(promote_panels, heading='Promote'),
        ObjectList(Page.settings_panels,
                   heading='Settings',
                   classname="settings"),
    ])
예제 #19
0
class SublandingPage(CFGOVPage):
    header = StreamField([
        ('hero', molecules.Hero()),
    ], blank=True)
    content = StreamField([
        ('text_introduction', molecules.TextIntroduction()),
        ('featured_content', molecules.FeaturedContent()),
        ('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()),
        ('post_preview_snapshot', organisms.PostPreviewSnapshot()),
        ('well', organisms.Well()),
        ('table', organisms.Table()),
        ('contact', organisms.MainContactInfo()),
        ('formfield_with_button', molecules.FormFieldWithButton()),
        ('reg_comment', organisms.RegComment()),
    ],
                          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()),
    ],
                                   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'

    def get_browsefilterable_posts(self, request, limit):
        filter_pages = [
            p.specific
            for p in self.get_appropriate_descendants(request.site.hostname)
            if 'FilterablePage' in p.specific_class.__name__
            and 'archive' not in p.title.lower()
        ]
        filtered_controls = {}
        for page in filter_pages:
            id = str(util.get_form_id(page, request.GET))
            if id not in filtered_controls.keys():
                filtered_controls.update({id: []})
            form_class = page.get_form_class()
            posts = filterable_context.get_page_set(
                page, form_class(parent=page, hostname=request.site.hostname),
                request.site.hostname)
            if filtered_controls[id]:
                filtered_controls[id] += posts
            else:
                filtered_controls[id] = posts
        posts_tuple_list = [(id, post)
                            for id, posts in filtered_controls.iteritems()
                            for post in posts]
        posts = sorted(posts_tuple_list,
                       key=lambda p: p[1].date_published,
                       reverse=True)[:limit]
        return posts
예제 #20
0
class PortalSearchPage(RoutablePageMixin, SecondaryNavigationJSMixin,
                       CFGOVPage):
    """
    A routable page type for Ask CFPB portal search ("see-all") pages.
    """

    objects = CFGOVPageManager()
    portal_topic = models.ForeignKey(PortalTopic,
                                     blank=True,
                                     null=True,
                                     related_name='portal_search_pages',
                                     on_delete=models.SET_NULL)
    portal_category = None
    query_base = None
    glossary_terms = None
    overview = models.TextField(blank=True)
    content_panels = CFGOVPage.content_panels + [
        FieldPanel('portal_topic'),
        FieldPanel('overview'),
    ]
    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
        ObjectList(CFGOVPage.settings_panels, heading='Configuration'),
    ])

    @property
    def category_map(self):
        """
        Return an ordered dictionary of translated-slug:object pairs.

        We use this custom sequence for categories in the navigation sidebar,
        controlled by the 'display_order' field of portal categories:
        - Basics
        - Key terms
        - Common issues
        - Know your rights
        - How-to guides
        """
        categories = PortalCategory.objects.all()
        sorted_mapping = OrderedDict()
        for category in categories:
            sorted_mapping.update(
                {slugify(category.title(self.language)): category})
        return sorted_mapping

    def results_message(self, count, heading, search_term):
        if search_term:
            _for_term = '{} "{}"'.format(_('for'), search_term)
        else:
            _for_term = ''
        if count == 1:
            _showing = _('Showing ')  # trailing space triggers singular es
            _results = _('result')
        else:
            _showing = _('Showing')
            _results = _('results')
        if self.portal_category and search_term:
            return format_html(
                '{} {} {} {} {} {}'
                '<span class="results-link"><a href="../?search_term={}">'
                '{} {}</a></span>', _showing, count, _results, _for_term,
                _('within'), heading.lower(), search_term,
                _('See all results within'),
                self.portal_topic.title(self.language).lower())
        elif self.portal_category:
            return '{} {} {} {} {}'.format(_showing, count, _results,
                                           _('within'), heading.lower())
        return '{} {} {} {} {} {}'.format(_showing, count, _results, _for_term,
                                          _('within'), heading.lower())

    def get_heading(self):
        if self.portal_category:
            return self.portal_category.title(self.language)
        else:
            return self.portal_topic.title(self.language)

    def get_context(self, request, *args, **kwargs):
        if self.language != 'en':
            activate(self.language)
        else:
            deactivate_all()
        return super(PortalSearchPage,
                     self).get_context(request, *args, **kwargs)

    def get_nav_items(self, request, page):
        """Return sorted nav items for sidebar."""
        sorted_categories = [{
            'title':
            category.title(self.language),
            'url':
            "{}{}/".format(page.url, slug),
            'active': (False if not page.portal_category else category.title(
                self.language) == page.portal_category.title(self.language))
        } for slug, category in self.category_map.items()]
        return [{
            'title': page.portal_topic.title(self.language),
            'url': page.url,
            'active': False if page.portal_category else True,
            'expanded': True,
            'children': sorted_categories
        }], True

    def get_results(self, request):
        context = self.get_context(request)
        search_term = request.GET.get('search_term', '').strip()
        if not search_term or len(unquote(search_term)) == 1:
            results = self.query_base
        else:
            search = AskSearch(search_term=search_term,
                               query_base=self.query_base)
            results = search.queryset
            if results.count() == 0:
                # No results, so let's try to suggest a better query
                search.suggest(request=request)
                results = search.queryset
                search_term = search.search_term
        search_message = self.results_message(results.count(),
                                              self.get_heading(), search_term)
        paginator = Paginator(results, 10)
        page_number = validate_page_number(request, paginator)
        context.update({
            'search_term': search_term,
            'results_message': search_message,
            'pages': paginator.page(page_number),
            'paginator': paginator,
            'current_page': page_number,
            'get_secondary_nav_items': self.get_nav_items,
        })
        return TemplateResponse(request, 'ask-cfpb/see-all.html', context)

    def get_glossary_terms(self):
        if self.language == 'es':
            terms = self.portal_topic.glossary_terms.order_by('name_es')
        else:
            terms = self.portal_topic.glossary_terms.order_by('name_en')
        for term in terms:
            if term.name(self.language) and term.definition(self.language):
                yield term

    @route(r'^$')
    def portal_topic_page(self, request):
        self.query_base = SearchQuerySet().filter(
            portal_topics=self.portal_topic.heading, language=self.language)
        self.portal_category = None
        return self.get_results(request)

    @route(r'^(?P<category>[^/]+)/$')
    def portal_category_page(self, request, **kwargs):
        category_slug = kwargs.get('category')
        if category_slug not in self.category_map:
            raise Http404
        self.portal_category = self.category_map.get(category_slug)
        self.title = "{} {}".format(
            self.portal_topic.title(self.language),
            self.portal_category.title(self.language).lower())
        if self.portal_category.heading == 'Key terms':
            self.glossary_terms = self.get_glossary_terms()
            context = self.get_context(request)
            context.update({'get_secondary_nav_items': self.get_nav_items})
            return TemplateResponse(request, 'ask-cfpb/see-all.html', context)
        self.query_base = SearchQuerySet().filter(
            portal_topics=self.portal_topic.heading,
            language=self.language,
            portal_categories=self.portal_category.heading)
        return self.get_results(request)
예제 #21
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)
예제 #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)."))
    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
예제 #23
0
파일: models.py 프로젝트: Qnetis/university
class TabbedSettings(TestSetting):
    edit_handler = TabbedInterface([
        ObjectList([FieldPanel('title')], heading='First tab'),
        ObjectList([FieldPanel('email')], heading='Second tab'),
    ])
예제 #24
0
class ArticlePage(CFGOVPage):
    """
    General article page type.
    """
    category = models.CharField(
        choices=[
            ('basics', 'Basics'),
            ('common_issues', 'Common issues'),
            ('howto', 'How to'),
            ('know_your_rights', 'Know your rights'),
        ],
        max_length=255,
    )
    heading = models.CharField(
        max_length=255,
        blank=False,
    )
    intro = models.TextField(blank=False)
    inset_heading = models.CharField(max_length=255,
                                     blank=True,
                                     verbose_name="Heading")
    sections = StreamField([
        ('section',
         blocks.StructBlock([
             ('heading',
              blocks.CharBlock(max_length=255,
                               required=True,
                               label='Section heading')),
             ('summary',
              blocks.TextBlock(required=False,
                               blank=True,
                               label='Section summary')),
             ('link_text',
              blocks.CharBlock(required=False,
                               blank=True,
                               label="Section link text")),
             ('url',
              blocks.CharBlock(
                  required=False,
                  blank=True,
                  label='Section link URL',
                  max_length=255,
              )),
             ('subsections',
              blocks.ListBlock(
                  blocks.StructBlock([
                      ('heading',
                       blocks.CharBlock(max_length=255,
                                        required=False,
                                        blank=True,
                                        label='Subsection heading')),
                      ('summary',
                       blocks.TextBlock(required=False,
                                        blank=True,
                                        label='Subsection summary')),
                      ('link_text',
                       blocks.CharBlock(required=True,
                                        label='Subsection link text')),
                      ('url',
                       blocks.CharBlock(required=True,
                                        label='Subsection link URL'))
                  ])))
         ]))
    ])
    content_panels = CFGOVPage.content_panels + [
        MultiFieldPanel([
            FieldPanel('category'),
            FieldPanel('heading'),
            FieldPanel('intro')
        ],
                        heading="Heading",
                        classname="collapsible"),
        MultiFieldPanel([
            FieldPanel('inset_heading'),
            InlinePanel('article_links', label='Inset link', max_num=2),
        ],
                        heading="Inset links",
                        classname="collapsible"),
        StreamFieldPanel('sections'),
    ]

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

    sidebar_panels = [
        StreamFieldPanel('sidebar'),
    ]

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

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

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

    objects = CFGOVPageManager()

    def get_context(self, request, *args, **kwargs):
        context = super(ArticlePage, self).get_context(request)
        context['about_us'] = get_standard_text(self.language, 'about_us')
        return context

    def __str__(self):
        return self.title
예제 #25
0
class TopicListPage(RoutablePageMixin, ThemeablePage):
    articles_per_page = models.IntegerField(default=20)

    @property
    def topics(self):
        popular_topics = Topic.objects.annotate(
            num_articles=Count('article_links') + Count('articles') + Count('series')).order_by("-num_articles")[:25]
        return sorted(popular_topics, key=lambda x: x.name)

    @route(r'^$', name="topic_list")
    def topics_list(self, request):
        context = {
            "self": self,
        }
        return render(request, "articles/topic_list_page.html", context)

    @route(r'^([\w-]+)/$', name="topic")
    def topic_view(self, request, topic_slug):
        topic = get_object_or_404(Topic, slug=topic_slug)

        articles = topic.item_list

        paginator = Paginator(articles, self.articles_per_page)
        page = request.GET.get('page')

        try:
            articles = paginator.page(page)
        except PageNotAnInteger:
            articles = paginator.page(1)
        except EmptyPage:
            articles = paginator.page(paginator.num_pages)

        context = {
            "self": self,
            "topic": topic,
            "articles": articles,
        }
        return render(request, "articles/topic_page.html", context)

    def get_cached_paths(self):
        yield '/'

        for topic in Topic.objects.all():
            articles = ArticlePage.objects.live().filter(
                models.Q(primary_topic=topic) | models.Q(topic_links__topic=topic)
            ).order_by('-first_published_at').distinct()
            paginator = Paginator(articles, self.articles_per_page)

            topic_url = '/{}/'.format(topic.slug)
            yield topic_url

            for page_number in range(2, paginator.num_pages + 1):
                yield topic_url + '?page=' + str(page_number)

    content_panels = Page.content_panels + [
        FieldPanel('articles_per_page'),
    ]

    style_panels = ThemeablePage.style_panels

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
        ObjectList(style_panels, heading='Page Style Options'),
        ObjectList(Page.promote_panels, heading='Promote'),
        ObjectList(Page.settings_panels, heading='Settings', classname="settings"),
    ])
예제 #26
0
 def setUp(self):
     fake_child = self.FakeChild()
     self.TabbedInterfaceClass = TabbedInterface([fake_child])
     self.tabbed_interface = self.TabbedInterfaceClass(instance=True,
                                                       form=True)
예제 #27
0
class SeriesPage(ThemeablePage, FeatureStyleFields, Promotable, ShareLinksMixin, PageLayoutOptions, VideoDocumentMixin):
    subtitle = RichTextField(blank=True, default="")
    short_description = RichTextField(blank=True, default="")
    body = article_fields.BodyField(blank=True, default="")

    main_image = models.ForeignKey(
        'images.AttributedImage',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+'
    )
    feature_image = models.ForeignKey(
        'images.AttributedImage',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+'
    )
    primary_topic = models.ForeignKey(
        'articles.Topic',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='series'
    )
    project = models.ForeignKey(
        "projects.ProjectPage",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
    )

    search_fields = Page.search_fields + [
        index.SearchField('subtitle', partial_match=True),
        index.SearchField('body', partial_match=True),
        index.SearchField('get_primary_topic_name', partial_match=True),
        index.SearchField('get_topic_names', partial_match=True),
    ]

    number_of_related_articles = models.PositiveSmallIntegerField(default=6,
                                                                  verbose_name="Number of Related Articles to Show")

    def get_primary_topic_name(self):
        if self.primary_topic:
            return self.primary_topic.name
        else:
            ""

    def get_topic_names(self):
        return '\n'.join([topic.name if topic else "" for topic in self.topics])

    def get_author_names(self):
        return '\n'.join([author.full_name if author else "" for author in self.authors])

    @property
    def articles(self):
        article_list = []
        for article_link in self.related_article_links.all():
            if article_link.article:
                article_link.article.override_text = article_link.override_text
                article_link.article.override_image = article_link.override_image
                article_list.append(article_link.article)
        return article_list

    @property
    def authors(self):
        author_list = []
        for article_link in self.related_article_links.all():
            if article_link.article:
                if article_link.article:
                    for author_link in article_link.article.author_links.all():
                        if author_link.author:
                            if author_link.author not in author_list:
                                author_list.append(author_link.author)
        author_list.sort(key=attrgetter('last_name'))
        return author_list

    @property
    def topics(self):
        all_topics = []
        if self.primary_topic:
            all_topics.append(self.primary_topic)
        for article_link in self.related_article_links.all():
            if article_link.article:
                all_topics.extend(article_link.article.topics)

        all_topics = list(set(all_topics))
        if all_topics:
            all_topics.sort(key=attrgetter('name'))
        return all_topics

    @property
    def related_series(self):
        related_series_list = []
        if self.project:
            related_series_list = self.project.get_related_series(self)
        return related_series_list

    def related_articles(self, number):
        articles = []
        if self.primary_topic:
            articles = list(ArticlePage.objects.live().filter(primary_topic=self.primary_topic).distinct().order_by(
                '-first_published_at')[:number])

        current_total = len(articles)
        if current_total < number:
            for article in self.articles:
                articles.extend(list(article.related_articles(number)))
                articles = list(set(articles))[:number]
                current_total = len(articles)

                if current_total >= number:
                    return articles

        return articles

    content_panels = Page.content_panels + [
        FieldPanel('subtitle'),
        FieldPanel('short_description'),
        PageChooserPanel('project'),
        ImageChooserPanel('main_image'),
        ImageChooserPanel('feature_image'),
        DocumentChooserPanel('video_document'),
        StreamFieldPanel('body'),
        InlinePanel('related_article_links', label="Articles"),
        SnippetChooserPanel('primary_topic'),
    ]

    promote_panels = Page.promote_panels + [
        MultiFieldPanel(
            [
                FieldPanel('sticky'),
                FieldPanel('sticky_for_type_section'),
                FieldPanel('slippery'),
                FieldPanel('slippery_for_type_section'),
                FieldPanel('editors_pick'),
                FieldPanel('feature_style'),
                FieldPanel('title_size'),
                FieldPanel('fullbleed_feature'),
                FieldPanel('image_overlay_opacity'),
            ],
            heading="Featuring Settings"
        )
    ]

    style_panels = ThemeablePage.style_panels + [
        MultiFieldPanel(
            [
                FieldPanel('include_main_image'),
                FieldPanel('include_main_image_overlay'),
                FieldPanel('full_bleed_image_size'),
                FieldPanel('include_caption_in_footer'),
            ],
            heading="Main Image"
        ),
        MultiFieldPanel(
            [
                FieldPanel('number_of_related_articles'),
            ],
            heading="Sections"
        )
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
        ObjectList(style_panels, heading='Page Style Options'),
        ObjectList(promote_panels, heading='Promote'),
        ObjectList(Page.settings_panels, heading='Settings', classname="settings"),
    ])
예제 #28
0
파일: models.py 프로젝트: fellab/moore
class FormPage(AbstractEmailForm):
    title_sv = models.CharField(max_length=255)
    translated_title = TranslatedField('title', 'title_sv')

    intro_en = StreamField(
        WAGTAIL_STATIC_BLOCKTYPES + [
            ('contact_card', ContactCardBlock()),
        ],
        verbose_name=_('English Introduction'),
        blank=True,
    )
    intro_sv = StreamField(
        WAGTAIL_STATIC_BLOCKTYPES + [
            ('contact_card', ContactCardBlock()),
        ],
        verbose_name=_('Swedish Introduction'),
        blank=True,
    )
    intro = TranslatedField('intro_en', 'intro_sv')

    thank_you_text_en = StreamField(
        WAGTAIL_STATIC_BLOCKTYPES + [
            ('contact_card', ContactCardBlock()),
        ],
        verbose_name=_('English Thank You Text'),
        blank=True,
    )
    thank_you_text_sv = StreamField(
        WAGTAIL_STATIC_BLOCKTYPES + [
            ('contact_card', ContactCardBlock()),
        ],
        verbose_name=_('Swedish Thank You Text'),
        blank=True,
    )
    thank_you_text = TranslatedField('thank_you_text_en', 'thank_you_text_sv')

    form_title_en = models.CharField(
        verbose_name=_('English Form Title'),
        max_length=255,
        blank=True
    )
    form_title_sv = models.CharField(
        verbose_name=_('Swedish Form Title'),
        max_length=255,
        blank=True,
    )
    form_title = TranslatedField('form_title_en', 'form_title_sv')

    general_panels = [
        InlinePanel('form_fields', label="Form fields"),
        MultiFieldPanel([
            FieldRowPanel([
                FieldPanel('from_address', classname="col6"),
                FieldPanel('to_address', classname="col6"),
            ]),
            FieldPanel('subject'),
        ], "Email"),
    ]

    content_panels_en = AbstractEmailForm.content_panels + [
        StreamFieldPanel('intro_en'),
        FieldPanel('form_title_en', classname="full title"),
        StreamFieldPanel('thank_you_text_en'),
    ]

    content_panels_sv = [
        FieldPanel('title_sv', classname="full title"),
        StreamFieldPanel('intro_sv'),
        FieldPanel('form_title_sv', classname="full title"),
        StreamFieldPanel('thank_you_text_sv'),
    ]

    edit_handler = TabbedInterface([
        ObjectList(general_panels, heading=_('General')),
        ObjectList(content_panels_en, heading=_('English')),
        ObjectList(content_panels_sv, heading=_('Swedish')),
        ObjectList(Page.promote_panels, heading=_('Promote')),
        ObjectList(Page.settings_panels, heading=_('Settings')),
    ])
예제 #29
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"
예제 #30
0
class UrbanDesign(Page):

    # Database fields
    date = models.DateField("Updated On")
    introductory_text = models.CharField(max_length=255,
                                         default='',
                                         null=False,
                                         blank=False)
    introductory_image = models.ForeignKey('wagtailimages.Image',
                                           null=True,
                                           blank=True,
                                           on_delete=models.SET_NULL,
                                           related_name='+')
    city_beautiful = StreamField([
        ('heading', blocks.CharBlock(classname="heading")),
        ('paragraph', blocks.RichTextBlock()),
        ('image', ImageChooserBlock()),
        ('video', EmbedBlock()),
        ('document', DocumentChooserBlock()),
        ('draw', DrawMapBlock()),
    ])
    transects = StreamField([
        ('heading', blocks.CharBlock(classname="heading")),
        ('paragraph', blocks.RichTextBlock()),
        ('image', ImageChooserBlock()),
        ('video', EmbedBlock()),
        ('document', DocumentChooserBlock()),
        ('draw', DrawMapBlock()),
    ])
    diversity = StreamField([
        ('heading', blocks.CharBlock(classname="heading")),
        ('paragraph', blocks.RichTextBlock()),
        ('image', ImageChooserBlock()),
        ('video', EmbedBlock()),
        ('document', DocumentChooserBlock()),
        ('draw', DrawMapBlock()),
    ])
    civic_art = StreamField([
        ('heading', blocks.CharBlock(classname="heading")),
        ('paragraph', blocks.RichTextBlock()),
        ('image', ImageChooserBlock()),
        ('video', EmbedBlock()),
        ('document', DocumentChooserBlock()),
        ('draw', DrawMapBlock()),
    ])
    final_project = StreamField([
        ('heading', blocks.CharBlock(classname="heading")),
        ('paragraph', blocks.RichTextBlock()),
        ('image', ImageChooserBlock()),
        ('video', EmbedBlock()),
        ('document', DocumentChooserBlock()),
        ('draw', DrawMapBlock()),
    ])

    # Search index configuraiton
    search_fields = Page.search_fields + [
        index.SearchField('introductory_text'),
        index.SearchField('city_beautiful'),
        index.SearchField('transects'),
        index.SearchField('diversity'),
        index.SearchField('civic_art'),
        index.SearchField('final_project'),
        index.FilterField('date'),
    ]

    # Editor panels configuration
    content_panels = Page.content_panels + [
        FieldPanel('introductory_text'),
        ImageChooserPanel('introductory_image'),
        StreamFieldPanel('city_beautiful'),
        StreamFieldPanel('transects'),
        StreamFieldPanel('diversity'),
        StreamFieldPanel('civic_art'),
        StreamFieldPanel('final_project'),
        FieldPanel('date'),
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
    ])

    # Parent page / subpage type rules
    parent_page_types = ['neighborhoods.HomePage']
    subpage_types = []