Ejemplo n.º 1
0
class Group(BasePage):
    parent_page_types = ["GroupIndexPage", "SimpleGroupIndexPage", "Group"]
    subpage_types = ["Project", "StandardPage", "Group"]
    # title is auto added

    subtitle = models.CharField(
        "Untertitel",
        max_length=80,
        null=True,
        blank=True,
        help_text="Z.B. eine sehr kurze Beschreibung",
    )

    body = StreamField(
        StandardStreamBlock(),
        blank=True,
        verbose_name="Inhalt",
    )

    logo = models.ForeignKey(
        Image,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="+",
        verbose_name="Logo",
        # help_text=""
    )

    contact_mail = models.EmailField(
        "E-Mail",
        null=True,
        blank=True,
    )
    contact_name = models.CharField(
        "Name",
        max_length=255,
        null=True,
        blank=True,
    )
    contact_phone = PhoneNumberField(
        "Telefonnummer",
        null=True,
        blank=True,
    )
    facebook_url = FacebookProfileURLField(
        "Facebook-Profil",
        null=True,
        blank=True,
    )
    instagram_url = models.URLField(
        "Instagram-Profil",
        null=True,
        blank=True,
    )
    website = PrettyURLField(
        "externe Website",
        null=True,
        blank=True,
    )

    address = models.CharField(
        "Adresse",
        help_text="Adresse, an dem die Gruppe zu finden ist",
        max_length=255,
        null=True,
        blank=True,
    )

    list_on_group_index_page = models.BooleanField(
        default="True",
        verbose_name="Auf Netzwerk & Projekte auflisten?",
    )

    objects = GroupManager()

    search_fields = BasePage.search_fields + [
        index.SearchField("subtitle"),
        index.SearchField("body"),
        index.SearchField("address"),
        index.SearchField("contact_mail"),
        index.SearchField("contact_name"),
        index.SearchField("website"),
    ]

    def get_context(self, request):
        context = super().get_context(request)
        context["parent_group"] = Group.objects.parent_of(self).first()
        context["projects"] = Group.objects.live().child_of(self)
        context["subpages"] = (Page.objects.live().child_of(self).exclude(
            pk__in=context["projects"]).specific())
        context["upcoming_events"] = self.get_related_upcoming_events()
        context["articles"] = self.articles.all().live()
        return context

    def get_related_upcoming_events(self):
        from events.models import EventPage

        now = timezone.now()

        return (EventPage.objects.filter(
            related_groups__group=self).live().filter(
                Q(start_datetime__date__gte=now)
                | Q(end_datetime__date__gte=now)))

    content_panels = [
        FieldPanel("title", classname="full title"),
        FieldPanel("subtitle"),
        ImageChooserPanel("logo"),
        FieldPanel("facebook_url"),
        FieldPanel("instagram_url"),
        FieldPanel("website"),
        FieldPanel("address"),
        MultiFieldPanel(
            [
                FieldPanel("contact_name"),
                FieldPanel("contact_mail"),
                FieldPanel("contact_phone",
                           widget=PhoneNumberInternationalFallbackWidget),
            ],
            heading="Kontakt",
        ),
        StreamFieldPanel("body"),
    ]
    promote_panels = BasePage.promote_panels + [
        FieldPanel("list_on_group_index_page"),
    ]

    class Meta:
        verbose_name = "Gruppe"
        verbose_name_plural = "Gruppen"
Ejemplo n.º 2
0
class MozfestPrimaryPage(FoundationMetadataPageMixin,
                         FoundationBannerInheritanceMixin, Page):
    header = models.CharField(max_length=250, blank=True)

    banner = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='mozfest_primary_banner',
        verbose_name='Hero Image',
        help_text=
        'Choose an image that\'s bigger than 4032px x 1152px with aspect ratio 3.5:1',
    )

    intro = RichTextField(help_text='Page intro content', blank=True)

    signup = models.ForeignKey(
        Signup,
        related_name='mozfestpage',
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        help_text='Choose an existing, or create a new, sign-up form')

    body = StreamField(base_fields)

    content_panels = Page.content_panels + [
        FieldPanel('header'),
        ImageChooserPanel('banner'),
        FieldPanel('intro'),
        SnippetChooserPanel('signup'),
        StreamFieldPanel('body'),
    ]

    subpage_types = [
        'MozfestPrimaryPage',
    ]

    show_in_menus_default = True

    use_wide_template = models.BooleanField(
        default=False,
        help_text=
        "Make the body content wide, useful for components like directories")

    settings_panels = Page.settings_panels + [FieldPanel('use_wide_template')]

    def get_template(self, request):
        if self.use_wide_template:
            return 'mozfest/mozfest_primary_page_wide.html'

        return 'mozfest/mozfest_primary_page.html'

    def get_context(self, request, bypass_menu_buildstep=False):
        context = super().get_context(request)
        context = set_main_site_nav_information(self, context,
                                                'MozfestHomepage')
        context = get_page_tree_information(self, context)

        # primary nav information
        context['menu_root'] = self
        context['menu_items'] = self.get_children().live().in_menu()

        # Also make sure that these pages always tap into the mozfest newsletter for the footer!
        mozfest_footer = Signup.objects.filter(
            name_en__iexact='mozfest').first()
        context['mozfest_footer'] = mozfest_footer

        if not bypass_menu_buildstep:
            context = set_main_site_nav_information(self, context,
                                                    'MozfestHomepage')

        return context
Ejemplo n.º 3
0
class Article(Page):
    strap = models.TextField(blank=True)
    content = RichTextField(blank=True, verbose_name="Content - Deprecated. Use 'MODULAR CONTENT' instead.")
    modular_content = StreamField([
        ('paragraph', ParagraphBlock()),
        ('n_column_paragraph', NColumnParagraphBlock()),
        ('paragraph_with_map', ParagraphWithMapBlock()),
        ('paragraph_with_page', ParagraphWithPageBlock()),

        ('paragraph_with_quote', ParagraphWithBlockQuoteBlock()),
        ('full_width_quote', FullWidthBlockQuote()),
        ('video_with_quote', VideoWithQuoteBlock()),

        ('image_with_quote_and_paragraph', ImageWithQuoteAndParagraphBlock()),
        ('full_width_image', FullWidthImageBlock()),
        ('columnar_image_with_text', NColumnImageWithTextBlock()),

        ('full_width_embed', FullWidthEmbedBlock()),
        ('paragraph_with_embed', ParagraphWithEmbedBlock()),
        ('paragraph_with_raw_embed', ParagraphWithRawEmbedBlock()),

    ], null=True, blank=True)
    show_modular_content = models.BooleanField(default=False)
    language = models.CharField(max_length=7, choices=settings.LANGUAGES)
    original_published_date = models.DateField(null=True, blank=True)
    show_day = models.BooleanField(default=True)
    show_month = models.BooleanField(default=True)
    show_year = models.BooleanField(default=True)
    featured_image = models.ForeignKey('core.AffixImage',
                                       null=True, blank=True,
                                       on_delete=models.SET_NULL)
    show_featured_image = models.BooleanField(default=True)

    categories = ParentalManyToManyField("category.Category", related_name="articles_by_category")
    locations = ParentalManyToManyField("location.Location", related_name="articles_by_location", blank=True)

    content_panels = Page.content_panels + [
        FieldPanel('strap'),
        InlinePanel('authors', label='Authors', min_num=1),
        FieldPanel('language'),
        MultiFieldPanel(
            [
                FieldPanel('original_published_date'),
                FieldRowPanel(
                    [
                        FieldPanel('show_day', classname="col4"),
                        FieldPanel('show_month', classname="col4"),
                        FieldPanel('show_year', classname="col4")
                    ])
            ], 'Date'),
        FieldPanel('content'),
        MultiFieldPanel(
            [
                ImageChooserPanel('featured_image'),
                FieldPanel('show_featured_image'),
            ], 'Cover Image'),
        FieldPanel('categories'),
        M2MFieldPanel('locations'),
    ]

    tags = ClusterTaggableManager(through=ArticleTag, blank=True)
    promote_panels = Page.promote_panels + [
        FieldPanel('tags'),
    ]

    search_fields = Page.search_fields + [
        index.SearchField('title', partial_match=True, boost=SearchBoost.TITLE),
        index.SearchField('get_authors', partial_match=True, boost=SearchBoost.AUTHOR),
        index.SearchField('strap', partial_match=True, boost=SearchBoost.DESCRIPTION),
        index.SearchField('content', partial_match=True, boost=SearchBoost.CONTENT),
        index.SearchField('modular_content', partial_match=True, boost=SearchBoost.CONTENT),
        index.SearchField('get_district_from_location', partial_match=True, boost=SearchBoost.LOCATION),
        index.SearchField('language', partial_match=True),
        index.FilterField('language'),
        index.FilterField('get_search_type'),
        index.FilterField('get_categories'),
        index.FilterField('get_minimal_locations'),
        index.FilterField('get_authors_or_photographers'),
        index.FilterField('title'),
        index.FilterField('get_state_from_locations')
    ]

    def __str__(self):
        return self.title

    def get_authors(self):
        return [article_author.author.name for article_author in self.authors.all()]

    def get_author_role_map(self, **kwargs):
        authors_with_role = kwargs['authors_with_role']
        temp = defaultdict(list)
        for awr in authors_with_role:
            role = awr.role
            if role:
                temp[role.name].append(awr.author)
            else:
                temp['None'].append(awr.author)
        return dict((key, tuple(val)) for key, val in temp.items())

    def beginning_authors_with_role(self):
        return self.get_author_role_map(authors_with_role=self.authors.filter(show_in_beginning=True).all())

    def end_authors_with_role(self):
        return self.get_author_role_map(authors_with_role=self.authors.filter(show_in_end=True).all())

    def get_authors_or_photographers(self):
        return self.get_authors()

    def get_district_from_location(self):
        return [location.address for location in self.locations.all()]

    def get_minimal_locations(self):
        return [location.minimal_address for location in self.locations.all()]

    def get_state_from_locations(self):
        return [location.state for location in self.locations.all()]

    def get_context(self, request, *args, **kwargs):
        try:
            site = Site.objects.get(hostname=request.get_host())
        except Site.DoesNotExist:
            site = Site.objects.filter(is_default_site=True)[0]
        return {
            'article': self,
            'beginning_authors_with_role': self.beginning_authors_with_role(),
            'end_authors_with_role': self.end_authors_with_role(),
            'request': request,
            'site': site
        }

    def get_absolute_url(self):
        name = "article-detail"
        return reverse(name, kwargs={"slug": self.slug})

    def get_translation(self):
        return get_translations_for_page(self)

    # Elastic search related methods
    def get_search_type(self):
        categories = [category.name for category in self.categories.all()]

        if 'VideoZone' in categories:
            return 'video'
        elif 'AudioZone' in categories:
            return 'audio'
        else:
            return 'article'

    def get_categories(self):
        return [category.name for category in self.categories.all()]

    def related_articles(self):
        if not self.pk:
            # In preview mode
            return []

        max_results = getattr(settings, "MAX_RELATED_RESULTS", 4)
        es_backend = get_search_backend()
        mapping = Elasticsearch6Mapping(self.__class__)

        minimal_locations = ""
        if (self.get_minimal_locations()):
            minimal_locations = self.get_minimal_locations()

        state_locations = ""
        if (self.get_state_from_locations()):
            state_locations = self.get_state_from_locations()

        authors_of_article = ""

        if self.authors:
            authors_of_article = self.get_authors()

        query = {
            "track_scores": "true",
            "query": {
                "bool": {
                    "must": [
                        {
                            "multi_match": {
                                "fields": ["*language_filter"],
                                "query":  self.language
                            }

                        },
                        {"term": {"live_filter": "true"}}
                    ],
                    "must_not": [
                        {"term": {"title_filter": self.title}}

                    ],
                    "should": [
                        {
                            "multi_match": {
                                "fields": ["*get_authors_or_photographers_filter"],
                                "query": ' '.join(authors_of_article),
                                "type": "cross_fields",
                                "operator": "or"
                            }

                        },
                        {
                            "multi_match": {
                                "fields": ["*get_authors"],
                                "query": ' '.join(authors_of_article),
                                "type": "cross_fields",
                                "operator": "or"
                            }

                        },
                        {
                            "multi_match": {
                                "fields": ["*get_minimal_locations_filter"],
                                "query": ' '.join(minimal_locations),
                                "type": "cross_fields",
                                "operator": "and"
                            }
                        },
                        {
                            "multi_match": {
                                "fields": ["*get_state_from_locations_filter"],
                                "query": ' '.join(state_locations),
                                "type": "cross_fields",
                                "operator": "and"
                            }

                        },
                        {
                            "match": {
                                "title": self.title
                            }
                        }
                    ],
                    "minimum_should_match": 1
                }
            },
            "sort": [
                {"_score": {"order": "desc"}},
                {"article_article__get_authors_or_photographers_filter": {"order": "desc"}},
                {"album_album__get_authors_or_photographers_filter": {"order": "desc"}},
                {"face_face__get_authors_or_photographers_filter": {"order": "desc"}},
                {"first_published_at_filter": "desc"}
            ]
        }

        try:
            mlt = es_backend.es.search(
                doc_type=mapping.get_document_type(),
                body=query
            )
        except ConnectionError:
            return []
        # Get pks from results
        pks = [hit['_source']['pk'] for hit in mlt['hits']['hits']][:max_results]

        # Initialise results dictionary
        results = dict((str(pk), None) for pk in pks)

        # Find objects in database and add them to dict
        queryset = self._meta.default_manager.filter(pk__in=pks)
        for obj in queryset:
            results[str(obj.pk)] = obj

        # Return results in order given by ElasticSearch
        return [results[str(pk)] for pk in pks if results[str(pk)]]
Ejemplo n.º 4
0
class MeetingPage(Page):
    OPEN = 'O'
    EXECUTIVE = 'E'
    HEARING = 'H'
    MEETING_TYPE_CHOICES = (
        (OPEN, 'Open meeting'),
        (EXECUTIVE, 'Executive session'),
        (HEARING, 'Hearing'),
    )

    date = models.DateField(default=datetime.date.today)
    end_date = models.DateField(null=True, blank=True)
    time = models.TimeField(null=True,
                            blank=True,
                            help_text='If no time is entered,\
     the time will be set to 10 a.m.')
    meeting_type = models.CharField(max_length=2,
                                    choices=MEETING_TYPE_CHOICES,
                                    default=OPEN)
    additional_information = models.TextField(blank=True,
                                              help_text='This field\
        accepts html')
    draft_minutes_links = models.TextField(
        blank=True, help_text='URLs separated by a newline')
    approved_minutes_date = models.DateField(null=True, blank=True)
    approved_minutes_link = models.URLField(blank=True)
    sunshine_act_links = models.TextField(
        blank=True, help_text='URLs separated by a newline')
    live_video_embed = models.CharField(blank=True, max_length=255,\
        help_text='Youtube video ID of the live-stream of the meeting')

    imported_html = StreamField([('html_block', blocks.RawHTMLBlock())],
                                null=True,
                                blank=True)

    sunshine_act_doc_upld = StreamField(
        [('sunshine_act_upld', DocumentChooserBlock(required=False))],
        null=True,
        blank=True,
    )

    full_video_url = models.URLField(blank=True)
    full_audio_url = models.URLField(blank=True)
    mtg_transcript_url = models.URLField(blank=True)

    homepage_hide = models.BooleanField(default=False)

    agenda = StreamField(
        [('agenda_item',
          blocks.StructBlock([
              ('item_title', blocks.TextBlock(required=True)),
              ('item_text', blocks.RichTextBlock(required=False)),
              ('item_audio', DocumentChooserBlock(required=False)),
              ('item_video',
               blocks.URLBlock(required=False,
                               help_text='Add a Youtube URL to a specific\
                time in a video for this agenda item')),
          ]))],
        blank=True,
        null=True)

    content_panels = Page.content_panels + [
        FieldPanel('additional_information'),
        StreamFieldPanel('agenda'),
        MultiFieldPanel([
            FieldPanel('date'),
            FieldPanel('end_date'),
            FieldPanel('time'),
            FieldPanel('meeting_type'),
        ],
                        heading='Meeting details',
                        classname='collapsible collapsed'),
        MultiFieldPanel(
            [
                #FieldPanel('sunshine_act_links'),
                StreamFieldPanel('sunshine_act_doc_upld'),
            ],
            heading='Sunshine notices',
            classname='collapsible collapsed'),
        MultiFieldPanel([
            FieldPanel('draft_minutes_links'),
            FieldPanel('approved_minutes_link'),
            FieldPanel('approved_minutes_date'),
        ],
                        heading='Minutes',
                        classname='collapsible collapsed'),
        MultiFieldPanel([
            FieldPanel('full_video_url'),
            FieldPanel('full_audio_url'),
            FieldPanel('mtg_transcript_url'),
            FieldPanel('live_video_embed')
        ],
                        heading='Meeting media',
                        classname='collapsible collapsed'),
        MultiFieldPanel([
            FieldPanel('imported_html'),
        ],
                        heading='Imported meeting content',
                        classname='collapsible collapsed')
    ]

    promote_panels = Page.promote_panels + [FieldPanel('homepage_hide')]

    search_fields = Page.search_fields + [
        index.FilterField('title'),
        index.FilterField('meeting_type'),
        index.FilterField('date'),
        index.SearchField('imported_html'),
        index.SearchField('agenda')
    ]

    @property
    def get_update_type(self):
        return constants.update_types['commission-meeting']

    @property
    def content_section(self):
        return 'about'

    @property
    def social_image_identifier(self):
        return 'meeting-page'
Ejemplo n.º 5
0
class CommissionerPage(Page):
    first_name = models.CharField(max_length=255, default='', blank=False)
    middle_initial = models.CharField(max_length=255, blank=True)
    last_name = models.CharField(max_length=255, default='', blank=False)
    picture = models.ForeignKey('wagtailimages.Image',
                                null=True,
                                blank=True,
                                on_delete=models.SET_NULL,
                                related_name='+')
    sworn_in = models.DateField(null=True, blank=True)
    term_expiration = models.DateField(null=True, blank=True)
    reappointed_dates = models.CharField(max_length=255, blank=True)
    party_affiliation = models.CharField(max_length=2,
                                         choices=(
                                             ('D', 'Democrat'),
                                             ('R', 'Republican'),
                                             ('I', 'Independent'),
                                         ))
    commissioner_title = models.CharField(max_length=255, blank=True)

    commissioner_bio = StreamField([('paragraph', blocks.RichTextBlock())],
                                   null=True,
                                   blank=True)

    commissioner_email = models.CharField(max_length=255, blank=True)
    commissioner_phone = models.CharField(max_length=255,
                                          null=True,
                                          blank=True)
    commissioner_twitter = models.CharField(max_length=255,
                                            null=True,
                                            blank=True)

    content_panels = Page.content_panels + [
        FieldPanel('first_name'),
        FieldPanel('middle_initial'),
        FieldPanel('last_name'),
        ImageChooserPanel('picture'),
        FieldPanel('sworn_in'),
        FieldPanel('term_expiration'),
        FieldPanel('reappointed_dates'),
        FieldPanel('party_affiliation'),
        FieldPanel('commissioner_title'),
        StreamFieldPanel('commissioner_bio'),
        FieldPanel('commissioner_email'),
        FieldPanel('commissioner_phone'),
        FieldPanel('commissioner_twitter'),
    ]

    def get_context(self, request):
        context = super(CommissionerPage, self).get_context(request)

        # Breadcrumbs for Commissioner pages
        context['ancestors'] = [{
            'title': 'About the FEC',
            'url': '/about/',
        }, {
            'title': 'Leadership and Structure',
            'url': '/about/leadership-and-structure',
        }, {
            'title':
            'All Commissioners',
            'url':
            '/about/leadership-and-structure/commissioners',
        }]

        return context
Ejemplo n.º 6
0
class BlogPage(Page):
    """
    A Blog Page

    We access the People object with an inline panel that references the
    ParentalKey's related_name in BlogPeopleRelationship. More docs:
    http://docs.wagtail.io/en/latest/topics/pages.html#inline-models
    """
    introduction = models.TextField(help_text='Text to describe the page',
                                    blank=True)
    image = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        help_text=
        'Landscape mode only; horizontal width between 1000px and 3000px.')
    body = StreamField(BaseStreamBlock(), verbose_name="Page body", blank=True)
    subtitle = models.CharField(blank=True, max_length=255)
    tags = ClusterTaggableManager(through=BlogPageTag, blank=True)
    date_published = models.DateField("Date article published",
                                      blank=True,
                                      null=True)

    content_panels = Page.content_panels + [
        FieldPanel('subtitle', classname="full"),
        FieldPanel('introduction', classname="full"),
        ImageChooserPanel('image'),
        StreamFieldPanel('body'),
        FieldPanel('date_published'),
        InlinePanel('blog_person_relationship',
                    label="Author(s)",
                    panels=None,
                    min_num=1),
        FieldPanel('tags'),
    ]

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

    def authors(self):
        """
        Returns the BlogPage's related People. Again note that we are using
        the ParentalKey's related_name from the BlogPeopleRelationship model
        to access these objects. This allows us to access the People objects
        with a loop on the template. If we tried to access the blog_person_
        relationship directly we'd print `blog.BlogPeopleRelationship.None`
        """
        authors = [n.people for n in self.blog_person_relationship.all()]

        return authors

    @property
    def get_tags(self):
        """
        Similar to the authors function above we're returning all the tags that
        are related to the blog post into a list we can access on the template.
        We're additionally adding a URL to access BlogPage objects with that tag
        """
        tags = self.tags.all()
        for tag in tags:
            tag.url = '/' + '/'.join(
                s.strip('/')
                for s in [self.get_parent().url, 'tags', tag.slug])
        return tags

    # Specifies parent to BlogPage as being BlogIndexPages
    parent_page_types = ['BlogIndexPage']

    # Specifies what content types can exist as children of BlogPage.
    # Empty list means that no child content types are allowed.
    subpage_types = []
Ejemplo n.º 7
0
class Story(Page):
    summary = models.CharField(max_length=1000, blank=True)
    intro = models.CharField(max_length=1000, blank=True)
    body = StreamField(StoryStreamBlock(), blank=True)
    skip_home = models.BooleanField(default=None)
    # location = models.CharField(max_length=255, blank=True)

    translation_language = models.CharField(max_length=2, blank=True)
    translation_for = models.ForeignKey('self', on_delete=models.SET_NULL,
                                        blank=True, null=True,
                                        related_name='translations')

    format = models.CharField(max_length=50, blank=True)
    tags = ClusterTaggableManager(through=StoryTag, blank=True)

    author_ids = JSONField(default=list)
    type = models.ForeignKey(StoryType, on_delete=models.SET_NULL, blank=True,
                             null=True)
    template = models.ForeignKey(StoryTemplate, on_delete=models.PROTECT)
    dossier = models.ForeignKey(StoryDossier, on_delete=models.SET_NULL,
                                blank=True, null=True)
    image = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='stories_related'
    )
    feed_image = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+'
    )

    @property
    def authors(self):
        rv = []
        for pk in self.author_ids:
            try:
                author = Author.objects.get(pk=pk)
            except Author.DoesNotExist:
                continue
            else:
                rv.append(author)
        return rv

    @property
    def stories_index(self):
        # Find closest ancestor which is a blog index
        return self.get_ancestors().type(StoriesIndex).last()

    @property
    def summary_or_intro(self):
        return self.summary or self.intro

    search_fields = Page.search_fields + [
        index.SearchField('summary'),
        index.SearchField('intro'),
        index.SearchField('body'),
        index.SearchField('authors'),
    ]

    content_panels = Page.content_panels + [
        FieldPanel('summary', classname='full'),
        FieldPanel('intro', classname='full'),
        ImageChooserPanel('image'),
        MultiFieldPanel([
            FieldPanel('author_ids', widget=AuthorsWidget),
            FieldPanel('type'),
            FieldPanel('dossier'),
            FieldPanel('first_published_at'),
        ]),
        InlinePanel('locations', label="Locations"),
    ]

    promote_panels = Page.promote_panels + [
        MultiFieldPanel([
            FieldPanel('tags'),
            ImageChooserPanel('feed_image'),
            FieldPanel('skip_home'),
        ], heading="Extra"),
        MultiFieldPanel([
            InlinePanel('related_pages', label="Related Pages"),
            InlinePanel('related_links', label="External links"),
        ], heading="Related"),
    ]

    blocks_panels = [
        StreamFieldPanel('body', classname='full'),
    ]

    settings_panels = Page.settings_panels + [
        PageChooserPanel('translation_for'),
        FieldPanel('template'),
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content details'),
        ObjectList(blocks_panels, heading='Content'),
        ObjectList(promote_panels, heading='Promote'),
        ObjectList(settings_panels, heading='Settings', classname="settings"),
    ])

    def serve(self, request):
        template = f'blacktail/story/{self.template}.html'
        return render(request, template, {
            'page': self,
        })
Ejemplo n.º 8
0
class HomePage(BasePage):
    # Only allow creating HomePages at the root level
    parent_page_types = ['wagtailcore.Page']

    NUM_RELATED = 6

    strapline = models.CharField(blank=True, max_length=255)
    strapline_link = models.ForeignKey('wagtailcore.Page',
                                       related_name='+',
                                       on_delete=models.PROTECT)
    strapline_link_text = models.CharField(max_length=255)

    news_title = models.CharField(blank=True, max_length=255)
    news_link = models.ForeignKey('wagtailcore.Page',
                                  blank=True,
                                  null=True,
                                  related_name='+',
                                  on_delete=models.PROTECT)
    news_link_text = models.CharField(blank=True, max_length=255)

    our_work_title = models.CharField(max_length=255)
    our_work = StreamField([
        ('work', OurWorkBlock()),
    ])
    our_work_link = models.ForeignKey('wagtailcore.Page',
                                      related_name='+',
                                      on_delete=models.PROTECT)
    our_work_link_text = models.CharField(max_length=255)

    funds_title = models.CharField(max_length=255)
    funds_intro = models.TextField(blank=True)
    funds_link = models.ForeignKey('wagtailcore.Page',
                                   related_name='+',
                                   on_delete=models.PROTECT)
    funds_link_text = models.CharField(max_length=255)

    labs_title = models.CharField(max_length=255)
    labs_intro = models.TextField(blank=True)
    labs_link = models.ForeignKey('wagtailcore.Page',
                                  related_name='+',
                                  on_delete=models.PROTECT)
    labs_link_text = models.CharField(max_length=255)

    rfps_title = models.CharField(max_length=255)
    rfps_intro = models.TextField(blank=True)

    search_fields = BasePage.search_fields + [
        index.SearchField('strapline'),
    ]

    content_panels = BasePage.content_panels + [
        MultiFieldPanel([
            FieldPanel('strapline'),
            PageChooserPanel('strapline_link'),
            FieldPanel('strapline_link_text'),
        ],
                        heading='Introduction'),
        MultiFieldPanel([
            FieldPanel('news_title'),
            PageChooserPanel('news_link'),
            FieldPanel('news_link_text'),
        ],
                        heading='News'),
        MultiFieldPanel([
            FieldPanel('our_work_title'),
            StreamFieldPanel('our_work'),
            PageChooserPanel('our_work_link'),
            FieldPanel('our_work_link_text'),
        ],
                        heading='Our Work'),
        MultiFieldPanel([
            FieldPanel('funds_title'),
            FieldPanel('funds_intro'),
            InlinePanel(
                'promoted_funds', label='Promoted Funds', max_num=NUM_RELATED),
            PageChooserPanel('funds_link'),
            FieldPanel('funds_link_text'),
        ],
                        heading='Funds'),
        MultiFieldPanel([
            FieldPanel('labs_title'),
            FieldPanel('labs_intro'),
            InlinePanel(
                'promoted_labs', label='Promoted Labs', max_num=NUM_RELATED),
            PageChooserPanel('labs_link'),
            FieldPanel('labs_link_text'),
        ],
                        heading='Labs'),
        MultiFieldPanel([
            FieldPanel('rfps_title'),
            FieldPanel('rfps_intro'),
            InlinePanel(
                'promoted_rfps', label='Promoted RFPs', max_num=NUM_RELATED),
        ],
                        heading='Labs'),
    ]

    def get_related(self, page_type, base_list):
        related = page_type.objects.filter(
            id__in=base_list.values_list('page')).live().public()
        yield from related
        selected = list(related.values_list('id', flat=True))
        extra_needed = self.NUM_RELATED - len(selected)
        extra_qs = page_type.objects.public().live().exclude(id__in=selected)
        displayed = 0
        for page in self.sorted_by_deadline(extra_qs):
            if page.is_open:
                yield page
                displayed += 1
            if displayed >= extra_needed:
                break

    def sorted_by_deadline(self, qs):
        def sort_by_deadline(value):
            try:
                return value.deadline or datetime.date.max
            except AttributeError:
                return datetime.date.max

        yield from sorted(qs, key=sort_by_deadline)

    def pages_from_related(self, related):
        for related in related.all():
            if related.page.live and related.page.public:
                yield related.page.specific

    def get_context(self, *args, **kwargs):
        context = super().get_context(*args, **kwargs)
        context['lab_list'] = list(
            self.get_related(LabPage, self.promoted_labs))
        context['fund_list'] = list(
            self.get_related(FundPage, self.promoted_funds))
        context['rfps_list'] = list(
            self.get_related(RFPPage, self.promoted_rfps))
        return context
Ejemplo n.º 9
0
class ResetNetworkResourcePage(ResetNetworkBasePage):
    class Meta:
        verbose_name = "Reset Network Resource Page"

    parent_page_types = ['reset_network_resources.ResetNetworkResourcesPage']
    subpage_types = []

    content_heading = models.CharField(verbose_name='Heading',
                                       max_length=255,
                                       blank=False)
    content_image = models.ForeignKey('images.CustomImage',
                                      verbose_name='Image',
                                      null=True,
                                      blank=True,
                                      related_name='+',
                                      on_delete=models.SET_NULL)
    content_text = StreamField([('text', blocks.RichTextBlock()),
                                ('image', ImageChooserBlock()),
                                ('embed', EmbedBlock())])

    category = models.CharField(verbose_name='Tag',
                                max_length=255,
                                null=True,
                                blank=True)
    categories = ClusterTaggableManager(
        through=ResetNetworkResourcePageCategory, blank=True)

    content_assets_heading = models.CharField(verbose_name='Heading',
                                              max_length=255,
                                              blank=False)

    content_links_heading = models.CharField(verbose_name='Heading',
                                             max_length=255,
                                             blank=False)

    card_heading = models.CharField(max_length=100, blank=False)
    card_text = models.TextField(blank=True)
    card_image = models.ForeignKey('images.CustomImage',
                                   null=True,
                                   blank=True,
                                   related_name='+',
                                   on_delete=models.SET_NULL)

    content_panels = Page.content_panels + [
        MultiFieldPanel([
            FieldPanel('content_heading'),
            ImageChooserPanel('content_image'),
            StreamFieldPanel('content_text'),
            FieldPanel('categories'),
        ],
                        heading='Content'),
        MultiFieldPanel([
            FieldPanel('content_assets_heading'),
            InlinePanel('reset_network_resource_page_assets',
                        label='Asset',
                        heading='Assets'),
        ], 'Content - Assets'),
        MultiFieldPanel([
            FieldPanel('content_links_heading'),
            InlinePanel('reset_network_resource_page_links',
                        label='Link',
                        heading='Links'),
        ], 'Content - Links'),
        MultiFieldPanel([
            FieldPanel('card_heading'),
            FieldPanel('card_text'),
            ImageChooserPanel('card_image'),
            FieldPanel('category'),
        ],
                        heading='Card Details'),
    ]
Ejemplo n.º 10
0
class Track(index.Indexed, TimeStampMixin):
    title = models.CharField(null=True, blank=False, max_length=250)
    audio_file = models.FileField(upload_to="tracks/",
                                  blank=True,
                                  validators=[validate_audio_file])
    audio_channel = models.CharField(null=True, blank=True, max_length=250)
    audio_format = models.CharField(null=True, blank=True, max_length=250)
    audio_codec = models.CharField(null=True, blank=True, max_length=250)
    audio_bitrate = models.CharField(null=True, blank=True, max_length=250)
    description = models.TextField(null=True, blank=True)
    tags = StreamField([("tag", TagBlock(required=True, icon="tag"))],
                       blank=True)
    attendees = StreamField(
        [("attendee", AttendeeBlock(required=True, icon="user"))], blank=True)
    transcript = models.TextField(null=True, blank=True)
    pac = ParentalKey("ProjectAudioChannel",
                      related_name="tracks",
                      on_delete=models.CASCADE)

    graphql_fields = [
        GraphQLString("title", required=True),
        GraphqlDatetime("created_at", required=True),
        GraphQLString("audio_file_url"),
        GraphQLString("audio_channel"),
        GraphQLString("audio_format"),
        GraphQLString("audio_codec"),
        GraphQLString("audio_bitrate"),
        GraphQLString("description"),
        GraphQLStreamfield("tags"),
        GraphQLStreamfield("attendees"),
        GraphQLString("transcript"),
        GraphQLForeignKey("pac", "track.ProjectAudioChannel"),
    ]

    search_fields = [
        index.SearchField("title"),
        index.SearchField("created_at"),
        index.SearchField("description"),
        index.SearchField("tags"),
        index.SearchField("attendees"),
        index.SearchField("transcript"),
        index.FilterField("pac"),
        index.FilterField("snekuser_id"),
    ]

    panels = [
        FieldPanel("title"),
        ReadOnlyPanel("created_at"),
        FieldPanel("audio_file"),
        FieldPanel("audio_channel"),
        FieldPanel("audio_format"),
        FieldPanel("audio_codec"),
        FieldPanel("audio_bitrate"),
        FieldPanel("description"),
        StreamFieldPanel("tags"),
        StreamFieldPanel("attendees"),
        FieldPanel("transcript"),
        FieldPanel("pac"),
    ]

    def audio_file_url(self, info, **kwargs):
        return ("%s%s" % (settings.BASE_URL, self.audio_file.url)
                if self.audio_file.name else None)

    def __str__(self):
        return f"{self.title}"

    @classmethod
    @login_required
    def bifrost_queryset(cls, info, **kwargs):
        return cls.objects.filter(pac__members=info.context.user)
Ejemplo n.º 11
0
class ArticlePage(RoutablePageMixin, Page):
    headline = RichTextField(features=["italic"])
    subdeck = RichTextField(features=["italic"], null=True, blank=True)
    kicker = models.ForeignKey(Kicker,
                               null=True,
                               blank=True,
                               on_delete=models.PROTECT)
    body = StreamField(
        [
            ("paragraph", RichTextBlock()),
            ("photo", PhotoBlock()),
            ("photo_gallery", ListBlock(GalleryPhotoBlock(), icon="image")),
            ("embed", EmbeddedMediaBlock()),
        ],
        blank=True,
    )
    summary = RichTextField(
        features=["italic"],
        null=True,
        blank=True,
        help_text=
        "Displayed on the home page or other places to provide a taste of what the article is about.",
    )
    featured_image = models.ForeignKey(
        CustomImage,
        null=True,
        blank=True,
        on_delete=models.PROTECT,
        help_text="Shown at the top of the article and on the home page.",
    )
    featured_caption = RichTextField(features=["italic"],
                                     blank=True,
                                     null=True)

    content_panels = [
        MultiFieldPanel(
            [FieldPanel("headline", classname="title"),
             FieldPanel("subdeck")]),
        MultiFieldPanel(
            [
                InlinePanel(
                    "authors",
                    panels=[
                        AutocompletePanel("author",
                                          target_model="core.Contributor")
                    ],
                    label="Author",
                ),
                AutocompletePanel("kicker", target_model="core.Kicker"),
                ImageChooserPanel("featured_image"),
                FieldPanel("featured_caption"),
            ],
            heading="Metadata",
            classname="collapsible",
        ),
        FieldPanel("summary"),
        StreamFieldPanel("body"),
    ]

    search_fields = Page.search_fields + [
        index.SearchField("headline"),
        index.SearchField("subdeck"),
        index.SearchField("body"),
        index.SearchField("summary"),
        index.RelatedFields("kicker", [index.SearchField("title")]),
        index.SearchField("get_author_names", partial_match=True),
    ]

    subpage_types = []

    def clean(self):
        super().clean()

        soup = BeautifulSoup(self.headline, "html.parser")
        self.title = soup.text

    @route(r"^$")
    def post_404(self, request):
        """Return an HTTP 404 whenever the page is accessed directly.

        This is because it should instead by accessed by its date-based path,
        i.e. `<year>/<month>/<slug>/`."""
        raise Http404

    def set_url_path(self, parent):
        """Make sure the page knows its own path. The published date might not be set,
        so we have to take that into account and ignore it if so."""
        date = self.get_published_date() or timezone.now()
        self.url_path = f"{parent.url_path}{date.year}/{date.month:02d}/{self.slug}/"
        return self.url_path

    def serve_preview(self, request, mode_name):
        request.is_preview = True
        return self.serve(request)

    def get_context(self, request):
        context = super().get_context(request)
        context["authors"] = self.get_authors()
        return context

    def get_authors(self):
        return [r.author for r in self.authors.select_related("author")]

    def get_author_names(self):
        return [a.name for a in self.get_authors()]

    def get_published_date(self):
        return (self.go_live_at or self.first_published_at
                or getattr(self.get_latest_revision(), "created_at", None))

    def get_text_html(self):
        """Get the HTML that represents paragraphs within the article as a string."""
        builder = ""
        for block in self.body:
            if block.block_type == "paragraph":
                builder += str(block.value)
        return builder

    def get_plain_text(self):
        builder = ""
        soup = BeautifulSoup(self.get_text_html(), "html.parser")
        for para in soup.findAll("p"):
            builder += para.text
            builder += " "
        return builder[:-1]

    def get_related_articles(self):
        found_articles = []
        related_articles = []
        current_article_text = self.get_plain_text()
        if current_article_text is not None:
            current_article_words = set(current_article_text.split(" "))
            authors = self.get_authors()
            for author in authors:
                articles = author.get_articles()
                for article in articles:
                    if article.headline != self.headline:
                        text_to_match = article.get_plain_text()
                        article_words = set(text_to_match.split(" "))
                        found_articles.append((
                            article,
                            len(
                                list(
                                    current_article_words.intersection(
                                        article_words))),
                        ))
            found_articles.sort(key=operator.itemgetter(1), reverse=True)
            for i in range(min(4, len(found_articles))):
                related_articles.append(found_articles[i][0])
        return related_articles

    def get_first_chars(self, n=100):
        """Convert the body to HTML, extract the text, and then build
        a string out of it until we have at least n characters.
        If this isn't possible, then return None."""

        text = self.get_plain_text()
        if len(text) < n:
            return None

        punctuation = {".", "!"}
        for i in range(n, len(text)):
            if text[i] in punctuation:
                if i + 1 == len(text):
                    return text
                elif text[i + 1] == " ":
                    return text[:i + 1]

        return None

    def get_meta_tags(self):
        tags = {}
        tags["og:type"] = "article"
        tags["og:title"] = self.title
        tags["og:url"] = self.full_url
        tags["og:site_name"] = self.get_site().site_name

        # description: either the article's summary or first paragraph
        if self.summary is not None:
            soup = BeautifulSoup(self.summary, "html.parser")
            tags["og:description"] = soup.get_text()
            tags["twitter:description"] = soup.get_text()
        else:
            first_chars = self.get_first_chars()
            if first_chars is not None:
                tags["og:description"] = first_chars
                tags["twitter:description"] = first_chars

        # image
        if self.featured_image is not None:
            # pylint: disable=E1101
            rendition = self.featured_image.get_rendition("min-1200x1200")
            rendition_url = self.get_site().root_url + rendition.url
            tags["og:image"] = rendition_url
            tags["twitter:image"] = rendition_url
        else:
            tags["og:image"] = (self.get_site().root_url +
                                "/static/images/minimal_logo_tag_padding.png")
            tags["twitter:image"] = (
                self.get_site().root_url +
                "static/images/minimal_logo_tag_padding.png")

        tags["twitter:site"] = "@rpipoly"
        tags["twitter:title"] = self.title
        if "twitter:description" in tags and "twitter:image" in tags:
            tags["twitter:card"] = "summary_large_image"
        else:
            tags["twitter:card"] = "summary"

        return tags
Ejemplo n.º 12
0
class PromoLandingPage(Page):
    TMP_CURRENCIES = (
        ('USD', '$'),
        ('GBP', '£'),
        ('EUR', '€'),
    )
    hero_title = models.CharField(max_length=100)
    hero_subtitle = models.CharField(max_length=200, blank=True, null=True)
    overview = StreamField(blocks.StreamBlock(
        [('freeform', blocks.RichTextBlock()),
         ('two_three_column_grid', ColumnBlocks())],
        max_num=3),
                           null=True,
                           blank=True)
    body = StreamField(blocks.StreamBlock([('content', ContentBlock())],
                                          max_num=3),
                       verbose_name="Features")
    extra_content = StreamField(blocks.StreamBlock(
        [('freeform', blocks.RichTextBlock()),
         ('two_three_column_grid', ColumnBlocks())],
        max_num=3),
                                null=True,
                                blank=True)
    hero_image = models.ForeignKey('wagtailimages.Image',
                                   null=True,
                                   blank=True,
                                   on_delete=models.SET_NULL,
                                   related_name='+')
    plan = models.ForeignKey(PromoPlan,
                             null=True,
                             blank=True,
                             on_delete=models.SET_NULL)
    price = models.DecimalField(decimal_places=2, max_digits=10)
    currency = models.CharField(max_length=3, choices=TMP_CURRENCIES)
    promo_duration = models.DateTimeField(
        help_text="Set the time when you want this to expire.",
        blank=True,
        null=True)
    coupon = models.CharField(max_length=50, blank=True, null=True)
    benefits = RichTextField(features=['h4', 'bold', 'italic', 'ol', 'ul'],
                             blank=True,
                             null=True)
    CTA = models.CharField(max_length=200, default="Start Your Plan Now.")

    created_date = models.DateTimeField(editable=False, auto_now_add=True)
    modified_date = models.DateTimeField(editable=False,
                                         db_index=True,
                                         auto_now=True)

    content_panels = [
        ImageChooserPanel('hero_image'),
        FieldPanel('title'),
        FieldPanel('hero_title'),
        FieldPanel('hero_subtitle'),
        StreamFieldPanel('overview'),
        StreamFieldPanel('body'),
        StreamFieldPanel('extra_content'),
        MultiFieldPanel([
            FieldPanel('plan'),
            FieldPanel('price'),
            FieldPanel('currency'),
            FieldPanel('coupon'),
            FieldPanel('promo_duration'),
            FieldPanel('CTA'),
            FieldPanel('benefits')
        ],
                        heading="Promo Plan Details",
                        classname="collapsible"),
    ]
Ejemplo n.º 13
0
class PrimaryPage(FoundationMetadataPageMixin, Page):
    """
    Basically a straight copy of modular page, but with
    restrictions on what can live 'under it'.

    Ideally this is just PrimaryPage(ModularPage) but
    setting that up as a migration seems to be causing
    problems.
    """
    header = models.CharField(max_length=250, blank=True)

    banner = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='primary_banner',
        verbose_name='Hero Image',
        help_text=
        'Choose an image that\'s bigger than 4032px x 1152px with aspect ratio 3.5:1',
    )

    intro = models.CharField(
        max_length=250,
        blank=True,
        help_text='Intro paragraph to show in hero cutout box')

    narrowed_page_content = models.BooleanField(
        default=False,
        help_text=
        'For text-heavy pages, turn this on to reduce the overall width of the content on the page.'
    )

    zen_nav = models.BooleanField(
        default=False,
        help_text=
        'For secondary nav pages, use this to collapse the primary nav under a toggle hamburger.'
    )

    body = StreamField(base_fields)

    settings_panels = Page.settings_panels + [
        MultiFieldPanel([
            FieldPanel('narrowed_page_content'),
        ]),
        MultiFieldPanel([
            FieldPanel('zen_nav'),
        ])
    ]

    content_panels = Page.content_panels + [
        FieldPanel('header'),
        ImageChooserPanel('banner'),
        FieldPanel('intro'),
        StreamFieldPanel('body'),
    ]

    subpage_types = ['PrimaryPage', 'RedirectingPage', 'BanneredCampaignPage']

    show_in_menus_default = True

    def get_context(self, request):
        context = super().get_context(request)
        context = set_main_site_nav_information(self, context, 'Homepage')
        context = get_page_tree_information(self, context)
        return context
Ejemplo n.º 14
0
class SimpleGroupIndexPage(BasePage):
    """GroupIndexPage für Lüneburg"""

    # title is auto added
    subpage_types = ["Group", "Project"]
    parent_page_types = ["HomePage"]
    max_count_per_parent = 1

    heading = models.CharField("Überschrift", max_length=255, blank=True)
    highlight_in_heading = models.CharField(
        "Hervorhebungen in der Überschrift",
        help_text=
        "Wiederhole Text aus der Überschrift der farblich hervorgehoben werden soll",
        blank=True,
        max_length=255,
    )
    subtitle = models.CharField("Untertitel", max_length=255, blank=True)

    groups_text = RichTextField(
        blank=True,
        verbose_name="Gruppen-Text",
        help_text="Wird bei den Gruppen angezeigt.",
    )
    projects_text = RichTextField(
        blank=True,
        verbose_name="Projekte-Text",
        help_text="Wird bei den Projekten angezeigt.",
    )
    coops_text = RichTextField(
        blank=True,
        verbose_name="Kooperationen-Text",
        help_text="Wird bei den Kooperationen angezeigt.",
    )

    coops = StreamField([("coop", CoopBlock())],
                        blank=True,
                        verbose_name="Kooperationen")

    class Meta:
        verbose_name = "Auflistung von Gruppen (Lüneburg)"

    def get_context(self, request):
        context = super().get_context(request)
        context["projects"] = (
            Project.objects.descendant_of(self).live().filter(
                list_on_group_index_page=True))
        context["groups"] = (Group.objects.child_of(self).live().filter(
            list_on_group_index_page=True).exclude(
                pk__in=[p.pk for p in context["projects"]]))
        return context

    content_panels = [
        FieldPanel("title"),
        MultiFieldPanel(
            [
                FieldPanel("heading"),
                FieldPanel("highlight_in_heading"),
                FieldPanel("subtitle"),
            ],
            "Header",
        ),
        FieldPanel("groups_text"),
        FieldPanel("projects_text"),
        FieldPanel("coops_text"),
        StreamFieldPanel("coops"),
    ]
Ejemplo n.º 15
0
class HomePage(Page):
    parent_page_types = ["wagtailcore.Page"]
    subpage_types = [
        "flex.FlexPage", "services.service_listing_page", "contact.ContactPage"
    ]
    max_count = 1
    lead_text = models.CharField(
        max_length=140,
        blank=True,
        help_text='Subheading text under the banner title',
    )
    button = models.ForeignKey(
        'wagtailcore.Page',
        blank=True,
        null=True,
        related_name='+',
        help_text='Select an optional page to link to',
        on_delete=models.SET_NULL,
    )
    button_text = models.CharField(
        max_length=50,
        default='Read more',
        blank=False,
        help_text='Button text',
    )
    banner_background_image = models.ForeignKey(
        'wagtailimages.Image',
        blank=False,
        null=True,
        related_name='+',
        help_text='The banner background image',
        on_delete=models.SET_NULL,
    )

    body = StreamField(
        [("title", blocks.TitleBlock()), ("cards", blocks.CardsBlock()),
         ("image_and_text", blocks.ImageAndTextBlock()),
         ("cta", blocks.CallToActionBlock()),
         ("testimonial",
          SnippetChooserBlock(target_model='testimonials.Testimonial',
                              template="streams/testimonial_block.html")),
         ("pricing_table",
          blocks.PricingTableBlock(table_options=new_table_options, ))],
        null=True,
        blank=True)

    content_panels = Page.content_panels + [
        FieldPanel("lead_text"),
        PageChooserPanel("button"),
        FieldPanel("button_text"),
        ImageChooserPanel("banner_background_image"),
        StreamFieldPanel("body"),
    ]

    def save(self, *args, **kwargs):

        key = make_template_fragment_key(
            "home_page_streams",
            [self.id],
        )
        cache.delete(key)

        return super().save(*args, **kwargs)
Ejemplo n.º 16
0
class NewsArticle(Page):
    date = models.DateField("Post date")
    heading = models.CharField(max_length=250,
                               help_text="Heading displayed on website")
    subheading = models.CharField(max_length=250, blank=True, null=True)
    author = models.CharField(max_length=250)
    featured_image = models.ForeignKey('wagtailimages.Image',
                                       null=True,
                                       blank=True,
                                       on_delete=models.SET_NULL,
                                       related_name='+',
                                       help_text="Image should be 1200 x 600")
    featured_image_alt_text = models.CharField(max_length=250,
                                               blank=True,
                                               null=True)

    def get_article_image(self):
        return build_image_url(self.featured_image)

    article_image = property(get_article_image)
    tags = ClusterTaggableManager(through=NewsArticleTag, blank=True)
    body = StreamField(BlogStreamBlock())
    pin_to_top = models.BooleanField(default=False)
    promote_image = models.ForeignKey('wagtailimages.Image',
                                      null=True,
                                      blank=True,
                                      on_delete=models.SET_NULL,
                                      related_name='+')

    @property
    def first_paragraph(self):
        paragraphs = []
        for block in self.body:
            if block.block_type == 'paragraph':
                paragraphs.append(str(block.value))

        first_paragraph_parsed = []
        soup = BeautifulSoup(paragraphs[0], "html.parser")
        for tag in soup.findAll('p'):
            first_paragraph_parsed.append(tag)

        return str(first_paragraph_parsed[0])

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

    content_panels = Page.content_panels + [
        FieldPanel('date'),
        FieldPanel('title'),
        FieldPanel('heading'),
        FieldPanel('subheading'),
        FieldPanel('author'),
        ImageChooserPanel('featured_image'),
        FieldPanel('featured_image_alt_text'),
        FieldPanel('tags'),
        StreamFieldPanel('body'),
        FieldPanel('pin_to_top'),
    ]

    promote_panels = [
        FieldPanel('slug'),
        FieldPanel('seo_title'),
        FieldPanel('search_description'),
        ImageChooserPanel('promote_image')
    ]

    api_fields = [
        APIField('date'),
        APIField('title'),
        APIField('heading'),
        APIField('subheading'),
        APIField('author'),
        APIField('article_image'),
        APIField('featured_image_small',
                 serializer=ImageRenditionField('width-420',
                                                source='featured_image')),
        APIField('featured_image_alt_text'),
        APIField('tags'),
        APIField('body'),
        APIField('pin_to_top'),
        APIField('slug'),
        APIField('seo_title'),
        APIField('search_description'),
        APIField('promote_image')
    ]

    parent_page_types = ['news.NewsIndex']

    def save(self, *args, **kwargs):
        if self.pin_to_top:
            current_pins = self.__class__.objects.filter(pin_to_top=True)
            for pin in current_pins:
                if pin != self:
                    pin.pin_to_top = False
                    pin.save()

        return super(NewsArticle, self).save(*args, **kwargs)

    def get_sitemap_urls(self, request=None):
        return [{
            'location':
            '{}/blog/{}/'.format(
                Site.find_for_request(request).root_url, self.slug),
            'lastmod': (self.last_published_at
                        or self.latest_revision_created_at),
        }]
Ejemplo n.º 17
0
class MentorPage(Page):
    CATEGORY_CHOICES = (('signing', '签约导师'), ('cooperation', '合作导师'),
                        ('infinite', '无界导师'))
    date = models.DateField('发表日期')
    intro = RichTextField('简介', blank=True)
    category = models.CharField('导师类别',
                                choices=CATEGORY_CHOICES,
                                max_length=16,
                                default='infinite')
    mentortitle = models.CharField('导师头衔', max_length=64, blank=True)
    body = StreamField([
        ('标题', blocks.CharBlock(classname="full title")),
        ('段落', blocks.RichTextBlock()),
        ('图片', ImageChooserBlock()),
    ])
    tags = ClusterTaggableManager(through=MentorPageTag, blank=True)

    class Meta:
        verbose_name = '老师详情页'
        verbose_name_plural = verbose_name

    def thumbnail_image(self):
        gallery_item = self.gallery_images.first()
        if gallery_item:
            return gallery_item.image
        else:
            return None

    def banner_image(self):
        gallery_items = self.gallery_images.all()
        if len(gallery_items) < 2:
            return None
        else:
            return gallery_items[1].image

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

    content_panels = Page.content_panels + [
        MultiFieldPanel([
            FieldPanel('date'),
            FieldPanel('tags'),
        ],
                        heading="内容属性"),
        MultiFieldPanel([
            FieldPanel('mentortitle'),
            FieldPanel('category'),
            FieldPanel('intro'),
        ],
                        heading='导师简介'),
        StreamFieldPanel('body'),
        InlinePanel('gallery_images', label="Gallery images"),
    ]

    def get_context(self, request):
        wallpaper = Image.objects.filter(
            tags__name="导师壁纸").order_by('-created_at')[0]
        # Update template context
        context = super().get_context(request)
        context['wallpaper'] = wallpaper
        return context
Ejemplo n.º 18
0
class PressIndex(Page):
    press_kit = models.ForeignKey('wagtaildocs.Document',
                                  null=True,
                                  blank=True,
                                  on_delete=models.SET_NULL,
                                  related_name='+')

    def get_press_kit(self):
        return build_image_url(self.press_kit)

    press_kit_url = property(get_press_kit)

    press_inquiry_name = models.CharField(max_length=255,
                                          blank=True,
                                          null=True)
    press_inquiry_phone = models.CharField(max_length=255)
    press_inquiry_email = models.EmailField()
    experts_heading = models.CharField(max_length=255)
    experts_blurb = models.TextField()
    mentions = StreamField([
        ('mention', NewsMentionBlock(icon='document')),
    ],
                           null=True)
    promote_image = models.ForeignKey('wagtailimages.Image',
                                      null=True,
                                      blank=True,
                                      on_delete=models.SET_NULL,
                                      related_name='+')

    def get_sitemap_urls(self, request=None):
        return [{
            'location':
            '{}/press/'.format(Site.find_for_request(request).root_url),
            'lastmod': (self.last_published_at
                        or self.latest_revision_created_at),
        }]

    @property
    def releases(self):
        releases = PressRelease.objects.live().child_of(self)
        releases_data = {}
        for release in releases:
            releases_data['press/{}'.format(release.slug)] = {
                'detail_url': '/apps/cms/api/v2/pages/{}/'.format(release.pk),
                'date': release.date,
                'heading': release.heading,
                'excerpt': release.excerpt,
                'author': release.author,
            }
        return releases_data

    content_panels = Page.content_panels + [
        DocumentChooserPanel('press_kit'),
        FieldPanel('press_inquiry_name'),
        FieldPanel('press_inquiry_phone'),
        FieldPanel('press_inquiry_email'),
        FieldPanel('experts_heading'),
        FieldPanel('experts_blurb'),
        InlinePanel('experts_bios', label="Experts"),
        StreamFieldPanel('mentions'),
        InlinePanel('mission_statements', label="Mission Statement"),
    ]

    promote_panels = [
        FieldPanel('slug'),
        FieldPanel('seo_title'),
        FieldPanel('search_description'),
        ImageChooserPanel('promote_image')
    ]

    api_fields = [
        APIField('press_kit'),
        APIField('press_kit_url'),
        APIField('releases'),
        APIField('slug'),
        APIField('seo_title'),
        APIField('search_description'),
        APIField('promote_image'),
        APIField('experts_heading'),
        APIField('experts_blurb'),
        APIField('experts_bios'),
        APIField('mentions'),
        APIField('mission_statements'),
        APIField('press_inquiry_name'),
        APIField('press_inquiry_phone'),
        APIField('press_inquiry_email')
    ]

    subpage_types = ['news.PressRelease']
    parent_page_types = ['pages.HomePage']
    max_count = 1
Ejemplo n.º 19
0
class ExternalArticle(ExternalContent):
    class Meta:
        verbose_name = "External Post"
        verbose_name_plural = "External Posts"

    resource_type = "article"  #  if you change this, amend the related CSS, too

    date = DateField(
        "External Post date",
        default=datetime.date.today,
        help_text="The date the external post was published",
    )
    authors = StreamField(
        StreamBlock(
            [
                ("author", PageChooserBlock(target_model="people.Person")),
                ("external_author", ExternalAuthorBlock()),
            ],
            required=False,
        ),
        blank=True,
        null=True,
        help_text=("Optional list of the external post's authors. "
                   "Use ‘External author’ to add "
                   "guest authors without creating a profile on the system"),
    )
    read_time = CharField(
        max_length=30,
        blank=True,
        help_text=("Optional, approximate read-time for this external post, "
                   "e.g. “2 mins”. This is shown as a small hint when the "
                   "external post is displayed as a card."),
    )

    meta_panels = [
        FieldPanel("date"),
        StreamFieldPanel("authors"),
        MultiFieldPanel(
            [InlinePanel("topics")],
            heading="Topics",
            help_text="The topic pages this external post will appear on",
        ),
        FieldPanel("read_time"),
    ]

    settings_panels = BasePage.settings_panels + [FieldPanel("slug")]

    edit_handler = TabbedInterface([
        ObjectList(ExternalContent.card_panels, heading="Card"),
        ObjectList(meta_panels, heading="Meta"),
        ObjectList(settings_panels, heading="Settings", classname="settings"),
    ])

    @property
    def article(self):
        return self

    @property
    def month_group(self):
        return self.date.replace(day=1)

    def has_author(self, person):
        for author in self.authors:  # pylint: disable=not-an-iterable
            if author.block_type == "author" and str(author.value) == str(
                    person.title):
                return True
        return False
Ejemplo n.º 20
0
class PressRelease(Page):
    date = models.DateField("PR date")
    heading = models.CharField(max_length=250,
                               help_text="Heading displayed on website")
    subheading = models.CharField(max_length=250, blank=True, null=True)
    author = models.CharField(max_length=250)

    featured_image = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
    )
    featured_image_alt_text = models.CharField(max_length=250,
                                               blank=True,
                                               null=True)

    def get_article_image(self):
        return build_image_url(self.featured_image)

    article_image = property(get_article_image)
    excerpt = models.CharField(max_length=255)

    body = StreamField(BlogStreamBlock())

    promote_image = models.ForeignKey('wagtailimages.Image',
                                      null=True,
                                      blank=True,
                                      on_delete=models.SET_NULL,
                                      related_name='+')

    def get_sitemap_urls(self, request=None):
        return [{
            'location':
            '{}/press/{}'.format(
                Site.find_for_request(request).root_url, self.slug),
            'lastmod': (self.last_published_at
                        or self.latest_revision_created_at),
        }]

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

    content_panels = Page.content_panels + [
        FieldPanel('date'),
        FieldPanel('title'),
        FieldPanel('heading'),
        FieldPanel('subheading'),
        FieldPanel('author'),
        ImageChooserPanel('featured_image'),
        FieldPanel('featured_image_alt_text'),
        FieldPanel('excerpt'),
        StreamFieldPanel('body'),
    ]

    promote_panels = [
        FieldPanel('slug'),
        FieldPanel('seo_title'),
        FieldPanel('search_description'),
        ImageChooserPanel('promote_image')
    ]

    api_fields = [
        APIField('date'),
        APIField('title'),
        APIField('heading'),
        APIField('subheading'),
        APIField('author'),
        APIField('article_image'),
        APIField('featured_image_alt_text'),
        APIField('excerpt'),
        APIField('body'),
        APIField('slug'),
        APIField('seo_title'),
        APIField('search_description'),
        APIField('promote_image')
    ]
Ejemplo n.º 21
0
class Post(Page):
    introduction = models.TextField(help_text='Text to describe the post',
                                    blank=True)
    image = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        help_text=
        'Landscape mode only; horizontal width between 1000px and 3000px.')
    body = StreamField(BaseStreamBlock(), verbose_name="Page body", blank=True)
    subtitle = models.CharField(max_length=255)
    tags = ClusterTaggableManager(through='PostTag', blank=True)
    date_published = models.DateField("Date article published",
                                      blank=True,
                                      null=True)
    categories = ParentalManyToManyField('blog.Category', blank=True)

    content_panels = Page.content_panels + [
        FieldPanel('subtitle', classname='full'),
        FieldPanel('introduction', classname='full'),
        ImageChooserPanel('image'),
        StreamFieldPanel('body'),
        FieldPanel('date_published'),
        InlinePanel('post_author_relationship',
                    label="Author(s)",
                    panels=None,
                    min_num=1),
        MultiFieldPanel([
            FieldPanel('categories', widget=forms.CheckboxSelectMultiple),
            FieldPanel('tags'),
        ],
                        heading="Blog information"),
    ]

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

    def authors(self):
        authors = [n.author for n in self.post_author_relationship.all()]
        return authors

    @property
    def get_tags(self):
        tags = self.tags.all()
        for tag in tags:
            tag.url = '/' + '/'.join(
                s.strip('/')
                for s in [self.get_parent().url, 'tags', tag.slug])
        return tags

    @property
    def get_categories(self):
        categories = self.categories.all()
        for category in categories:
            category.url = '/' + '/'.join(
                s.strip('/')
                for s in [self.get_parent().url, 'categories', category.slug])
        return categories

    parent_page_types = ['BlogIndex']

    subpage_types = []
Ejemplo n.º 22
0
 class InvalidStreamModel(models.Model):
     body = StreamField([
         ('heading', blocks.CharBlock()),
         ('rich text', blocks.RichTextBlock()),
     ])
Ejemplo n.º 23
0
class CustomPage(Page):
    """Flexible customizable page."""
    author = models.CharField(max_length=255)
    date = models.DateField('Creation date')
    body = StreamField([
        ('heading', blocks.CharBlock(classname='full title')),
        ('paragraph', blocks.RichTextBlock()),
        ('html', blocks.RawHTMLBlock()),
        ('example_image', ExampleImage()),
        ('image', ImageChooserBlock()),
        ('table', TableBlock(table_options=core_table_options)),
        ('example_paragraph', ExampleParagraph()),
        ('example_forms', ExampleForms()),
        ('reporting_example_cards', ReportingExampleCards()),
        ('contact_info', ContactInfoBlock()),
        ('internal_button', InternalButtonBlock()),
        ('external_button', ExternalButtonBlock()),
        ('contribution_limits_table',
         SnippetChooserBlock('home.EmbedTableSnippet',
                             template='blocks/embed-table.html',
                             icon='table')),
    ],
                       null=True)
    sidebar = stream_factory(null=True, blank=True)
    related_topics = StreamField(
        [('related_topics',
          blocks.ListBlock(blocks.PageChooserBlock(label="Related topic")))],
        null=True,
        blank=True)
    citations = StreamField(
        [('citations', blocks.ListBlock(CitationsBlock()))],
        null=True,
        blank=True)
    record_articles = StreamField(
        [('record_articles',
          blocks.ListBlock(blocks.PageChooserBlock(target_model=RecordPage)))],
        null=True,
        blank=True)
    continue_learning = StreamField([
        ('continue_learning',
         blocks.ListBlock(ThumbnailBlock(), icon='doc-empty')),
    ],
                                    null=True,
                                    blank=True)
    show_contact_link = models.BooleanField(max_length=255,
                                            default=True,
                                            null=False,
                                            blank=False,
                                            choices=[
                                                (True, 'Show contact link'),
                                                (False,
                                                 'Do not show contact link')
                                            ])

    content_panels = Page.content_panels + [
        FieldPanel('author'),
        FieldPanel('date'),
        StreamFieldPanel('body'),
        StreamFieldPanel('related_topics'),
        StreamFieldPanel('citations'),
        StreamFieldPanel('continue_learning'),
        MultiFieldPanel([
            StreamFieldPanel('sidebar'),
            StreamFieldPanel('record_articles'),
            FieldPanel('show_contact_link'),
        ],
                        heading="Sidebar",
                        classname="collapsible")
    ]

    # Adds a settings field for making a custom title that displays in the Wagtail page explorer
    menu_title = models.CharField(max_length=255, null=True)
    settings_panels = Page.settings_panels + [FieldPanel('menu_title')]

    def get_admin_display_title(self):
        return self.menu_title if self.menu_title else self.title

    @property
    def content_section(self):
        return get_content_section(self)
Ejemplo n.º 24
0
 def test_blank_field_is_not_required(self):
     field = StreamField([('paragraph', blocks.CharBlock())], blank=True)
     self.assertFalse(field.stream_block.required)
Ejemplo n.º 25
0
class ResourcePage(Page):
    # Class for pages that include a side nav, multiple sections and citations
    date = models.DateField(default=datetime.date.today)
    intro = StreamField([('paragraph', blocks.RichTextBlock())],
                        null=True,
                        blank=True)
    sidebar_title = models.CharField(max_length=255, null=True, blank=True)
    related_pages = StreamField(
        [('related_pages', blocks.ListBlock(blocks.PageChooserBlock()))],
        null=True,
        blank=True)
    sections = StreamField([('sections', ResourceBlock())],
                           null=True,
                           blank=True)
    citations = StreamField(
        [('citations', blocks.ListBlock(CitationsBlock()))],
        null=True,
        blank=True)
    related_topics = StreamField(
        [('related_topics',
          blocks.ListBlock(blocks.PageChooserBlock(label="Related topic")))],
        null=True,
        blank=True)
    category = models.CharField(
        max_length=255,
        choices=constants.report_child_categories.items(),
        help_text='If this is a report, add a category',
        blank=True,
        null=True)
    #breadcrumb_style removed from promote-panel in favor of controlling breadcrumbs by parent section
    breadcrumb_style = models.CharField(max_length=255,
                                        choices=[('primary', 'Blue'),
                                                 ('secondary', 'Red')],
                                        default='primary')
    show_contact_card = models.BooleanField(max_length=255,
                                            default=False,
                                            null=False,
                                            blank=False,
                                            choices=[
                                                (True, 'Show contact card'),
                                                (False,
                                                 'Do not show contact card')
                                            ])

    content_panels = Page.content_panels + [
        StreamFieldPanel('intro'),
        FieldPanel('sidebar_title'),
        StreamFieldPanel('related_pages'),
        StreamFieldPanel('sections'),
        StreamFieldPanel('citations'),
        StreamFieldPanel('related_topics'),
        FieldPanel('show_contact_card')
    ]

    promote_panels = Page.promote_panels + [
        FieldPanel('category'), FieldPanel('date')
    ]

    # Adds a settings field for making a custom title that displays in the Wagtail page explorer
    menu_title = models.CharField(max_length=255, null=True)
    settings_panels = Page.settings_panels + [FieldPanel('menu_title')]

    def get_admin_display_title(self):
        return self.menu_title if self.menu_title else self.title

    @property
    def display_date(self):
        return self.date.strftime('%B %Y')

    @property
    def content_section(self):
        return get_content_section(self)
Ejemplo n.º 26
0
class ApplicationSubmission(
        WorkflowHelpers,
        BaseStreamForm,
        AccessFormData,
        AbstractFormSubmission,
        metaclass=ApplicationSubmissionMetaclass,
):
    field_template = 'funds/includes/submission_field.html'

    form_data = JSONField(encoder=StreamFieldDataEncoder)
    form_fields = StreamField(ApplicationCustomFormFieldsBlock())
    page = models.ForeignKey('wagtailcore.Page', on_delete=models.PROTECT)
    round = models.ForeignKey('wagtailcore.Page', on_delete=models.PROTECT, related_name='submissions', null=True)
    lead = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        limit_choices_to=LIMIT_TO_STAFF,
        related_name='submission_lead',
        on_delete=models.PROTECT,
    )
    next = models.OneToOneField('self', on_delete=models.CASCADE, related_name='previous', null=True)
    reviewers = models.ManyToManyField(
        settings.AUTH_USER_MODEL,
        related_name='submissions_reviewer',
        limit_choices_to=LIMIT_TO_STAFF_AND_REVIEWERS,
        blank=True,
    )
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True)
    search_data = models.TextField()

    # Workflow inherited from WorkflowHelpers
    status = FSMField(default=INITIAL_STATE, protected=True)

    is_draft = False

    live_revision = models.OneToOneField(
        'ApplicationRevision',
        on_delete=models.CASCADE,
        related_name='live',
        null=True,
        editable=False,
    )
    draft_revision = models.OneToOneField(
        'ApplicationRevision',
        on_delete=models.CASCADE,
        related_name='draft',
        null=True,
        editable=False,
    )

    # Meta: used for migration purposes only
    drupal_id = models.IntegerField(null=True, blank=True, editable=False)

    objects = ApplicationSubmissionQueryset.as_manager()

    def not_progressed(self):
        return not self.next

    @transition(
        status, source='*',
        target=RETURN_VALUE(INITIAL_STATE, 'draft_proposal', 'invited_to_proposal'),
        permission=make_permission_check({UserPermissions.ADMIN}),
    )
    def restart_stage(self, **kwargs):
        """
        If running form the console please include your user using the kwarg "by"

        u = User.objects.get(email="<*****@*****.**>")
        for a in ApplicationSubmission.objects.all():
            a.restart_stage(by=u)
            a.save()
        """
        if hasattr(self, 'previous'):
            return 'draft_proposal'
        elif self.next:
            return 'invited_to_proposal'
        return INITIAL_STATE

    @property
    def stage(self):
        return self.phase.stage

    @property
    def phase(self):
        return self.workflow.get(self.status)

    @property
    def active(self):
        return self.status in active_statuses

    def ensure_user_has_account(self):
        if self.user and self.user.is_authenticated:
            self.form_data['email'] = self.user.email
            self.form_data['full_name'] = self.user.get_full_name()
        else:
            # Rely on the form having the following must include fields (see blocks.py)
            email = self.form_data.get('email')
            full_name = self.form_data.get('full_name')

            User = get_user_model()
            if 'skip_account_creation_notification' in self.form_data:
                self.form_data.pop('skip_account_creation_notification', None)
                self.user, _ = User.objects.get_or_create(
                    email=email,
                    defaults={'full_name': full_name}
                )
            else:
                self.user, _ = User.objects.get_or_create_and_notify(
                    email=email,
                    site=self.page.get_site(),
                    defaults={'full_name': full_name}
                )

    def get_from_parent(self, attribute):
        try:

            return getattr(self.round.specific, attribute)
        except AttributeError:
            # We are a lab submission
            return getattr(self.page.specific, attribute)

    def progress_application(self, **kwargs):
        target = None
        for phase in STAGE_CHANGE_ACTIONS:
            transition = self.get_transition(phase)
            if can_proceed(transition):
                # We convert to dict as not concerned about transitions from the first phase
                # See note in workflow.py
                target = dict(PHASES)[phase].stage
        if not target:
            raise ValueError('Incorrect State for transition')

        submission_in_db = ApplicationSubmission.objects.get(id=self.id)

        self.id = None
        self.form_fields = self.get_from_parent('get_defined_fields')(target)

        self.live_revision = None
        self.draft_revision = None
        self.save()

        submission_in_db.next = self
        submission_in_db.save()

    def new_data(self, data):
        self.is_draft = False
        self.form_data = data
        return self

    def from_draft(self):
        self.is_draft = True
        self.form_data = self.deserialised_data(self.draft_revision.form_data, self.form_fields)
        return self

    def create_revision(self, draft=False, force=False, by=None, **kwargs):
        # Will return True/False if the revision was created or not
        self.clean_submission()
        current_submission = ApplicationSubmission.objects.get(id=self.id)
        current_data = current_submission.form_data
        if current_data != self.form_data or force:
            if self.live_revision == self.draft_revision:
                revision = ApplicationRevision.objects.create(submission=self, form_data=self.form_data, author=by)
            else:
                revision = self.draft_revision
                revision.form_data = self.form_data
                revision.author = by
                revision.save()

            if draft:
                self.form_data = current_submission.form_data
            else:
                self.live_revision = revision

            self.draft_revision = revision
            self.save()
            return revision
        return None

    def clean_submission(self):
        self.process_form_data()
        self.ensure_user_has_account()
        self.process_file_data(self.form_data)

    def process_form_data(self):
        for field_name, field_id in self.named_blocks.items():
            response = self.form_data.pop(field_id, None)
            if response:
                self.form_data[field_name] = response

    def extract_files(self):
        files = {}
        for field in self.form_fields:
            if isinstance(field.block, UploadableMediaBlock):
                files[field.id] = self.data(field.id) or []
                self.form_data.pop(field.id, None)
        return files

    def process_file_data(self, data):
        for field in self.form_fields:
            if isinstance(field.block, UploadableMediaBlock):
                file = self.process_file(data.get(field.id, []))
                folder = os.path.join('submission', str(self.id), field.id)
                try:
                    file.save(folder)
                except AttributeError:
                    for f in file:
                        f.save(folder)
                self.form_data[field.id] = file

    def save(self, *args, update_fields=list(), **kwargs):
        if update_fields and 'form_data' not in update_fields:
            # We don't want to use this approach if the user is sending data
            return super().save(*args, update_fields=update_fields, **kwargs)

        if self.is_draft:
            raise ValueError('Cannot save with draft data')

        creating = not self.id

        if creating:
            # We are creating the object default to first stage
            self.workflow_name = self.get_from_parent('workflow_name')
            # Copy extra relevant information to the child
            self.lead = self.get_from_parent('lead')

            # We need the submission id to correctly save the files
            files = self.extract_files()

        self.clean_submission()

        # add a denormed version of the answer for searching
        self.search_data = ' '.join(self.prepare_search_values())

        super().save(*args, **kwargs)

        if creating:
            self.process_file_data(files)
            self.reviewers.set(self.get_from_parent('reviewers').all())
            first_revision = ApplicationRevision.objects.create(
                submission=self,
                form_data=self.form_data,
                author=self.user,
            )
            self.live_revision = first_revision
            self.draft_revision = first_revision
            self.save()

    @property
    def missing_reviewers(self):
        return self.reviewers.exclude(id__in=self.reviews.submitted().values('author'))

    @property
    def staff_not_reviewed(self):
        return self.missing_reviewers.staff()

    @property
    def reviewers_not_reviewed(self):
        return self.missing_reviewers.reviewers().exclude(id__in=self.staff_not_reviewed)

    def reviewed_by(self, user):
        return self.reviews.submitted().filter(author=user).exists()

    def has_permission_to_review(self, user):
        if user.is_apply_staff:
            return True

        if user in self.reviewers_not_reviewed:
            return True

        return False

    def can_review(self, user):
        if self.reviewed_by(user):
            return False

        return self.has_permission_to_review(user)

    def prepare_search_values(self):
        for field_id in self.question_field_ids:
            field = self.field(field_id)
            data = self.data(field_id)
            value = field.block.get_searchable_content(field.value, data)
            if value:
                if isinstance(value, list):
                    yield ', '.join(value)
                else:
                    yield value

        # Add named fields into the search index
        for field in ['full_name', 'email', 'title']:
            yield getattr(self, field)

    def get_absolute_url(self):
        return reverse('funds:submissions:detail', args=(self.id,))

    def __str__(self):
        return f'{self.title} from {self.full_name} for {self.page.title}'

    def __repr__(self):
        return f'<{self.__class__.__name__}: {self.user}, {self.round}, {self.page}>'

    # Methods for accessing data on the submission

    def get_data(self):
        # Updated for JSONField - Not used but base get_data will error
        form_data = self.form_data.copy()
        form_data.update({
            'submit_time': self.submit_time,
        })

        return form_data

    # Template methods for metaclass
    def _get_REQUIRED_display(self, name):
        return self.render_answer(name)

    def _get_REQUIRED_value(self, name):
        return self.form_data[name]
Ejemplo n.º 27
0
class HomePage(Page):

    # Hero section of HomePage
    image = models.ForeignKey('wagtailimages.Image',
                              null=True,
                              blank=True,
                              on_delete=models.SET_NULL,
                              related_name='+',
                              help_text='Homepage image')
    hero_text = models.CharField(
        max_length=255, help_text='Write an introduction for the Site')

    # Body section of the HomePage
    body = StreamField(BaseStreamBlock(),
                       verbose_name="Home content block",
                       blank=True)

    bottom = StreamField(BaseStreamBlock(),
                         verbose_name="Home content block",
                         blank=True)

    # Promo section of the HomePage
    promo_image = models.ForeignKey('wagtailimages.Image',
                                    null=True,
                                    blank=True,
                                    on_delete=models.SET_NULL,
                                    related_name='+',
                                    help_text='Promo image')
    promo_title = models.CharField(
        null=True,
        blank=True,
        max_length=255,
        help_text='Title to display above the promo copy')
    promo_text = RichTextField(null=True,
                               blank=True,
                               help_text='Write some promotional copy')

    featured_section_1_title = models.CharField(
        null=True,
        blank=True,
        max_length=255,
        help_text='Title to display above the promo copy')
    featured_section_1 = models.ForeignKey(
        'wagtailcore.Page',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        help_text='First featured section for the homepage. Will display up to '
        'three child items.',
        verbose_name='Featured section 1')

    featured_section_2_title = models.CharField(
        null=True,
        blank=True,
        max_length=255,
        help_text='Title to display above the promo copy')
    featured_section_2 = models.ForeignKey(
        'wagtailcore.Page',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        help_text='Second featured section for the homepage. Will display up to '
        'three child items.',
        verbose_name='Featured section 2')

    featured_section_3_title = models.CharField(
        null=True,
        blank=True,
        max_length=255,
        help_text='Title to display above the promo copy')
    featured_section_3 = models.ForeignKey(
        'wagtailcore.Page',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        help_text='Third featured section for the homepage. Will display up to '
        'six child items.',
        verbose_name='Featured section 3')

    content_panels = Page.content_panels + [
        MultiFieldPanel([
            ImageChooserPanel('image'),
            FieldPanel('hero_text', classname="full"),
        ],
                        heading="Hero section"),
        MultiFieldPanel([
            ImageChooserPanel('promo_image'),
            FieldPanel('promo_title'),
            FieldPanel('promo_text'),
        ],
                        heading="Promo section"),
        StreamFieldPanel('body'),
        MultiFieldPanel([
            MultiFieldPanel([
                FieldPanel('featured_section_1_title'),
                PageChooserPanel('featured_section_1'),
            ]),
            MultiFieldPanel([
                FieldPanel('featured_section_2_title'),
                PageChooserPanel('featured_section_2'),
            ]),
            MultiFieldPanel([
                FieldPanel('featured_section_3_title'),
                PageChooserPanel('featured_section_3'),
            ])
        ],
                        heading="Featured homepage sections",
                        classname="collapsible"),
        StreamFieldPanel('bottom')
    ]

    def __str__(self):
        return self.title

    class Meta:
        verbose_name = "homepage"
Ejemplo n.º 28
0
class HomePage(Page):
    """
    The Home Page. This looks slightly more complicated than it is. You can
    see if you visit your site and edit the homepage that it is split between
    a:
    - Hero area
    - Body area
    - A promotional area
    - Moveable featured site sections
    """

    # Hero section of HomePage
    image = models.ForeignKey('wagtailimages.Image',
                              null=True,
                              blank=True,
                              on_delete=models.SET_NULL,
                              related_name='+',
                              help_text='Homepage image')
    hero_text = models.CharField(
        max_length=255, help_text='Write an introduction for the bakery')
    hero_cta = models.CharField(verbose_name='Hero CTA',
                                max_length=255,
                                help_text='Text to display on Call to Action')
    hero_cta_link = models.ForeignKey(
        'wagtailcore.Page',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        verbose_name='Hero CTA link',
        help_text='Choose a page to link to for the Call to Action')

    # Body section of the HomePage
    body = StreamField(BaseStreamBlock(),
                       verbose_name="Home content block",
                       blank=True)

    # Promo section of the HomePage
    promo_image = models.ForeignKey('wagtailimages.Image',
                                    null=True,
                                    blank=True,
                                    on_delete=models.SET_NULL,
                                    related_name='+',
                                    help_text='Promo image')
    promo_title = models.CharField(
        null=True,
        blank=True,
        max_length=255,
        help_text='Title to display above the promo copy')
    promo_text = RichTextField(null=True,
                               blank=True,
                               help_text='Write some promotional copy')

    # Featured sections on the HomePage
    # You will see on templates/base/home_page.html that these are treated
    # in different ways, and displayed in different areas of the page.
    # Each list their children items that we access via the children function
    # that we define on the individual Page models e.g. BlogIndexPage
    featured_section_1_title = models.CharField(
        null=True,
        blank=True,
        max_length=255,
        help_text='Title to display above the promo copy')
    featured_section_1 = models.ForeignKey(
        'wagtailcore.Page',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        help_text='First featured section for the homepage. Will display up to '
        'three child items.',
        verbose_name='Featured section 1')

    featured_section_2_title = models.CharField(
        null=True,
        blank=True,
        max_length=255,
        help_text='Title to display above the promo copy')
    featured_section_2 = models.ForeignKey(
        'wagtailcore.Page',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        help_text='Second featured section for the homepage. Will display up to '
        'three child items.',
        verbose_name='Featured section 2')

    featured_section_3_title = models.CharField(
        null=True,
        blank=True,
        max_length=255,
        help_text='Title to display above the promo copy')
    featured_section_3 = models.ForeignKey(
        'wagtailcore.Page',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        help_text='Third featured section for the homepage. Will display up to '
        'six child items.',
        verbose_name='Featured section 3')

    content_panels = Page.content_panels + [
        MultiFieldPanel([
            ImageChooserPanel('image'),
            FieldPanel('hero_text', classname="full"),
            MultiFieldPanel([
                FieldPanel('hero_cta'),
                PageChooserPanel('hero_cta_link'),
            ]),
        ],
                        heading="Hero section"),
        MultiFieldPanel([
            ImageChooserPanel('promo_image'),
            FieldPanel('promo_title'),
            FieldPanel('promo_text'),
        ],
                        heading="Promo section"),
        StreamFieldPanel('body'),
        MultiFieldPanel([
            MultiFieldPanel([
                FieldPanel('featured_section_1_title'),
                PageChooserPanel('featured_section_1'),
            ]),
            MultiFieldPanel([
                FieldPanel('featured_section_2_title'),
                PageChooserPanel('featured_section_2'),
            ]),
            MultiFieldPanel([
                FieldPanel('featured_section_3_title'),
                PageChooserPanel('featured_section_3'),
            ]),
        ],
                        heading="Featured homepage sections",
                        classname="collapsible")
    ]

    def __str__(self):
        return self.title
Ejemplo n.º 29
0
class Edition(RoutablePageMixin, EditionSubpage):
    start_date = models.DateTimeField(null=True,
                                      blank=True,
                                      verbose_name=_('edition start date'))
    end_date = models.DateTimeField(null=True,
                                    blank=True,
                                    verbose_name=_('edition end date'))

    content = StreamField([
        ('agenda', AgendaBlock()),
        ('paragraph', blocks.RichTextBlock()),
        ('image', ImageChooserBlock()),
        ('event_index', EventIndexBlock()),
        ('event_schedule', EventScheduleBlock()),
        ('section_title', SectionTitleBlock()),
        ('section_subtitle', SectionSubtitleBlock()),
        ('section_divider', SectionDividerBlock()),
        ('dropdown', DropdownBlock()),
        ('photo_gallery', PhotoGallery()),
        ('photo_slider', PhotoSlider()),
        ('map', MapBlock()),
        ('raw_html', RawHTMLBlock()),
    ],
                          null=True,
                          blank=True,
                          verbose_name=_('content'))

    default_featured_image = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        verbose_name=_('default featured image'))
    edition_footer = StreamField([
        ('paragraph', blocks.RichTextBlock()),
        ('image', ImageChooserBlock()),
        ('section_title', SectionTitleBlock()),
        ('section_subtitle', SectionSubtitleBlock()),
        ('section_divider', SectionDividerBlock()),
        ('dropdown', DropdownBlock()),
        ('photo_gallery', PhotoGallery()),
        ('photo_slider', PhotoSlider()),
        ('raw_html', blocks.RawHTMLBlock()),
    ],
                                 null=True,
                                 blank=True,
                                 verbose_name=_('edition footer'))

    generate_podcast_feed = models.BooleanField(
        default=False, verbose_name=_('generate podcast feed'))

    def get_edition_footer(self):
        return self.edition_footer

    def get_edition(self):
        return self

    content_panels = SFIPage.content_panels + [
        FieldPanel('start_date'),
        FieldPanel('end_date'),
        StreamFieldPanel('content'),
        ImageChooserPanel('default_featured_image'),
        InlinePanel('event_categories', label='Event categories'),
        StreamFieldPanel('edition_footer'),
    ]

    settings_panels = SFIPage.settings_panels + [
        FieldPanel('generate_podcast_feed')
    ]

    parent_page_types = ['EditionIndex']
    subpage_types = ['EventIndex']

    class Meta:
        verbose_name = _('edition')
        verbose_name_plural = _('editions')

    @route(r'^c/([\w\-_]+)/$')
    def get_category_event_index(self, request, category_name):
        indexes = EventIndex.objects.child_of(self).live().all()
        categories = Category.objects.filter(
            edition=self).order_by('name').all()
        category = categories.filter(slug=category_name).first()
        if not category:
            raise Http404
        events = Event.objects.live().public().descendant_of(self).filter(
            event_category=category).order_by('date')
        posts = paginate(events, request, EventIndex.EVENTS_PER_PAGE)
        relative_url = self.reverse_subpage('get_category_event_index',
                                            (category_name, ))
        return TemplateResponse(
            request, 'agenda/event_index.html', {
                'posts':
                posts,
                'indexes':
                indexes,
                'categories':
                categories,
                'title':
                '{} - {}'.format(category.name, self.title),
                'description':
                '{} - {}'.format(category.name, self.title),
                'canonical_url':
                self.full_url + relative_url,
                'featured_image':
                self.get_featured_image(),
                'locales': [(lang_code, lang_name, url + relative_url,
                             full_url + relative_url) for lang_code, lang_name,
                            url, full_url in self._get_locales()],
            })
Ejemplo n.º 30
0
class HomePage(RoutablePageMixin, Page):
    """Home page model."""

    template = "home/home_page.html"
    subpage_types = [
        'blog.BlogListingPage',
        'contact.ContactPage',
        'flex.FlexPage',
    ]
    parent_page_type = [
        'wagtailcore.Page'
    ]

    banner_title = models.CharField(max_length=100, blank=False, null=True)
    banner_subtitle = RichTextField(features=["bold", "italic"])
    banner_image = models.ForeignKey(
        "wagtailimages.Image",
        null=True,
        blank=False,
        on_delete=models.SET_NULL,
        related_name="+",
    )
    banner_cta = models.ForeignKey(
        "wagtailcore.Page",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="+",
    )

    content = StreamField([
        ("cta", blocks.CTABlock()),
    ], null=True, blank=True)

    api_fields = [
        APIField("banner_title"),
        APIField("banner_subtitle"),
        APIField("banner_image"),
        APIField("banner_cta"),
        APIField("carousel_images"),
        APIField("content"),
    ]

    content_panels = Page.content_panels + [
        MultiFieldPanel(
            [InlinePanel("carousel_images", max_num=5, min_num=1, label="Image")],
            heading="Carousel Images",
        ),
        StreamFieldPanel("content"),
    ]

    # This is how you'd normally hide promote and settings tabs
    # promote_panels = []
    # settings_panels = []

    banner_panels = [
        MultiFieldPanel(
            [
                FieldPanel("banner_title"),
                FieldPanel("banner_subtitle"),
                ImageChooserPanel("banner_image"),
                PageChooserPanel("banner_cta"),
            ],
            heading="Banner Options",
        ),
    ]

    edit_handler = TabbedInterface(
        [
            ObjectList(content_panels, heading='Content'),
            ObjectList(banner_panels, heading="Banner Settings"),
            ObjectList(Page.promote_panels, heading='Promotional Stuff'),
            ObjectList(Page.settings_panels, heading='Settings Stuff'),
        ]
    )

    class Meta:

        verbose_name = "Home Page"
        verbose_name_plural = "Home Pages"

    @route(r'^subscribe/$')
    def the_subscribe_page(self, request, *args, **kwargs):
        context = self.get_context(request, *args, **kwargs)
        return render(request, "home/subscribe.html", context)

    def get_admin_display_title(self):
        return "Custom Home Page Title"