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"
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
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"), ]
class TestModel(TranslatableMixin): title = models.CharField(max_length=255) test_charfield = models.CharField(max_length=255, blank=True) test_textfield = models.TextField(blank=True) test_emailfield = models.EmailField(blank=True) translatable_fields = [ TranslatableField("test_charfield"), TranslatableField("test_textfield"), TranslatableField("test_emailfield"), ]
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
class DonationModal(TranslatableMixin, models.Model): name = models.CharField( default='', max_length=100, help_text='Identify this component for other editors', ) header = models.CharField( max_length=500, help_text='Donation header', default="Thanks for signing! While you're here, we need your help.", ) body = models.TextField( help_text='Donation text', default='Mozilla is a nonprofit organization fighting for ' 'a healthy internet, where privacy is included by ' 'design and you have more control over your personal ' 'information. We depend on contributions from people ' 'like you to carry out this work. Can you donate today?') donate_text = models.CharField( max_length=150, help_text='Donate button label', default="Yes, I'll chip in", ) dismiss_text = models.CharField( max_length=150, help_text='Dismiss button label', default="No thanks", ) translatable_fields = [ TranslatableField('header'), TranslatableField('body'), TranslatableField('donate_text'), TranslatableField('dismiss_text'), ] def to_simple_dict(self): keys = ['name', 'header', 'body', 'donate_text', 'dismiss_text'] values = map(lambda k: getattr(self, k), keys) return dict(zip(keys, values)) def __str__(self): return self.name class Meta(TranslatableMixin.Meta): verbose_name_plural = 'Donation CTA'
class TestNonParentalChildObject(TranslatableMixin, Orderable): page = models.ForeignKey(TestPage, on_delete=models.CASCADE, related_name="test_nonparentalchildobjects") field = models.TextField() translatable_fields = [TranslatableField("field")]
class TestChildObject(TranslatableMixin, Orderable): page = ParentalKey(TestPage, related_name="test_childobjects") field = models.TextField() translatable_fields = [TranslatableField("field")] class Meta(TranslatableMixin.Meta, Orderable.Meta): pass
class TestModelWithInvalidForeignKey(TranslatableMixin, models.Model): fk = models.ForeignKey('wagtailcore.Site', on_delete=models.CASCADE) # This should raise an error as the model being pointed to is not # translatable! translatable_fields = [ TranslatableField('fk'), ]
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 ContributorSupportPage(TranslatablePageMixin, Page): template = 'pages/core/contributor_support_page.html' parent_page_types = ['core.LandingPage'] # This page does not have subpages translatable_fields = [ TranslatableField('title'), TranslatableField('seo_title'), TranslatableField('search_description'), ] def get_context(self, request): ctx = super().get_context(request) ctx.update({ 'orgid': settings.SALESFORCE_ORGID, }) return ctx
class Signup(TranslatableMixin, CTA): campaign_id = models.CharField( max_length=20, help_text='Which campaign identifier should this petition be tied to?', null=True, blank=True, ) ask_name = models.BooleanField( help_text='Check this box to show (optional) name fields', default=False, ) translatable_fields = [ # Fields from the CTA model TranslatableField('header'), TranslatableField('description'), ] class Meta(TranslatableMixin.Meta): verbose_name = 'signup snippet'
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'), ]
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"
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' ]
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'
class TestOverrideTranslatableFieldsPage(TestGenerateTranslatableFieldsPage): override_translatable_fields = [ SynchronizedField('test_charfield'), TranslatableField('test_emailfield'), ]
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"), ]
class Highlight(TranslatableMixin, SortableMixin): """ An data type to highlight things like pulse projects, custom pages, etc Especially on the homepage under "Get Involved" """ title = models.CharField( max_length=300, help_text='Title of the higlight', ) description = models.TextField( max_length=5000, help_text='Description of the higlight', ) link_label = models.CharField( max_length=300, help_text='Text to show that links to this higlight\'s ' 'details page', ) link_url = models.URLField( max_length=2048, help_text='Link to this higlight\'s details page', ) image = models.ForeignKey( 'wagtailimages.Image', on_delete=models.SET_NULL, blank=False, null=True, related_name='+', ) footer = RichTextField( "footer", help_text="Content to appear after description (view more projects " "link or something similar)", null=True, ) publish_after = models.DateTimeField( help_text='Make this highlight visible only ' 'after this date and time (UTC)', null=True, ) expires = models.DateTimeField( help_text='Hide this highlight after this date and time (UTC)', default=None, null=True, blank=True, ) order = models.PositiveIntegerField( default=0, editable=False, db_index=True, ) panels = [ FieldPanel("title"), FieldPanel("description"), FieldPanel("link_label"), FieldPanel("link_url"), ImageChooserPanel("image"), FieldPanel("footer"), FieldPanel("publish_after"), FieldPanel("expires"), ] translatable_fields = [ TranslatableField('title'), TranslatableField('description'), TranslatableField('link_label'), TranslatableField('footer'), ] objects = HighlightQuerySet.as_manager() class Meta(TranslatableMixin.Meta): verbose_name_plural = 'highlights' ordering = ('order', ) def __str__(self): return str(self.title)
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')
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'
class TestSnippet(TranslatableMixin, models.Model): field = models.TextField() translatable_fields = [TranslatableField("field")]
class TestSynchronizedChildObject(Orderable): page = ParentalKey(TestPage, related_name="test_synchronized_childobjects") field = models.TextField() translatable_fields = [TranslatableField("field")]
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'
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"
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 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')
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'), ])
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'
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'