Exemplo n.º 1
0
class VideoPage(CoderedArticlePage):
    class Meta:
        verbose_name = "Video Page"
        ordering = [
            "-first_published_at",
        ]

    # Override to have default today
    date_display = models.DateTimeField(verbose_name=_("Publish date"),
                                        default=timezone.now)

    link_youtube = models.URLField(max_length=511)

    # Additional attribute
    hits = models.IntegerField(default=0, editable=False)

    # Override from streamfield to richtextfield
    body_text = RichTextField(verbose_name=_("body"))

    @property
    def body(self):
        return self.body_text

    @body.setter
    def body(self, body):
        self.body_text = body

    # get set
    def add_hits(self):
        self.hits += 1
        self.save()
        return ""

    def get_pub_date(self):
        """
        Gets published date.
        """
        locale.setlocale(locale.LC_ALL, "en_US")
        if hasattr(self, "date_display") and self.date_display:
            return self.date_display.strftime("%d %B %Y")
        return ""

    template = "video/video_page.html"

    # Override to become empty
    layout_panels = []

    # Friend panels
    promote_panels = [
        MultiFieldPanel(
            [
                FieldPanel("slug"),
                FieldPanel("seo_title"),
                FieldPanel("search_description"),
                ImageChooserPanel("og_image"),
            ],
            _("Page Meta Data"),
        ),
    ]

    # Override to become empty
    body_content_panels = []

    # Override without content walls
    settings_panels = Page.settings_panels

    # Override with additional hits attribute
    content_panels = (Page.content_panels + [FieldPanel("link_youtube")] + [
        MultiFieldPanel(
            [
                FieldPanel("author"),
                FieldPanel("date_display"),
                ReadOnlyPanel("hits", heading="Hits"),
            ],
            _("Publication Info"),
        ),
    ] + [
        FieldPanel(
            "body_text",
            classname="full",
        ),
    ])

    # Override edit_handler to not contain layout tabs
    @cached_classmethod
    def get_edit_handler(cls):  # noqa
        """
        Override to "lazy load" the panels overriden by subclasses.
        """
        panels = [
            ObjectList(
                cls.content_panels + cls.body_content_panels +
                cls.bottom_content_panels,
                heading=_("Content"),
            ),
            ObjectList(cls.classify_panels, heading=_("Classify")),
            ObjectList(cls.promote_panels, heading=_("SEO"), classname="seo"),
            ObjectList(cls.settings_panels,
                       heading=_("Settings"),
                       classname="settings"),
        ]

        if cls.integration_panels:
            panels.append(
                ObjectList(
                    cls.integration_panels,
                    heading="Integrations",
                    classname="integrations",
                ))

        return TabbedInterface(panels).bind_to(model=cls)

    search_fields = CoderedArticlePage.search_fields + [
        index.SearchField("body_text", partial_match=True)
    ]

    parent_page_types = ["video.VideoIndexPage"]
Exemplo n.º 2
0
    def test_render_with_panel_overrides(self):
        """
        Check that inline panel renders the panels listed in the InlinePanel definition
        where one is specified
        """
        speaker_object_list = ObjectList([
            InlinePanel('speakers',
                        label="Speakers",
                        panels=[
                            FieldPanel('first_name', widget=forms.Textarea),
                            ImageChooserPanel('image'),
                        ]),
        ]).bind_to(model=EventPage, request=self.request)
        speaker_inline_panel = speaker_object_list.children[0]
        EventPageForm = speaker_object_list.get_form_class()

        # speaker_inline_panel should instruct the form class to include a 'speakers' formset
        self.assertEqual(['speakers'], list(EventPageForm.formsets.keys()))

        event_page = EventPage.objects.get(slug='christmas')

        form = EventPageForm(instance=event_page)
        panel = speaker_inline_panel.bind_to(instance=event_page, form=form)

        result = panel.render_as_field()

        # rendered panel should contain first_name rendered as a text area, but no last_name field
        self.assertIn('<label for="id_speakers-0-first_name">Name:</label>',
                      result)
        self.assertIn('Father</textarea>', result)
        self.assertNotIn(
            '<label for="id_speakers-0-last_name">Surname:</label>', result)

        # test for #338: surname field should not be rendered as a 'stray' label-less field
        self.assertTagInHTML('<input id="id_speakers-0-last_name">',
                             result,
                             count=0,
                             allow_extra_attrs=True)

        self.assertIn('<label for="id_speakers-0-image">Image:</label>',
                      result)
        self.assertIn('Choose an image', result)

        # rendered panel must also contain hidden fields for id, DELETE and ORDER
        self.assertTagInHTML(
            '<input id="id_speakers-0-id" name="speakers-0-id" type="hidden">',
            result,
            allow_extra_attrs=True)
        self.assertTagInHTML(
            '<input id="id_speakers-0-DELETE" name="speakers-0-DELETE" type="hidden">',
            result,
            allow_extra_attrs=True)
        self.assertTagInHTML(
            '<input id="id_speakers-0-ORDER" name="speakers-0-ORDER" type="hidden">',
            result,
            allow_extra_attrs=True)

        # rendered panel must contain maintenance form for the formset
        self.assertTagInHTML(
            '<input id="id_speakers-TOTAL_FORMS" name="speakers-TOTAL_FORMS" type="hidden">',
            result,
            allow_extra_attrs=True)

        # render_js_init must provide the JS initializer
        self.assertIn('var panel = InlinePanel({', panel.render_js_init())
Exemplo n.º 3
0
        return self.get_ancestors().type(SiteIndexPage).last()


SitePage.content_panels = [
    FieldPanel('site'),
    FieldPanel('title', classname="full title"),
    FieldPanel('date'),
    FieldPanel('intro', classname="full"),
    StreamFieldPanel('body'),
    InlinePanel('carousel_items', label="Carousel items"),
    InlinePanel('related_links', label="Related links"),
    GeoPanel('location')
]

SitePage.promote_panels = Page.promote_panels + [
    ImageChooserPanel('feed_image'),
    FieldPanel('tags'),
]

# Prototype Fossil List Views
# class FossilListViewRelatedLink(Orderable, RelatedLink):
#     page = ParentalKey('FossilListView', related_name='fossil_related_links')
#
#
# class FossilListView(Page):
#     """
#     Type Fossil List View Page
#     """
#     TEMPLATE_CHOICES = [
#         ('origins/fossil_list_view.html', 'Default Template'),
#     ]
Exemplo n.º 4
0
class PostPage(Page, HitCountMixin):
    """Page for posts, the main content of the website."""

    # Database fields

    header_image = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
    )
    body = StreamField([
        ('cut',
         blocks.CharBlock(
             classname='full subtitle',
             help_text=
             'After this block the post will be cutted when displayed on the home page. On the post page this field is ignored.'
         )),
        ('paragraph', blocks.RichTextBlock()),
        ('quote', CustomBlockquoteBlock(classname='full')),
        ('figure', CaptionedImageBlock(label='Figure')),
        ('embed', EmbedBlock()),
        ('document',
         DocumentChooserBlock(
             help_text=
             'All the text in other blocks, which is the same as document title will be replaced with the link to the document.'
         )),
        ('markdown', MarkdownxBlock()),
        ('equation', CaptionedEquationBlock()),
        ('pages', blocks.PageChooserBlock()),
        ('columns', TwoColumnBlock()),
        ('table', CaptionedTableBlock()),
        ('table_figure',
         CaptionedImageBlock(label='Table as Figure', icon='table')),
    ])
    pin_on_home = models.BooleanField(
        default=False,
        help_text=_('Indicates if the Post is pinned on the Home page.'),
        verbose_name=_('Pin on Home page'))
    show_sidebar = models.BooleanField(
        default=True,
        help_text=
        _('Indicates if the sidebar with contents, figures and equations is shown on the page.'
          ),
        verbose_name=_('Show sidebar'))
    show_comments = models.BooleanField(
        default=True,
        help_text=_('Indicates if comments are shown on the page.'),
        verbose_name=_('Show comments'))
    generate_figure_numbers = models.BooleanField(
        default=False,
        help_text=
        _('Indcates if figure numbers (such as Figure 1) should be generated for Figure block when rendring post.'
          ),
        verbose_name=_('Generate figure numbers'))
    generate_table_numbers = models.BooleanField(
        default=False,
        help_text=
        _('Indicates if  table numbers (such as Table 1) should be geberated for Table block when rendering post.'
          ),
        verbose_name=_('Generate table numbers'))
    generate_equation_numbers = models.BooleanField(
        default=False,
        help_text=
        _('Indicates if equation numbers (such as (1)) should be added on the right side of the Equation block when rendering post.'
          ),
        verbose_name=_('Generate equation numbers'))
    categories = ParentalManyToManyField(
        'main.BlogCategory',
        verbose_name=_('Categories'),
        blank=True,
    )
    tags = ClusterTaggableManager(through='main.PostTag',
                                  help_text=None,
                                  blank=True)
    hit_count_generic = GenericRelation(
        HitCount,
        object_id_field='pk',
        related_query_name='hit_count_relation',
    )

    # Search index configuration

    search_fields = Page.search_fields + [
        index.SearchField('body', partial_match=True, boost=4),
        index.FilterField('pin_on_home'),
        index.FilterField('categories'),
        index.FilterField('tags')
    ]

    # Editor panels configuration

    content_panels = Page.content_panels + [StreamFieldPanel('body')]

    promote_panels = Page.promote_panels + [
        ImageChooserPanel('header_image'),
        FieldPanel('tags'),
        InlinePanel('blog_categories', label=_('Categories'))
    ]

    settings_panels = Page.settings_panels + [
        MultiFieldPanel([
            FieldPanel('pin_on_home'),
            FieldPanel('show_sidebar'),
            FieldPanel('show_comments'),
            FieldPanel('generate_figure_numbers'),
            FieldPanel('generate_table_numbers'),
            FieldPanel('generate_equation_numbers')
        ],
                        heading=_('Post settings')),
        MultiFieldPanel([
            ReadOnlyPanel('first_published_at', heading='First published at'),
            ReadOnlyPanel('last_published_at', heading='Last published at'),
            ReadOnlyPanel('hit_counts', heading='Number of views')
        ],
                        heading=_('General information')),
    ]

    # Parent page / subpage type rules
    # PostPage can have children PostPages. In this case the parent page is
    # considered as 'series' type post and the links to the children pages are
    # generated, when page is accessed. Also the links to parent page and siblings
    # are rendered on the child page.

    parent_page_types = ['main.HomePage', 'main.PostPage']
    subpage_types = ['main.PostPage']

    # Methods

    def update_body(self):
        """Updates captions of figures, tables and equations if PostPage settings require so.
        Collects figures and tables into a separate list to ease the rendering of Graphics
        sidebar on the PostPage. Collects equations into another separate list to ease the 
        rendering of Equations sidebar on the PostPage."""
        fig_idx = 1
        tbl_idx = 1
        eq_idx = 1
        graphics = list()
        equations = list()
        for block in self.body:
            if block.block_type == 'figure':
                if self.generate_figure_numbers:
                    block.value['caption'] = _('<b>Figure ') + str(
                        fig_idx) + '.</b> ' + block.value['caption']
                    fig_idx += 1
                graphics.append(block)
            if block.block_type == 'table' or block.block_type == 'table_figure':
                if self.generate_table_numbers:
                    block.value['caption'] = _('<b>Table ') + str(
                        tbl_idx) + '.</b> ' + block.value['caption']
                    tbl_idx += 1
                graphics.append(block)
            if block.block_type == 'equation':
                if self.generate_equation_numbers:
                    block.value['caption'] = _('<b>Equation ') + str(
                        eq_idx) + '.</b> ' + block.value['caption']
                    eq_idx += 1
                equations.append(block)
            if block.block_type == 'columns':
                for column in [block.value['left'], block.value['right']]:
                    for col_block in column:
                        if col_block.block_type == 'figure':
                            if self.generate_figure_numbers:
                                col_block.value['caption'] = _(
                                    '<b>Figure ') + str(
                                        fig_idx
                                    ) + '.</b> ' + col_block.value['caption']
                                fig_idx += 1
                            graphics.append(col_block)
                        if col_block.block_type == 'table' or col_block.block_type == 'table_figure':
                            if self.generate_table_numbers:
                                col_block.value['caption'] = _(
                                    '<b>Table ') + str(
                                        tbl_idx
                                    ) + '.</b> ' + col_block.value['caption']
                                tbl_idx += 1
                            graphics.append(col_block)
                        if col_block.block_type == 'equation':
                            if self.generate_equation_numbers:
                                col_block.value['caption'] = _(
                                    '<b>Equation ') + str(
                                        eq_idx
                                    ) + '.</b> ' + col_block.value['caption']
                                eq_idx += 1
                            equations.append(col_block)
        return graphics, equations

    def is_series(self):
        """Verifies that post is series"""
        # parent_page = self.get_parent().specific
        if self.get_parent().specific_class == PostPage:
            # this is the child post of series
            return True
        if self.get_descendant_count() > 0:
            # this is parent post of series
            return True
        return False

    def get_context(self, request, *args, **kwargs):

        graphics, equations = self.update_body()
        context = super().get_context(request, *args, **kwargs)
        context['post'] = self
        context['graphics'] = graphics
        context['equations'] = equations
        context['previous_post'] = PostPage.objects.live().filter(
            first_published_at__lt=self.first_published_at).order_by(
                '-first_published_at').first()
        context['next_post'] = PostPage.objects.live().filter(
            first_published_at__gt=self.first_published_at).order_by(
                'first_published_at').first()

        context['is_series'] = False
        # verify post is series
        parent_page = self.get_parent().specific
        if parent_page.specific_class == PostPage:
            # this is a child post from the series
            context['is_series'] = True
            context['parent_post'] = parent_page
            context['child_posts'] = parent_page.get_descendants().live()
        else:
            # this is a normal post, check if it is series
            if self.get_descendant_count() > 0:
                # this post is the parent post for series
                context['is_series'] = True
                context['parent_post'] = self
                context['child_posts'] = self.get_descendants().live()

        return context

    def hit_counts(self):
        if self.pk is not None:
            # the page is created and hitcounts make sense
            return self.hit_count.hits
        else:
            return 0

    def serve(self, request, *args, **kwargs):
        hit_count = HitCount.objects.get_for_object(self)
        ViewHitCountMixin.hit_count(request, hit_count)
        return super().serve(request, *args, **kwargs)
Exemplo n.º 5
0
class PublicationPage(FoundationMetadataPageMixin, Page):
    """
    This is the root page of a publication.

    From here the user can browse to the various sections (called chapters).
    It will have information on the publication, its authors, and metadata from it's children

    TODO: this poem is beautiful, but it may not belong here
    Publications are collections of Articles
    Publications can also be broken down into Chapters, which are really just child publication pages
    Each of those Chapters may have several Articles
    An Article can only belong to one Chapter/Publication Page

    """

    subpage_types = ['ArticlePage', 'PublicationPage']

    hero_image = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='publication_hero_image',
        verbose_name='Publication Hero Image',
    )
    subtitle = models.CharField(
        blank=True,
        max_length=250,
    )
    secondary_subtitle = models.CharField(
        blank=True,
        max_length=250,
    )
    publication_date = models.DateField("Publication date",
                                        null=True,
                                        blank=True)
    publication_file = models.ForeignKey('wagtaildocs.Document',
                                         null=True,
                                         blank=True,
                                         on_delete=models.SET_NULL,
                                         related_name='+')
    additional_author_copy = models.CharField(
        help_text="Example: with contributing authors",
        max_length=100,
        blank=True,
    )
    notes = RichTextField(blank=True, )
    contents_title = models.CharField(
        blank=True,
        default="Table of Contents",
        max_length=250,
    )
    content_panels = Page.content_panels + [
        MultiFieldPanel([
            FieldPanel('subtitle'),
            FieldPanel('secondary_subtitle'),
            FieldPanel('publication_date'),
            ImageChooserPanel('hero_image'),
            DocumentChooserPanel('publication_file'),
            InlinePanel("authors", label="Author"),
            FieldPanel("additional_author_copy"),
        ],
                        heading="Hero"),
        FieldPanel('contents_title'),
        FieldPanel('notes')
    ]
Exemplo n.º 6
0
class ArticlePage(Page):
    """ Represents a blog article page.

    This Page subclass provide a way to define blog article pages through the Wagtail's admin.
    It defines the basic fields and information that are generally associated with blog pages.

    """

    # Basically a blog article page is characterized by a body field (the actual content of the blog
    # post), a date and a title (which is provided by the wagtail's Page model).
    body = RichTextField(verbose_name=_('Introduction'))
    date = models.DateField(verbose_name=_('Post date'), default=dt.datetime.today)

    # A blog article page can have an header image that'll be used when rendering the blog post.
    # It'll also be displayed if the blog post is featured in the home page.
    header_image = models.ForeignKey(
        'wagtailimages.Image',
        blank=False,
        null=True,
        on_delete=models.SET_NULL,
        related_name='+',
        verbose_name=_('Header image'),
        help_text=_('Header image displayed when rendering the page.'),
    )

    ##############################
    # SEARCH INDEX CONFIGURATION #
    ##############################

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

    ###############################
    # EDITOR PANELS CONFIGURATION #
    ###############################

    content_panels = Page.content_panels + [
        FieldPanel('date'),
        FieldPanel('body', classname='full'),
        ImageChooserPanel('header_image'),
    ]

    ####################################
    # PARENT PAGE / SUBPAGE TYPE RULES #
    ####################################

    parent_page_types = ['blog.BlogPage']
    subpage_types: List[str] = []

    class Meta:
        verbose_name = _('Article')
        verbose_name_plural = _('Articles')

    def get_context(self, request, *args, **kwargs):
        """ Returns a dictionary of variables to bind into the template. """
        context = super().get_context(request, *args, **kwargs)

        # Inserts the top-level blog page into the context.
        context['blog_page'] = self.get_parent().specific

        return context
Exemplo n.º 7
0
class SimplePage(Page):
    """ Represents a simple page. """

    # A simple page has a single body field, which degines the actual content of the page.
    body = RichTextField(verbose_name=_('Body'))

    # A simple page can have an header image that'll be used when rendering the page.
    header_image = models.ForeignKey(
        'wagtailimages.Image',
        blank=False,
        null=True,
        on_delete=models.SET_NULL,
        related_name='+',
        verbose_name=_('Header image'),
        help_text=_('Header image displayed when rendering the page.'),
    )

    # It is possible to block indexation for a specific page by setting the "noindex" option to
    # true.
    noindex = models.BooleanField(default=False, verbose_name=_('Disable robots indexation'))

    ###############################
    # EDITOR PANELS CONFIGURATION #
    ###############################

    content_panels = Page.content_panels + [
        FieldPanel('body', classname='full'),
        ImageChooserPanel('header_image'),
    ]

    promote_panels = [
        MultiFieldPanel(
            [
                FieldPanel('slug'),
                FieldPanel('seo_title'),
                FieldPanel('show_in_menus'),
                FieldPanel('search_description'),
                FieldPanel('noindex'),
            ],
            _('Common page configuration')
        ),
    ]

    ####################################
    # PARENT PAGE / SUBPAGE TYPE RULES #
    ####################################

    parent_page_types = ['blog.BlogPage']
    subpage_types: List[str] = []

    class Meta:
        verbose_name = _('Simple page')
        verbose_name_plural = _('Simple pages')

    def get_context(self, request, *args, **kwargs):
        """ Returns a dictionary of variables to bind into the template. """
        context = super().get_context(request, *args, **kwargs)

        # Inserts the top-level blog page into the context.
        context['blog_page'] = self.get_parent().specific

        return context

    def get_sitemap_urls(self, request=None):
        """ Returns URLs to include in sitemaps. """
        return [] if self.noindex else super().get_sitemap_urls(request=request)
Exemplo n.º 8
0
class Team(models.Model):
    """This class represents a working group within UTN"""
    class Meta:
        verbose_name = _('Team')
        verbose_name_plural = _('Teams')
        default_permissions = ()
        ordering = ["name_sv"]

    # ---- General Information ------
    name_en = models.CharField(
        max_length=255,
        verbose_name=_('English team name'),
        help_text=_('Enter the name of the team'),
        blank=False,
    )

    name_sv = models.CharField(
        max_length=255,
        verbose_name=_('Swedish team name'),
        help_text=_('Enter the name of the team'),
        blank=False,
    )

    name = TranslatedField('name_en', 'name_sv')

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

    description_en = models.TextField(
        verbose_name=_('English team description'),
        help_text=_('Enter a description of the team'),
        blank=True,
    )

    description_sv = models.TextField(
        verbose_name=_('Swedish team description'),
        help_text=_('Enter a description of the team'),
        blank=True,
    )

    description = TranslatedField('description_en', 'description_sv')

    def __str__(self) -> str:
        return '{}'.format(self.name)

    @property
    def members(self):
        return get_user_model().objects.filter(
            application__position__role__teams__pk=self.pk,
            application__position__term_from__lte=date.today(),
            application__position__term_to__gte=date.today(),
            application__status='appointed',
        )

    @property
    def manual_members(self):
        members = self.members.values('pk')
        return get_user_model().objects.filter(groups=self.group).exclude(
            pk__in=members)

    # ------ Administrator settings ------
    panels = [
        MultiFieldPanel([
            FieldRowPanel([
                FieldPanel('name_en'),
                FieldPanel('name_sv'),
            ]),
            ImageChooserPanel('logo'),
            FieldPanel('description_en'),
            FieldPanel('description_sv'),
        ])
    ]
Exemplo n.º 9
0
class HeroSection(SectionBase, SectionTitleBlock, ButtonAction, Page):

    hero_layout = models.CharField(
        blank=True,
        max_length=100,
        verbose_name='Layout',
        choices=[('simple_centered', 'Simple centered'),
                 ('image_right', 'Image on right')],
        default='simple_centered',
    )
    hero_first_button_text = models.CharField(
        blank=True,
        max_length=100,
        verbose_name='Hero button text',
        default='Subscribe',
        help_text="Leave field empty to hide.",
    )
    hero_second_button_text = models.CharField(
        blank=True,
        max_length=100,
        verbose_name='Hero button text',
        default='Subscribe',
        help_text="Leave field empty to hide.",
    )
    hero_image = models.ForeignKey(
        'wagtailimages.Image',
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        verbose_name='Image',
        related_name='+',
    )
    hero_image_size = models.CharField(
        max_length=50,
        choices=cr_settings['HERO_IMAGE_SIZE_CHOICES'],
        default=cr_settings['HERO_IMAGE_SIZE_CHOICES_DEFAULT'],
        verbose_name=('Image size'),
    )
    hero_action_type_1 = models.CharField(
        max_length=50,
        choices=cr_settings['HERO_ACTION_TYPE_CHOICES'],
        default=cr_settings['HERO_ACTION_TYPE_CHOICES_DEFAULT'],
        verbose_name=('Action type (First)'),
    )
    hero_action_type_2 = models.CharField(
        max_length=50,
        choices=cr_settings['HERO_ACTION_TYPE_CHOICES'],
        default=cr_settings['HERO_ACTION_TYPE_CHOICES_DEFAULT'],
        verbose_name=('Action type (Second)'),
    )
    hero_buttons = StreamField([('action_button', ActionButton()),
                                ('primary_button', PrimaryButton())],
                               null=True,
                               verbose_name="Buttons",
                               help_text="Please choose Buttons")

    # basic tab panels
    basic_panels = Page.content_panels + [
        FieldPanel('hero_layout', heading='Layout', classname="title full"),
        MultiFieldPanel(
            [
                FieldRowPanel([
                    FieldPanel('hero_layout', classname="col6"),
                    FieldPanel('hero_image_size', classname="col6"),
                ]),
                FieldRowPanel([
                    FieldPanel('section_heading',
                               heading='Heading',
                               classname="col6"),
                    FieldPanel('section_subheading',
                               heading='Subheading',
                               classname="col6"),
                ]),
                FieldRowPanel([
                    FieldPanel('section_description',
                               heading='Description',
                               classname="col6"),
                ]),
                FieldPanel('hero_first_button_text'),
                FieldPanel('hero_second_button_text'),
                ImageChooserPanel('hero_image'),
            ],
            heading='Content',
        ),
        SectionBase.section_layout_panels,
        SectionBase.section_design_panels,
    ]

    # advanced tab panels
    advanced_panels = (SectionTitleBlock.title_basic_panels,
                       ) + ButtonAction.button_action_panels

    # Register Tabs
    edit_handler = TabbedInterface([
        ObjectList(basic_panels, heading="Basic"),
        ObjectList(advanced_panels, heading="Plus+"),
    ])

    # Page settings
    template = 'sections/hero_section_preview.html'
    parent_page_types = ['home.HomePage']
    subpage_types = []

    # Overring methods
    def set_url_path(self, parent):
        """
        Populate the url_path field based on this page's slug and the specified parent page.
        (We pass a parent in here, rather than retrieving it via get_parent, so that we can give
        new unsaved pages a meaningful URL when previewing them; at that point the page has not
        been assigned a position in the tree, as far as treebeard is concerned.
        """
        if parent:
            self.url_path = ''
        else:
            # a page without a parent is the tree root, which always has a url_path of '/'
            self.url_path = '/'

        return self.url_path
Exemplo n.º 10
0
class Content(CreateMixin, ClusterableModel):
    author = models.ForeignKey(User, on_delete=models.DO_NOTHING, null=True)
    image = models.ForeignKey('wagtailimages.Image',
                              on_delete=models.DO_NOTHING,
                              related_name='+',
                              verbose_name="Imagen")
    title = models.CharField(max_length=250, verbose_name='Título')
    body = RichTextField(blank=True, verbose_name="Descripción")
    tags = TaggableManager(through=ContentTags, blank=True)
    publish_at = models.DateTimeField('Publicar el', default=now)
    unpublish_at = models.DateTimeField('Despublicar el',
                                        null=True,
                                        blank=True)
    pinned = models.BooleanField(
        'Destacar',
        default=False,
        help_text='Destacar el contenido para que aparezca al comienzo.')

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

    panels = [
        FieldPanel('title', classname='title'),
        FieldPanel('body', classname="full"),
        ImageChooserPanel('image', heading='heading'),
        FieldPanel('pinned', classname="full"),
    ]

    end_panels = [
        ReadOnlyPanel('author', heading="Autor"),
        FieldPanel('tags'),
        MultiFieldPanel(
            [
                FieldRowPanel([
                    FieldPanel(
                        'publish_at', classname="col6", heading='heading'),
                    FieldPanel('unpublish_at', classname="col6"),
                ])
            ],
            heading="Publicar automáticamente",
            help_text=
            'Agenda este contenido para ser publicado y/o despublicado automáticamente. Si el campo \'despublicar\' está vacío no se agendará la despublicación.'
        ),
        UnnorderedInlinePanel(
            'notifications',
            label="Notificación automática",
            help_text=
            'Selecciona un canal, una fecha y hora para notificar este contenido automáticamente a todos los estudiantes suscritos.'
        ),
        UnnorderedInlinePanel(
            'sharings',
            label="Publicación en red social",
            help_text=
            'Selecciona una red social, una fecha y hora para publicar este contenido automáticamente en dicha red social.'
        )
    ]

    api_fields = [
        APIField('title'),
        APIField('body', serializer=RichTextRendereableField()),
        APIField('image'),
        APIField('author', serializer=UserSerializer()),
        APIField('tags'),
        APIField('publish_at'),
        APIField('pinned'),
    ]

    @property
    def image_path(self):
        return self.image.file.path

    @property
    def body_as_html(self):
        rich_text = RichText(self.body)
        return rich_text.__html__()

    def __str__(self):
        date = format_datetime(self.created_at.astimezone(
            pytz.timezone("America/Santiago")),
                               'dd/MMM/YYYY',
                               locale='es')
        return '%s. %s - %s' % (date, self.title, self.get_published_label())

    def after_save(self, request):
        self.author = request.user
        return self.save()

    def is_published(self):
        dt_now = now()
        if (self.publish_at is not None and self.publish_at < dt_now) and (
                self.unpublish_at is None or self.unpublish_at > dt_now):
            return True
        return False

    def was_unpublished(self):
        dt_now = now()
        if self.unpublish_at is not None and self.unpublish_at < dt_now:
            return True
        return False

    def is_pinned(self):
        return self.pinned

    def get_published_label(self):
        if self.was_unpublished():
            label = 'Publicación finalizada'
        elif self.is_published():
            label = 'Publicado'
        else:
            dt_now = now()
            days = (self.publish_at - dt_now).days
            label = 'queda %d día' if days == 1 else 'quedan %d días'
            label = ('Por publicar (%s)' % label) % days
        label = '%s%s' % ('📌 ' if self.is_pinned() else '', label)
        return label + self.get_notification_labels(
        ) + self.get_sharing_labels()

    def get_notification_labels(self):
        label = ''
        mobile_notifications = self.notifications.filter(channel='MOBILE')
        email_notifications = self.notifications.filter(channel='EMAIL')
        if mobile_notifications.count():
            count = mobile_notifications.filter(notified=True).count()
            label = label + ' 🔔%d' % count
        if email_notifications.count():
            count = email_notifications.filter(notified=True).count()
            label = label + ' @%d' % count
        return label

    def get_sharing_labels(self):
        label = ''
        twitter_notifications = self.sharings.filter(channel='TWITTER')
        ig_notifications = self.sharings.filter(channel='INSTAGRAM')
        if twitter_notifications.count():
            count = twitter_notifications.filter(published=True).count()
            label = label + ' 🕊%d' % count
        if ig_notifications.count():
            count = ig_notifications.filter(published=True).count()
            label = label + ' ⧇%d' % count
        return label
Exemplo n.º 11
0
class ToolkitPage(Page):
    title_fi = models.CharField(_('Title (Finnish)'),
                                max_length=200,
                                blank=True,
                                null=True)
    header = RichTextField(_('Header (English)'), max_length=500)
    header_fi = RichTextField(_('Header (Finnish)'),
                              max_length=500,
                              blank=True,
                              null=True)
    excerpt = RichTextField(
        _('Excerpt'),
        null=True,
        help_text='This text will appear on the toolkits index page.')
    excerpt_fi = RichTextField(
        _('Excerpt (Finnish)'),
        null=True,
        help_text=_('This text will appear on the toolkits index page.'),
    )
    text = RichTextField(_('Text (English)'))
    text_fi = RichTextField(_('Text (Finnish)'), blank=True, null=True)
    image = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        on_delete=models.SET_NULL,
        related_name='+',
        help_text='This image will appear on the toolkits index page.')
    background_image = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        on_delete=models.SET_NULL,
        related_name='+',
        help_text="This is the background image of the toolkit's detail page.")
    tools = StreamField([
        ('tool',
         blocks.StructBlock([
             ('title', blocks.CharBlock(label=_('Title (English)'))),
             ('title_fi',
              blocks.CharBlock(label=_('Title (Finnish)'), required=False)),
             ('thumbnail', ImageChooserBlock()),
             ('description',
              blocks.RichTextBlock(label=_('Description (English)'),
                                   required=False)),
             ('description_fi',
              blocks.RichTextBlock(label=_('Description (Finnish)'),
                                   required=False)),
             ('url', blocks.URLBlock(label=_('Link (English)'),
                                     required=False)),
             ('url_fi',
              blocks.URLBlock(label=_('Link (Finnish)'), required=False)),
         ])),
        ('doc',
         blocks.StructBlock([
             ('title', blocks.CharBlock(label=_('Title (English)'))),
             ('title_fi',
              blocks.CharBlock(label=_('Title (Finnish)'), required=False)),
             ('thumbnail', ImageChooserBlock()),
             ('description',
              blocks.RichTextBlock(label=_('Description (English)'),
                                   required=False)),
             ('description_fi',
              blocks.RichTextBlock(label=_('Description (Finnish)'),
                                   required=False)),
             ('file',
              DocumentChooserBlock(label=_('File (English)'), required=False)),
             ('file_fi',
              DocumentChooserBlock(label=_('File (Finnish)'), required=False)),
         ]))
    ])

    content_panels = Page.content_panels + [
        FieldPanel('title_fi'),
        MultiFieldPanel([
            FieldPanel('header'),
            FieldPanel('header_fi'),
            FieldPanel('excerpt'),
            FieldPanel('excerpt_fi'),
            FieldPanel('text'),
            FieldPanel('text_fi'),
            ImageChooserPanel('image'),
            ImageChooserPanel('background_image'),
        ],
                        heading="Toolkit information"),
        StreamFieldPanel('tools'),
    ]

    def get_title(self):
        return self.header

    def get_thumbnail(self):
        return self.image

    def get_category(self):
        return 'Toolkits'
Exemplo n.º 12
0
class Report(RoutablePageMixin, Post):
    """
    Report class that inherits from the abstract
    Post model and creates pages for Policy Papers.
    """
    parent_page_types = ['ReportsHomepage']
    subpage_types = []

    sections = StreamField(
        [('section',
          ReportSectionBlock(template="components/report_section_body.html",
                             required=False))],
        null=True,
        blank=True)

    abstract = RichTextField(blank=True, null=True)

    acknowledgements = RichTextField(blank=True, null=True)

    source_word_doc = models.ForeignKey('wagtaildocs.Document',
                                        null=True,
                                        blank=True,
                                        on_delete=models.SET_NULL,
                                        related_name='+',
                                        verbose_name='Source Word Document')

    report_pdf = models.ForeignKey('wagtaildocs.Document',
                                   null=True,
                                   blank=True,
                                   on_delete=models.SET_NULL,
                                   related_name='+',
                                   verbose_name='Report PDF')

    dataviz_src = models.CharField(blank=True,
                                   null=True,
                                   max_length=300,
                                   help_text="")

    overwrite_sections_on_save = models.BooleanField(
        default=False,
        help_text=
        'If checked, sections and endnote fields ⚠ will be overwritten ⚠ with Word document source on save. Use with CAUTION!'
    )
    generate_pdf_on_publish = models.BooleanField(
        'Generate PDF on save',
        default=False,
        help_text=
        '⚠ Save latest content before checking this ⚠\nIf checked, the "Report PDF" field will be filled with a generated pdf. Otherwise, leave this unchecked and upload a pdf to the "Report PDF" field.'
    )
    revising = False

    featured_sections = StreamField([
        ('featured', FeaturedReportSectionBlock(required=False, null=True)),
    ],
                                    null=True,
                                    blank=True)

    endnotes = StreamField([
        ('endnote', EndnoteBlock(required=False, null=True)),
    ],
                           null=True,
                           blank=True)

    report_url = StreamField([
        ('report_url', URLBlock(required=False, null=True)),
    ],
                             null=True,
                             blank=True)

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

    partner_logo = models.ForeignKey('home.CustomImage',
                                     null=True,
                                     blank=True,
                                     on_delete=models.SET_NULL,
                                     related_name='+')

    theme_full_bleed = models.BooleanField(
        default=False, help_text="Display bleed image on landing page")

    content_panels = [
        MultiFieldPanel([
            FieldPanel('title'),
            FieldPanel('subheading'),
            FieldPanel('date'),
            ImageChooserPanel('story_image'),
        ]),
        InlinePanel('authors', label=("Authors")),
        InlinePanel('programs', label=("Programs")),
        InlinePanel('subprograms', label=("Subprograms")),
        InlinePanel('topics', label=("Topics")),
        InlinePanel('location', label=("Locations")),
        MultiFieldPanel([
            FieldPanel('abstract'),
            StreamFieldPanel('featured_sections'),
            FieldPanel('acknowledgements'),
        ])
    ]

    sections_panels = [StreamFieldPanel('sections')]

    endnote_panels = [StreamFieldPanel('endnotes')]

    settings_panels = Post.settings_panels + [
        FieldPanel('theme_full_bleed'),
        FieldPanel('dataviz_src')
    ]

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

    pdf_panels = [
        MultiFieldPanel([
            DocumentChooserPanel('source_word_doc'),
            FieldPanel('overwrite_sections_on_save'),
        ],
                        heading='Word Doc Import'),
        MultiFieldPanel([
            FieldPanel('generate_pdf_on_publish'),
            DocumentChooserPanel('report_pdf'),
            StreamFieldPanel('attachment')
        ],
                        heading='PDF Generation'),
        ImageChooserPanel('partner_logo')
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading="Landing"),
        ObjectList(sections_panels, heading="Sections"),
        ObjectList(endnote_panels, heading="Endnotes"),
        ObjectList(promote_panels, heading="Promote"),
        ObjectList(settings_panels, heading='Settings', classname="settings"),
        ObjectList(pdf_panels, heading="PDF Publishing")
    ])

    search_fields = Post.search_fields + [index.SearchField('sections')]

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

        if getattr(request, 'is_preview', False):
            import newamericadotorg.api.report
            revision = PageRevision.objects.filter(
                page=self).last().as_page_object()
            report_data = newamericadotorg.api.report.serializers.ReportDetailSerializer(
                revision).data
            context['initial_state'] = json.dumps(report_data)

        return context

    def save(self, *args, **kwargs):
        super(Report, self).save(*args, **kwargs)

        if not self.overwrite_sections_on_save and not self.generate_pdf_on_publish:
            self.revising = False

        if not self.revising and self.source_word_doc is not None and self.overwrite_sections_on_save:
            self.revising = True
            parse_pdf(self)
            self.overwrite_sections_on_save = False
            self.save_revision()

        if not self.revising and self.generate_pdf_on_publish:
            generate_pdf.apply_async(args=(self.id, ))

    # Extra views

    @route(r'pdf/$')
    def pdf(self, request):
        if not self.report_pdf:
            return self.pdf_render(request)
        url = 'https://s3.amazonaws.com/newamericadotorg/' + self.report_pdf.file.name
        return redirect(url)

    @route(r'pdf/render/$')
    def pdf_render(self, request):
        response = HttpResponse(content_type='application/pdf;')
        response[
            'Content-Disposition'] = 'inline; filename=%s.pdf' % self.title
        response['Content-Transfer-Encoding'] = 'binary'
        protocol = 'https://' if request.is_secure() else 'http://'
        base_url = protocol + request.get_host()

        contents = generate_report_contents(self)
        authors = get_report_authors(self)

        html = loader.get_template('report/pdf.html').render({
            'page': self,
            'contents': contents,
            'authors': authors
        })
        pdf = write_pdf(response, html, base_url)

        return response

    @route(r'print/$')
    def print(self, request):
        contents = generate_report_contents(self)
        authors = get_report_authors(self)

        return render(request,
                      'report/pdf.html',
                      context={
                          'page': self,
                          'contents': contents,
                          'authors': authors
                      })

    @route(r'[a-zA-Z0-9_\.\-]*/$')
    def section(self, request):
        # Serve the whole report, subsection routing is handled by React
        return self.serve(request)

    class Meta:
        verbose_name = 'Report'
Exemplo n.º 13
0
class SectionBase(models.Model):
    # Content
    section_name = models.CharField(
        blank=True,
        null=True,
        max_length=100,
        verbose_name='Title',
    )
    section_heading = models.CharField(
        blank=True,
        null=True,
        max_length=100,
        verbose_name='Hero title',
        default='We are heroes',
    )
    section_subheading = models.CharField(
        blank=True,
        null=True,
        max_length=100,
        verbose_name='Hero subtitle',
        default='What business are you?',
        help_text=mark_safe(
            "Leave field empty to hide. <a href='/contract.pdf'>contract</a>.")
    )
    section_description = models.TextField(
        blank=True,
        null=True,
        max_length=400,
        verbose_name='Hero description',
        default=
        ('The thing we do is better than any other similar thing and this hero panel will convince you of that, just by having a glorious background image.'
         ),
        help_text="Leave field empty to hide.",
    )
    # Background
    section_background_type = models.CharField(
        blank=True,
        null=True,
        choices=[
            ('transparent', 'Transparent'),
            ('solid', 'Solid Color'),
            ('gradient', 'Gradient Color'),
            ('image', 'Background Image'),
        ],
        verbose_name='Type',
        default='trasparent',
        max_length=100,
        help_text=
        'Transparent background will use the background-color "page settings".',
    )
    section_background_color = ColorField(
        blank=True,
        null=True,
        help_text="Choose background color",
        verbose_name=('Color 1'),
    )
    section_background_color_2 = ColorField(
        blank=True,
        null=True,
        help_text="Choose background color",
        verbose_name=('Color 2'),
    )
    section_background_image = models.ForeignKey(
        'wagtailimages.Image',
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        verbose_name='Image',
        related_name='+',
    )
    # Layout
    section_color_theme = models.CharField(
        null=True,
        blank=True,
        max_length=50,
        choices=cr_settings['SECTION_COLOR_THEME_CHOICES'],
        verbose_name='Theme',
        # help_text='Choose font weight.',
    )
    section_top_bottom_padding = models.CharField(
        null=True,
        blank=True,
        max_length=50,
        choices=cr_settings['SECTION_TOP_BOTTOM_PADDING_CHOICES'],
        default=cr_settings['SECTION_TOP_BOTTOM_PADDING_CHOICES_DEFAULT'],
        verbose_name='Height',
        # help_text='Choose font weight.',
    )
    section_container_width = models.CharField(
        null=True,
        blank=True,
        max_length=50,
        choices=cr_settings['SECTION_CONTAINER_WIDTH_CHOICES'],
        default=cr_settings['SECTION_CONTAINER_WIDTH_CHOICES_DEFAULT'],
        verbose_name='Width',
        # help_text='Choose font weight.',
    )

    # Basic Panels
    section_content_panels = MultiFieldPanel(
        [
            FieldRowPanel([
                FieldPanel(
                    'section_heading', heading='Heading', classname="col6"),
                FieldPanel('section_subheading',
                           heading='Subheading',
                           classname="col6"),
            ]),
            FieldRowPanel([
                FieldPanel('section_description',
                           heading='Description',
                           classname="col6"),
            ]),
        ],
        heading='Content',
    )

    section_layout_panels = MultiFieldPanel(
        [
            FieldRowPanel([
                FieldPanel('section_top_bottom_padding',
                           heading='Size',
                           classname="col6"),
                FieldPanel('section_container_width',
                           heading='Width',
                           classname="col6"),
            ]),
            FieldRowPanel([
                FieldPanel(
                    'section_color_theme', heading='Theme', classname="col6"),
            ]),
        ],
        heading='Layout',
    )

    section_design_panels = MultiFieldPanel(
        [
            FieldRowPanel([
                FieldPanel('section_background_type',
                           heading='Type',
                           classname="col6"),
            ]),
            FieldRowPanel([
                NativeColorPanel('section_background_color',
                                 heading='Color 1',
                                 classname="col6"),
                NativeColorPanel('section_background_color_2',
                                 heading='Color 2',
                                 classname="col6"),
            ]),
            FieldRowPanel([
                ImageChooserPanel('section_background_image', heading='Image'),
            ]),
        ],
        heading='Design',
    )

    # Legacy
    advanced_tab_panels = [
        MultiFieldPanel(
            [
                FieldPanel('section_background_type'),
            ],
            heading='Background > Type',
            classname='collapsible',
        ),
        MultiFieldPanel([
            NativeColorPanel('section_background_color'),
            NativeColorPanel('section_background_color_2'),
        ],
                        heading='Background > Color',
                        classname='collapsible',
                        help_text="Please select Background Type first."),
        MultiFieldPanel(
            [
                ImageChooserPanel('section_background_image'),
            ],
            heading='Background > Image',
            classname='collapsible',
        ),
    ]

    # Define abstract to dont create own database table for this model - fields are created in the child class
    class Meta:
        abstract = True

    def __str__(self):
        if self.section_name:
            return self.section_name + " (Hero Section)"
        else:
            return super(SectionBase, self).__str__()
Exemplo n.º 14
0
class HomePage(Page):
    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_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
Exemplo n.º 15
0
class OldHomePage(Page):
    """ This is legacy HP model. It will be deleted once `HomePage` model (above) is complete and operational. """
    is_creatable = False

    header = models.CharField(max_length=255)
    specializations_headline = models.CharField(max_length=128)

    r_and_d_center_headline = models.CharField(max_length=128,
                                               null=True,
                                               blank=True)
    r_and_d_center_body = models.CharField(max_length=256,
                                           null=True,
                                           blank=True)

    join_us_headline = models.CharField(max_length=128)
    join_us_body = models.TextField()
    join_us_background = models.ForeignKey('wagtailimages.Image',
                                           null=True,
                                           on_delete=models.SET_NULL,
                                           related_name='+')

    content_panels = Page.content_panels + [
        FieldPanel('header'),
        FieldPanel('specializations_headline'),
        MultiFieldPanel([
            FieldPanel('r_and_d_center_headline', classname="full"),
            FieldPanel('r_and_d_center_body'),
        ],
                        heading="R&D center section"),
        MultiFieldPanel([
            FieldPanel('join_us_headline', classname="full"),
            FieldPanel('join_us_body'),
            ImageChooserPanel('join_us_background'),
        ],
                        heading=_("Join us section")),
        InlinePanel('cooperators_logos', heading="We work with")
    ]

    @property
    def articles(self):
        return NewsPage.objects.live().descendant_of(self).order_by(
            '-marked', '-publication_date')

    @property
    def our_initiatives(self):
        ProjectPage = apps.get_model('projects', 'ProjectPage')
        return ProjectPage.objects.live().filter(self_initiated=True)

    @property
    def info_pages(self):
        return InfoPage.objects.live().descendant_of(self)

    @property
    def topics(self):
        return TopicPage.objects.live().descendant_of(self).filter(marked=True)

    @property
    def random_team_member(self):
        """
        Every hit on home page should present a randomly picked team member. This method of random selection is based
        on https://books.agiliq.com/projects/django-orm-cookbook/en/latest/random.html
        Should be more efficient than Model.objects.order_by("?").first()
        """
        TeamMember = apps.get_model('projects', 'TeamMember')
        team_member_queryset = TeamMember.objects.live().descendant_of(self)
        max_id = team_member_queryset.aggregate(max_id=Max("id"))['max_id']
        if max_id is None:
            # there are no team members specified
            return None
        while True:
            pk = random.randint(1, max_id)
            team_member = team_member_queryset.filter(pk=pk).first()
            if team_member:
                return team_member.specific

    @property
    def rnd_block(self):
        return custom_blocks.RNDBlock().bind({
            'headline': self.r_and_d_center_headline,
            'body': self.r_and_d_center_body,
        })

    @property
    def specializations_block(self):
        return custom_blocks.TriptychBlock().bind({
            'headline':
            self.specializations_headline,
            'tiles': [{
                'background_image': specialization.background_image,
                'content': specialization.short_description,
                'page': specialization,
            } for specialization in
                      SpecializationPage.objects.live().descendant_of(self)],
        })

    @property
    def our_stories_block(self):
        return custom_blocks.HeroCarouselBlock().bind({
            'headline':
            _('Poznaj nas przez nasze historie'),
            'tiles': [{
                'background_image': news.photo,
                'headline': news.headline,
                'page': news,
                'secondary_page': news.specialization,
            } for news in self.articles[:3]],
        })

    @property
    def topics_block(self):
        return custom_blocks.HeroSwitchBlock().bind({
            'headline':
            _('Działamy w tematach'),
            'tiles': [{
                'background_image': topic.background_image,
                'title': topic.title,
                'page': topic.projects.first(),
                'side_image': topic.phone_image,
            } for topic in TopicPage.objects.live().descendant_of(self).filter(
                marked=True)],
        })

    @property
    def animated_process_block(self):
        return custom_blocks.AnimatedProcessBlock().bind(None)

    def join_us_block(self):
        return custom_blocks.HeroJoinUsBlock().bind({
            'background_image':
            self.join_us_background,
            'headline':
            self.join_us_headline,
            'body':
            self.join_us_body,
            'page':
            JobOfferIndexPage.objects.live().descendant_of(self).first(),
        })

    @property
    def our_initiatives_block(self):
        return custom_blocks.TriptychBlock().bind({
            'headline':
            _('Nasze inicjatywy'),
            'tiles': [{
                'background_image': project.background_image,
                'content': project.subtitle,
                'page': project,
                'external_url': project.project_url,
            } for project in self.our_initiatives],
        })

    @property
    def cooperation_block(self):
        return custom_blocks.LogoWallBlock().bind({
            'title':
            _("We have worked with"),
            'logos': [logo.image for logo in self.cooperators_logos.all()],
        })

    @property
    def member_block(self):
        member = self.random_team_member
        if not member:
            return None
        return custom_blocks.HeroStaticLeftBlock().bind({
            'background_image':
            member.photo,
            'title':
            _("Team"),
            'headline':
            member.name,
            'body':
            member.long_description,
            'page':
            TeamIndexPage.objects.live().descendant_of(self).first(),
        })
Exemplo n.º 16
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
Exemplo n.º 17
0
class SeoMixin(Page):
    og_title = models.CharField(
        max_length=40,
        blank=True,
        null=True,
        verbose_name=_("Facebook title"),
        help_text=_("Fallbacks to seo title if empty"),
    )

    og_description = models.CharField(
        max_length=300,
        blank=True,
        null=True,
        verbose_name=_("Facebook description"),
        help_text=_("Fallbacks to seo description if empty"),
    )

    og_image = models.ForeignKey(
        "customimage.CustomImage",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        help_text=_("If you want to override the image used on Facebook for \
                    this item, upload an image here. \
                    The recommended image size for Facebook is 1200 × 630px"),
        related_name="+",
    )

    twitter_title = models.CharField(
        max_length=40,
        blank=True,
        null=True,
        verbose_name=_("Twitter title"),
        help_text=_("Fallbacks to facebook title if empty"),
    )

    twitter_description = models.CharField(
        max_length=300,
        blank=True,
        null=True,
        verbose_name=_("Twitter description"),
        help_text=_("Fallbacks to facebook description if empty"),
    )

    twitter_image = models.ForeignKey(
        "customimage.CustomImage",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="+",
        verbose_name=_("Twitter image"),
        help_text=_("Fallbacks to facebook image if empty"),
    )

    robot_noindex = models.BooleanField(
        default=False,
        verbose_name=_("No index"),
        help_text=_("Check to add noindex to robots"),
    )

    robot_nofollow = models.BooleanField(
        default=False,
        verbose_name=_("No follow"),
        help_text=_("Check to add nofollow to robots"),
    )

    canonical_link = models.URLField(blank=True,
                                     null=True,
                                     verbose_name=_("Canonical link"))

    promote_panels = [
        FieldPanel("slug"),
        MultiFieldPanel(
            [FieldPanel("seo_title"),
             FieldPanel("search_description")],
            _("SEO settings"),
        ),
        MultiFieldPanel(
            [
                FieldPanel("og_title"),
                FieldPanel("og_description"),
                ImageChooserPanel("og_image"),
                FieldPanel("twitter_title"),
                FieldPanel("twitter_description"),
                ImageChooserPanel("twitter_image"),
            ],
            _("Social settings"),
        ),
        MultiFieldPanel(
            [
                FieldPanel("robot_noindex"),
                FieldPanel("robot_nofollow"),
                FieldPanel("canonical_link"),
            ],
            _("Robot settings"),
        ),
    ]

    og_image_list = ["og_image"]

    @cached_property
    def seo_og_image(self):
        images = [getattr(self, x) for x in self.og_image_list]
        images = list(filter(None.__ne__, images))

        if not len(images):
            return None

        return images[0]

    @cached_property
    def seo_html_title(self):
        return self.seo_title or self.title

    @cached_property
    def seo_meta_description(self):
        return self.search_description

    @cached_property
    def seo_og_title(self):
        return self.og_title or self.title

    @cached_property
    def seo_og_description(self):
        return self.og_description or self.title

    @cached_property
    def seo_og_url(self):
        return self.seo_canonical_link

    @cached_property
    def seo_canonical_link(self):
        return self.canonical_link or self.full_url

    @cached_property
    def seo_og_type(self):
        return None

    @cached_property
    def seo_twitter_title(self):
        return self.twitter_title or self.title

    @cached_property
    def seo_twitter_description(self):
        return self.twitter_description

    @cached_property
    def seo_twitter_url(self):
        return self.seo_canonical_link

    @cached_property
    def seo_twitter_image(self):
        return self.twitter_image or self.seo_og_image

    @cached_property
    def seo_meta_robots(self):
        index = "noindex" if self.robot_noindex else "index"
        follow = "nofollow" if self.robot_nofollow else "follow"
        return "{},{}".format(index, follow)

    class Meta:
        abstract = True
class LayoutSettings(BaseSetting):
    """
    Branding, navbar, and theme settings.
    """
    class Meta:
        verbose_name = _('Layout')

    logo = models.ForeignKey(
        get_image_model_string(),
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        verbose_name=_('Logo'),
        help_text=_('Brand logo used in the navbar and throughout the site'))
    favicon = models.ForeignKey(
        get_image_model_string(),
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='favicon',
        verbose_name=_('Favicon'),
    )
    navbar_color_scheme = models.CharField(
        blank=True,
        max_length=50,
        choices=cr_settings['FRONTEND_NAVBAR_COLOR_SCHEME_CHOICES'],
        default=cr_settings['FRONTEND_NAVBAR_COLOR_SCHEME_DEFAULT'],
        verbose_name=_('Navbar color scheme'),
        help_text=
        _('Optimizes text and other navbar elements for use with light or dark backgrounds.'
          ),  # noqa
    )
    navbar_class = models.CharField(
        blank=True,
        max_length=255,
        default=cr_settings['FRONTEND_NAVBAR_CLASS_DEFAULT'],
        verbose_name=_('Navbar CSS class'),
        help_text=
        _('Custom classes applied to navbar e.g. "bg-light", "bg-dark", "bg-primary".'
          ),
    )
    navbar_fixed = models.BooleanField(
        default=False,
        verbose_name=_('Fixed navbar'),
        help_text=_(
            'Fixed navbar will remain at the top of the page when scrolling.'),
    )
    navbar_wrapper_fluid = models.BooleanField(
        default=True,
        verbose_name=_('Full width navbar'),
        help_text=_('The navbar will fill edge to edge.'),
    )
    navbar_content_fluid = models.BooleanField(
        default=False,
        verbose_name=_('Full width navbar contents'),
        help_text=_('Content within the navbar will fill edge to edge.'),
    )
    navbar_collapse_mode = models.CharField(
        blank=True,
        max_length=50,
        choices=cr_settings['FRONTEND_NAVBAR_COLLAPSE_MODE_CHOICES'],
        default=cr_settings['FRONTEND_NAVBAR_COLLAPSE_MODE_DEFAULT'],
        verbose_name=_('Collapse navbar menu'),
        help_text=
        _('Control on what screen sizes to show and collapse the navbar menu links.'
          ),
    )
    navbar_format = models.CharField(
        blank=True,
        max_length=50,
        choices=cr_settings['FRONTEND_NAVBAR_FORMAT_CHOICES'],
        default=cr_settings['FRONTEND_NAVBAR_FORMAT_DEFAULT'],
        verbose_name=_('Navbar format'),
    )
    navbar_search = models.BooleanField(
        default=True,
        verbose_name=_('Search box'),
        help_text=_('Show search box in navbar'))
    frontend_theme = models.CharField(
        blank=True,
        max_length=50,
        choices=cr_settings['FRONTEND_THEME_CHOICES'],
        default=cr_settings['FRONTEND_THEME_DEFAULT'],
        verbose_name=_('Theme variant'),
        help_text=cr_settings['FRONTEND_THEME_HELP'],
    )

    panels = [
        MultiFieldPanel([
            ImageChooserPanel('logo'),
            ImageChooserPanel('favicon'),
        ],
                        heading=_('Branding')),
        MultiFieldPanel([
            FieldPanel('navbar_color_scheme'),
            FieldPanel('navbar_class'),
            FieldPanel('navbar_fixed'),
            FieldPanel('navbar_wrapper_fluid'),
            FieldPanel('navbar_content_fluid'),
            FieldPanel('navbar_collapse_mode'),
            FieldPanel('navbar_format'),
            FieldPanel('navbar_search'),
        ],
                        heading=_('Site Navbar Layout')),
        MultiFieldPanel([
            FieldPanel('frontend_theme'),
        ], heading=_('Theming')),
    ]
Exemplo n.º 19
0
class RecipePage(Page):
    """ Represents a blog recipe page.

    This Page subclass provide a way to define blog recipe pages through the Wagtail's admin.
    It defines the basic fields and information that are generally associated with recipes showcased
    in the context of a blog application.

    """

    # Like any blog article, a recipe has a date and a title. But it has no body: instead it only
    # has an introduction field to contain a small content to be displayed before the recipe
    # details.
    introduction = RichTextField(verbose_name=_('Introduction'))
    date = models.DateField(verbose_name=_('Post date'), default=dt.datetime.today)

    # A blog recipe page can have an header image that'll be used when rendering the blog post.
    header_image = models.ForeignKey(
        'wagtailimages.Image',
        blank=False,
        null=True,
        on_delete=models.SET_NULL,
        related_name='+',
        verbose_name=_('Header image'),
        help_text=_('Header image displayed when rendering the page.'),
    )

    # The following fields define basic meta-information regarding a recipe (times, yiels, etc).
    preparation_time = models.DurationField(
        blank=True,
        null=True,
        verbose_name=_('Preparation time'),
    )
    cook_time = models.DurationField(
        blank=True,
        null=True,
        verbose_name=_('Cook time'),
    )
    fridge_time = models.DurationField(
        blank=True,
        null=True,
        verbose_name=_('Fridge time'),
    )
    rest_time = models.DurationField(
        blank=True,
        null=True, verbose_name=_('Rest time'),
    )
    recipe_yield = models.CharField(
        max_length=127,
        blank=True,
        verbose_name=_('Yield'),
        help_text=_('Enter a yield indication such as "4 persons", "3 servings", etc.'),
    )

    # A recipe is defined by at least one dish type.
    DISH_TYPE_APPETIZERS = 'appetizers'
    DISH_TYPE_BEVERAGES = 'beverages'
    DISH_TYPE_BREAKFAST = 'breakfast'
    DISH_TYPE_DESSERTS = 'desserts'
    DISH_TYPE_MAIN_COURSE = 'main-course'
    DISH_TYPE_SAUCES_SALAD_DRESSINGS = 'sauces+salad-dressings'
    DISH_TYPE_SOUPS = 'soups'
    DISH_TYPE_VEGETABLES_SALADS = 'vegetables+salads'
    DISH_TYPE_CHOICES = (
        (DISH_TYPE_APPETIZERS, _('Appetizers')),
        (DISH_TYPE_BEVERAGES, _('Beverages')),
        (DISH_TYPE_BREAKFAST, _('Breakfast')),
        (DISH_TYPE_DESSERTS, _('Desserts')),
        (DISH_TYPE_MAIN_COURSE, _('Main course')),
        (DISH_TYPE_SAUCES_SALAD_DRESSINGS, _('Sauces and salad dressings')),
        (DISH_TYPE_SOUPS, _('Soups')),
        (DISH_TYPE_VEGETABLES_SALADS, _('Vegetables and salads')),
    )
    dish_types = ChoiceArrayField(
        models.CharField(max_length=64, choices=DISH_TYPE_CHOICES),
        size=3,
        default=list,
        verbose_name=_('Dish types'),
    )

    ##############################
    # SEARCH INDEX CONFIGURATION #
    ##############################

    search_fields = Page.search_fields + [
        index.SearchField('introduction'),
        index.FilterField('date'),
    ]

    ###############################
    # EDITOR PANELS CONFIGURATION #
    ###############################

    content_panels = Page.content_panels + [
        FieldPanel('introduction', classname='full'),
        FieldPanel('dish_types', widget=CheckboxSelectMultiple),
        MultiFieldPanel(
            [
                FieldPanel('preparation_time', widget=ShortDurationSelectWidget),
                FieldPanel('cook_time', widget=ShortDurationSelectWidget),
                FieldPanel('fridge_time', widget=ShortDurationSelectWidget),
                FieldPanel('rest_time', widget=ShortDurationSelectWidget),
                FieldPanel('recipe_yield'),
            ],
            heading=_('Recipe information'),
        ),
        InlinePanel('ingredients_sections', label=_('Recipe ingredients sections')),
        InlinePanel('instructions_sections', label=_('Recipe instructions sections')),
        ImageChooserPanel('header_image'),
        FieldPanel('date'),
    ]

    ####################################
    # PARENT PAGE / SUBPAGE TYPE RULES #
    ####################################

    parent_page_types = ['blog.BlogPage']
    subpage_types: List[str] = []

    class Meta:
        verbose_name = _('Recipe')
        verbose_name_plural = _('Recipes')

    @property
    def verbose_dish_types(self):
        """ Returns verbose names of dish types. """
        dish_types_choices = dict(self.DISH_TYPE_CHOICES)
        return [dish_types_choices[d] for d in self.dish_types]

    def get_context(self, request, *args, **kwargs):
        """ Returns a dictionary of variables to bind into the template. """
        context = super().get_context(request, *args, **kwargs)

        # Inserts the top-level blog page into the context.
        context['blog_page'] = self.get_parent().specific

        return context
Exemplo n.º 20
0
class LocationPage(Page):
    """
    Detail for a specific bakery location.
    """
    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)
    address = models.TextField()
    lat_long = models.CharField(
        max_length=36,
        help_text="Comma separated lat/long. (Ex. 64.144367, -21.939182) \
                   Right click Google Maps and select 'What\'s Here'",
        validators=[
            RegexValidator(
                regex=r'^(\-?\d+(\.\d+)?),\s*(\-?\d+(\.\d+)?)$',
                message=
                'Lat Long must be a comma-separated numeric lat and long',
                code='invalid_lat_long'),
        ])

    # Search index configuration
    search_fields = Page.search_fields + [
        index.SearchField('address'),
        index.SearchField('body'),
    ]

    # Fields to show to the editor in the admin view
    content_panels = [
        FieldPanel('title', classname="full"),
        FieldPanel('introduction', classname="full"),
        ImageChooserPanel('image'),
        StreamFieldPanel('body'),
        FieldPanel('address', classname="full"),
        FieldPanel('lat_long'),
        InlinePanel('hours_of_operation', label="Hours of Operation"),
    ]

    def __str__(self):
        return self.title

    @property
    def operating_hours(self):
        hours = self.hours_of_operation.all()
        return hours

    # Determines if the location is currently open. It is timezone naive
    def is_open(self):
        now = datetime.now()
        current_time = now.time()
        current_day = now.strftime('%a').upper()
        try:
            self.operating_hours.get(day=current_day,
                                     opening_time__lte=current_time,
                                     closing_time__gte=current_time)
            return True
        except LocationOperatingHours.DoesNotExist:
            return False

    # Makes additional context available to the template so that we can access
    # the latitude, longitude and map API key to render the map
    def get_context(self, request):
        context = super(LocationPage, self).get_context(request)
        context['lat'] = self.lat_long.split(",")[0]
        context['long'] = self.lat_long.split(",")[1]
        context['google_map_api_key'] = settings.GOOGLE_MAP_API_KEY
        return context

    # Can only be placed under a LocationsIndexPage object
    parent_page_types = ['LocationsIndexPage']
Exemplo n.º 21
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__iexact='mozfest').first()
        context['mozfest_footer'] = mozfest_footer

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

        return context
Exemplo n.º 22
0
class AllTagsPage(Page):
    parent_page_types = ['home.HomePage']
    subpage_types = []
    max_count = 1
    banner = models.ForeignKey('wagtailimages.Image',
                               on_delete=models.SET_NULL,
                               related_name='+',
                               null=True)

    @property
    def breadcrumbs(self):
        breadcrumbs = []
        for page in self.get_ancestors()[2:]:
            breadcrumbs.append({'title': page.title, 'url': page.url})
        breadcrumbs.append({'title': self.title, 'url': self.url})
        return breadcrumbs

    @property
    def all_tags(self):
        tags = TaggitTag.objects.all()

        tags_list = []
        for tag in tags:
            tags_list.append({
                'id': tag.id,
                'name': tag.name,
                'slug': tag.slug
            })
        tags_sorted = sorted(tags_list, key=lambda i: str(i['name']).lower())
        letter_groups = set()
        for tag in tags_sorted:
            letter_groups.add(tag['name'][0].lower())
        letter_groups = sorted(list(letter_groups))

        result = []
        for letter in letter_groups:
            letter_dict = {'group': letter.upper(), 'tags': []}
            for tag in tags_sorted:
                if tag['name'][0].lower() == letter:
                    letter_dict['tags'].append(tag)
            result.append(letter_dict)
        return result

    content_panels = Page.content_panels + [
        ImageChooserPanel('banner', heading='Иллюстрация')
    ]

    api_fields = [
        APIField('breadcrumbs'),
        APIField('all_tags'),
        APIField('banner', serializer=ImageRenditionField(BANNER_SIZE))
    ]

    def get_sitemap_urls(self, request):
        return [{
            'location': self.full_url[:-1],
            'lastmod': self.last_published_at,
        }]

    class Meta:
        verbose_name = 'Страница тегов'
Exemplo n.º 23
0
class ProjectsIndexPage(Page):
    """
    Index page for projects.

    This is more complex than other index pages on the bakery demo site as we've
    included pagination. We've separated the different aspects of the index page
    to be discrete functions to make it easier to follow
    """

    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.')

    content_panels = Page.content_panels + [
        FieldPanel('introduction', classname="full"),
        ImageChooserPanel('image'),
    ]

    # Can only have BreadPage children
    subpage_types = ['ProjectPage']

    # Returns a queryset of BreadPage objects that are live, that are direct
    # descendants of this index page with most recent first
    def get_projects(self):
        return ProjectPage.objects.live().descendant_of(self).order_by(
            '-first_published_at')

    # Allows child objects (e.g. BreadPage objects) to be accessible via the
    # template. We use this on the HomePage to display child items of featured
    # content
    def children(self):
        return self.get_children().specific().live()

    # Pagination for the index page. We use the `django.core.paginator` as any
    # standard Django app would, but the difference here being we have it as a
    # method on the model rather than within a view function
    def paginate(self, request, *args):
        page = request.GET.get('page')
        paginator = Paginator(self.get_projects(), 12)
        try:
            pages = paginator.page(page)
        except PageNotAnInteger:
            pages = paginator.page(1)
        except EmptyPage:
            pages = paginator.page(paginator.num_pages)
        return pages

    # Returns the above to the get_context method that is used to populate the
    # template
    def get_context(self, request):
        context = super(ProjectsIndexPage, self).get_context(request)

        # BreadPage objects (get_breads) are passed through pagination
        projects = self.paginate(request, self.get_projects())

        context['projects'] = projects

        return context
Exemplo n.º 24
0
class Author(index.Indexed, models.Model):
    """Авторы для различного контента"""

    surname = models.CharField(max_length=50,
                               verbose_name='Фамилия',
                               null=True,
                               blank=True)
    name = models.CharField(max_length=50,
                            verbose_name='Имя',
                            null=True,
                            blank=True)
    middle_name = models.CharField(max_length=50,
                                   verbose_name='Отчество',
                                   null=True,
                                   blank=True)
    alias = models.CharField(max_length=50,
                             verbose_name='Псевдоним',
                             null=True,
                             blank=True)
    profession = models.CharField(max_length=200,
                                  verbose_name='Профессия/Должность/...',
                                  null=True,
                                  blank=True)

    # TODO добавить выбор страницы сайта

    handhelp_url = models.ForeignKey('wagtailcore.Page',
                                     null=True,
                                     blank=True,
                                     related_name='+',
                                     on_delete=models.CASCADE,
                                     verbose_name='Страница на hand-help')
    outer_url = models.URLField(blank=True,
                                null=True,
                                default='hand-help.ru',
                                verbose_name='Страница на стороннем сайте')
    image = models.ForeignKey('wagtailimages.Image',
                              on_delete=models.SET_NULL,
                              related_name='+',
                              null=True,
                              verbose_name='Изображение')

    @property
    def full_name(self):
        if self.alias:
            return self.alias
        else:
            return f'{self.name} {self.surname}'

    @property
    def short_name(self):  ## Фамилия И.О.
        try:
            return f'{self.surname} {self.name[0]}.{self.middle_name[0]}.'
        except TypeError:
            try:
                return f'{self.surname} {self.name[0]}.'
            except TypeError:
                return f'{self.name}'

    @property
    def inner_link(self):
        if self.handhelp_url:
            return self.handhelp_url.url

    @property
    def outer_link(self):
        return self.outer_url

    # @property
    # def link(self):
    #     if self.handhelp_url:
    #         return self.handhelp_url.url
    #     elif self.outer_url:
    #         return self.outer_url
    #     return '#'

    @property
    def ava(self):  # маленькая аватарка
        return self.image.get_rendition('fill-24x24')

    panels = [
        MultiFieldPanel([
            FieldPanel('surname'),
            FieldPanel('name'),
            FieldPanel('middle_name')
        ],
                        heading='ФИО'),
        FieldPanel('alias'),
        FieldPanel('profession'),
        MultiFieldPanel(
            [PageChooserPanel('handhelp_url'),
             FieldPanel('outer_url')],
            heading='Страница'),
        ImageChooserPanel('image'),
    ]

    api_fields = [
        APIField('alias'),
        APIField('profession'),
        APIField('image', serializer=ImageRenditionField(
            'fill-100x100')),  #  TODO размеры по макету
        APIField('image_small',
                 serializer=ImageRenditionField('fill-20x20', source='image'))
    ]

    search_fields = [index.SearchField('full_name', partial_match=True)]

    def __str__(self):
        return self.full_name  # TODO исправить с учетом разных контекстов, изменить порядок на ФИ

    class Meta:
        verbose_name = "Автор"
        verbose_name_plural = "Авторы"
        ordering = [
            'surname',
        ]
Exemplo n.º 25
0
class Person(Page):
    resource_type = 'person'
    parent_page_types = ['People']
    subpage_types = []
    template = 'person.html'

    # Content fields
    job_title = CharField(max_length=250)
    role = CharField(max_length=250, choices=ROLE_CHOICES, default='staff')
    description = RichTextField(default='', blank=True)
    image = ForeignKey(
        'mozimages.MozImage',
        null=True,
        blank=True,
        on_delete=SET_NULL,
        related_name='+'
    )

    # Card fields
    card_title = CharField('Title', max_length=140, blank=True, default='')
    card_description = TextField('Description', max_length=140, blank=True, default='')
    card_image = ForeignKey(
        'mozimages.MozImage',
        null=True,
        blank=True,
        on_delete=SET_NULL,
        related_name='+',
        verbose_name='Image',
    )

    # Meta
    twitter = CharField(max_length=250, blank=True, default='')
    facebook = CharField(max_length=250, blank=True, default='')
    linkedin = CharField(max_length=250, blank=True, default='')
    github = CharField(max_length=250, blank=True, default='')
    email = CharField(max_length=250, blank=True, default='')
    websites = StreamField(
        StreamBlock([
            ('website', PersonalWebsiteBlock())
        ], min_num=0, max_num=3, required=False),
        null=True,
        blank=True,
    )
    keywords = ClusterTaggableManager(through=PersonTag, blank=True)

     # Content panels
    content_panels = [
        MultiFieldPanel([
            CustomLabelFieldPanel('title', label='Full name'),
            FieldPanel('job_title'),
            FieldPanel('role'),
        ], heading='About'),
        FieldPanel('description'),
        ImageChooserPanel('image'),
    ]

    # Card panels
    card_panels = [
        FieldPanel('card_title'),
        FieldPanel('card_description'),
        ImageChooserPanel('card_image'),
    ]

    # Meta panels
    meta_panels = [
        MultiFieldPanel([
            InlinePanel('topics'),
        ], heading='Topics interested in'),
        MultiFieldPanel([
            FieldPanel('twitter'),
            FieldPanel('facebook'),
            FieldPanel('linkedin'),
            FieldPanel('github'),
            FieldPanel('email'),
        ], heading='Profiles'),
        StreamFieldPanel('websites'),
        MultiFieldPanel([
            FieldPanel('seo_title'),
            FieldPanel('search_description'),
            FieldPanel('keywords'),
        ], heading='SEO'),
    ]

    # Settings panels
    settings_panels = [
        FieldPanel('slug'),
    ]

    # Tabs
    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
        ObjectList(card_panels, heading='Card'),
        ObjectList(meta_panels, heading='Meta'),
        ObjectList(settings_panels, heading='Settings', classname='settings'),
    ])

    @property
    def events(self):
        '''
        Return upcoming events where this person is a speaker,
        ordered by start date
        '''
        from ..events.models import Event

        upcoming_events = (Event
                .objects
                .filter(start_date__gte=datetime.datetime.now())
                .live()
                .public()
        )

        speaker_events = Event.objects.none()

        for event in upcoming_events.all():
            # add the event to the list if the current person is a speaker
            if event.has_speaker(self):
                speaker_events = speaker_events | Event.objects.page(event)

        return speaker_events.order_by('start_date')

    @property
    def articles(self):
        '''
        Return articles and external articles where this person is (one of) the authors,
        ordered by article date, most recent first
        '''
        from ..articles.models import Article
        from ..externalcontent.models import ExternalArticle

        articles = Article.objects.none()
        external_articles = ExternalArticle.objects.none()

        all_articles = Article.objects.live().public().all()
        all_external_articles = ExternalArticle.objects.live().public().all()

        for article in all_articles:
            if article.has_author(self):
                articles = articles | Article.objects.page(article)

        for external_article in all_external_articles:
            if external_article.has_author(self):
                external_articles = external_articles | ExternalArticle.objects.page(external_article)

        return sorted(chain(articles, external_articles), key=attrgetter('date'), reverse=True)

    @property
    def videos(self):
        '''
        Return the most recent videos and external videos where this person is (one of)
        the speakers.
        '''
        from ..videos.models import Video
        from ..externalcontent.models import ExternalVideo

        videos = Video.objects.none()
        external_videos = ExternalVideo.objects.none()

        all_videos = Video.objects.live().public().all()
        all_external_videos = ExternalVideo.objects.live().public().all()

        for video in all_videos:
            if video.has_speaker(self):
                videos = videos | Video.objects.page(video)

        for external_video in all_external_videos:
            if external_video.has_speaker(self):
                external_videos = external_videos | ExternalVideo.objects.page(external_video)

        return sorted(chain(videos, external_videos), key=attrgetter('date'), reverse=True)

    @property
    def role_group(self):
        return {
            'slug': self.role,
            'title': dict(ROLE_CHOICES).get(self.role, ''),
        }
Exemplo n.º 26
0
class PublicationPage(
    BasicPageAbstract,
    ContentPage,
    FeatureablePageAbstract,
    FromTheArchivesPageAbstract,
    ShareablePageAbstract,
):
    """View publication page"""

    class BookFormats(models.TextChoices):
        HARDCOVER = ('HC', 'Hardcover')
        PAPERBACK = ('PB', 'Paperback')
        TRADE_PB = ('TP', 'Trade PB')

    class PublicationTypes(models.TextChoices):
        BOOKS = ('books', 'Books')
        CIGI_COMMENTARIES = ('cigi_commentaries', 'CIGI Commentaries')
        CIGI_PAPERS = ('cigi_papers', 'CIGI Papers')
        COLLECTED_SERIES = ('collected_series', 'Collected Series')
        CONFERENCE_REPORTS = ('conference_reports', 'Conference Reports')
        ESSAY_SERIES = ('essay_series', 'Essay Series')
        POLICY_BRIEFS = ('policy_briefs', 'Policy Briefs')
        POLICY_MEMOS = ('policy_memos', 'Policy Memos')
        SPECIAL_REPORTS = ('special_reports', 'Special Reports')
        SPEECHES = ('speeches', 'Speeches')
        STUDENT_ESSAY = ('student_essay', 'Student Essay')

    book_excerpt = RichTextField(
        blank=True,
        features=['bold', 'italic', 'link'],
        verbose_name='Excerpt',
    )
    book_excerpt_download = models.ForeignKey(
        'wagtaildocs.Document',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        verbose_name='Excerpt Download',
    )
    book_format = models.CharField(
        blank=True,
        max_length=2,
        choices=BookFormats.choices,
        verbose_name='Format',
        help_text='Select the formation of this book/publication.',
    )
    book_pages = models.IntegerField(
        blank=True,
        null=True,
        verbose_name='Pages',
        help_text='Enter the number of pages in the book.',
    )
    book_publisher = models.CharField(blank=True, max_length=255)
    book_publisher_url = models.URLField(blank=True)
    book_purchase_links = StreamField(
        [
            ('purchase_link', BookPurchaseLinkBlock())
        ],
        blank=True,
    )
    ctas = StreamField(
        [
            ('ctas', CTABlock())
        ],
        blank=True,
        verbose_name='Call to Action Buttons',
    )
    editorial_reviews = StreamField(
        [
            ('editorial_review', RichTextBlock(
                features=['bold', 'italic', 'link'],
            )),
        ],
        blank=True,
    )
    embed_issuu = models.URLField(
        blank=True,
        verbose_name='Issuu Embed',
        help_text='Enter the Issuu URL (https://issuu.com/cigi/docs/modern_conflict_and_ai_web) to add an embedded Issuu document.',
    )
    embed_youtube = models.URLField(
        blank=True,
        verbose_name='YouTube Embed',
        help_text='Enter the YouTube URL (https://www.youtube.com/watch?v=4-Xkn1U1DkA) or short URL (https://youtu.be/o5acQ2GxKbQ) to add an embedded video.',
    )
    image_cover = models.ForeignKey(
        'images.CigionlineImage',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        verbose_name='Cover image',
        help_text='An image of the cover of the publication.',
    )
    image_poster = models.ForeignKey(
        'images.CigionlineImage',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        verbose_name='Poster image',
        help_text='A poster image which will be used in the highlights section of the homepage.',
    )
    isbn = models.CharField(
        blank=True,
        max_length=32,
        verbose_name='ISBN',
        help_text='Enter the print ISBN for this book.',
    )
    isbn_ebook = models.CharField(
        blank=True,
        max_length=32,
        verbose_name='eBook ISBN',
        help_text='Enter the ISBN for the eBook version of this publication.',
    )
    isbn_hardcover = models.CharField(
        blank=True,
        max_length=32,
        verbose_name='Hardcover ISBN',
        help_text='Enter the ISBN for the hardcover version of this publication.',
    )
    pdf_downloads = StreamField(
        [
            ('pdf_download', PDFDownloadBlock())
        ],
        blank=True,
        verbose_name='PDF Downloads',
    )
    publication_series = models.ForeignKey(
        'publications.PublicationSeriesPage',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
    )
    publication_type = models.ForeignKey(
        'publications.PublicationTypePage',
        null=True,
        blank=False,
        on_delete=models.SET_NULL,
        related_name='publications',
    )
    short_description = RichTextField(
        blank=True,
        null=False,
        features=['bold', 'italic', 'link'],
    )

    # Reference field for the Drupal-Wagtail migrator. Can be removed after.
    drupal_node_id = models.IntegerField(blank=True, null=True)

    def featured_person_list(self):
        """
        For featured publications, only display the first 3 authors/editors.
        """

        # @todo test
        person_list = list(self.authors.all()) + list(self.editors.all())
        del person_list[3:]
        result = []
        for person in person_list:
            if person.author:
                result.append(person.author)
            elif person.editor:
                result.append(person.editor)
        return result

    def featured_person_list_has_more(self):
        """
        If there are more than 3 authors/editors for featured publications,
        display "and more".
        """

        # @todo test
        return (self.author_count + self.editor_count) > 3

    def has_book_metadata(self):
        return (
            self.publication_type and
            self.publication_type.title == 'Books' and
            (self.book_format or self.book_pages or self.book_publisher or self.isbn or self.isbn_ebook or self.isbn_hardcover)
        )

    content_panels = [
        BasicPageAbstract.title_panel,
        MultiFieldPanel(
            [
                FieldPanel('short_description'),
                StreamFieldPanel('body'),
            ],
            heading='Body',
            classname='collapsible collapsed',
        ),
        MultiFieldPanel(
            [
                PageChooserPanel(
                    'publication_type',
                    ['publications.PublicationTypePage'],
                ),
                FieldPanel('publishing_date'),
            ],
            heading='General Information',
            classname='collapsible collapsed',
        ),
        ContentPage.authors_panel,
        ContentPage.editors_panel,
        MultiFieldPanel(
            [
                FieldPanel('book_publisher'),
                FieldPanel('book_publisher_url'),
                FieldPanel('book_format'),
                FieldPanel('isbn'),
                FieldPanel('isbn_hardcover'),
                FieldPanel('isbn_ebook'),
                FieldPanel('book_pages'),
                StreamFieldPanel('book_purchase_links'),
                DocumentChooserPanel('book_excerpt_download'),
                FieldPanel('book_excerpt'),
            ],
            heading='Book Info',
            classname='collapsible collapsed',
        ),
        MultiFieldPanel(
            [
                StreamFieldPanel('editorial_reviews'),
            ],
            heading='Editorial Reviews',
            classname='collapsible collapsed',
        ),
        MultiFieldPanel(
            [
                ImageChooserPanel('image_cover'),
                ImageChooserPanel('image_poster'),
            ],
            heading='Images',
            classname='collapsible collapsed',
        ),
        MultiFieldPanel(
            [
                FieldPanel('embed_issuu'),
                StreamFieldPanel('pdf_downloads'),
                StreamFieldPanel('ctas'),
                FieldPanel('embed_youtube'),
            ],
            heading='Media',
            classname='collapsible collapsed',
        ),
        ContentPage.recommended_panel,
        MultiFieldPanel(
            [
                FieldPanel('topics'),
                PageChooserPanel(
                    'publication_series',
                    ['publications.PublicationSeriesPage'],
                ),
                FieldPanel('projects'),
            ],
            heading='Related',
            classname='collapsible collapsed',
        ),
        FromTheArchivesPageAbstract.from_the_archives_panel,
    ]
    promote_panels = Page.promote_panels + [
        FeatureablePageAbstract.feature_panel,
        ShareablePageAbstract.social_panel,
        SearchablePageAbstract.search_panel,
    ]

    search_fields = BasicPageAbstract.search_fields \
        + ContentPage.search_fields \
        + [
            index.FilterField('publication_series'),
            index.FilterField('publication_type'),
            index.FilterField('publishing_date'),
        ]

    parent_page_types = ['publications.PublicationListPage']
    subpage_types = []
    templates = 'publications/publication_page.html'

    class Meta:
        verbose_name = 'Publication'
        verbose_name_plural = 'Publications'
Exemplo n.º 27
0
class PublicationPage(FoundationMetadataPageMixin, Page):
    """
    This is the root page of a publication.

    From here the user can browse to the various sections (called chapters).
    It will have information on the publication, its authors, and metadata from it's children

    Publications are collections of Articles
    Publications can also be broken down into Chapters, which are really just child publication pages
    Each of those Chapters may have several Articles
    An Article can only belong to one Chapter/Publication Page
    """

    subpage_types = ['ArticlePage', 'PublicationPage']

    hero_image = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='publication_hero_image',
        verbose_name='Publication Hero Image',
    )
    subtitle = models.CharField(
        blank=True,
        max_length=250,
    )
    secondary_subtitle = models.CharField(
        blank=True,
        max_length=250,
    )
    publication_date = models.DateField("Publication date",
                                        null=True,
                                        blank=True)
    publication_file = models.ForeignKey(
        'wagtaildocs.Document',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
    )
    additional_author_copy = models.CharField(
        help_text="Example: with contributing authors",
        max_length=100,
        blank=True,
    )
    notes = RichTextField(blank=True, features=['link', 'bold', 'italic'])
    contents_title = models.CharField(
        blank=True,
        default="Table of Contents",
        max_length=250,
    )
    content_panels = Page.content_panels + [
        MultiFieldPanel(
            [
                FieldPanel('subtitle'),
                FieldPanel('secondary_subtitle'),
                FieldPanel('publication_date'),
                ImageChooserPanel('hero_image'),
                DocumentChooserPanel('publication_file'),
                InlinePanel('authors', label='Author'),
                FieldPanel('additional_author_copy'),
            ],
            heading='Hero',
        ),
        FieldPanel('contents_title'),
        FieldPanel('notes'),
    ]

    @property
    def is_chapter_page(self):
        """
        A PublicationPage nested under a PublicationPage is considered to be a
        "ChapterPage". The templates used very similar logic and structure, and
        all the fields are the same.
        """
        parent = self.get_parent().specific
        return parent.__class__ is PublicationPage

    @property
    def next_page(self):
        """
        Only applies to Chapter Publication (sub-Publication Pages).
        Returns a Page object or None.
        """
        if self.is_chapter_page:
            next_page = self.get_siblings().filter(path__gt=self.path,
                                                   live=True).first()
            if not next_page:
                # If there is no more chapters. Return the parent page.
                next_page = self.get_parent()
        return next_page

    @property
    def prev_page(self):
        """
        Only applies to Chapter Publication (sub-Publication Pages).
        Returns a Page object or None.
        """
        if self.is_chapter_page:
            prev_page = self.get_siblings().filter(
                path__lt=self.path, live=True).reverse().first()
            if not prev_page:
                # If there is no more chapters. Return the parent page.
                prev_page = self.get_parent()
        return prev_page

    @property
    def zen_nav(self):
        return True

    def breadcrumb_list(self):
        """
        Get all the parent PublicationPages and return a QuerySet
        """
        return Page.objects.ancestor_of(self).type(PublicationPage).live()

    def get_context(self, request, *args, **kwargs):
        context = super().get_context(request, *args, **kwargs)
        pages = []
        for page in self.get_children():
            if request.user.is_authenticated:
                # User is logged in, and can preview a page. Get all pages, even drafts.
                pages.append({
                    'child': page,
                    'grandchildren': page.get_children()
                })
            elif page.live:
                # User is not logged in AND this page is live. Only fetch live grandchild pages.
                pages.append({
                    'child': page,
                    'grandchildren': page.get_children().live()
                })
        context['child_pages'] = pages
        return set_main_site_nav_information(self, context, 'Homepage')
Exemplo n.º 28
0
class BlogPage(HeadlessPreviewMixin, Page):
    date = models.DateField("Post date")
    advert = models.ForeignKey(
        "home.Advert",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="+",
    )
    cover = models.ForeignKey(
        "wagtailimages.Image",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="+",
    )
    book_file = models.ForeignKey(
        "wagtaildocs.Document",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="+",
    )
    featured_media = models.ForeignKey(
        "wagtailmedia.Media",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="+",
    )
    author = models.ForeignKey(AuthorPage,
                               null=True,
                               blank=True,
                               on_delete=models.SET_NULL,
                               related_name="+")
    body = StreamField(StreamFieldBlock())

    content_panels = Page.content_panels + [
        FieldPanel("date"),
        ImageChooserPanel("cover"),
        StreamFieldPanel("body"),
        InlinePanel("related_links", label="Related links"),
        InlinePanel("authors", label="Authors"),
        FieldPanel("author"),
        SnippetChooserPanel("advert"),
        DocumentChooserPanel("book_file"),
        MediaChooserPanel("featured_media"),
    ]

    @property
    def copy(self):
        return self

    graphql_fields = [
        GraphQLString("heading"),
        GraphQLString("date", required=True),
        GraphQLStreamfield("body"),
        GraphQLCollection(
            GraphQLForeignKey,
            "related_links",
            "home.blogpagerelatedlink",
            required=True,
            item_required=True,
        ),
        GraphQLCollection(GraphQLString,
                          "related_urls",
                          source="related_links.url"),
        GraphQLCollection(GraphQLString,
                          "authors",
                          source="authors.person.name"),
        GraphQLSnippet("advert", "home.Advert"),
        GraphQLImage("cover"),
        GraphQLDocument("book_file"),
        GraphQLMedia("featured_media"),
        GraphQLForeignKey("copy", "home.BlogPage"),
        GraphQLPage("author"),
    ]
class BookPage(Page):
    """A page for an individual book."""
    book_title = models.CharField(
        max_length=200,
        help_text="""The exact title of the book. It will 
    be saved in this format in the database to display on related pages.""")
    author = models.ForeignKey(
        'wagtailcore.Page',
        on_delete=models.SET_NULL,
        blank=True,
        null=True,
        related_name='+',
        help_text=
        """Choose one of your Author Pages to assign a pen name to this book. 
        If you don't see the pen name you want, you might need to set up a new AuthorPage 
        for that pen name!""")
    series = models.ForeignKey(
        'wagtailcore.Page',
        on_delete=models.SET_NULL,
        blank=True,
        null=True,
        related_name='+',
        help_text=
        """Choose a series page to assign this book to a Series. If you don't see
        the series you want, you might need to set up a new Series Page.""")
    release_date = models.DateField(blank=True,
                                    null=True,
                                    help_text="""The book release date. This
    field is optional.""")
    genre = models.ManyToManyField('books.Genre',
                                   blank=True,
                                   help_text="""Optional: Choose one or more
    genres for this book. If you don't see the genre you need, click "Add Genre to List" from the menu
    on the left and add in your genre.""")
    teaser = models.TextField(blank=True,
                              null=True,
                              help_text="""A brief teaser description of your 
    book. This is the paragraph that will show up on the "all books" page.""")
    description = RichTextField(blank=True,
                                null=True,
                                help_text="""A description of your book.""")
    content_warnings = models.ManyToManyField('books.ContentWarning',
                                              blank=True,
                                              help_text="""(Optional):
    Choose one or more content warnings for this book. If you don't see the warning you want listed here,
    click "Add Content Warnings on the menu on the left and add in your warnings."""
                                              )
    cover_image = models.ForeignKey('wagtailimages.Image',
                                    on_delete=models.SET_NULL,
                                    related_name='+',
                                    null=True,
                                    blank=True)
    other_text = RichTextField(blank=True,
                               null=True,
                               help_text="""This is an optional field. Text
    added and formatted here will appear close to the bottom of the book page, right above the section
    for content warnings.""")
    sort_order = models.IntegerField(
        null=True,
        blank=True,
        help_text="""This field is optional, but if filled in,
    it lets you pick where this book shows up in the "all books" section of your website. The higher the number, the 
    closer to the top of the page the book will be.""")
    # Add content panels
    content_panels = Page.content_panels + [
        FieldPanel('book_title', classname="full"),
        PageChooserPanel('author', 'books.AuthorPage'),
        FieldPanel('release_date'),
        FieldPanel('genre'),
        FieldPanel('description', classname="full"),
        ImageChooserPanel('cover_image'),
        FieldPanel('sort_order'),
        FieldPanel('content_warnings'),
        InlinePanel('buy_links', label="Buy Links"),
        InlinePanel('book_reviews', label="Book Reviews"),
    ]
    # Parent page / subpage type rules
    parent_page_types = ['books.BooksIndexPage']
Exemplo n.º 30
0
class VideoIndexPage(CoderedArticleIndexPage):
    class Meta:
        verbose_name = "Video Landing Page"

    hits = models.IntegerField(default=0, editable=False)
    body = None

    def add_hits(self):
        self.hits += 1
        self._meta.get_field(
            "index_order_by").choices = self.index_order_by_choices
        self.save()
        return ""

    search_fields = []
    # Panel
    # Friend panels
    promote_panels = [
        MultiFieldPanel(
            [
                FieldPanel("slug"),
                FieldPanel("seo_title"),
                FieldPanel("search_description"),
                ImageChooserPanel("og_image"),
            ],
            _("Page Meta Data"),
        ),
    ]

    # Override to not contain template form
    layout_panels = []

    # Override to become empty
    body_content_panels = []

    # Override without content walls
    settings_panels = Page.settings_panels

    # Override with additional hits attribute
    content_panels = Page.content_panels + [
        MultiFieldPanel(
            [
                ReadOnlyPanel("hits", heading="Hits"),
            ],
            _("Publication Info"),
        ),
    ]

    # Override to specify custom index ordering choice/default.
    index_query_pagemodel = "video.VideoPage"

    template = "video/video_index_page.html"

    @cached_classmethod
    def get_edit_handler(cls):  # noqa
        """
        Override to "lazy load" the panels overriden by subclasses.
        """
        panels = [
            ObjectList(
                cls.content_panels + cls.body_content_panels +
                cls.bottom_content_panels,
                heading=_("Content"),
            ),
            ObjectList(cls.classify_panels, heading=_("Classify")),
            ObjectList(cls.promote_panels, heading=_("SEO"), classname="seo"),
            ObjectList(cls.settings_panels,
                       heading=_("Settings"),
                       classname="settings"),
        ]

        if cls.integration_panels:
            panels.append(
                ObjectList(
                    cls.integration_panels,
                    heading="Integrations",
                    classname="integrations",
                ))

        return TabbedInterface(panels).bind_to(model=cls)

    # Only allow VideoPages beneath this page.
    parent_page_types = ["home.HomePage"]