class HeroSection(SectionBase, SectionTitleBlock, ButtonAction, Page): hero_layout = models.CharField( blank=True, max_length=100, verbose_name='Layout', choices=[('simple_centered', 'Simple centered'), ('image_right', 'Image on right')], default='simple_centered', ) hero_first_button_text = models.CharField( blank=True, max_length=100, verbose_name='Hero button text', default='Subscribe', help_text="Leave field empty to hide.", ) hero_second_button_text = models.CharField( blank=True, max_length=100, verbose_name='Hero button text', default='Subscribe', help_text="Leave field empty to hide.", ) hero_image = models.ForeignKey( 'wagtailimages.Image', blank=True, null=True, on_delete=models.SET_NULL, verbose_name='Image', related_name='+', ) hero_image_size = models.CharField( max_length=50, choices=cr_settings['HERO_IMAGE_SIZE_CHOICES'], default=cr_settings['HERO_IMAGE_SIZE_CHOICES_DEFAULT'], verbose_name=('Image size'), ) hero_action_type_1 = models.CharField( max_length=50, choices=cr_settings['HERO_ACTION_TYPE_CHOICES'], default=cr_settings['HERO_ACTION_TYPE_CHOICES_DEFAULT'], verbose_name=('Action type (First)'), ) hero_action_type_2 = models.CharField( max_length=50, choices=cr_settings['HERO_ACTION_TYPE_CHOICES'], default=cr_settings['HERO_ACTION_TYPE_CHOICES_DEFAULT'], verbose_name=('Action type (Second)'), ) hero_buttons = StreamField([('action_button', ActionButton()), ('primary_button', PrimaryButton())], null=True, verbose_name="Buttons", help_text="Please choose Buttons") # basic tab panels basic_panels = Page.content_panels + [ FieldPanel('hero_layout', heading='Layout', classname="title full"), MultiFieldPanel( [ FieldRowPanel([ FieldPanel('hero_layout', classname="col6"), FieldPanel('hero_image_size', classname="col6"), ]), FieldRowPanel([ FieldPanel('section_heading', heading='Heading', classname="col6"), FieldPanel('section_subheading', heading='Subheading', classname="col6"), ]), FieldRowPanel([ FieldPanel('section_description', heading='Description', classname="col6"), ]), FieldPanel('hero_first_button_text'), FieldPanel('hero_second_button_text'), ImageChooserPanel('hero_image'), ], heading='Content', ), SectionBase.section_layout_panels, SectionBase.section_design_panels, ] # advanced tab panels advanced_panels = (SectionTitleBlock.title_basic_panels, ) + ButtonAction.button_action_panels # Register Tabs edit_handler = TabbedInterface([ ObjectList(basic_panels, heading="Basic"), ObjectList(advanced_panels, heading="Plus+"), ]) # Page settings template = 'sections/hero_section_preview.html' parent_page_types = ['home.HomePage'] subpage_types = [] # Overring methods def set_url_path(self, parent): """ Populate the url_path field based on this page's slug and the specified parent page. (We pass a parent in here, rather than retrieving it via get_parent, so that we can give new unsaved pages a meaningful URL when previewing them; at that point the page has not been assigned a position in the tree, as far as treebeard is concerned. """ if parent: self.url_path = '' else: # a page without a parent is the tree root, which always has a url_path of '/' self.url_path = '/' return self.url_path
class FlatPage(Page): headers = StreamField([ ('h_hero', _H_HeroBlock(icon='image')), # ('code', blocks.RawHTMLBlock(null=True, blank=True, classname="full", icon='code')) ]) price = models.DecimalField(verbose_name="Preis", max_digits=11, decimal_places=2, null=True, blank=False) br_choices = ( ('RE', 'Miete'), ('BU', 'Kauf'), ) buy_or_rent = models.CharField(verbose_name="Mieten oder Kaufen?", max_length=4, choices=br_choices, default='RE') lead = models.CharField(null=True, blank=True, max_length=512) available = models.BooleanField(verbose_name="Verfügbar") # ground_plan = ImageChooserBlock( # required=True, blank=False, help_text="Raumplan") sections = StreamField( [('s_contentcenter', _S_ContentCenter(icon='fa-info')), ('s_contentright', _S_ContentRight(icon='fa-info')), ('s_contentleft', _S_ContentLeft(icon='fa-info'))], null=True, blank=True) gallery = StreamField([('g_gallery', _G_GalleryBlock(icon='fa-info'))], null=True, blank=True) ground_plan = StreamField( [('p_groundplan', _P_GroundPlanBlock(icon='fa-info'))], null=True, blank=True, verbose_name="Grundrissplan") main_content_panels = [ StreamFieldPanel('headers'), FieldPanel('price'), FieldPanel('buy_or_rent'), FieldPanel('lead'), FieldPanel('available'), # FieldPanel('ground_plan'), StreamFieldPanel('ground_plan'), StreamFieldPanel('sections'), StreamFieldPanel('gallery'), # StreamFieldPanel('footers') ] edit_handler = TabbedInterface([ ObjectList(Page.content_panels + main_content_panels, heading='Main'), ObjectList(Page.promote_panels, heading='Settings', classname="settings") ]) preview_modes = []
FieldPanel('title', classname="full title"), FieldPanel('seo_title'), FieldPanel('slug'), InlinePanel('advert_placements', label="Adverts"), ] StandardIndex.promote_panels = [] class StandardChild(Page): pass # Test overriding edit_handler with a custom one StandardChild.edit_handler = TabbedInterface([ ObjectList(StandardChild.content_panels, heading='Content'), ObjectList(StandardChild.promote_panels, heading='Promote'), ObjectList(StandardChild.settings_panels, heading='Settings', classname='settings'), ObjectList([], heading='Dinosaurs'), ], base_form_class=WagtailAdminPageForm) class BusinessIndex(Page): """ Can be placed anywhere, can only have Business children """ subpage_types = ['tests.BusinessChild', 'tests.BusinessSubIndex'] class BusinessSubIndex(Page): """ Can be placed under BusinessIndex, and have BusinessChild children """ # BusinessNowherePage is 'incorrectly' added here as a possible child. # The rules on BusinessNowherePage prevent it from being a child here though. subpage_types = ['tests.BusinessChild', 'tests.BusinessNowherePage']
class EnforcementActionPage(AbstractFilterPage): public_enforcement_action = models.CharField(max_length=150, blank=True) initial_filing_date = models.DateField(null=True, blank=True) settled_or_contested_at_filing = models.CharField(max_length=10, choices=[('Settled', 'Settled'), ('Contested', 'Contested')], blank=True) court = models.CharField(default='', max_length=150, blank=True) content = StreamField([ ('full_width_text', organisms.FullWidthText()), ('expandable', organisms.Expandable()), ('expandable_group', organisms.ExpandableGroup()), ('notification', molecules.Notification()), ('table_block', organisms.AtomicTableBlock(table_options={'renderer': 'html'})), ('feedback', v1_blocks.Feedback()), ], blank=True) content_panels = [StreamFieldPanel('header'), StreamFieldPanel('content')] metadata_panels = [ FieldPanel('public_enforcement_action'), FieldPanel('initial_filing_date'), InlinePanel('defendant_types', label='Defendant/Respondent Type'), InlinePanel('categories', label="Forum", min_num=1, max_num=2), FieldPanel('court'), InlinePanel('docket_numbers', label="Docket Number", min_num=1), FieldPanel('settled_or_contested_at_filing'), InlinePanel('statuses', label="Status", min_num=1), InlinePanel('products', label="Products"), InlinePanel('at_risk_groups', label="At Risk Groups"), InlinePanel('statutes', label="Statutes/Regulations"), InlinePanel('enforcement_dispositions', label='Final Disposition'), ] settings_panels = [ MultiFieldPanel(CFGOVPage.promote_panels, 'Settings'), MultiFieldPanel([ FieldPanel('preview_title'), FieldPanel('preview_subheading'), FieldPanel('preview_description'), FieldPanel('secondary_link_url'), FieldPanel('secondary_link_text'), ImageChooserPanel('preview_image'), ], heading='Page Preview Fields', classname='collapsible'), FieldPanel('authors', 'Authors'), MultiFieldPanel([ FieldPanel('date_published'), FieldPanel('comments_close_by'), ], 'Relevant Dates', classname='collapsible'), MultiFieldPanel(Page.settings_panels, 'Scheduled Publishing'), FieldPanel('language', 'Language'), MultiFieldPanel(CFGOVPage.archive_panels, 'Archive'), ] edit_handler = TabbedInterface([ ObjectList(AbstractFilterPage.content_panels + content_panels, heading='General Content'), ObjectList(metadata_panels, heading='Metadata'), ObjectList(CFGOVPage.sidefoot_panels, heading='Sidebar'), ObjectList(settings_panels, heading='Configuration') ]) template = 'enforcement-action/index.html' objects = PageManager() search_fields = AbstractFilterPage.search_fields + [ index.SearchField('content') ] @classmethod def all_actions(cls): # Return the collection of all Enforcement Action Pages. # Exclude any pages in the Trash or otherwise not a child of the # EnforcementActionsFilterPage. try: # TODO: find a less hacky way to get only the pages in the # correct part of the page tree pg_id = 1327 parent_page = Page.objects.get(id=pg_id) query = cls.objects.child_of(parent_page) except (Page.DoesNotExist): query = cls.objects query = query.filter(initial_filing_date__isnull=False) query = query.live().order_by('-initial_filing_date') return query def get_context(self, request): context = super(EnforcementActionPage, self).get_context(request) dispositions = self.enforcement_dispositions.all() context.update({ 'total_consumer_relief': sum(disp.final_order_consumer_redress + disp.final_order_other_consumer_relief for disp in dispositions), 'total_cmp': sum(disp.final_order_civil_money_penalty for disp in dispositions), 'defendant_types': [ d.get_defendant_type_display() for d in self.defendant_types.all() ], 'statutes': [s.statute for s in self.statutes.all()], 'products': [p.product for p in self.products.all()], 'at_risk_groups': [g.at_risk_group for g in self.at_risk_groups.all()] }) return context
class UseCaseIndexPage(Page): subtitle_de = models.CharField(max_length=250, blank=True, verbose_name="Title") subtitle_en = models.CharField(max_length=250, blank=True, verbose_name="Title") demo_link = models.URLField(blank=True, verbose_name='Demo site') subtitle = TranslatedField('subtitle_de', 'subtitle_en') form_page = models.ForeignKey( 'a4_candy_cms_contacts.FormPage', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', ) @property def form(self): return self.form_page.get_form() @property def use_cases(self): use_cases = UseCasePage.objects.live() return use_cases def get_context(self, request): use_cases = self.use_cases category = request.GET.get('category') if category: try: use_cases = use_cases.filter(category=category) except ValueError: use_cases = [] context = super().get_context(request) context['use_cases'] = use_cases context['categories'] = CATEGORY_CHOICES if category: context['current_category'] = category for category_choice in CATEGORY_CHOICES: if category_choice[0] == category: context['get_current_category_display'] = ( category_choice[1]) return context de_content_panels = [ FieldPanel('subtitle_de'), ] en_content_panels = [ FieldPanel('subtitle_en'), ] common_panels = [ FieldPanel('title'), FieldPanel('slug'), FieldPanel('demo_link'), PageChooserPanel('form_page'), ] edit_handler = TabbedInterface([ ObjectList(common_panels, heading='Common'), ObjectList(en_content_panels, heading='English'), ObjectList(de_content_panels, heading='German') ]) subpage_types = ['a4_candy_cms_use_cases.UseCasePage']
class CybergatewayHomePage(Page): """ The Cybergateway themed template Page """ # Hero section of HomePage site_logo = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', help_text='Site Logo Image' ) site_link = models.CharField( max_length=255, default="#", help_text='Give a site redirect link', ) site_text = models.CharField( max_length=50, default="#", help_text='Give a Site Name', ) site_header = models.CharField( max_length=70, default="#", help_text='Give a Site Header Name', ) site_link1 = models.CharField( max_length=70, default="#", help_text='Give a Site Nav Link [1]', ) site_link_text1 = models.CharField( max_length=70, help_text='Give a Site Nav Link Text [1]', ) site_link2 = models.CharField( max_length=70, default='#', help_text='Give a Site Nav Link [2]', ) site_link_text2 = models.CharField( max_length=70, help_text='Give a Site Nav Link Text [2]', ) site_link3 = models.CharField( max_length=70, default="#", help_text='Give a Site Nav Link [3]', ) site_link_text3 = models.CharField( max_length=70, help_text='Give a Site Nav Link Text [3]', ) contact = StreamField( BaseStreamBlock(), verbose_name="Contact Info Block", blank=True, null=True) footer = StreamField( BaseStreamBlock(), verbose_name="Footer Content Block", blank=True, null=True) boolean_choices = ( ("yes", "Yes"), ("no", "No") ) show_navbar = models.CharField( choices=boolean_choices, max_length=5, help_text="Choose yes if you want to display the navbar on home page and no if you don't want to.", default="yes") show_nav_extra = models.CharField( choices=boolean_choices, max_length=5, help_text="Choose yes if you want the secondary navbar to show on home page or no if you don't want to", default="yes") show_footer = models.CharField( choices=boolean_choices, max_length=5, help_text="Choose yes if you want the Footer to show on home page or no if you don't want to", default="yes") content_panels = Page.content_panels + [ MultiFieldPanel([ ImageChooserPanel('site_logo'), FieldPanel('site_link'), FieldPanel('site_text'), FieldPanel('site_header'), FieldPanel('site_link1'), FieldPanel('site_link_text1'), FieldPanel('site_link2'), FieldPanel('site_link_text2'), FieldPanel('site_link3'), FieldPanel('site_link_text3'), ], heading="Navbar Section"), InlinePanel("row", label="row"), StreamFieldPanel('contact'), StreamFieldPanel('footer'), ] customization_panels = [ FieldPanel('show_navbar'), FieldPanel('show_nav_extra'), FieldPanel('show_footer'), ] edit_handler = TabbedInterface([ ObjectList(content_panels, heading='Content'), ObjectList(customization_panels, heading='Customization'), ObjectList(Page.promote_panels, heading='Promote'), ObjectList(Page.settings_panels, heading='Settings', classname="settings"), ]) def __str__(self): return self.title
class TabbedSettings(TestSetting): edit_handler = TabbedInterface([ ObjectList([FieldPanel('title')], heading='First tab'), ObjectList([FieldPanel('email')], heading='Second tab'), ])
linkpage_panels = [ MultiFieldPanel([ FieldPanel('title', classname="title"), PageChooserPanel('link_page'), FieldPanel('link_url'), FieldPanel('url_append'), FieldPanel('extra_classes'), ]) ] linkpage_tab = ObjectList( linkpage_panels, heading=_("Settings"), classname="settings" ) linkpage_edit_handler = TabbedInterface([linkpage_tab]) # ######################################################## # For MenuPageMixin # ######################################################## menupage_panel = MultiFieldPanel( heading=_("Advanced menu behaviour"), classname="collapsible collapsed", children=( FieldPanel('repeat_in_subnav'), FieldPanel('repeated_item_text'), ) )
class Topic(BasePage): resource_type = "topic" parent_page_types = ["Topics"] subpage_types = ["Topic", "content.ContentPage"] template = "topic.html" # Content fields description = RichTextField( blank=True, default="", features=RICH_TEXT_FEATURES_SIMPLE, help_text="Optional short text description, max. 400 characters", max_length=400, ) featured = StreamField( StreamBlock( [ ( "post", PageChooserBlock(target_model=( "articles.Article", "externalcontent.ExternalArticle", )), ), ("external_page", FeaturedExternalBlock()), ], max_num=4, required=False, ), null=True, blank=True, help_text="Optional space for featured posts, max. 4", ) tabbed_panels = StreamField( StreamBlock([("panel", TabbedPanelBlock())], max_num=3, required=False), null=True, blank=True, help_text= "Optional tabbed panels for linking out to other resources, max. 3", verbose_name="Tabbed panels", ) latest_articles_count = IntegerField( choices=RESOURCE_COUNT_CHOICES, default=3, help_text="The number of posts to display for this topic.", ) # Card fields card_title = CharField("Title", max_length=140, blank=True, default="") card_description = TextField("Description", max_length=400, blank=True, default="") card_image = ForeignKey( "mozimages.MozImage", null=True, blank=True, on_delete=SET_NULL, related_name="+", verbose_name="Image", ) # Meta icon = FileField( upload_to="topics/icons", blank=True, default="", help_text=("MUST be a black-on-transparent SVG icon ONLY, " "with no bitmap embedded in it."), validators=[check_for_svg_file], ) color = CharField(max_length=14, choices=COLOR_CHOICES, default="blue-40") keywords = ClusterTaggableManager(through=TopicTag, blank=True) # Content panels content_panels = BasePage.content_panels + [ FieldPanel("description"), StreamFieldPanel("featured"), StreamFieldPanel("tabbed_panels"), FieldPanel("latest_articles_count"), MultiFieldPanel( [InlinePanel("people")], heading="People", help_text= "Optional list of people associated with this topic as experts", ), ] # Card panels card_panels = [ FieldPanel("card_title"), FieldPanel("card_description"), ImageChooserPanel("card_image"), ] # Meta panels meta_panels = [ MultiFieldPanel( [ InlinePanel("parent_topics", label="Parent topic(s)"), InlinePanel("child_topics", label="Child topic(s)"), ], heading="Parent/child topic(s)", classname="collapsible collapsed", help_text=("Topics with no parent (i.e. top-level topics) will be " "listed on the home page. Child topics are listed " "on the parent topic’s page."), ), MultiFieldPanel( [FieldPanel("icon"), FieldPanel("color")], heading="Theme", help_text=( "Theme settings used on topic page and any tagged content. " "For example, a post tagged with this topic " "will use the color specified here as its accent color."), ), MultiFieldPanel( [ FieldPanel("seo_title"), FieldPanel("search_description"), ImageChooserPanel("social_image"), FieldPanel("keywords"), ], heading="SEO", help_text=("Optional fields to override the default " "title and description for SEO purposes"), ), ] # Settings panels settings_panels = BasePage.settings_panels + [ FieldPanel("slug"), FieldPanel("show_in_menus"), ] # Tabs edit_handler = TabbedInterface([ ObjectList(content_panels, heading="Content"), ObjectList(card_panels, heading="Card"), ObjectList(meta_panels, heading="Meta"), ObjectList(settings_panels, heading="Settings", classname="settings"), ]) @property def articles(self): return get_combined_articles(self, topics__topic__pk=self.pk) @property def events(self): """Return upcoming events for this topic, ignoring events in the past, ordered by start date""" return get_combined_events(self, topics__topic__pk=self.pk, start_date__gte=get_past_event_cutoff()) @property def experts(self): """Return Person instances for topic experts""" return [person.person for person in self.people.all()] @property def videos(self): """Return the latest videos and external videos for this topic. """ return get_combined_videos(self, topics__topic__pk=self.pk) @property def color_value(self): return dict(COLOR_VALUES)[self.color] @property def subtopics(self): return [topic.child for topic in self.child_topics.all()]
class RegulationsSearchPage(RoutablePageMixin, CFGOVPage): """A page for the custom search interface for regulations.""" objects = PageManager() parent_page_types = ['regulations3k.RegulationLandingPage'] subpage_types = [] results = {} content_panels = CFGOVPage.content_panels edit_handler = TabbedInterface([ ObjectList(content_panels, heading='Content'), ObjectList(CFGOVPage.settings_panels, heading='Configuration'), ]) def get_template(self, request): template = 'regulations3k/search-regulations.html' if 'partial' in request.GET: template = 'regulations3k/search-regulations-results.html' return template @route(r'^results/') def regulation_results_page(self, request): all_regs = Part.objects.order_by('part_number') regs = validate_regs_list(request) order = validate_order(request) search_query = request.GET.get('q', '').strip() payload = { 'search_query': search_query, 'results': [], 'total_results': 0, 'regs': regs, 'all_regs': [], } if not search_query or len(urllib.parse.unquote(search_query)) == 1: self.results = payload return TemplateResponse(request, self.get_template(request), self.get_context(request)) search = SectionParagraphDocument.search().query('match', text={ "query": search_query, "operator": "AND" }) search = search.highlight('text', pre_tags="<strong>", post_tags="</strong>") total_results = search.count() all_regs = [{ 'short_name': reg.short_name, 'id': reg.part_number, 'num_results': search.filter('term', part=reg.part_number).count(), 'selected': reg.part_number in regs } for reg in all_regs] payload.update({'all_regs': all_regs, 'total_count': total_results}) if regs: search = search.filter('terms', part=regs) if order == 'regulation': search = search.sort('part', 'section_order') search = search[0:total_results] response = search.execute() for hit in response[0:total_results]: try: snippet = Markup("".join(hit.meta.highlight.text[0])) except TypeError as e: logger.warning( "Query string {} produced a TypeError: {}".format( search_query, e)) continue hit_payload = { 'id': hit.paragraph_id, 'part': hit.part, 'reg': hit.short_name, 'label': hit.title, 'snippet': snippet, 'url': "{}{}/{}/#{}".format(self.parent().url, hit.part, hit.section_label, hit.paragraph_id), } payload['results'].append(hit_payload) payload.update({'current_count': search.count()}) self.results = payload context = self.get_context(request) num_results = validate_num_results(request) paginator = Paginator(payload['results'], num_results) page_number = validate_page_number(request, paginator) paginated_page = paginator.page(page_number) context.update({ 'current_count': payload['current_count'], 'total_count': payload['total_count'], 'paginator': paginator, 'current_page': page_number, 'num_results': num_results, 'order': order, 'results': paginated_page, 'show_filters': any(reg['selected'] is True for reg in payload['all_regs']) }) return TemplateResponse(request, self.get_template(request), context)
class HomePage(MetadataPageMixin, TranslatablePage): # Register the fields for your personal information name = models.CharField(max_length=255, blank=True, null=True, help_text='Your name', default='John Doe') heading = models.CharField(max_length=255, blank=True, null=True, help_text='Your title', default='Web Developer') email = models.CharField(max_length=255, blank=True, null=True, help_text='Your email', default='*****@*****.**') telephone = models.CharField(max_length=255, blank=True, null=True, help_text='Your telephone', default='06123456789') profile_pic = models.ForeignKey('wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') button = models.CharField(max_length=255, blank=True, null=True, default='Portfolio') # Add the intro fields to homepage model intro_heading = models.CharField(max_length=255, blank=True, null=True, default='More about me.') intro_sub_heading = models.CharField(max_length=255, blank=True, null=True, default='A SHORT INTRODUCTION') intro_left = RichTextField(blank=True) intro_right = RichTextField(blank=True) portfolio_heading = models.CharField(max_length=255, blank=True, null=True, default='Finished projects') portfolio_sub_heading = models.CharField( max_length=255, blank=True, null=True, default='A GLIMPSE OF MY PORTFOLIO') # Metadata search image search_image = models.ForeignKey( get_image_model_string(), null=True, blank=True, related_name='+', on_delete=models.SET_NULL, verbose_name=ugettext_lazy('Search image')) # Register the fields in Wagtail admin, we use FieldRowPanel here for better user experience. content_panels = Page.content_panels + [ MultiFieldPanel( [ FieldRowPanel([ FieldPanel('intro_heading'), FieldPanel('intro_sub_heading'), ]), FieldRowPanel([ FieldPanel('portfolio_heading'), FieldPanel('portfolio_sub_heading'), ]), ], heading="Headings", classname="collapsible", ), FieldPanel('intro_left', classname="full"), FieldPanel('intro_right', classname="full"), ] # Information panel in wagtail admin. information_content_panels = [ MultiFieldPanel( [ FieldRowPanel([ FieldPanel('name'), FieldPanel('heading'), ]), FieldRowPanel([ FieldPanel('email'), FieldPanel('telephone'), ]), FieldRowPanel([ ImageChooserPanel('profile_pic'), FieldPanel('button'), ]), ], heading="Details", classname="collapsible", ), ] # This has to be defined because we use Wagtailtrans. Otherwise the search image does not get displayed. promote_panels = [ MultiFieldPanel([ FieldPanel('slug'), FieldPanel('seo_title'), FieldPanel('show_in_menus'), FieldPanel('search_description'), ImageChooserPanel('search_image'), ], ugettext_lazy('Common page configuration')), ] # The edit handler for displaying the tabs we previously defined edit_handler = TabbedInterface([ ObjectList(content_panels, heading='Info'), ObjectList(information_content_panels, heading='Personal information'), ObjectList(promote_panels, heading='Promote'), ObjectList(TranslatablePage.settings_panels, heading='Settings', classname="settings"), ]) subpage_types = []
class RegulationPage(RoutablePageMixin, SecondaryNavigationJSMixin, CFGOVPage): """A routable page for serving an eregulations page by Section ID.""" objects = PageManager() parent_page_types = ['regulations3k.RegulationLandingPage'] subpage_types = [] template = 'regulations3k/browse-regulation.html' header = StreamField([ ('text_introduction', molecules.TextIntroduction()), ], blank=True) content = StreamField([ ('info_unit_group', organisms.InfoUnitGroup()), ('full_width_text', organisms.FullWidthText()), ], null=True, blank=True) regulation = models.ForeignKey(Part, blank=True, null=True, on_delete=models.PROTECT, related_name='page') content_panels = CFGOVPage.content_panels + [ StreamFieldPanel('header'), StreamFieldPanel('content'), FieldPanel('regulation', Part), ] secondary_nav_exclude_sibling_pages = models.BooleanField(default=False) sidefoot_panels = CFGOVPage.sidefoot_panels + [ FieldPanel('secondary_nav_exclude_sibling_pages'), ] edit_handler = TabbedInterface([ ObjectList(content_panels, heading='General Content'), ObjectList(sidefoot_panels, heading='Sidebar'), ObjectList(CFGOVPage.settings_panels, heading='Configuration'), ]) def can_serve_draft_versions(self, request): perms = request.user.get_all_permissions() if (request.user.is_superuser or getattr(request, 'served_by_wagtail_sharing', False) or 'regulations3k.change_section' in perms): return True return False def get_versions_query(self, request): versions = self.regulation.versions if not self.can_serve_draft_versions(request): versions = versions.filter(draft=False) return versions def get_effective_version(self, request, date_str=None): """ Get the requested effective version if the user has permission """ query_filter = {} if date_str is None: query_filter['effective_date__lte'] = date.today() else: query_filter['effective_date'] = date_str draft_permission = self.can_serve_draft_versions(request) if not draft_permission: query_filter['draft'] = False effective_version = self.regulation.versions.filter( **query_filter).order_by('-effective_date').first() if effective_version is None: raise Http404 return effective_version def get_section_query(self, request=None, effective_version=None): """Query set for Sections in this regulation's effective version.""" if effective_version is None: effective_version = self.get_effective_version(request) return Section.objects.filter(subpart__version=effective_version) def get_context(self, request, *args, **kwargs): context = super(RegulationPage, self).get_context(request, *args, **kwargs) context.update({ 'regulation': self.regulation, 'current_version': self.get_effective_version(request), 'breadcrumb_items': self.get_breadcrumbs(request, *args, **kwargs), 'search_url': (self.get_parent().url + 'search-regulations/results/?regs=' + self.regulation.part_number), 'num_versions': self.get_versions_query(request).count(), }) return context def get_breadcrumbs(self, request, section=None, **kwargs): crumbs = super(RegulationPage, self).get_breadcrumbs(request) if section is not None: crumbs = crumbs + [ { 'href': self.url + self.reverse_subpage( 'index', kwargs={ k: v for k, v in kwargs.items() if k == 'date_str' }), 'title': str(section.subpart.version.part), }, ] return crumbs def get_urls_for_version(self, effective_version, section=None): base_url = self.get_full_url() versions_url = urljoin(base_url, 'versions') + '/' if effective_version.live_version: # This is the current version version_url = base_url else: # It's a past or future version, URLs have the date str date_str = str(effective_version.effective_date) version_url = urljoin(base_url, date_str) + '/' yield version_url if section is not None: yield urljoin(version_url, section.label) + '/' else: sections = self.get_section_query( effective_version=effective_version) yield version_url yield versions_url for section in sections.all(): yield urljoin(version_url, section.label) + '/' def render_interp(self, context, raw_contents, **kwargs): template = get_template('regulations3k/inline_interps.html') # Extract the title from the raw regdown section_title_match = re.search(r'#+\s?(?P<section_title>.*)\s', raw_contents) if section_title_match is not None: context.update({'section_title': section_title_match.group(1)}) span = section_title_match.span() raw_contents = raw_contents[:span[0]] + raw_contents[span[1]:] context.update({'contents': regdown(raw_contents)}) context.update(kwargs) return template.render(context) @route(r'^(?:(?P<date_str>[0-9]{4}-[0-9]{2}-[0-9]{2})/)?$', name="index") def index_route(self, request, date_str=None): request.is_preview = getattr(request, 'is_preview', False) effective_version = self.get_effective_version(request, date_str=date_str) section_query = self.get_section_query( effective_version=effective_version) sections = list(section_query.all()) context = self.get_context(request) context.update({ 'requested_version': effective_version, 'sections': sections, 'get_secondary_nav_items': partial(get_secondary_nav_items, sections=sections, date_str=date_str), }) if date_str is not None: context['date_str'] = date_str return TemplateResponse(request, self.get_template(request), context) @route(r'^versions/(?:(?P<section_label>' + label_re_str + r')/)?$', name="versions") def versions_page(self, request, section_label=None): section_query = self.get_section_query(request=request) sections = list(section_query.all()) context = self.get_context(request, sections=sections) versions = [{ 'effective_date': v.effective_date, 'date_str': str(v.effective_date), 'sections': self.get_section_query(effective_version=v).all(), 'draft': v.draft } for v in self.get_versions_query(request).order_by('-effective_date') ] context.update({ 'versions': versions, 'section_label': section_label, 'get_secondary_nav_items': partial(get_secondary_nav_items, sections=sections), }) return TemplateResponse(request, self.template, context) @route(r'^(?:(?P<date_str>[0-9]{4}-[0-9]{2}-[0-9]{2})/)?' r'(?P<section_label>' + label_re_str + r')/$', name="section") def section_page(self, request, date_str=None, section_label=None): """ Render a section of the currently effective regulation """ effective_version = self.get_effective_version(request, date_str=date_str) section_query = self.get_section_query( effective_version=effective_version) next_version = self.get_versions_query(request).filter( effective_date__gt=effective_version.effective_date).first() kwargs = {} if date_str is not None: kwargs['date_str'] = date_str try: section = section_query.get(label=section_label) except Section.DoesNotExist: return redirect(self.url + self.reverse_subpage("index", kwargs=kwargs)) sections = list(section_query.all()) current_index = sections.index(section) context = self.get_context(request, section, sections=sections, **kwargs) content = regdown( section.contents, url_resolver=get_url_resolver(self, date_str=date_str), contents_resolver=get_contents_resolver(effective_version), render_block_reference=partial(self.render_interp, context)) next_section = get_next_section(sections, current_index) previous_section = get_previous_section(sections, current_index) context.update({ 'requested_version': effective_version, 'next_version': next_version, 'section': section, 'content': content, 'get_secondary_nav_items': partial(get_secondary_nav_items, sections=sections, date_str=date_str), 'next_section': next_section, 'next_url': get_section_url(self, next_section, date_str=date_str), 'previous_section': previous_section, 'previous_url': get_section_url(self, previous_section, date_str=date_str), }) return TemplateResponse(request, self.template, context)
class Person(Page): resource_type = 'person' parent_page_types = ['People'] subpage_types = [] template = 'person.html' # Content fields job_title = CharField(max_length=250) role = CharField(max_length=250, choices=ROLE_CHOICES, default='staff') description = RichTextField(default='', blank=True) image = ForeignKey( 'mozimages.MozImage', null=True, blank=True, on_delete=SET_NULL, related_name='+' ) # Card fields card_title = CharField('Title', max_length=140, blank=True, default='') card_description = TextField('Description', max_length=140, blank=True, default='') card_image = ForeignKey( 'mozimages.MozImage', null=True, blank=True, on_delete=SET_NULL, related_name='+', verbose_name='Image', ) # Meta twitter = CharField(max_length=250, blank=True, default='') facebook = CharField(max_length=250, blank=True, default='') linkedin = CharField(max_length=250, blank=True, default='') github = CharField(max_length=250, blank=True, default='') email = CharField(max_length=250, blank=True, default='') websites = StreamField( StreamBlock([ ('website', PersonalWebsiteBlock()) ], min_num=0, max_num=3, required=False), null=True, blank=True, ) keywords = ClusterTaggableManager(through=PersonTag, blank=True) # Content panels content_panels = [ MultiFieldPanel([ CustomLabelFieldPanel('title', label='Full name'), FieldPanel('job_title'), FieldPanel('role'), ], heading='About'), FieldPanel('description'), ImageChooserPanel('image'), ] # Card panels card_panels = [ FieldPanel('card_title'), FieldPanel('card_description'), ImageChooserPanel('card_image'), ] # Meta panels meta_panels = [ MultiFieldPanel([ InlinePanel('topics'), ], heading='Topics interested in'), MultiFieldPanel([ FieldPanel('twitter'), FieldPanel('facebook'), FieldPanel('linkedin'), FieldPanel('github'), FieldPanel('email'), ], heading='Profiles'), StreamFieldPanel('websites'), MultiFieldPanel([ FieldPanel('seo_title'), FieldPanel('search_description'), FieldPanel('keywords'), ], heading='SEO'), ] # Settings panels settings_panels = [ FieldPanel('slug'), ] # Tabs edit_handler = TabbedInterface([ ObjectList(content_panels, heading='Content'), ObjectList(card_panels, heading='Card'), ObjectList(meta_panels, heading='Meta'), ObjectList(settings_panels, heading='Settings', classname='settings'), ]) @property def events(self): ''' Return upcoming events where this person is a speaker, ordered by start date ''' from ..events.models import Event upcoming_events = (Event .objects .filter(start_date__gte=datetime.datetime.now()) .live() .public() ) speaker_events = Event.objects.none() for event in upcoming_events.all(): # add the event to the list if the current person is a speaker if event.has_speaker(self): speaker_events = speaker_events | Event.objects.page(event) return speaker_events.order_by('start_date') @property def articles(self): ''' Return articles and external articles where this person is (one of) the authors, ordered by article date, most recent first ''' from ..articles.models import Article from ..externalcontent.models import ExternalArticle articles = Article.objects.none() external_articles = ExternalArticle.objects.none() all_articles = Article.objects.live().public().all() all_external_articles = ExternalArticle.objects.live().public().all() for article in all_articles: if article.has_author(self): articles = articles | Article.objects.page(article) for external_article in all_external_articles: if external_article.has_author(self): external_articles = external_articles | ExternalArticle.objects.page(external_article) return sorted(chain(articles, external_articles), key=attrgetter('date'), reverse=True) @property def videos(self): ''' Return the most recent videos and external videos where this person is (one of) the speakers. ''' from ..videos.models import Video from ..externalcontent.models import ExternalVideo videos = Video.objects.none() external_videos = ExternalVideo.objects.none() all_videos = Video.objects.live().public().all() all_external_videos = ExternalVideo.objects.live().public().all() for video in all_videos: if video.has_speaker(self): videos = videos | Video.objects.page(video) for external_video in all_external_videos: if external_video.has_speaker(self): external_videos = external_videos | ExternalVideo.objects.page(external_video) return sorted(chain(videos, external_videos), key=attrgetter('date'), reverse=True) @property def role_group(self): return { 'slug': self.role, 'title': dict(ROLE_CHOICES).get(self.role, ''), }
class HomePage(ThemeablePage): subpage_types = [ article_models.ArticleListPage, article_models.SeriesListPage, article_models.TopicListPage, people_models.ContributorListPage, project_models.ProjectListPage, newsletter_models.NewsletterListPage, event_models.EventListPage, article_models.ExternalArticleListPage, jobs_models.JobPostingListPage, StreamPage, ] featured_item = models.ForeignKey('wagtailcore.Page', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') number_of_rows_of_articles = models.IntegerField(default=12, verbose_name="Rows") number_of_columns_of_articles = models.IntegerField(default=3, verbose_name="Columns") number_of_rows_of_series = models.IntegerField(default=1, verbose_name="Rows") number_of_rows_of_external_articles = models.IntegerField( default=2, verbose_name="Rows") number_of_columns_of_external_articles = models.IntegerField( default=2, verbose_name="Columns") number_of_rows_of_visualizations = models.IntegerField(default=2, verbose_name="Rows") number_of_columns_of_visualizations = models.IntegerField( default=2, verbose_name="Columns") full_bleed_image_size = models.PositiveSmallIntegerField( default=90, help_text= "Enter a value from 0 - 100, indicating the percentage of the screen to use for the featured image layout." ) _articles = None _most_popular_article = None def __str__(self): return self.title def get_article_set(self, columns, rows, article_list, used): if columns == 0 and rows == 0 or not article_list: return [] current_set = [] while rows > 0: row, height = self._fill_row(columns, article_list, used, rows) if height == 0: break current_set.append(row) rows = rows - height return current_set def _fill_row(self, columns, article_list, used, max_height): if columns == 0 or not article_list: return [], 0 for article in article_list: typed_article = article.content_type.get_object_for_this_type( id=article.id) if typed_article.feature_style.number_of_columns <= columns \ and typed_article.id not in used\ and typed_article.feature_style.number_of_rows <= max_height: columns = columns - typed_article.feature_style.number_of_columns used.append(typed_article.id) row = [typed_article] max_height = min(max_height, typed_article.feature_style.number_of_rows) if max_height > 1 and columns > 0: subset = self.get_article_set(columns, max_height, article_list, used) row.append(subset) else: recursive_row, height = self._fill_row( columns, article_list, used, max_height) row.extend(recursive_row) return row, max_height return [], 0 @property def articles(self): if self._articles is not None: return self._articles article_content_type = ContentType.objects.get_for_model( article_models.ArticlePage) series_content_type = ContentType.objects.get_for_model( article_models.SeriesPage) articles = Page.objects.live().filter( models.Q(content_type=article_content_type) | models.Q(content_type=series_content_type)).annotate( sticky=models.Case( models.When(models.Q(seriespage__sticky=True) | (models.Q(articlepage__sticky=True)), then=models.Value(1)), default=models.Value(0), output_field=models.IntegerField())).exclude( models.Q(seriespage__slippery=True) | models.Q(articlepage__slippery=True)).order_by( "-sticky", "-first_published_at") articles = list(articles[:42]) used = [] if self.featured_item: used.append(self.featured_item.id) self._articles = self.get_article_set( self.number_of_columns_of_articles, self.number_of_rows_of_articles, articles, used) return self._articles @property def most_popular_article(self): if self._most_popular_article is not None: return self._most_popular_article def get_views(article): try: if article.analytics: return article.analytics.last_period_views except: return 0 # flatten the list of lists with generator articles = itertools.chain.from_iterable(self.articles) articles_by_popularity = sorted(articles, key=get_views) self._most_popular_article = articles_by_popularity[-1] return self._most_popular_article @property def typed_featured_item(self): if self.featured_item: featured_item = self.featured_item.content_type.get_object_for_this_type( id=self.featured_item.id) return featured_item def get_rows(self, number_of_rows, number_of_columns, items): rows = [] for row_index in range(0, number_of_rows): row = items[(row_index * number_of_columns):(row_index * number_of_columns) + number_of_columns] rows.append(row) return rows @property def external_articles(self): number_of_external_articles = self.number_of_rows_of_external_articles * self.number_of_columns_of_external_articles external_article_list = article_models.ExternalArticlePage.objects.live( ).order_by("-first_published_at")[:number_of_external_articles] return self.get_rows(self.number_of_rows_of_external_articles, self.number_of_columns_of_external_articles, external_article_list) @property def graphics(self): number_of_graphics = self.number_of_rows_of_visualizations * self.number_of_columns_of_visualizations graphics_list = article_models.ArticlePage.objects.live().filter( visualization=True).annotate(sticky_value=models.Case( models.When(models.Q(sticky_for_type_section=True), then=models.Value(1)), default=models.Value(0), output_field=models.IntegerField())).exclude( slippery_for_type_section=True).order_by( "-sticky_value", "-first_published_at")[:number_of_graphics] return self.get_rows(self.number_of_rows_of_visualizations, self.number_of_columns_of_visualizations, graphics_list) @property def series(self): number_of_series = self.number_of_rows_of_series series_list = article_models.SeriesPage.objects.live().annotate( sticky_value=models.Case(models.When( models.Q(sticky_for_type_section=True), then=models.Value(1)), default=models.Value(0), output_field=models.IntegerField()) ).exclude(slippery_for_type_section=True).order_by( "-sticky_value", "-first_published_at")[:number_of_series] return self.get_rows(self.number_of_rows_of_series, 1, series_list) content_panels = Page.content_panels + [ MultiFieldPanel([ PageChooserPanel("featured_item", "wagtailcore.Page"), FieldPanel("full_bleed_image_size"), ], heading="Main Feature"), MultiFieldPanel([ FieldPanel("number_of_rows_of_articles"), FieldPanel("number_of_columns_of_articles"), ], heading="Main Feed Settings"), MultiFieldPanel([ FieldPanel("number_of_rows_of_series"), ], heading="Series Section Settings"), MultiFieldPanel([ FieldPanel("number_of_rows_of_external_articles"), FieldPanel("number_of_columns_of_external_articles"), ], heading="External Articles Section Settings"), MultiFieldPanel([ FieldPanel("number_of_rows_of_visualizations"), FieldPanel("number_of_columns_of_visualizations"), ], heading="Visualization Section Settings"), ] style_panels = ThemeablePage.style_panels edit_handler = TabbedInterface([ ObjectList(content_panels, heading='Content'), ObjectList(style_panels, heading='Page Style Options'), ObjectList(Page.promote_panels, heading='Promote'), ObjectList(Page.settings_panels, heading='Settings', classname="settings"), ])
class HomePage(Page): subpage_types = [ 'articles.Articles', 'content.ContentPage', 'events.Events', 'people.People', 'topics.Topics', 'videos.Videos', ] template = 'home.html' # Content fields subtitle = TextField(max_length=250, blank=True, default='') button_text = CharField(max_length=30, blank=True, default='') button_url = CharField(max_length=2048, blank=True, default='') image = ForeignKey('mozimages.MozImage', null=True, blank=True, on_delete=SET_NULL, related_name='+') external_promos = StreamField( StreamBlock([ ('external_promo', FeaturedExternalBlock()), ], max_num=2, required=False), null=True, blank=True, help_text= 'Optional promo space under the header for linking to external sites, max. 2', ) featured = StreamField( StreamBlock([ ('article', PageChooserBlock(target_model=( 'articles.Article', 'externalcontent.ExternalArticle', ))), ('external_page', FeaturedExternalBlock()), ], max_num=4, required=False), null=True, blank=True, help_text='Optional space for featured articles, max. 4', ) about_title = TextField(max_length=250, blank=True, default='') about_subtitle = TextField(max_length=250, blank=True, default='') about_button_text = CharField(max_length=30, blank=True, default='') about_button_url = URLField(max_length=140, blank=True, default='') # Card fields card_title = CharField('Title', max_length=140, blank=True, default='') card_description = TextField('Description', max_length=400, blank=True, default='') card_image = ForeignKey( 'mozimages.MozImage', null=True, blank=True, on_delete=SET_NULL, related_name='+', verbose_name='Image', ) # Meta fields keywords = ClusterTaggableManager(through=HomePageTag, blank=True) # Editor panel configuration content_panels = Page.content_panels + [ MultiFieldPanel([ FieldPanel('subtitle'), FieldPanel('button_text'), FieldPanel('button_url'), ], heading='Header section', help_text='Optional fields for the header section'), MultiFieldPanel( [ ImageChooserPanel('image'), ], heading='Image', help_text= 'Optional image shown when sharing this page through social media' ), StreamFieldPanel('external_promos'), StreamFieldPanel('featured'), MultiFieldPanel( [ FieldPanel('about_title'), FieldPanel('about_subtitle'), FieldPanel('about_button_text'), FieldPanel('about_button_url'), ], heading='About section', help_text='Optional section to explain more about Mozilla'), ] # Card panels card_panels = [ MultiFieldPanel( [ FieldPanel('card_title'), FieldPanel('card_description'), ImageChooserPanel('card_image'), ], heading='Card overrides', help_text= ('Optional fields to override the default title, description and image when this page is shown as a card' )), ] # Meta panels meta_panels = [ MultiFieldPanel( [ FieldPanel('seo_title'), FieldPanel('search_description'), FieldPanel('keywords'), ], heading='SEO', help_text= 'Optional fields to override the default title and description for SEO purposes' ), ] # Settings panels settings_panels = [ FieldPanel('slug'), ] # Tabs edit_handler = TabbedInterface([ ObjectList(content_panels, heading='Content'), ObjectList(card_panels, heading='Card'), ObjectList(meta_panels, heading='Meta'), ObjectList(settings_panels, heading='Settings', classname='settings'), ]) @classmethod def can_create_at(cls, parent): # Allow only one instance of this page type return super().can_create_at(parent) and not cls.objects.exists() @property def primary_topics(self): """The site’s top-level topics, i.e. topics without a parent topic.""" from ..topics.models import Topic return Topic.objects.filter( parent_topics__isnull=True).live().public().order_by('title')
class ProjectPage(ContactFieldsMixin, BasePage): template = "patterns/pages/project/project_detail.html" hero_image = models.ForeignKey( "images.CustomImage", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", ) introduction = models.CharField(max_length=500, blank=True) introduction_image = models.ForeignKey( get_image_model_string(), null=True, blank=True, on_delete=models.SET_NULL, related_name="+", ) video_caption = models.CharField( blank=True, max_length=80, help_text=_("The text displayed next to the video play button"), ) video = models.URLField(blank=True) body = StreamField( [ ("quote_block", QuoteBlock()), ( "rich_text_block", RichTextBlock(features=[ "h2", "h3", "bold", "italic", "image", "ul", "ol", "link" ]), ), ("link_block", LinkBlock()), ], blank=True, verbose_name=_("Body copy"), ) start_date = models.DateField(blank=True, null=True) end_date = models.DateField(blank=True, null=True) funding = models.CharField(max_length=250, blank=True) specification_document = models.ForeignKey( "documents.CustomDocument", null=True, blank=True, related_name="+", on_delete=models.SET_NULL, verbose_name=_("Project PDF"), ) specification_document_link_text = models.CharField( max_length=80, blank=True, null=True, verbose_name=_("Project PDF link text"), help_text= _("You must enter link text if you add a Project PDF, e.g. 'Download project PDF'" ), ) gallery = StreamField([("slide", GalleryBlock())], blank=True, verbose_name=_("Gallery")) more_information_title = models.CharField(max_length=80, default="More information") more_information = StreamField( [("accordion_block", AccordionBlockWithTitle())], blank=True, verbose_name=_("More information"), ) partners = StreamField([("link", LinkBlock())], blank=True, verbose_name=_("Links to partners")) funders = StreamField([("link", LinkBlock())], blank=True, verbose_name=_("Links to funders")) quote_carousel = StreamField([("quote", QuoteBlock())], blank=True, verbose_name=_("Quote carousel")) external_links = StreamField([("link", LinkBlock())], blank=True, verbose_name="External Links") content_panels = BasePage.content_panels + [ MultiFieldPanel( [ImageChooserPanel("hero_image")], heading=_("Hero"), ), MultiFieldPanel( [ FieldPanel("introduction"), ImageChooserPanel("introduction_image"), FieldPanel("video"), FieldPanel("video_caption"), ], heading=_("Introduction"), ), StreamFieldPanel("body"), StreamFieldPanel("gallery"), MultiFieldPanel( [ FieldPanel("more_information_title"), StreamFieldPanel("more_information"), ], heading=_("More information"), ), MultiFieldPanel( [ InlinePanel( "project_lead", label="Project team lead", max_num=1), InlinePanel("related_staff", label="Project team"), ], "Project team and staff", ), InlinePanel("related_student_pages", label="Project students"), StreamFieldPanel("partners"), StreamFieldPanel("funders"), StreamFieldPanel("quote_carousel"), StreamFieldPanel("external_links"), MultiFieldPanel([*ContactFieldsMixin.panels], heading="Contact information"), ] key_details_panels = [ InlinePanel("related_sectors", label=_("Innovation RCA sectors")), InlinePanel("related_research_themes", label=_("Research themes")), InlinePanel("expertise", label=_("RCA Expertise")), InlinePanel("related_school_pages", label=_("Related schools")), InlinePanel("related_research_pages", label=_("Related research centres")), InlinePanel("research_types", label=_("Research types")), FieldPanel("start_date"), FieldPanel("end_date"), FieldPanel("funding"), MultiFieldPanel( [ DocumentChooserPanel("specification_document"), FieldPanel("specification_document_link_text"), ], heading="PDF download", ), ] edit_handler = TabbedInterface([ ObjectList(content_panels, heading="Content"), ObjectList(key_details_panels, heading="Key details"), ObjectList(BasePage.promote_panels, heading="Promote"), ObjectList(BasePage.settings_panels, heading="Settings"), ]) def get_related_projects(self): """ Displays latest projects from the parent School/Centre the project belongs to. IF there are no projects with the same theme School/Centre latest projects with a matching research_type will be displayed. IF there are no projects with a matching research_type, the latest projects with matching expertise tags will be displayed. Returns: List -- of filtered and formatted ProjectPages """ all_projects = ProjectPage.objects.live().public().not_page(self) schools = self.related_school_pages.values_list("page_id") projects = all_projects.filter( related_school_pages__page_id__in=schools).distinct() if projects: return format_projects_for_gallery(projects) research_centres = self.related_research_pages.values_list("page_id") projects = all_projects.filter( related_research_pages__page_id__in=research_centres).distinct() if projects: return format_projects_for_gallery(projects) research_types = self.research_types.values_list("research_type_id") projects = all_projects.filter( research_types__research_type_id__in=research_types).distinct() if projects: return format_projects_for_gallery(projects) expertise = self.expertise.values_list("area_of_expertise_id") projects = all_projects.filter( expertise__area_of_expertise_id__in=expertise).distinct() if projects: return format_projects_for_gallery(projects) def clean(self): super().clean() errors = defaultdict(list) if self.end_date and self.end_date < self.start_date: errors["end_date"].append( _("Events involving time travel are not supported")) if self.specification_document and not self.specification_document_link_text: errors["specification_document_link_text"].append( "You must provide link text for the document") if errors: raise ValidationError(errors) def get_related_school_or_centre(self): # returns the first related schools page, if none, return the related research # centre page related_school = self.related_school_pages.first() related_research_page = self.related_research_pages.first() if related_school: return related_school.page elif related_research_page: return related_research_page.page def get_expertise_linked_filters(self): """ For the expertise taxonomy thats listed out in key details, they need to link to the parent project picker page with a filter pre selected""" # Get parent page parent_picker = ProjectPickerPage.objects.parent_of( self).live().first() expertise = [] for i in self.expertise.all().select_related("area_of_expertise"): if parent_picker: expertise.append({ "title": i.area_of_expertise.title, "link": f"{parent_picker.url}?expertise={i.area_of_expertise.slug}", }) else: expertise.append({"title": i.area_of_expertise.title}) return expertise def get_sector_linked_filters(self): """ For the sector taxonomy thats listed out in key details, they need to link to the parent project picker page with a filter pre selected""" parent_picker = ProjectPickerPage.objects.parent_of( self).live().first() sectors = [] for i in self.related_sectors.all().select_related("sector"): if parent_picker: sectors.append({ "title": i.sector.title, "link": f"{parent_picker.url}?sector={i.sector.slug}", }) else: sectors.append({"title": i.sector.title}) return sectors def get_context(self, request, *args, **kwargs): context = super().get_context(request, *args, **kwargs) taxonomy_tags = [] if self.related_school_pages: for i in self.related_school_pages.all(): taxonomy_tags.append({"title": i.page.title}) if self.related_research_pages: for i in self.related_research_pages.all(): taxonomy_tags.append({"title": i.page.title}) if self.research_types: for i in self.research_types.all(): taxonomy_tags.append({"title": i.research_type.title}) context["expertise"] = self.get_expertise_linked_filters() context["sectors"] = self.get_sector_linked_filters() context["project_lead"] = self.project_lead.select_related("image") context["related_staff"] = self.related_staff.select_related("image") context["taxonomy_tags"] = taxonomy_tags context["related_projects"] = self.get_related_projects() return context @cached_property def is_startup_project(self): return len( self.research_types.filter(research_type__title="Start-up")) > 0
class HomePage(Page): """ The Home Page. This looks slightly more complicated than it is. You can see if you visit your site and edit the homepage that it is split between a: - Hero area - Body area - A promotional area - Moveable featured site sections """ # Hero section of HomePage image = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', help_text='Homepage image' ) hero_text = models.CharField( max_length=255, help_text='Write an introduction for the bakery', null=True, blank=True, ) hero_cta = models.CharField( verbose_name='Hero CTA', max_length=255, help_text='Text to display on Call to Action', null=True, blank=True, ) hero_cta_link = models.ForeignKey( 'wagtailcore.Page', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', verbose_name='Hero CTA link', help_text='Choose a page to link to for the Call to Action' ) # Body section of the HomePage body = StreamField( BaseStreamBlock(), verbose_name="Home content block", blank=True, null=True) # Promo section of the HomePage site_logo = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', help_text='Site Logo' ) features_text = RichTextField( null=True, blank=True, help_text='Write some feature description' ) feature_logo_1 = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', help_text='Feature Logo 1' ) feature_1_title = models.CharField( max_length=255, help_text='Feature Title 1' ) feature_1_text = RichTextField( null=True, blank=True, help_text='Write some feature 1 text description' ) feature_logo_2 = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', help_text='Feature Logo 2' ) feature_2_title = models.CharField( max_length=255, help_text='Feature Title 2' ) feature_2_text = RichTextField( null=True, blank=True, help_text='Write some feature 2 text description' ) feature_logo_3 = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', help_text='Feature Logo 3' ) feature_3_title = models.CharField( max_length=255, help_text='Feature Title 3' ) feature_3_text = RichTextField( null=True, blank=True, help_text='Write some feature 3 text description' ) feature_logo_4 = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', help_text='Feature Logo 4' ) feature_4_title = models.CharField( max_length=255, help_text='Feature Title 4' ) feature_4_text = RichTextField( null=True, blank=True, help_text='Write some feature 4 text description' ) custom_body_message = RichTextField( null=True, blank=True, help_text='Write some custom body message!' ) banner_image = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', help_text='Choose Banner Image' ) boolean_choices = ( ("yes", "Yes"), ("no", "No") ) show_navbar = models.CharField( choices=boolean_choices, max_length=5, help_text="Choose yes if you want to display the navbar on home page and no if you don't want to.", default=True) show_nav_extra = models.CharField( choices=boolean_choices, max_length=5, help_text="Choose yes if you want the secondary navbar to show on home page or no if you don't want to", default=True) show_footer = models.CharField( choices=boolean_choices, max_length=5, help_text="Choose yes if you want the Footer to show on home page or no if you don't want to", default="yes") content_panels = Page.content_panels + [ MultiFieldPanel([ ImageChooserPanel('image'), FieldPanel('hero_text', classname="full"), MultiFieldPanel([ FieldPanel('hero_cta'), PageChooserPanel('hero_cta_link'), ]) ], heading="Hero section"), StreamFieldPanel('body'), MultiFieldPanel([ ImageChooserPanel('site_logo'), FieldPanel('features_text'), MultiFieldPanel([ ImageChooserPanel('feature_logo_1'), FieldPanel('feature_1_title'), FieldPanel('feature_1_text'), ]), MultiFieldPanel([ ImageChooserPanel('feature_logo_2'), FieldPanel('feature_2_title'), FieldPanel('feature_2_text'), ]), MultiFieldPanel([ ImageChooserPanel('feature_logo_3'), FieldPanel('feature_3_title'), FieldPanel('feature_3_text'), ]), MultiFieldPanel([ ImageChooserPanel('feature_logo_4'), FieldPanel('feature_4_title'), FieldPanel('feature_4_text'), ]) ], heading="Feature section", classname="collapsible"), FieldPanel('custom_body_message'), ImageChooserPanel('banner_image') ] customization_panels = [ FieldPanel('show_navbar'), FieldPanel('show_nav_extra'), FieldPanel('show_footer') ] edit_handler = TabbedInterface([ ObjectList(content_panels, heading='Content'), ObjectList(customization_panels, heading='Customization'), ObjectList(Page.promote_panels, heading='Promote'), ObjectList(Page.settings_panels, heading='Settings', classname="settings"), ]) def __str__(self): return self.title
class HomePage(BasePage): subpage_types = [ "articles.Articles", "content.ContentPage", "events.Events", "people.People", "topics.Topics", "videos.Videos", ] template = "home.html" # Content fields subtitle = TextField(max_length=250, blank=True, default="") button_text = CharField(max_length=30, blank=True, default="") button_url = CharField(max_length=2048, blank=True, default="") image = ForeignKey( "mozimages.MozImage", null=True, blank=True, on_delete=SET_NULL, related_name="+", ) featured = StreamField( StreamBlock( [ ( "post", PageChooserBlock(target_model=( "articles.Article", "externalcontent.ExternalArticle", )), ), ( "content_page", PageChooserBlock(target_model=("content.ContentPage", )), ), ("external_page", FeaturedExternalBlock()), ( "video", PageChooserBlock(target_model=( "videos.Video", # NB: ExternalVideo is NOT allowed on the homepage # "externalcontent.ExternalVideo" )), ), ], min_num=2, max_num=5, required=False, ), null=True, blank=True, help_text= ("Optional space for featured posts, videos or links, min. 2, max. 5. " "Note that External Video is NOT allowed here."), ) featured_people = StreamField( StreamBlock( [("person", PageChooserBlock(target_model="people.Person"))], max_num=3, required=False, ), null=True, blank=True, help_text="Optional featured people, max. 3", ) about_title = TextField(max_length=250, blank=True, default="") about_subtitle = TextField(max_length=250, blank=True, default="") about_button_text = CharField(max_length=30, blank=True, default="") about_button_url = URLField(max_length=140, blank=True, default="") # Card fields card_title = CharField("Title", max_length=140, blank=True, default="") card_description = TextField("Description", max_length=400, blank=True, default="") card_image = ForeignKey( "mozimages.MozImage", null=True, blank=True, on_delete=SET_NULL, related_name="+", verbose_name="Image", ) # Meta fields keywords = ClusterTaggableManager(through=HomePageTag, blank=True) # Editor panel configuration content_panels = BasePage.content_panels + [ MultiFieldPanel( [ FieldPanel("subtitle"), FieldPanel("button_text"), FieldPanel("button_url"), ], heading="Header section", help_text="Optional fields for the header section", ), MultiFieldPanel( [ImageChooserPanel("image")], heading="Image", help_text= ("Optional image shown when sharing this page through social media" ), ), StreamFieldPanel("featured"), StreamFieldPanel("featured_people"), MultiFieldPanel( [ FieldPanel("about_title"), FieldPanel("about_subtitle"), FieldPanel("about_button_text"), FieldPanel("about_button_url"), ], heading="About section", help_text="Optional section to explain more about Mozilla", ), ] # Card panels card_panels = [ MultiFieldPanel( [ FieldPanel("card_title"), FieldPanel("card_description"), ImageChooserPanel("card_image"), ], heading="Card overrides", help_text=(( "Optional fields to override the default title, " "description and image when this page is shown as a card")), ) ] # Meta panels meta_panels = [ MultiFieldPanel( [ FieldPanel("seo_title"), FieldPanel("search_description"), ImageChooserPanel("social_image"), FieldPanel("keywords"), ], heading="SEO", help_text=("Optional fields to override the default " "title and description for SEO purposes"), ) ] # Settings panels settings_panels = [FieldPanel("slug")] # Tabs edit_handler = TabbedInterface([ ObjectList(content_panels, heading="Content"), ObjectList(card_panels, heading="Card"), ObjectList(meta_panels, heading="Meta"), ObjectList(settings_panels, heading="Settings", classname="settings"), ]) @classmethod def can_create_at(cls, parent): # Allow only one instance of this page type return super().can_create_at(parent) and not cls.objects.exists() @property def primary_topics(self): """The site’s top-level topics, i.e. topics without a parent topic.""" from ..topics.models import Topic return Topic.published_objects.filter(parent_topics__isnull=True)
class ContentPage(BasePage): parent_page_types = ["home.HomePage", "content.ContentPage", "topics.Topic"] subpage_types = ["people.People", "content.ContentPage"] template = "content.html" # Content fields description = RichTextField( blank=True, default="", features=RICH_TEXT_FEATURES_SIMPLE, help_text=( "Optional short text description, " f"max. {DESCRIPTION_MAX_LENGTH} characters" ), max_length=DESCRIPTION_MAX_LENGTH, ) body = CustomStreamField( help_text=( "Main page body content. Supports rich text, images, embed via URL, " "embed via HTML, and inline code snippets" ) ) sidebar = CustomStreamField( null=True, blank=True, help_text=( "Sidebar page body content (narrower than main body). Rendered to the " "right of the main body content in desktop and below it in mobile." "Supports rich text, images, embed via URL, " "embed via HTML, and inline code snippets" ), ) # Card fields card_title = CharField("Title", max_length=140, blank=True, default="") card_description = TextField("Description", max_length=140, blank=True, default="") card_image = ForeignKey( "mozimages.MozImage", null=True, blank=True, on_delete=SET_NULL, related_name="+", verbose_name="Image", help_text="An image in 16:9 aspect ratio", ) card_image = ForeignKey( "mozimages.MozImage", null=True, blank=True, on_delete=SET_NULL, related_name="+", verbose_name="Image", help_text="An image in 16:9 aspect ratio", ) card_image_3_2 = ForeignKey( "mozimages.MozImage", null=True, blank=True, on_delete=SET_NULL, related_name="+", verbose_name="Image", help_text="An image in 3:2 aspect ratio", ) # Meta fields nav_description = TextField( "Navigation description", max_length=DESCRIPTION_MAX_LENGTH, blank=True, default="", ) icon = FileField( upload_to="contentpage/icons", blank=True, default="", help_text=( "MUST be a black-on-transparent SVG icon ONLY, " "with no bitmap embedded in it." ), validators=[check_for_svg_file], ) keywords = ClusterTaggableManager(through=ContentPageTag, blank=True) # Editor panel configuration content_panels = BasePage.content_panels + [ FieldPanel("description"), StreamFieldPanel("body"), StreamFieldPanel("sidebar"), ] # Card panels card_panels = [ FieldPanel( "card_title", help_text=( "Title displayed when this page is " "represented by a card in a list of items. " "If blank, the page's title is used." ), ), FieldPanel( "card_description", help_text=( "Summary text displayed when this page is " "represented by a card in a list of items. " "If blank, the page's description is used." ), ), MultiFieldPanel( [ImageChooserPanel("card_image")], heading="16:9 Image", help_text=( "Image used for representing this page as a Card. " "Should be 16:9 aspect ratio. " "If not specified a fallback will be used. " "This image is also shown when sharing this page via social " "media unless a social image is specified." ), ), MultiFieldPanel( [ImageChooserPanel("card_image_3_2")], heading="3:2 Image", help_text=( "Image used for representing this page as a Card. " "Should be 3:2 aspect ratio. " "If not specified a fallback will be used. " ), ), ] # Meta panels meta_panels = [ FieldPanel( "nav_description", help_text="Text to display in the navigation with the title for this page.", ), MultiFieldPanel( [FieldPanel("icon")], heading="Theme", help_text=( "This icon will be used if, for example, this page is shown in a Menu" ), ), MultiFieldPanel( [ FieldPanel("seo_title"), FieldPanel("search_description"), ImageChooserPanel("social_image"), FieldPanel("keywords"), ], heading="SEO", help_text=( "Optional fields to override the default title and " "description for SEO purposes" ), ), ] # Settings panels settings_panels = BasePage.settings_panels + [ FieldPanel("slug"), FieldPanel("show_in_menus"), ] # Tabs edit_handler = TabbedInterface( [ ObjectList(content_panels, heading="Content"), ObjectList(card_panels, heading="Card"), ObjectList(meta_panels, heading="Meta"), ObjectList(settings_panels, heading="Settings", classname="settings"), ] ) # Search config search_fields = BasePage.search_fields + [ # Inherit search_fields from Page # "title" is already specced in BasePage index.SearchField("description"), index.SearchField("body"), index.SearchField("sidebar"), # Add FilterFields for things we may be filtering on (eg topics) index.FilterField("slug"), ]
class ResearchCentrePage(LegacyNewsAndEventsMixin, BasePage): template = "patterns/pages/researchcentre/research_centre.html" hero_image = models.ForeignKey( "images.CustomImage", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", ) introduction = models.TextField(max_length=500, blank=True) about_page_url = models.URLField(blank=True) about_page = models.ForeignKey( "wagtailcore.Page", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", ) about_page_link_text = models.CharField(blank=True, max_length=120) introduction_image = models.ForeignKey( get_image_model_string(), null=True, blank=True, on_delete=models.SET_NULL, related_name="+", ) video_caption = models.CharField( blank=True, max_length=80, help_text=_("The text dipsplayed next to the video play button"), ) video = models.URLField(blank=True) primary_staff_url = models.URLField( blank=True, help_text=_("The external URL to the staff member page"), verbose_name=_("Head of centre URL"), ) primary_staff_name = models.CharField( blank=True, max_length=250, help_text=_("The name of the staff member"), verbose_name=_("Head of centre full name"), ) primary_staff_role = models.CharField( blank=True, max_length=120, help_text=_("The role of the staff member, E.G 'Head of Programme'"), verbose_name=_("Head of centre role"), ) centre_address = RichTextField(blank=True, features=["link"]) centre_tel = PhoneNumberField(blank=True) centre_tel_display_text = models.CharField( max_length=120, help_text=( "Specify specific text or numbers to display for the linked tel " "number, e.g. +44 (0)20 7590 1234 or +44 (0)7749 183783"), blank=True, ) twitter_username = models.CharField( blank=True, max_length=15, help_text=_("The Research Centres Twitter username")) centre_email = models.EmailField(blank=True) more_research_centre_content_title = models.CharField( blank=True, max_length=250, help_text=_( "The title value displayed above the Research centre news carousel" ), ) highlights_title = models.CharField( max_length=120, blank=True, help_text= _("The title value displayed above the Research highlights gallery showing project pages" ), ) related_programmes_title = models.CharField( max_length=250, help_text= _("The title value displayed above the Research centre related_programmes" ), ) staff_title = models.CharField( blank=True, max_length=250, help_text=_("The title value displayed above the related staff grid"), ) staff_link = models.URLField(blank=True, help_text=_("Add a link to see all staff")) staff_link_text = models.CharField( blank=True, help_text=_("The text to display on the link to all staff"), max_length=80, ) related_links = StreamField([("link", LinkBlock())], blank=True, verbose_name="Related Links") research_projects_link = models.URLField( blank=True, help_text=_( "Add a link to a pre-filtered project listing, " "E.G https://rca.ac.uk/research/projects/?school-or-centre=helen-hamlyn-centre#results" ), ) content_panels = BasePage.content_panels + [ MultiFieldPanel( [ImageChooserPanel("hero_image")], heading="Hero", ), MultiFieldPanel( [ FieldPanel("introduction"), ImageChooserPanel("introduction_image"), FieldPanel("about_page_url"), PageChooserPanel("about_page"), FieldPanel("about_page_link_text"), FieldPanel("video"), FieldPanel("video_caption"), ], heading=_("Introduction"), ), MultiFieldPanel( [InlinePanel("research_spaces", label="Research spaces")], heading="Research spaces", ), MultiFieldPanel( [ FieldPanel("highlights_title"), InlinePanel( "research_projects", label=_("Project pages"), max_num=8), FieldPanel("research_projects_link"), ], heading=_("Research centre highlights"), ), MultiFieldPanel( [ InlinePanel("research_opportunities", label="Research opportunities") ], heading="Research opportunities", ), MultiFieldPanel( [ FieldPanel("more_research_centre_content_title"), InlinePanel("research_news", label="Research guides"), ], heading="More research centre content", ), MultiFieldPanel( [ FieldPanel("staff_title"), InlinePanel("related_staff", label="staff"), FieldPanel("staff_link"), FieldPanel("staff_link_text"), ], heading="Research Centre Staff", ), FieldPanel("related_programmes_title"), StreamFieldPanel("related_links"), FieldPanel("legacy_news_and_event_tags"), ] key_details_panels = [ MultiFieldPanel( [ FieldPanel("primary_staff_name"), FieldPanel("primary_staff_url"), FieldPanel("primary_staff_role"), ], heading="Primary staff member", ), MultiFieldPanel( [ FieldPanel("centre_address"), FieldPanel("centre_tel"), FieldPanel("centre_tel_display_text"), FieldPanel("centre_email"), ], heading="Centre contact information", ), FieldPanel("twitter_username"), ] edit_handler = TabbedInterface([ ObjectList(content_panels, heading="Content"), ObjectList(key_details_panels, heading="Key details"), ObjectList(BasePage.promote_panels, heading="Promote"), ObjectList(BasePage.settings_panels, heading="Settings"), ]) def get_related_projects(self): child_projects = [] for value in self.research_projects.select_related("page"): if value.page and value.page.live: page = value.page.specific child_projects.append({ "title": page.title, "link": page.url, "image": page.hero_image, "description": page.introduction, }) return child_projects def get_research_spaces(self): research_spaces = [] for value in self.research_spaces.select_related("page"): if value.page and value.page.live: page = value.page.specific research_spaces.append({ "title": page.title, "link": page.url, "image": page.listing_image, "description": page.introduction if hasattr(page, "introduction") else None, }) return research_spaces def get_research_opportunities(self): research_opportunities = [] for value in self.research_opportunities.select_related("page"): if value.page.live: page = value.page.specific research_opportunities.append({ "title": page.title, "link": page.url, "image": page.listing_image, "description": page.introduction, }) return research_opportunities def get_research_news(self): research_news = { "title": self.more_research_centre_content_title, "slides": [] } for value in self.research_news.select_related("page"): if value.page.live: page = value.page.specific project_type = "PROJECT" if hasattr(page, "research_types") and page.research_types.first(): project_type = page.research_types.first( ).research_type.title page_type_mapping = { "GuidePage": "GUIDE", "ProjectPage": project_type, "ResearchCentrePage": "RESEARCH CENTRE", "ShortCoursePage": "SHORT COURSE", "ProgrammePage": "PROGRAMME", } page_type = page_type_mapping.get(page.__class__.__name__, None) research_news["slides"].append({ "value": { "title": page.title, "link": page.url, "image": page.hero_image if hasattr(page, "hero_image") else page.listing_image, "summary": page.introduction if hasattr(page, "introduction") else page.listing_summary, "type": page_type, } }) return research_news def get_related_programme_pages(self): from rca.programmes.models import ProgrammePage from rca.shortcourses.models import ShortCoursePage programme_pages_qs = ProgrammePage.objects.filter( related_schools_and_research_pages__page_id=self.id).live() short_course_pages_qs = ShortCoursePage.objects.filter( related_schools_and_research_pages__page_id=self.id).live() qs = list(chain(programme_pages_qs, short_course_pages_qs)) programme_pages = [{ "title": self.related_programmes_title, "related_items": qs }] return programme_pages def clean(self): errors = defaultdict(list) if self.twitter_username.startswith("@"): self.twitter_username = self.twitter_username[1:] if self.about_page and self.about_page_url: errors["about_page"].append( "Please choose between an internal page, or an external link") if (self.about_page and not self.about_page_link_text or self.about_page_url and not self.about_page_link_text): errors["about_page"].append( "Please add some link text for the about page") if self.staff_link and not self.staff_link_text: errors["staff_link_text"].append( "Please add some text for the link to all staff") if errors: raise ValidationError(errors) def get_context(self, request, *args, **kwargs): context = super().get_context(request, *args, **kwargs) context["about_page"] = (self.about_page.url if self.about_page else self.about_page_url) context["projects"] = self.get_related_projects() context["research_spaces"] = self.get_research_spaces() context["research_opportunities"] = self.get_research_opportunities() context["research_news"] = self.get_research_news() context["related_staff"] = self.related_staff.all context["related_programmes"] = self.get_related_programme_pages() context[ "staff_title"] = self.staff_title if self.staff_title else "Staff" return context
class ProjectPage(Page): title_de = models.CharField(max_length=255, blank=True) subtitle = models.CharField(max_length=255, blank=True) subtitle_de = models.CharField(max_length=255, blank=True) teaser = RichTextField(blank=True) teaser_de = RichTextField(blank=True) BODY_BLOCKS = [ ('richtext', RichTextBlock()), ('html', RawHTMLBlock()), ('image', ImageChooserBlock()), ] body = StreamField(BODY_BLOCKS, blank=True) body_de = StreamField(BODY_BLOCKS, blank=True) collaboration = RichTextField(blank=True) collaboration_de = RichTextField(blank=True) publications = RichTextField(blank=True) publications_de = RichTextField(blank=True) references = RichTextField(blank=True) references_de = RichTextField(blank=True) start_date = models.DateField(blank=True, null=True) end_date = models.DateField(blank=True, null=True) STATUS_IN_PROGRESS = 'IN_PROGRESS' STATUS_COMPLETED = 'COMPLETED' STATUS_CHOICES = ((STATUS_IN_PROGRESS, _('in progress')), (STATUS_COMPLETED, _('completed'))) status = models.CharField(max_length=32, choices=STATUS_CHOICES, default=STATUS_IN_PROGRESS) trans_title = TranslatedTextField('title') trans_subtitle = TranslatedTextField('subtitle') trans_teaser = TranslatedTextField('teaser') trans_body = TranslatedStreamField('body') trans_collaboration = TranslatedTextField('collaboration') trans_publications = TranslatedTextField('publications') trans_references = TranslatedTextField('references') search_fields = Page.search_fields + [ index.SearchField('title_de'), index.SearchField('subtitle'), index.SearchField('subtitle_de'), index.SearchField('teaser'), index.SearchField('teaser_de'), index.SearchField('body'), index.SearchField('body_de'), ] content_panels = [ FieldPanel('title', classname="full title"), FieldPanel('subtitle', classname="full title"), FieldPanel('teaser', classname='full'), StreamFieldPanel('body', classname='full'), FieldPanel('collaboration', classname='full'), FieldPanel('publications', classname='full'), FieldPanel('references', classname='full'), ] content_de_panels = [ FieldPanel('title_de', classname="full title"), FieldPanel('subtitle_de', classname="full title"), FieldPanel('teaser_de', classname='full'), StreamFieldPanel('body_de', classname='full'), FieldPanel('collaboration_de', classname='full'), FieldPanel('publications_de', classname='full'), FieldPanel('references_de', classname='full'), ] misc_panels = [ FieldPanel('status'), FieldPanel('start_date'), FieldPanel('end_date'), ] edit_handler = TabbedInterface([ ObjectList(content_panels, heading='Content'), ObjectList(content_de_panels, heading='Content DE'), ObjectList(misc_panels, heading='Misc'), ObjectList(Page.promote_panels, heading='Promote'), ObjectList(Page.settings_panels, heading='Settings', classname='settings'), ]) parent_page_types = ['projects.ProjectIndexPage'] @property def color(self): return self.get_parent().specific.color
class FormPage(WagtailCaptchaEmailForm): title_sv = models.CharField(max_length=255) translated_title = TranslatedField('title', 'title_sv') use_recaptcha = models.BooleanField( default=False, verbose_name=_("Use Recaptcha"), ) intro_en = StreamField( WAGTAIL_STATIC_BLOCKTYPES + [ ('contact_card', ContactCardBlock()), ], verbose_name=_('English Introduction'), blank=True, ) intro_sv = StreamField( WAGTAIL_STATIC_BLOCKTYPES + [ ('contact_card', ContactCardBlock()), ], verbose_name=_('Swedish Introduction'), blank=True, ) intro = TranslatedField('intro_en', 'intro_sv') thank_you_text_en = StreamField( WAGTAIL_STATIC_BLOCKTYPES + [ ('contact_card', ContactCardBlock()), ], verbose_name=_('English Thank You Text'), blank=True, ) thank_you_text_sv = StreamField( WAGTAIL_STATIC_BLOCKTYPES + [ ('contact_card', ContactCardBlock()), ], verbose_name=_('Swedish Thank You Text'), blank=True, ) thank_you_text = TranslatedField('thank_you_text_en', 'thank_you_text_sv') form_title_en = models.CharField(verbose_name=_('English Form Title'), max_length=255, blank=True) form_title_sv = models.CharField( verbose_name=_('Swedish Form Title'), max_length=255, blank=True, ) form_title = TranslatedField('form_title_en', 'form_title_sv') general_panels = [ InlinePanel('form_fields', label="Form fields"), MultiFieldPanel([ FieldRowPanel([ FieldPanel('from_address', classname="col6"), FieldPanel('to_address', classname="col6"), ]), FieldPanel('subject'), ], "Email"), ] content_panels_en = AbstractEmailForm.content_panels + [ StreamFieldPanel('intro_en'), FieldPanel('form_title_en', classname="full title"), StreamFieldPanel('thank_you_text_en'), ] content_panels_sv = [ FieldPanel('title_sv', classname="full title"), StreamFieldPanel('intro_sv'), FieldPanel('form_title_sv', classname="full title"), StreamFieldPanel('thank_you_text_sv'), ] custom_settings_panel = Page.settings_panels + [ MultiFieldPanel([ FieldPanel('use_recaptcha'), ], 'Recaptcha') ] edit_handler = TabbedInterface([ ObjectList(general_panels, heading=_('General')), ObjectList(content_panels_en, heading=_('English')), ObjectList(content_panels_sv, heading=_('Swedish')), ObjectList(Page.promote_panels, heading=_('Promote')), ObjectList(custom_settings_panel, heading=_('Settings')), ])
class UseCasePage(Page): category = models.CharField(max_length=2, choices=CATEGORY_CHOICES) image = models.ForeignKey( 'a4_candy_cms_images.CustomImage', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', verbose_name="Use Case Header Image", help_text="The Image that is shown on the use case item page " + "and the use case index page") title_de = models.CharField(max_length=250, blank=True, verbose_name="German Title") title_en = models.CharField(max_length=250, blank=True, verbose_name="English Title") body_streamfield_de = fields.StreamField( [('paragraph', blocks.RichTextBlock()), ('html', blocks.RawHTMLBlock()), ('examples', ExampleBlock())], blank=True) body_streamfield_en = fields.StreamField( [('paragraph', blocks.RichTextBlock()), ('html', blocks.RawHTMLBlock()), ('examples', ExampleBlock())], blank=True) subtitle = TranslatedField('title_de', 'title_en') teaser = TranslatedField('teaser_de', 'teaser_en') body = TranslatedField('body_streamfield_de', 'body_streamfield_en') def get_context(self, request): category = self.category if category: try: use_cases = UseCasePage.objects\ .filter(category=category)\ .exclude(id=self.id) except ValueError: use_cases = [] context = super().get_context(request) context['other_use_cases'] = use_cases return context en_content_panels = [ FieldPanel('title_en'), StreamFieldPanel('body_streamfield_en') ] de_content_panels = [ FieldPanel('title_de'), StreamFieldPanel('body_streamfield_de') ] common_panels = [ FieldPanel('title'), ImageChooserPanel('image'), FieldPanel('slug'), FieldPanel('category') ] edit_handler = TabbedInterface([ ObjectList(common_panels, heading='Common'), ObjectList(en_content_panels, heading='English'), ObjectList(de_content_panels, heading='German') ]) subpage_types = []
class BlogIndexPage(Page): featured_page = models.ForeignKey( 'cms_blog.BlogPage', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', ) subtitle = models.CharField(max_length=200) subpage_types = ['cms_blog.BlogPage'] @property def blogs(self): blogs = BlogPage.objects.live() blogs = blogs.order_by('-last_published_at') return blogs def get_context(self, request): blogs = self.blogs category = request.GET.get('category') if category: try: category = int(category) blogs = blogs.filter(categories__pk=category) except ValueError: blogs = [] page = request.GET.get('page', 1) paginator = Paginator(blogs, 5) try: blogs = paginator.page(page) except InvalidPage: raise Http404 context = super(BlogIndexPage, self).get_context(request) context['blogs'] = blogs context['categories'] = Category.objects.all() context['category'] = category return context content_panels = [ FieldPanel('title'), FieldPanel('subtitle'), PageChooserPanel('featured_page'), ] promote_panels = [ MultiFieldPanel([ FieldPanel('slug'), FieldPanel('seo_title'), FieldPanel('search_description') ]) ] edit_handler = TabbedInterface([ ObjectList(content_panels, heading='Content'), ObjectList(promote_panels, heading='Promote') ])
class ProjectsPage(Page): headers = StreamField([ ('h_hero', _H_HeroBlock(icon='image')), # ('code', blocks.RawHTMLBlock(null=True, blank=True, classname="full", icon='code')) ]) price_min = models.IntegerField( verbose_name="Preis der billigsten Einheit", null=True, blank=False) price_max = models.IntegerField( verbose_name= "Preis der teuersten Einheit (leer lassen wenn nur eine Einheit vorhanden ist)", null=True, blank=True) buy_available = models.BooleanField(verbose_name="Kaufmöglichkeit") rent_available = models.BooleanField(verbose_name="Mietmöglichkeit") location_name = models.CharField( verbose_name="Ortsname (z.B. Villach-Landskron)", null=True, blank=True, max_length=255) coordinates = models.CharField( verbose_name= "Standpunkt (Koordinaten, z.B. Beispiel: 46.6120061,13.916085)", null=True, blank=True, max_length=255) sections = StreamField( [ ('s_info', _S_InfoBlock(icon='fa-info')), ('s_contentcenter', _S_ContentCenter(icon='fa-info')), ('s_contentright', _S_ContentRight(icon='fa-info')), ('s_contentleft', _S_ContentLeft(icon='fa-info')), # ('s_imagegallery', _S_ImageGallery(icon='fa-info')) ], null=True, blank=True) gallery = StreamField([('g_gallery', _G_GalleryBlock(icon='fa-info'))], null=True, blank=True) flats = StreamField([('f_flats', _F_FlatsBlock(icon='fa-info'))], verbose_name="Wohneinheiten-Pages", null=True, blank=True) # footers = StreamField([ # ], null=True, blank=False) token = models.CharField(null=True, blank=True, max_length=255) # graphql_fields = [ # GraphQLStreamfield("headers"), # GraphQLStreamfield("sections"), # ] main_content_panels = [ StreamFieldPanel('headers'), FieldPanel('price_min'), FieldPanel('price_max'), FieldPanel('buy_available'), FieldPanel('rent_available'), FieldPanel('location_name'), FieldPanel('coordinates'), StreamFieldPanel('sections'), StreamFieldPanel('gallery'), StreamFieldPanel('flats') # StreamFieldPanel('footers') ] edit_handler = TabbedInterface([ ObjectList(Page.content_panels + main_content_panels, heading='Main'), ObjectList(Page.promote_panels, heading='Settings', classname="settings") ]) preview_modes = []
class Event(Page): resource_type = 'event' parent_page_types = ['events.Events'] subpage_types = [] template = 'event.html' # Content fields description = TextField(max_length=250, blank=True, default='') image = ForeignKey('mozimages.MozImage', null=True, blank=True, on_delete=SET_NULL, related_name='+') body = CustomStreamField(blank=True, null=True) agenda = StreamField(StreamBlock([ ('agenda_item', AgendaItemBlock()), ], required=False), blank=True, null=True) speakers = StreamField(StreamBlock([ ('speaker', PageChooserBlock(required=False, target_model='people.Person')), ('external_speaker', ExternalSpeakerBlock(required=False)), ], required=False), blank=True, null=True) # Card fields card_title = CharField('Title', max_length=140, blank=True, default='') card_description = TextField('Description', max_length=140, blank=True, default='') card_image = ForeignKey( 'mozimages.MozImage', null=True, blank=True, on_delete=SET_NULL, related_name='+', verbose_name='Image', ) # Meta fields start_date = DateField(default=datetime.date.today) end_date = DateField(blank=True, null=True) venue = TextField( max_length=250, blank=True, default='', help_text= 'Full address of the event venue, displayed on the event detail page') location = CharField( max_length=100, blank=True, default='', help_text= 'Location details (city and country), displayed on event cards') latitude = FloatField(blank=True, null=True) longitude = FloatField(blank=True, null=True) register_url = URLField('Register URL', blank=True, null=True) keywords = ClusterTaggableManager(through=EventTag, blank=True) # Content panels content_panels = Page.content_panels + [ FieldPanel('description'), ImageChooserPanel('image'), StreamFieldPanel('body'), StreamFieldPanel('agenda'), StreamFieldPanel('speakers'), ] # Card panels card_panels = [ FieldPanel('card_title'), FieldPanel('card_description'), ImageChooserPanel('card_image'), ] # Meta panels meta_panels = [ MultiFieldPanel([ FieldPanel('start_date'), FieldPanel('end_date'), FieldPanel('venue'), FieldPanel('location'), FieldPanel('latitude'), FieldPanel('longitude'), FieldPanel('register_url'), ], heading='Event details'), MultiFieldPanel( [ InlinePanel('topics'), ], heading='Topics', help_text= ('These are the topic pages the article will appear on. The first ' 'topic in the list will be treated as the primary topic.')), MultiFieldPanel([ FieldPanel('seo_title'), FieldPanel('search_description'), FieldPanel('keywords'), ], heading='SEO'), ] # Settings panels settings_panels = [ FieldPanel('slug'), ] edit_handler = TabbedInterface([ ObjectList(content_panels, heading='Content'), ObjectList(card_panels, heading='Card'), ObjectList(meta_panels, heading='Meta'), ObjectList(settings_panels, heading='Settings', classname='settings'), ]) @property def is_upcoming(self): """Returns whether an event is in the future.""" return self.start_date > datetime.date.today() @property def primary_topic(self): """Return the first (primary) topic specified for the event.""" article_topic = self.topics.first() return article_topic.topic if article_topic else None @property def month_group(self): return self.start_date.replace(day=1) @property def event_dates(self): """Return a formatted string of the event start and end dates""" event_dates = self.start_date.strftime("%b %-d") if self.end_date: event_dates += " – " start_month = self.start_date.strftime("%m") if self.end_date.strftime("%m") == start_month: event_dates += self.end_date.strftime("%-d") else: event_dates += self.end_date.strftime("%b %-d") return event_dates @property def event_dates_full(self): """Return a formatted string of the event start and end dates, including the year""" return self.event_dates + self.start_date.strftime(", %Y")
class CFGOVPage(Page): authors = ClusterTaggableManager(through=CFGOVAuthoredPages, blank=True, verbose_name='Authors', help_text='A comma separated list of ' + 'authors.', related_name='authored_pages') tags = ClusterTaggableManager(through=CFGOVTaggedPages, blank=True, related_name='tagged_pages') language = models.CharField(choices=ref.supported_languagues, default='en', max_length=2) social_sharing_image = models.ForeignKey( 'v1.CFGOVImage', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', help_text=( 'Optionally select a custom image to appear when users share this ' 'page on social media websites. Recommended size: 1200w x 630h. ' 'Maximum size: 4096w x 4096h.')) schema_json = JSONField( null=True, blank=True, verbose_name='Schema JSON', help_text=mark_safe( 'Enter structured data for this page in JSON-LD format, ' 'for use by search engines in providing rich search results. ' '<a href="https://developers.google.com/search/docs/guides/' 'intro-structured-data">Learn more.</a> ' 'JSON entered here will be output in the ' '<code><head></code> of the page between ' '<code><script type="application/ld+json"></code> and ' '<code></script></code> tags.'), ) force_breadcrumbs = models.BooleanField( "Force breadcrumbs on child pages", default=False, blank=True, help_text=( "Normally breadcrumbs don't appear on pages one or two levels " "below the homepage. Check this option to force breadcrumbs to " "appear on all children of this page no matter how many levels " "below the homepage they are (for example, if you want " "breadcrumbs to appear on all children of a top-level campaign " "page)."), ) # This is used solely for subclassing pages we want to make at the CFPB. is_creatable = False objects = CFGOVPageManager() search_fields = Page.search_fields + [ index.SearchField('sidefoot'), ] # These fields show up in either the sidebar or the footer of the page # depending on the page type. sidefoot = StreamField([ ('call_to_action', molecules.CallToAction()), ('related_links', molecules.RelatedLinks()), ('related_posts', organisms.RelatedPosts()), ('related_metadata', molecules.RelatedMetadata()), ('email_signup', organisms.EmailSignUp()), ('sidebar_contact', organisms.SidebarContactInfo()), ('rss_feed', molecules.RSSFeed()), ('social_media', molecules.SocialMedia()), ('reusable_text', v1_blocks.ReusableTextChooserBlock(ReusableText)), ], blank=True) # Panels promote_panels = Page.promote_panels + [ ImageChooserPanel('social_sharing_image'), FieldPanel('force_breadcrumbs', 'Breadcrumbs'), ] sidefoot_panels = [ StreamFieldPanel('sidefoot'), ] settings_panels = [ MultiFieldPanel(promote_panels, 'Settings'), InlinePanel('categories', label="Categories", max_num=2), FieldPanel('tags', 'Tags'), FieldPanel('authors', 'Authors'), FieldPanel('schema_json', 'Structured Data'), MultiFieldPanel(Page.settings_panels, 'Scheduled Publishing'), FieldPanel('language', 'language'), ] # Tab handler interface guide because it must be repeated for each subclass edit_handler = TabbedInterface([ ObjectList(Page.content_panels, heading='General Content'), ObjectList(sidefoot_panels, heading='Sidebar/Footer'), ObjectList(settings_panels, heading='Configuration'), ]) def clean(self): super(CFGOVPage, self).clean() validate_social_sharing_image(self.social_sharing_image) def get_authors(self): """ Returns a sorted list of authors. Default is alphabetical """ return self.alphabetize_authors() def alphabetize_authors(self): """ Alphabetize authors of this page by last name, then first name if needed """ # First sort by first name author_names = self.authors.order_by('name') # Then sort by last name return sorted(author_names, key=lambda x: x.name.split()[-1]) def related_metadata_tags(self): # Set the tags to correct data format tags = {'links': []} filter_page = self.get_filter_data() for tag in self.specific.tags.all(): tag_link = {'text': tag.name, 'url': ''} if filter_page: relative_url = filter_page.relative_url(filter_page.get_site()) param = '?topics=' + tag.slug tag_link['url'] = relative_url + param tags['links'].append(tag_link) return tags def get_filter_data(self): for ancestor in self.get_ancestors().reverse().specific(): if ancestor.specific_class.__name__ in [ 'BrowseFilterablePage', 'SublandingFilterablePage', 'EventArchivePage', 'NewsroomLandingPage' ]: return ancestor return None def get_breadcrumbs(self, request): ancestors = self.get_ancestors().specific() for i, ancestor in enumerate(ancestors): if ancestor.is_child_of(request.site.root_page): if ancestor.specific.force_breadcrumbs: return ancestors[i:] return ancestors[i + 1:] return [] def get_appropriate_descendants(self, inclusive=True): return CFGOVPage.objects.live().descendant_of(self, inclusive) def get_appropriate_siblings(self, inclusive=True): return CFGOVPage.objects.live().sibling_of(self, inclusive) def get_context(self, request, *args, **kwargs): context = super(CFGOVPage, self).get_context(request, *args, **kwargs) for hook in hooks.get_hooks('cfgovpage_context_handlers'): hook(self, request, context, *args, **kwargs) # Add any banners that are enabled and match the current request path # to a context variable. context['banners'] = Banner.objects \ .filter(enabled=True) \ .annotate( # This annotation creates a path field in the QuerySet # that we can use in the filter below to compare with # the url_pattern defined on each enabled banner. path=Value(request.path, output_field=models.CharField())) \ .filter(path__regex=F('url_pattern')) if self.schema_json: context['schema_json'] = self.schema_json return context def serve(self, request, *args, **kwargs): """ If request is ajax, then return the ajax request handler response, else return the super. """ if request.method == 'POST': return self.serve_post(request, *args, **kwargs) # Force the page's language on the request translation.activate(self.language) request.LANGUAGE_CODE = translation.get_language() return super(CFGOVPage, self).serve(request, *args, **kwargs) def _return_bad_post_response(self, request): if request.is_ajax(): return JsonResponse({'result': 'error'}, status=400) return HttpResponseBadRequest(self.url) def serve_post(self, request, *args, **kwargs): """Handle a POST to a specific form on the page. Attempts to retrieve form_id from the POST request, which must be formatted like "form-name-index" where the "name" part is the name of a StreamField on the page and the "index" part refers to the index of the form element in the StreamField. If form_id is found, it returns the response from the block method retrieval. If form_id is not found, or if form_id is not a block that implements get_result() to process the POST, it returns an error response. """ form_module = None form_id = request.POST.get('form_id', None) if form_id: form_id_parts = form_id.split('-') if len(form_id_parts) == 3: streamfield_name = form_id_parts[1] streamfield = getattr(self, streamfield_name, None) if streamfield is not None: try: streamfield_index = int(form_id_parts[2]) except ValueError: streamfield_index = None if streamfield_index is not None: try: form_module = streamfield[streamfield_index] except IndexError: form_module = None try: result = form_module.block.get_result(self, request, form_module.value, True) except AttributeError: return self._return_bad_post_response(request) if isinstance(result, HttpResponse): return result context = self.get_context(request, *args, **kwargs) context['form_modules'][streamfield_name].update( {streamfield_index: result}) return TemplateResponse(request, self.get_template(request, *args, **kwargs), context) class Meta: app_label = 'v1' def parent(self): parent = self.get_ancestors(inclusive=False).reverse()[0].specific return parent # To be overriden if page type requires JS files every time @property def page_js(self): return [] @property def streamfield_js(self): js = [] block_cls_names = get_page_blocks(self) for block_cls_name in block_cls_names: block_cls = import_string(block_cls_name) if hasattr(block_cls, 'Media') and hasattr(block_cls.Media, 'js'): js.extend(block_cls.Media.js) return js # Returns the JS files required by this page and its StreamField blocks. @property def media(self): return sorted(set(self.page_js + self.streamfield_js)) # Returns an image for the page's meta Open Graph tag @property def meta_image(self): return self.social_sharing_image @property def post_preview_cache_key(self): return 'post_preview_{}'.format(self.id)
class Events(Page): parent_page_types = ['home.HomePage'] subpage_types = ['events.Event'] template = 'events.html' # Content fields featured = StreamField( StreamBlock([ ('event', PageChooserBlock( required=False, target_model=[ 'events.Event', 'externalcontent.ExternalEvent', ], )), ('external_page', FeaturedExternalBlock()), ], min_num=0, max_num=1, required=False), null=True, blank=True, ) # Meta fields keywords = ClusterTaggableManager(through=EventsTag, blank=True) # Content panels content_panels = Page.content_panels + [StreamFieldPanel('featured')] # Meta panels meta_panels = [ MultiFieldPanel([ FieldPanel('seo_title'), FieldPanel('search_description'), FieldPanel('keywords'), ], heading='SEO'), ] # Settings panels settings_panels = [ FieldPanel('slug'), ] edit_handler = TabbedInterface([ ObjectList(content_panels, heading='Content'), ObjectList(meta_panels, heading='Meta'), ObjectList(settings_panels, heading='Settings', classname='settings'), ]) class Meta: verbose_name_plural = 'Events' def get_context(self, request): context = super().get_context(request) context['filters'] = self.get_filters() return context @property def events(self): """Return events in chronological order""" return get_combined_events(self) def get_filters(self): from ..topics.models import Topic return { 'months': True, 'topics': Topic.objects.live().public().order_by('title'), }
class ArticleConstants(models.Model): select_a_drug = TranslatedField() select_a_drug_en, select_a_drug_es = select_a_drug.init( models.CharField, ('select_a_drug_en', 'select_a_drug_es'), max_length=30, blank=True) no_drug_selected_text = TranslatedField() no_drug_selected_text_en, no_drug_selected_text_es = no_drug_selected_text.init( models.CharField, ('no_drug_selected_text_en', 'no_drug_selected_text_es'), max_length=255, blank=True) source = TranslatedField() source_en, source_es = source.init( models.CharField, ('source_en', 'source_es'), max_length=15, blank=True) campaign_caption = TranslatedField() campaign_caption_en, campaign_caption_es = campaign_caption.init( RichTextField, ('campaign_caption_en', 'campaign_caption_es'), blank=True) cookie_banner_message = TranslatedField() cookie_banner_message_en, cookie_banner_message_es = cookie_banner_message.init( models.TextField, ('cookie_banner_message_en', 'cookie_banner_message_es'), blank=True) cookie_banner_button = TranslatedField() cookie_banner_button_en, cookie_banner_button_es = cookie_banner_button.init( models.TextField, ('cookie_banner_button_en', 'cookie_banner_button_es'), blank=True) cookie_banner_link = TranslatedField() cookie_banner_link_en, cookie_banner_link_es = cookie_banner_link.init( models.URLField, ('cookie_banner_link_en', 'cookie_banner_link_es'), blank=True) default_share_blurb = TranslatedField() default_share_blurb_en, default_share_blurb_es = default_share_blurb.init( models.TextField, ('default_share_blurb_en', 'default_share_blurb_es'), blank=True) panels = [ FieldPanel('select_a_drug_en'), FieldPanel('no_drug_selected_text_en'), FieldPanel('source_en'), FieldPanel('campaign_caption_en'), FieldPanel('cookie_banner_message_en'), FieldPanel('cookie_banner_button_en'), FieldPanel('cookie_banner_link_en') ] panels_es = [ FieldPanel('select_a_drug_es'), FieldPanel('no_drug_selected_text_es'), FieldPanel('source_es'), FieldPanel('campaign_caption_es'), FieldPanel('cookie_banner_message_es'), FieldPanel('cookie_banner_button_es'), FieldPanel('cookie_banner_link_es') ] edit_handler = TabbedInterface([ ObjectList(panels, heading='EN Content'), ObjectList(panels_es, heading='ES content'), ]) def __str__(self): return "Article constants"
class UniquePage(Page): city = models.CharField(null=True, blank=False, max_length=255) zip_code = models.CharField(null=True, blank=False, max_length=255) address = models.CharField(null=True, blank=False, max_length=255) telephone = models.CharField(null=True, blank=False, max_length=255) telefax = models.CharField(null=True, blank=False, max_length=255) vat_number = models.CharField(null=True, blank=False, max_length=255) tax_id = models.CharField(null=True, blank=False, max_length=255) trade_register_number = models.CharField(null=True, blank=False, max_length=255) court_of_registry = models.CharField(null=True, blank=False, max_length=255) place_of_registry = models.CharField(null=True, blank=False, max_length=255) trade_register_number = models.CharField(null=True, blank=False, max_length=255) ownership = models.CharField(null=True, blank=False, max_length=255) email = models.CharField(null=True, blank=False, max_length=255) sociallinks = models.CharField(null=True, blank=False, max_length=255) copyrightholder = models.CharField(null=True, blank=False, max_length=255) privacy = RichTextField(null=True, blank=False, features=[ 'bold', 'italic', 'underline', 'strikethrough', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'ol', 'ul', 'hr', 'embed', 'link', 'superscript', 'subscript', 'document-link', 'image', 'code' ]) sociallinks = StreamField([('link', blocks.URLBlock())]) headers = StreamField( [('h_hero', _H_HeroBlock(null=True, blank=False, icon='image')), ('code', blocks.RawHTMLBlock( null=True, blank=True, classname="full", icon='code'))], null=True, blank=False) sections = StreamField( [('s_why', _S_WhyBlock(null=True, blank=False, icon='group')), ('s_individual', _S_IndividualBlock(null=True, blank=False, icon='user')), ('s_experts', _S_ExpertsBlock(null=True, blank=False, icon='pick')), ('s_lab', _S_LabBlock(null=True, blank=False, icon='snippet')), ('s_method', _S_MethodBlock(null=True, blank=False, icon='site')), ('s_services', _S_ServicesBlock(null=True, blank=False, icon='openquote')), ('s_reviews', _S_ReviewsBlock(null=True, blank=False, icon='form')), ('s_facebook', _S_FacebookBlock(null=True, blank=False, icon='fa-facebook-official')), ('s_instagram', _S_InstagramBlock(null=True, blank=False, icon='fa-instagram')), ('s_pricing', _S_PricingBlock(null=True, blank=False, icon='home')), ('s_about', _S_AboutBlock( null=True, blank=False, icon='fa-quote-left')), ('code', blocks.RawHTMLBlock( null=True, blank=True, classname="full", icon='code'))], null=True, blank=False) footers = StreamField( [('f_info', _F_InfoBlock(null=True, blank=False, icon='placeholder')), ('code', blocks.RawHTMLBlock( null=True, blank=True, classname="full", icon='code'))], null=True, blank=False) token = models.CharField(null=True, blank=False, max_length=255) main_content_panels = [ StreamFieldPanel('headers'), StreamFieldPanel('sections'), StreamFieldPanel('footers') ] imprint_panels = [ MultiFieldPanel( [ FieldPanel('city'), FieldPanel('zip_code'), FieldPanel('address'), FieldPanel('telephone'), FieldPanel('telefax'), FieldPanel('email'), FieldPanel('copyrightholder') ], heading="contact", ), MultiFieldPanel( [ FieldPanel('vat_number'), FieldPanel('tax_id'), FieldPanel('trade_register_number'), FieldPanel('court_of_registry'), FieldPanel('place_of_registry'), FieldPanel('trade_register_number'), FieldPanel('ownership') ], heading="legal", ), StreamFieldPanel('sociallinks'), MultiFieldPanel( [FieldPanel('privacy')], heading="privacy", ) ] token_panel = [FieldPanel('token')] edit_handler = TabbedInterface([ ObjectList(Page.content_panels + main_content_panels, heading='Main'), ObjectList(imprint_panels, heading='Imprint'), ObjectList(Page.promote_panels + token_panel + Page.settings_panels, heading='Settings', classname="settings") ])