class CampaignIndexPage(IndexPage):
    """
    The campaign index is specifically for campaign-related pages
    """

    subpage_types = [
        'BanneredCampaignPage', 'CampaignPage', 'DearInternetPage',
        'OpportunityPage', 'YoutubeRegretsPage', 'YoutubeRegretsReporterPage',
        'PublicationPage', 'ArticlePage'
    ]

    translatable_fields = [
        # Promote tab fields
        SynchronizedField('slug'),
        TranslatableField('seo_title'),
        SynchronizedField('show_in_menus'),
        TranslatableField('search_description'),
        SynchronizedField('search_image'),
        # Content tab fields from IndexPage
        TranslatableField('title'),
        TranslatableField('intro'),
        TranslatableField('header'),
        SynchronizedField('page_size'),
    ]

    template = 'wagtailpages/index_page.html'

    def get_context(self, request):
        # bootstrap the render context
        context = super().get_context(request)
        entries = self.get_all_entries().not_type(PublicationPage)
        context['entries'] = entries[0:self.page_size]
        # Which pagetype to exclude during the "load more" ajax request: PublicationPage
        context['exclude_pagetype'] = 'publicationpage'
        return context
Ejemplo n.º 2
0
class TestPage(Page):
    test_charfield = models.CharField(__("char field"),
                                      max_length=255,
                                      blank=True)
    test_textfield = models.TextField(blank=True)
    test_emailfield = models.EmailField(blank=True)
    test_slugfield = models.SlugField(blank=True)
    test_urlfield = models.URLField(blank=True)

    test_richtextfield = RichTextField(blank=True)
    test_streamfield = StreamField(TestStreamBlock, blank=True)

    test_snippet = models.ForeignKey(TestSnippet,
                                     null=True,
                                     blank=True,
                                     on_delete=models.SET_NULL)

    test_customfield = TestCustomField(blank=True)

    test_synchronized_charfield = models.CharField(max_length=255, blank=True)
    test_synchronized_textfield = models.TextField(blank=True)
    test_synchronized_emailfield = models.EmailField(blank=True)
    test_synchronized_slugfield = models.SlugField(blank=True)
    test_synchronized_urlfield = models.URLField(blank=True)

    test_synchronized_richtextfield = RichTextField(blank=True)
    test_synchronized_streamfield = StreamField(TestStreamBlock, blank=True)

    test_synchronized_snippet = models.ForeignKey(TestSnippet,
                                                  null=True,
                                                  blank=True,
                                                  on_delete=models.SET_NULL,
                                                  related_name="+")

    test_synchronized_customfield = TestCustomField(blank=True)

    translatable_fields = [
        TranslatableField("test_charfield"),
        TranslatableField("test_textfield"),
        TranslatableField("test_emailfield"),
        TranslatableField("test_slugfield"),
        TranslatableField("test_urlfield"),
        TranslatableField("test_richtextfield"),
        TranslatableField("test_streamfield"),
        TranslatableField("test_snippet"),
        TranslatableField("test_childobjects"),
        TranslatableField("test_customfield"),
        SynchronizedField("test_synchronized_charfield"),
        SynchronizedField("test_synchronized_textfield"),
        SynchronizedField("test_synchronized_emailfield"),
        SynchronizedField("test_synchronized_slugfield"),
        SynchronizedField("test_synchronized_urlfield"),
        SynchronizedField("test_synchronized_richtextfield"),
        SynchronizedField("test_synchronized_streamfield"),
        SynchronizedField("test_synchronized_snippet"),
        SynchronizedField("test_synchronized_childobjects"),
        SynchronizedField("test_synchronized_customfield"),
    ]
Ejemplo n.º 3
0
class CampaignPage(DonationPage):
    template = 'pages/core/campaign_page.html'
    parent_page_types = ['core.LandingPage', 'core.CampaignPage']
    subpage_types = ['core.CampaignPage']

    submit_to_pontoon_on_publish = False

    hero_image = models.ForeignKey(
        'wagtailimages.Image',
        models.PROTECT,
        related_name='+',
    )
    lead_text = models.CharField(max_length=800)
    intro = RichTextField()

    content_panels = Page.content_panels + [
        FieldPanel('project'),
        ImageChooserPanel('hero_image'),
        FieldPanel('lead_text'),
        FieldPanel('intro'),
        InlinePanel('donation_amounts', label='Donation amount overrides'),
    ]

    translatable_fields = [
        TranslatableField('title'),
        TranslatableField('seo_title'),
        TranslatableField('search_description'),
        SynchronizedField('project'),
        SynchronizedField('campaign_id'),
        SynchronizedField('hero_image'),
        TranslatableField('lead_text'),
        TranslatableField('intro'),
    ]

    @classmethod
    def amount_stream_to_list(cls, stream):
        return [Decimal(child.value) for child in stream]

    @classmethod
    def get_presets(cls, override):
        return {
            'single': cls.amount_stream_to_list(override.single_options),
            'monthly': cls.amount_stream_to_list(override.monthly_options),
        }

    @cached_property
    def currencies(self):
        currencies = super().currencies
        # Apply overrides for preset options
        for override in self.donation_amounts.all():
            currencies[override.currency]['presets'] = self.get_presets(
                override)
        return currencies
Ejemplo n.º 4
0
class CampaignPage(MiniSiteNameSpace):
    """
    these pages come with sign-a-petition CTAs
    """
    cta = models.ForeignKey(
        'Petition',
        related_name='page',
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        help_text='Choose existing or create new sign-up form')

    def get_donation_modal_json(self):
        modals = self.donation_modals.all()
        # This is where we can do server-side A/B testing,
        # by either sending all modals down the pipe, or
        # selectively only sending a single one based on
        # things like geolocation, time of day, etc.
        modals_json = [m.to_simple_dict() for m in modals]
        return json.dumps(modals_json)

    content_panels = Page.content_panels + [
        FieldPanel('header'),
        SnippetChooserPanel('cta'),
        InlinePanel('donation_modals', label='Donation Modal', max_num=4),
        StreamFieldPanel('body'),
    ]

    translatable_fields = [
        # Promote tab fields
        SynchronizedField('slug'),
        TranslatableField('seo_title'),
        SynchronizedField('show_in_menus'),
        TranslatableField('search_description'),
        SynchronizedField('search_image'),
        # Content tab fields
        TranslatableField('cta'),
        TranslatableField('title'),
        TranslatableField('header'),
        SynchronizedField('narrowed_page_content'),
        SynchronizedField('zen_nav'),
        TranslatableField('body'),
        TranslatableField('donation_modals'),
    ]

    subpage_types = [
        'CampaignPage', 'RedirectingPage', 'PublicationPage', 'ArticlePage'
    ]
Ejemplo n.º 5
0
class OpportunityPage(MiniSiteNameSpace):

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

    subpage_types = [
        'OpportunityPage', 'RedirectingPage', 'PublicationPage', 'ArticlePage'
    ]

    translatable_fields = [
        # Promote tab fields
        TranslatableField('seo_title'),
        TranslatableField('search_description'),
        SynchronizedField('search_image'),
        # Content tab fields
        TranslatableField('title'),
        TranslatableField('header'),
        TranslatableField('body'),
    ]

    class Meta:
        verbose_name = "Default Page"
        verbose_name_plural = "Default pages"
Ejemplo n.º 6
0
class LandingPage(TranslatablePageRoutingMixin, DonationPage):
    template = 'pages/core/landing_page.html'

    # Only allow creating landing pages at the root level
    parent_page_types = ['wagtailcore.Page']

    subpage_types = [
        'core.CampaignPage',
        'core.ContentPage',
        'core.ContributorSupportPage',
    ]

    featured_image = models.ForeignKey(
        'wagtailimages.Image',
        models.PROTECT,
        related_name='+',
    )
    intro = RichTextField()

    content_panels = Page.content_panels + [
        FieldPanel('project'),
        ImageChooserPanel('featured_image'),
        FieldPanel('intro'),
    ]

    translatable_fields = [
        TranslatableField('title'),
        TranslatableField('seo_title'),
        TranslatableField('search_description'),
        SynchronizedField('project'),
        SynchronizedField('campaign_id'),
        SynchronizedField('featured_image'),
        TranslatableField('intro'),
    ]

    def save(self, *args, **kwargs):
        # This slug isn't used anywhere but it does need to be unique for each language
        language_code = self.locale.language.code.lower()
        if language_code == 'en-us':
            # Needs to be something nice as translators will see it
            self.slug = 'mozilla-donate'
        else:
            self.slug = 'mozilla-donate-' + language_code

        super().save(*args, **kwargs)
class YoutubeRegretsReporterPage(FoundationMetadataPageMixin, Page):
    headline = models.CharField(
        max_length=500,
        help_text='Page headline',
        blank=True,
    )

    intro_text = StreamField([
        ('text', blocks.CharBlock()),
    ])

    intro_images = StreamField([
        ('image', customblocks.ImageBlock()),
    ])

    content_panels = Page.content_panels + [
        FieldPanel('headline'),
        StreamFieldPanel('intro_text'),
        StreamFieldPanel('intro_images'),
    ]

    translatable_fields = [
        # Promote tab fields
        SynchronizedField('slug'),
        TranslatableField('seo_title'),
        SynchronizedField('show_in_menus'),
        TranslatableField('search_description'),
        SynchronizedField('search_image'),
        # Content tab fields
        TranslatableField('title'),
        TranslatableField('headline'),
        TranslatableField('intro_text'),
        TranslatableField('intro_images'),
    ]

    zen_nav = True

    def get_context(self, request):
        context = super().get_context(request)
        return set_main_site_nav_information(self, context, 'Homepage')

    template = 'wagtailpages/pages/youtube_regrets_reporter_page.html'
    def test(self):
        translatable_fields = get_translatable_fields(
            TestOverrideTranslatableFieldsPage)

        self.assertEqual(
            translatable_fields,
            [
                TranslatableField("title"),
                TranslatableField("slug"),
                TranslatableField("seo_title"),
                SynchronizedField("show_in_menus"),
                TranslatableField("search_description"),
                SynchronizedField("test_charfield"),  # Overriden!
                SynchronizedField("test_charfield_with_choices"),
                TranslatableField("test_textfield"),
                TranslatableField("test_emailfield"),  # Overriden!
                TranslatableField("test_slugfield"),
                SynchronizedField("test_urlfield"),
                TranslatableField("test_richtextfield"),
                TranslatableField("test_streamfield"),
                TranslatableField("test_snippet"),
                SynchronizedField("test_nontranslatablesnippet"),
                TranslatableField("test_customfield"),
                TranslatableField("test_translatable_childobjects"),
                SynchronizedField("test_nontranslatable_childobjects"),
            ],
        )
class YoutubeRegrets2021Page(FoundationMetadataPageMixin, Page):

    template = 'wagtailpages/pages/youtube-regrets-2021/youtube_regrets_2021.html'
    max_count = 1
    zen_nav = True

    translatable_fields = [
        # Promote tab fields
        SynchronizedField('slug'),
        TranslatableField('seo_title'),
        SynchronizedField('show_in_menus'),
        TranslatableField('search_description'),
        SynchronizedField('search_image'),
        # Content tab fields
        TranslatableField('title'),
    ]

    def get_context(self, request):
        context = super().get_context(request)
        return set_main_site_nav_information(self, context, 'Homepage')

    class Meta:
        verbose_name = "YouTube Regrets 2021 Page"
        verbose_name_plural = "YouTube Regrets 2021 Pages"
Ejemplo n.º 10
0
class ContributorSupportPage(Page):
    template = 'pages/core/contributor_support_page.html'
    parent_page_types = ['core.LandingPage']

    override_translatable_fields = [
        SynchronizedField('slug'),
    ]

    # This page does not have subpages

    def get_context(self, request):
        ctx = super().get_context(request)
        ctx.update({
            'orgid': settings.SALESFORCE_ORGID,
            'record_type_id': settings.SALESFORCE_CASE_RECORD_TYPE_ID,
        })
        return ctx
Ejemplo n.º 11
0
class ContentPage(Page):
    template = 'pages/core/content_page.html'
    parent_page_types = ['core.LandingPage']
    subpage_types = ['core.ContentPage']

    call_to_action_text = models.CharField(max_length=255, blank=True)
    call_to_action_url = models.URLField(blank=True)

    body = StreamField(ContentBlock)

    content_panels = Page.content_panels + [
        FieldPanel('call_to_action_text'),
        FieldPanel('call_to_action_url'),
        StreamFieldPanel('body'),
    ]

    override_translatable_fields = [
        SynchronizedField('slug'),
    ]
Ejemplo n.º 12
0
class ContentPage(TranslatablePageMixin, Page):
    template = 'pages/core/content_page.html'
    parent_page_types = ['core.LandingPage']
    subpage_types = ['core.ContentPage']

    call_to_action_text = models.CharField(max_length=255, blank=True)
    call_to_action_url = models.URLField(blank=True)

    body = StreamField(ContentBlock)

    content_panels = Page.content_panels + [
        FieldPanel('call_to_action_text'),
        FieldPanel('call_to_action_url'),
        StreamFieldPanel('body'),
    ]

    translatable_fields = [
        TranslatableField('title'),
        TranslatableField('seo_title'),
        TranslatableField('search_description'),
        TranslatableField('call_to_action_text'),
        SynchronizedField('call_to_action_url'),
        TranslatableField('body'),
    ]
    def test(self):
        translatable_fields = get_translatable_fields(
            TestGenerateTranslatableFieldsPage)

        self.assertEqual(translatable_fields, [
            TranslatableField('title'),
            TranslatableField('slug'),
            TranslatableField('seo_title'),
            SynchronizedField('show_in_menus'),
            TranslatableField('search_description'),
            TranslatableField('test_charfield'),
            SynchronizedField('test_charfield_with_choices'),
            TranslatableField('test_textfield'),
            SynchronizedField('test_emailfield'),
            TranslatableField('test_slugfield'),
            SynchronizedField('test_urlfield'),
            TranslatableField('test_richtextfield'),
            TranslatableField('test_streamfield'),
            TranslatableField('test_snippet'),
            SynchronizedField('test_nontranslatablesnippet'),
            TranslatableField('test_customfield'),
            TranslatableField('test_translatable_childobjects'),
            SynchronizedField('test_nontranslatable_childobjects'),
        ])
Ejemplo n.º 14
0
class DonationPage(Page):

    PROJECT_MOZILLAFOUNDATION = 'mozillafoundation'
    PROJECT_THUNDERBIRD = 'thunderbird'
    PROJECT_CHOICES = (
        (PROJECT_MOZILLAFOUNDATION, 'Mozilla Foundation'),
        (PROJECT_THUNDERBIRD, 'Thunderbird'),
    )

    project = models.CharField(
        max_length=25,
        choices=PROJECT_CHOICES,
        default=PROJECT_MOZILLAFOUNDATION,
        help_text=
        'The project that donations from this campaign should be associated with'
    )
    campaign_id = models.CharField(
        max_length=255,
        blank=True,
        help_text='Used for analytics and reporting')

    settings_panels = Page.settings_panels + [
        FieldPanel('campaign_id'),
    ]

    override_translatable_fields = [
        SynchronizedField('slug'),
        SynchronizedField('campaign_id'),
    ]

    @cached_property
    def currencies(self):
        currencies = deepcopy(constants.CURRENCIES)
        # Re-order currency `single` and `monthly` amounts
        for currency in currencies:
            currencies[currency]['presets']['single'].sort()
            currencies[currency]['presets']['monthly'].sort()
        return currencies

    def get_initial_currency(self, request):
        # Query argument takes first preference
        if request.GET.get('currency') in constants.CURRENCIES:
            return request.GET['currency']

        # Otherwise use the language code determined by Django
        return get_default_currency(getattr(request, 'LANGUAGE_CODE', ''))

    def serve(self, request, *args, **kwargs):
        response = super().serve(request, *args, **kwargs)
        if request.GET.get('subscribed') == '1':
            # Set a cookie that expires at the end of the session
            response.set_cookie('subscribed', '1', httponly=True)
        return response

    def get_initial_frequency(self, request):
        frequency = request.GET.get('frequency', '')
        return frequency if frequency in dict(
            constants.FREQUENCY_CHOICES) else constants.FREQUENCY_SINGLE

    def get_initial_currency_info(self, request, initial_currency,
                                  initial_frequency):
        initial_currency_info = self.currencies[initial_currency]

        # Check if presets have been specified in a query arg
        custom_presets = request.GET.get('presets', '').split(',')
        try:
            min_amount = initial_currency_info.get('minAmount', 0)
            custom_presets = [
                amount for amount in [
                    Decimal(value).quantize(Decimal('0.01'))
                    if float(value) >= min_amount else None
                    for value in custom_presets
                ] if amount
            ]
        except (InvalidOperation, ValueError):
            return initial_currency_info

        if not custom_presets:
            return initial_currency_info

        if len(custom_presets) < 4:
            return initial_currency_info

        sorting = request.GET.get('sort', False)

        if sorting == 'reverse':
            custom_presets.sort(reverse=True)

        initial_currency_info['presets'][
            initial_frequency] = custom_presets[:4]
        return initial_currency_info

    def default_initial_amount(self, initial_currency_info, initial_frequency):
        """
        The default donation amount is the second lowest amount in the list of amounts.
        """
        return sorted(initial_currency_info['presets'][initial_frequency])[1]

    def get_initial_amount(self, request, initial_currency_info,
                           initial_frequency):
        """
        When called with ?amount=..., that value will be preselected if:

        1. it's a real number, and
        2. that number can be found in the current list of possibles values.

        If not, the default initial amount is used
        """
        amount = request.GET.get('amount', False)

        if amount is False or 'e' in amount:
            return self.default_initial_amount(initial_currency_info,
                                               initial_frequency)

        try:
            value = Decimal(amount).quantize(Decimal('0.01'))
        except InvalidOperation:
            return self.default_initial_amount(initial_currency_info,
                                               initial_frequency)

        if value in initial_currency_info['presets'][initial_frequency]:
            return value

        return self.default_initial_amount(initial_currency_info,
                                           initial_frequency)

    def get_initial_values(self, request):
        frequency = self.get_initial_frequency(request)
        currency = self.get_initial_currency(request)
        currency_info = self.get_initial_currency_info(request, currency,
                                                       frequency)
        amount = self.get_initial_amount(request, currency_info, frequency)

        return {
            "frequency": frequency,
            "currency": currency,
            "currency_info": currency_info,
            "amount": amount,
        }

    def get_context(self, request):
        ctx = super().get_context(request)
        values = self.get_initial_values(request)
        ctx.update({
            'currencies':
            self.currencies,
            'initial_currency_info':
            values['currency_info'],
            'initial_frequency':
            values['frequency'],
            'initial_amount':
            values['amount'],
            'braintree_params':
            settings.BRAINTREE_PARAMS,
            'braintree_form':
            BraintreePaypalPaymentForm(
                initial={
                    'landing_url': request.build_absolute_uri(),
                    'project': self.project,
                    'campaign_id': self.campaign_id,
                }),
            'currency_form':
            CurrencyForm(initial={'currency': values['currency']}),
            'recaptcha_site_key':
            settings.RECAPTCHA_SITE_KEY
            if settings.RECAPTCHA_ENABLED else None,
        })
        return ctx

    class Meta:
        abstract = True
Ejemplo n.º 15
0
class Petition(TranslatableMixin, CTA):
    campaign_id = models.CharField(
        max_length=20,
        help_text='Which campaign identifier should this petition be tied to?',
        null=True,
        blank=True,
    )

    requires_country_code = models.BooleanField(
        default=False,
        help_text='Will this petition require users to specify their country?',
    )

    requires_postal_code = models.BooleanField(
        default=False,
        help_text=
        'Will this petition require users to specify their postal code?',
    )

    COMMENT_CHOICES = (
        ('none', 'No comments'),
        ('optional', 'Optional comments'),
        ('required', 'Required comments'),
    )

    comment_requirements = models.CharField(
        choices=COMMENT_CHOICES,
        default='none',
        help_text='What is the comments policy for this petition?',
        max_length=8,
    )

    checkbox_1 = models.CharField(
        editable=False,
        max_length=1024,
        help_text='label for the first checkbox option (may contain HTML)',
        blank=True,
    )

    checkbox_2 = models.CharField(
        editable=False,
        max_length=1024,
        help_text='label for the second checkbox option (may contain HTML)',
        blank=True,
    )

    share_link = models.URLField(
        max_length=1024,
        help_text='Link that will be put in share button',
        blank=True,
        editable=False,
    )

    share_link_text = models.CharField(
        max_length=20,
        help_text='Text content of the share button',
        default='Share this',
        blank=True,
        editable=False,
    )

    share_twitter = models.CharField(
        max_length=20,
        help_text=
        'Share Progress id for twitter button, including the sp_... prefix',
        blank=True,
    )

    share_facebook = models.CharField(
        max_length=20,
        help_text=
        'Share Progress id for facebook button, including the sp_... prefix',
        blank=True,
    )

    share_email = models.CharField(
        max_length=20,
        help_text=
        'Share Progress id for email button, including the sp_... prefix',
        blank=True,
    )

    thank_you = models.CharField(
        max_length=140,
        help_text='Message to show after thanking people for signing',
        default='Thank you for signing too!',
    )

    translatable_fields = [
        # This models fields
        SynchronizedField('requires_country_code'),
        SynchronizedField('requires_postal_code'),
        TranslatableField('comment_requirements'),
        TranslatableField('checkbox_1'),
        TranslatableField('checkbox_2'),
        SynchronizedField('share_twitter'),
        SynchronizedField('share_facebook'),
        SynchronizedField('share_email'),
        TranslatableField('thank_you'),
        # Fields from the CTA model
        TranslatableField('header'),
        TranslatableField('description'),
    ]

    class Meta(TranslatableMixin.Meta):
        verbose_name = 'petition snippet'
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']

    toc_thumbnail_image = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='toc_thumbnail_image',
        verbose_name='Table of Content Thumbnail',
        help_text=
        'Thumbnail image to show on table of content. Use square image of 320×320 pixels or larger.',
    )

    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,
    )
    intro_notes = RichTextField(blank=True,
                                features=['link', 'bold', 'italic', 'h4'])
    notes = RichTextField(
        blank=True, features=['link', 'bold', 'italic', 'h4', 'ol', 'ul'])
    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('toc_thumbnail_image'),
                ImageChooserPanel('hero_image'),
                DocumentChooserPanel('publication_file'),
                InlinePanel('authors', label='Author'),
                FieldPanel('additional_author_copy'),
            ],
            heading='Hero',
        ),
        FieldPanel('intro_notes'),
        FieldPanel('contents_title'),
        FieldPanel('notes'),
    ]

    translatable_fields = [
        # Promote tab fields
        SynchronizedField('slug'),
        TranslatableField('seo_title'),
        SynchronizedField('show_in_menus'),
        TranslatableField('search_description'),
        SynchronizedField('search_image'),
        # Content tab fields
        TranslatableField("title"),
        TranslatableField("subtitle"),
        TranslatableField('secondary_subtitle'),
        SynchronizedField('toc_thumbnail_image'),
        SynchronizedField('hero_image'),
        SynchronizedField('publication_date'),
        SynchronizedField('publication_file'),
        TranslatableField('additional_author_copy'),
        TranslatableField('intro_notes'),
        TranslatableField('contents_title'),
        TranslatableField('notes'),
    ]

    @property
    def is_publication_page(self):
        """
        Returning true to let publication_hero.html to show breadcrumbs
        """
        return True

    @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.
        """
        next_page = self.get_parent()
        if self.is_chapter_page:
            sibling = self.get_siblings().filter(path__gt=self.path,
                                                 live=True).first()
            if sibling:
                # If there is no more chapters. Return the parent page.
                next_page = sibling
        return next_page

    @property
    def prev_page(self):
        """
        Only applies to Chapter Publication (sub-Publication Pages).
        Returns a Page object or None.
        """
        prev_page = self.get_parent()
        if self.is_chapter_page:
            sibling = self.get_siblings().filter(path__lt=self.path,
                                                 live=True).reverse().first()
            if sibling:
                # If there is no more chapters. Return the parent page.
                prev_page = sibling
        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')
Ejemplo n.º 17
0
class BanneredCampaignPage(PrimaryPage):
    """
    title, header, intro, and body are inherited from PrimaryPage
    """

    # Note that this is a different related_name, as the `page`
    # name is already taken as back-referenced to CampaignPage.
    cta = models.ForeignKey(
        'Petition',
        related_name='bcpage',
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        help_text='Choose an existing, or create a new, pettition form')

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

    tags = ClusterTaggableManager(through=BanneredCampaignTag, blank=True)

    panel_count = len(PrimaryPage.content_panels)
    n = panel_count - 1

    content_panels = PrimaryPage.content_panels[:n] + [
        SnippetChooserPanel('cta'),
        SnippetChooserPanel('signup'),
    ] + PrimaryPage.content_panels[n:]

    promote_panels = FoundationMetadataPageMixin.promote_panels + [
        FieldPanel('tags'),
    ]

    translatable_fields = [
        # Promote tab fields
        SynchronizedField('slug'),
        TranslatableField('seo_title'),
        SynchronizedField('show_in_menus'),
        TranslatableField('search_description'),
        SynchronizedField('search_image'),
        # Content tab fields
        TranslatableField('header'),
        TranslatableField('intro'),
        TranslatableField('body'),
        TranslatableField("title"),
        SynchronizedField("banner"),
        SynchronizedField("narrowed_page_content"),
        SynchronizedField("zen_nav"),
        TranslatableField("cta"),
        TranslatableField("signup"),
    ]

    subpage_types = [
        'BanneredCampaignPage', 'RedirectingPage', 'PublicationPage',
        'OpportunityPage', 'ArticlePage'
    ]

    show_in_menus_default = True

    def get_context(self, request):
        context = super().get_context(request)
        context['related_posts'] = get_content_related_by_tag(self)
        return get_page_tree_information(self, context)

    class Meta:
        verbose_name = "Banner Page"
        verbose_name_plural = "Banner pages"
Ejemplo n.º 18
0
class ArticlePage(FoundationMetadataPageMixin, Page):

    """

    Articles can belong to any page in the Wagtail Tree.
    An ArticlePage can have no children
    If not a child of a Publication Page, page nav at bottom of page
    and breadcrumbs will not render.
    """
    subpage_types = []
    body = StreamField(article_fields)

    toc_thumbnail_image = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        verbose_name='Table of Content Thumbnail',
        help_text='Thumbnail image to show on table of content. Use square image of 320×320 pixels or larger.',
    )

    hero_image = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        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)

    article_file = models.ForeignKey(
        'wagtaildocs.Document',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
    )

    show_side_share_buttons = models.BooleanField(
        default=True,
        help_text="Show social share buttons on the side"
    )

    content_panels = [
        FieldPanel(
            "title",
            classname="full title",
            widget=TitleWidget(attrs={"class": "max-length-warning", "data-max-length": 60})
        ),
        MultiFieldPanel([
            InlinePanel("authors", label="Author", min_num=0)
        ], heading="Author(s)"),
        MultiFieldPanel([
            ImageChooserPanel("toc_thumbnail_image"),
        ], heading="Table of Content Thumbnail"),
        MultiFieldPanel([
            ImageChooserPanel("hero_image"),
            FieldPanel('subtitle'),
            FieldPanel('secondary_subtitle'),
            FieldPanel('publication_date'),
            DocumentChooserPanel('article_file'),
        ], heading="Hero"),
        FieldPanel('show_side_share_buttons'),
        StreamFieldPanel('body'),
        InlinePanel("footnotes", label="Footnotes"),
    ]

    translatable_fields = [
        # Promote tab fields
        SynchronizedField('slug'),
        TranslatableField('seo_title'),
        SynchronizedField('show_in_menus'),
        TranslatableField('search_description'),
        SynchronizedField('search_image'),
        # Content tab fields
        TranslatableField('title'),
        SynchronizedField('toc_thumbnail_image'),
        SynchronizedField('hero_image'),
        TranslatableField('subtitle'),
        SynchronizedField('article_file'),
        TranslatableField('body'),
        TranslatableField('footnotes'),
    ]

    @property
    def is_publication_article(self):
        parent = self.get_parent().specific
        return parent.__class__ is PublicationPage

    @property
    def next_page(self):
        """
        Get the next page for a publication. Details below:

        Check the parent page type. If the parent page type is a "Chapter Page",
        then look for siblings of `this` page. If no next sibling can be found
        look for the parent page next sibling. And if that cannot be found,
        return the Chapter Page's parent (Publication Page).
        Otherwise if the parent page is a Publication page: look for the next sibling,
        if there is no next sibling page, return this pages' parent.
        """

        parent = self.get_parent().specific
        next_page = self.get_siblings().filter(path__gt=self.path, live=True).first()
        if parent.is_chapter_page:
            # if there is no next page look for the next chapter
            if not next_page:
                next_page = parent.get_siblings().filter(path__gt=self.path, live=True).first()
                # if there is no next chapter return to the parent.get_parent()
                if not next_page:
                    next_page = parent.get_parent()
        else:
            # Parent is a PublicationPage, not a chapter page
            # if there is no next page, return the parent
            if not next_page:
                next_page = parent

        return next_page

    @property
    def prev_page(self):
        """
        Get the previous page for a publication. Details below:

        Check the parent page type. If the parent page type is a "Chapter Page",
        then look for siblings of `this` page. If no previous sibling can be found
        look for the parent page previous sibling. And if that cannot be found,
        return the Chapter Page's parent (Publication Page).
        Otherwise if the parent page is a Publication page: look for the previous sibling,
        if there is no previous sibling page, return this pages' parent.
        """

        parent = self.get_parent().specific
        prev_page = self.get_siblings().filter(path__lt=self.path, live=True).reverse().first()
        if parent.is_chapter_page:
            # look for the previous page in this chapter
            # if there is no previous page look for the previous chapter
            if not prev_page:
                prev_page = parent.get_siblings().filter(path__lt=self.path, live=True).reverse().first()
                # if there is no previous chapter return to the parent.get_parent()
                if not prev_page:
                    prev_page = parent.get_parent()
        else:
            # Parent is a PublicationPage, not a chapter page
            # look for the previous page in this publication
            # if there is no previous page, return the parent
            if not prev_page:
                prev_page = parent

        return prev_page

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

    @property
    def zen_nav(self):
        return True

    def get_context(self, request, *args, **kwargs):
        context = super().get_context(request, *args, **kwargs)
        # Add get_titles to the page context. This is in get_context() because
        # we need access to the `request` object
        # menu_items is required for zen_nav in the templates
        context['get_titles'] = get_plaintext_titles(request, self.body, "content")
        return set_main_site_nav_information(self, context, 'Homepage')
Ejemplo n.º 19
0
class YoutubeRegretsPage(FoundationMetadataPageMixin, Page):
    headline = models.CharField(
        max_length=500,
        help_text='Page headline',
        blank=True,
    )

    intro_text = StreamField([
        ('text', blocks.CharBlock()),
    ])

    intro_images = StreamField([
        ('image', customblocks.ImageBlock()),
    ])

    faq = StreamField(
        [('paragraph',
          blocks.RichTextBlock(features=[
              'bold',
              'italic',
              'h2',
              'h3',
              'h4',
              'h5',
              'ol',
              'ul',
              'link',
              'hr',
          ]))],
        blank=True,
    )

    regret_stories = StreamField([
        ('regret_story', customblocks.YoutubeRegretBlock()),
    ])

    content_panels = Page.content_panels + [
        FieldPanel('headline'),
        StreamFieldPanel('intro_text'),
        StreamFieldPanel('intro_images'),
        StreamFieldPanel('faq'),
        StreamFieldPanel('regret_stories'),
    ]

    translatable_fields = [
        # Promote tab fields
        SynchronizedField('slug'),
        TranslatableField('seo_title'),
        SynchronizedField('show_in_menus'),
        TranslatableField('search_description'),
        SynchronizedField('search_image'),
        # Content tab fields
        TranslatableField('title'),
        TranslatableField('headline'),
        TranslatableField('intro_text'),
        TranslatableField('intro_images'),
        TranslatableField('faq'),
        TranslatableField('regret_stories'),
    ]

    zen_nav = True

    def get_context(self, request):
        context = super().get_context(request)
        return set_main_site_nav_information(self, context, 'Homepage')

    template = 'wagtailpages/pages/youtube_regrets_page.html'
Ejemplo n.º 20
0
class DearInternetPage(FoundationMetadataPageMixin, Page):
    intro_texts = StreamField([('intro_text',
                                blocks.RichTextBlock(features=[
                                    'bold',
                                    'italic',
                                    'link',
                                ]))], )

    letters_section_heading = models.CharField(
        max_length=300,
        default='Stories from around the world',
    )

    letters = StreamField([
        ('letter', customblocks.DearInternetLetterBlock()),
    ])

    cta = models.CharField(max_length=500, )

    cta_button_text = models.CharField(max_length=100, )

    cta_button_link = models.URLField()

    content_panels = Page.content_panels + [
        StreamFieldPanel('intro_texts'),
        FieldPanel('letters_section_heading'),
        StreamFieldPanel('letters'),
        MultiFieldPanel(
            [
                FieldPanel('cta'),
                FieldPanel('cta_button_text'),
                FieldPanel('cta_button_link'),
            ],
            heading='CTA',
        ),
    ]

    translatable_fields = [
        # Promote tab fields
        SynchronizedField('slug'),
        TranslatableField('seo_title'),
        SynchronizedField('show_in_menus'),
        TranslatableField('search_description'),
        SynchronizedField('search_image'),
        # Content tab fields
        TranslatableField('title'),
        TranslatableField('intro_texts'),
        TranslatableField('letters_section_heading'),
        TranslatableField('letters'),
        TranslatableField('cta'),
        TranslatableField('cta_button_text'),
        SynchronizedField('cta_button_link'),
    ]

    zen_nav = True

    def get_context(self, request):
        context = super().get_context(request)
        return set_main_site_nav_information(self, context, 'Homepage')

    template = 'wagtailpages/pages/dear_internet_page.html'
Ejemplo n.º 21
0
class MozfestHomepage(MozfestPrimaryPage):
    """
    MozFest Homepage

    'banner_video_type' determines what version of banner design the page should load
    """

    #  this tells the templates to load a hardcoded, pre-defined video in the banner background
    banner_video_type = "hardcoded"

    cta_button_label = models.CharField(
        max_length=250,
        blank=True,
        help_text='Label text for the CTA button in the primary nav bar',
    )

    cta_button_destination = models.CharField(
        max_length=2048,
        blank=True,
        help_text='The URL for the page that the CTA button in the primary nav bar should redirect to.'
                  'E.g., /proposals, https://example.com/external-link',
    )

    banner_heading = models.CharField(
        max_length=250,
        blank=True,
        help_text='A banner heading specific to the homepage'
    )

    banner_guide_text = models.CharField(
        max_length=1000,
        blank=True,
        help_text='A banner paragraph specific to the homepage'
    )

    banner_video_url = models.URLField(
        max_length=2048,
        blank=True,
        help_text='The video to play when users click "watch video"'
    )

    subpage_types = [
        'MozfestPrimaryPage',
        'MozfestHomepage',
    ]

    # Put everything above the body
    parent_panels = MozfestPrimaryPage.content_panels
    panel_count = len(parent_panels)
    n = panel_count - 1

    all_panels = parent_panels[:n] + [
        FieldPanel('cta_button_label'),
        FieldPanel('cta_button_destination'),
        FieldPanel('banner_heading'),
        FieldPanel('banner_guide_text'),
        FieldPanel('banner_video_url'),
    ] + parent_panels[n:]

    if banner_video_type == "hardcoded":
        # Hide all the panels that aren't relevant for the video banner version of the MozFest Homepage
        content_panels = [
            field for field in all_panels
            if field.field_name not in
            ['banner', 'header', 'intro', 'banner_guide_text', 'banner_video_url']
        ]
    else:
        content_panels = all_panels

    # Because we inherit from PrimaryPage, but the "use_wide_template" property does nothing
    # we should hide it and make sure we use the right template
    settings_panels = Page.settings_panels

    translatable_fields = [
        # Promote tab fields
        SynchronizedField('slug'),
        TranslatableField('seo_title'),
        SynchronizedField('show_in_menus'),
        TranslatableField('search_description'),
        SynchronizedField('search_image'),
        # Content tab fields
        TranslatableField('title'),
        TranslatableField('cta_button_label'),
        SynchronizedField('cta_button_destination'),
        TranslatableField('banner_heading'),
        TranslatableField('banner_guide_text'),
        SynchronizedField('banner_video_url'),
        TranslatableField('title'),
        TranslatableField('search_description'),
        TranslatableField('search_image'),
        TranslatableField('signup'),
        TranslatableField('body'),
        TranslatableField('footnotes'),
    ]

    def get_context(self, request):
        context = super().get_context(request)
        context['banner_video_type'] = self.specific.banner_video_type

        return context

    def get_template(self, request):
        return 'mozfest/mozfest_homepage.html'
Ejemplo n.º 22
0
class ModularPage(FoundationMetadataPageMixin, Page):
    """
    This base class offers universal component picking.
    Note: this is a legacy class, see
    https://github.com/mozilla/foundation.mozilla.org/issues/5071#issuecomment-675720719
    """

    header = models.CharField(
        max_length=250,
        blank=True
    )

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

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

    body = StreamField(base_fields)

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

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

    translatable_fields = [
        # Promote tab fields
        SynchronizedField('slug'),
        TranslatableField('seo_title'),
        SynchronizedField('show_in_menus'),
        TranslatableField('search_description'),
        SynchronizedField('search_image'),
        # Content tab fields
        TranslatableField('header'),
        SynchronizedField('narrowed_page_content'),
        SynchronizedField('zen_nav'),
    ]

    show_in_menus_default = True

    def get_context(self, request):
        context = super().get_context(request)
        return set_main_site_nav_information(self, context, 'Homepage')
Ejemplo n.º 23
0
class TestPage(Page):
    test_charfield = models.CharField(gettext_lazy("char field"),
                                      max_length=255,
                                      blank=True,
                                      null=True,
                                      default='')
    test_textfield = models.TextField(blank=True)
    test_emailfield = models.EmailField(blank=True)
    test_slugfield = models.SlugField(blank=True)
    test_urlfield = models.URLField(blank=True)

    test_richtextfield = RichTextField(blank=True)
    test_streamfield = StreamField(TestStreamBlock, blank=True)

    test_snippet = models.ForeignKey(TestSnippet,
                                     null=True,
                                     blank=True,
                                     on_delete=models.SET_NULL)

    test_customfield = TestCustomField(blank=True)

    test_synchronized_charfield = models.CharField(max_length=255, blank=True)
    test_synchronized_textfield = models.TextField(blank=True)
    test_synchronized_emailfield = models.EmailField(blank=True)
    test_synchronized_slugfield = models.SlugField(blank=True)
    test_synchronized_urlfield = models.URLField(blank=True)

    test_synchronized_richtextfield = RichTextField(blank=True)
    test_synchronized_streamfield = StreamField(TestStreamBlock, blank=True)

    test_synchronized_image = models.ForeignKey('wagtailimages.Image',
                                                null=True,
                                                blank=True,
                                                on_delete=models.SET_NULL,
                                                related_name="+")
    test_synchronized_document = models.ForeignKey('wagtaildocs.Document',
                                                   null=True,
                                                   blank=True,
                                                   on_delete=models.SET_NULL,
                                                   related_name="+")
    test_synchronized_snippet = models.ForeignKey(TestSnippet,
                                                  null=True,
                                                  blank=True,
                                                  on_delete=models.SET_NULL,
                                                  related_name="+")

    test_page = models.ForeignKey(Page,
                                  null=True,
                                  blank=True,
                                  on_delete=models.SET_NULL,
                                  related_name="+")

    test_page_specific_type = models.ForeignKey('TestHomePage',
                                                null=True,
                                                blank=True,
                                                on_delete=models.SET_NULL,
                                                related_name="+")

    test_page_with_restricted_types = models.ForeignKey(
        Page,
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="+")

    test_synchronized_customfield = TestCustomField(blank=True)

    translatable_fields = [
        TranslatableField("test_charfield"),
        TranslatableField("test_textfield"),
        TranslatableField("test_emailfield"),
        TranslatableField("test_slugfield"),
        TranslatableField("test_urlfield"),
        TranslatableField("test_richtextfield"),
        TranslatableField("test_streamfield"),
        TranslatableField("test_snippet"),
        TranslatableField("test_childobjects"),
        TranslatableField("test_customfield"),
        SynchronizedField("test_synchronized_charfield"),
        SynchronizedField("test_synchronized_textfield"),
        SynchronizedField("test_synchronized_emailfield"),
        SynchronizedField("test_synchronized_slugfield"),
        SynchronizedField("test_synchronized_urlfield"),
        SynchronizedField("test_synchronized_richtextfield"),
        SynchronizedField("test_synchronized_streamfield"),
        SynchronizedField("test_synchronized_image"),
        SynchronizedField("test_synchronized_document"),
        SynchronizedField("test_synchronized_snippet"),
        SynchronizedField("test_synchronized_childobjects"),
        SynchronizedField('test_page'),
        SynchronizedField('test_page_specific_type'),
        SynchronizedField('test_page_with_restricted_types'),
        SynchronizedField("test_synchronized_customfield"),
    ]

    content_panels = Page.content_panels + [
        FieldPanel("test_charfield"),
        FieldPanel("test_textfield"),
        FieldPanel("test_emailfield"),
        FieldPanel("test_slugfield"),
        FieldPanel("test_urlfield"),
        FieldPanel("test_richtextfield"),
        FieldPanel("test_streamfield"),
        FieldPanel("test_snippet"),
        InlinePanel("test_childobjects"),
        FieldPanel("test_customfield"),
        FieldPanel("test_synchronized_charfield"),
        FieldPanel("test_synchronized_textfield"),
        FieldPanel("test_synchronized_emailfield"),
        FieldPanel("test_synchronized_slugfield"),
        FieldPanel("test_synchronized_urlfield"),
        FieldPanel("test_synchronized_richtextfield"),
        FieldPanel("test_synchronized_streamfield"),
        FieldPanel("test_synchronized_image"),
        FieldPanel("test_synchronized_document"),
        FieldPanel("test_synchronized_snippet"),
        InlinePanel("test_synchronized_childobjects"),
        PageChooserPanel('test_page'),
        PageChooserPanel('test_page_specific_type'),
        PageChooserPanel('test_page_with_restricted_types', [
            'wagtail_localize_test.TestHomePage',
            'wagtail_localize_test.TestPage'
        ]),
        FieldPanel("test_synchronized_customfield"),
    ]
Ejemplo n.º 24
0
class TestOverrideTranslatableFieldsPage(TestGenerateTranslatableFieldsPage):
    override_translatable_fields = [
        SynchronizedField('test_charfield'),
        TranslatableField('test_emailfield'),
    ]
Ejemplo n.º 25
0
class PrimaryPage(FoundationMetadataPageMixin,
                  FoundationBannerInheritanceMixin, Page):
    """
    Basically a straight copy of modular page, but with
    restrictions on what can live 'under it'.

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

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

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

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

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

    body = StreamField(base_fields)

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

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

    translatable_fields = [
        # Promote tab fields
        SynchronizedField('slug'),
        TranslatableField('seo_title'),
        SynchronizedField('show_in_menus'),
        TranslatableField('search_description'),
        SynchronizedField('search_image'),
        # Content tab fields
        TranslatableField('title'),
        TranslatableField('header'),
        SynchronizedField('banner'),
        TranslatableField('intro'),
        TranslatableField('body'),
        SynchronizedField('narrowed_page_content'),
        SynchronizedField('zen_nav'),
    ]

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

    show_in_menus_default = True

    def get_context(self, request):
        context = super().get_context(request)
        context = set_main_site_nav_information(self, context, 'Homepage')
        context = get_page_tree_information(self, context)
        return context