class EventPage(Page): page_ptr = models.OneToOneField(Page, parent_link=True, related_name="+", on_delete=models.CASCADE) AUDIENCE_CHOICES = ( ("public", "Public"), ("private", "Private"), ) date_from = models.DateField("Start date") date_to = models.DateField( "End date", null=True, blank=True, help_text="Not required if event is on a single day", ) time_from = models.TimeField("Start time", null=True, blank=True) time_to = models.TimeField("End time", null=True, blank=True) audience = models.CharField(max_length=255, choices=AUDIENCE_CHOICES) location = models.CharField(max_length=255) body = RichTextField(blank=True) cost = models.CharField(max_length=255) signup_link = models.URLField(blank=True) feed_image = models.ForeignKey( "wagtailimages.Image", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", ) api_fields = ( "date_from", "date_to", "time_from", "time_to", "audience", "location", "body", "cost", "signup_link", "feed_image", "carousel_items", "related_links", "speakers", ) search_fields = Page.search_fields + [ index.SearchField("get_audience_display"), index.SearchField("location"), index.SearchField("body"), ] def get_event_index(self): # Find closest ancestor which is an event index return EventIndexPage.objects.ancester_of(self).last()
class RichTextSection(models.Model): snippet = ParentalKey("MultiSectionRichTextSnippet", related_name="sections", on_delete=models.CASCADE) body = RichTextField() panels = [ FieldPanel("body"), ]
class EventPage(Page): date_from = models.DateField("Start date", null=True) date_to = models.DateField( "End date", null=True, blank=True, help_text="Not required if event is on a single day", ) time_from = models.TimeField("Start time", null=True, blank=True) time_to = models.TimeField("End time", null=True, blank=True) audience = models.CharField(max_length=255, choices=EVENT_AUDIENCE_CHOICES) location = models.CharField(max_length=255) body = RichTextField(blank=True) cost = models.CharField(max_length=255) signup_link = models.URLField(blank=True) feed_image = models.ForeignKey( "wagtailimages.Image", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", ) categories = ParentalManyToManyField(EventCategory, blank=True) search_fields = [ index.SearchField("get_audience_display"), index.SearchField("location"), index.SearchField("body"), index.FilterField("url_path"), ] password_required_template = "tests/event_page_password_required.html" base_form_class = EventPageForm content_panels = [ FieldPanel("title", classname="full title"), FieldPanel("date_from"), FieldPanel("date_to"), FieldPanel("time_from"), FieldPanel("time_to"), FieldPanel("location"), FieldPanel("audience"), FieldPanel("cost"), FieldPanel("signup_link"), InlinePanel("carousel_items", label="Carousel items"), FieldPanel("body", classname="full"), InlinePanel("speakers", label="Speakers", heading="Speaker lineup"), InlinePanel("related_links", label="Related links"), FieldPanel("categories"), # InlinePanel related model uses `pk` not `id` InlinePanel("head_counts", label="Head Counts"), ] promote_panels = [ MultiFieldPanel(COMMON_PANELS, "Common page configuration"), FieldPanel("feed_image"), ]
class CustomDocument(AbstractDocument): description = models.TextField(blank=True) fancy_description = RichTextField(blank=True) admin_form_fields = Document.admin_form_fields + ( "description", "fancy_description", ) class Meta: unique_together = [("title", "collection")]
class CustomImage(AbstractImage): caption = models.CharField(max_length=255, blank=True) fancy_caption = RichTextField(blank=True) not_editable_field = models.CharField(max_length=255, blank=True) admin_form_fields = Image.admin_form_fields + ( "caption", "fancy_caption", ) class Meta: unique_together = [("title", "collection")]
class FormClassAdditionalFieldPage(Page): location = models.CharField(max_length=255) body = RichTextField(blank=True) content_panels = [ FieldPanel("title", classname="full title"), FieldPanel("location"), FieldPanel("body"), FieldPanel("code"), # not in model, see set base_form_class ] base_form_class = FormClassAdditionalFieldPageForm
class PersonPage(Page, ContactFieldsMixin): page_ptr = models.OneToOneField(Page, parent_link=True, related_name="+", on_delete=models.CASCADE) first_name = models.CharField(max_length=255) last_name = models.CharField(max_length=255) intro = RichTextField(blank=True) biography = RichTextField(blank=True) image = models.ForeignKey( "wagtailimages.Image", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", ) feed_image = models.ForeignKey( "wagtailimages.Image", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", ) api_fields = ( "first_name", "last_name", "intro", "biography", "image", "feed_image", "related_links", ) + ContactFieldsMixin.api_fields search_fields = Page.search_fields + [ index.SearchField("first_name"), index.SearchField("last_name"), index.SearchField("intro"), index.SearchField("biography"), ]
class CommitteesPage(MenuPage): subpage_types = ["CommitteePage"] description = RichTextField(null=True, blank=True) content_panels = Page.content_panels + [ FieldPanel("description"), ] def get_context(self, request): # Update context to include only published posts, ordered by reverse-chron context = super().get_context(request) committees = CommitteePage.objects.all().order_by("title") context["committees"] = committees return context
class FormPageWithCustomSubmissionListView(AbstractEmailForm): """Form Page with customised submissions listing view""" intro = RichTextField(blank=True) thank_you_text = RichTextField(blank=True) def get_submissions_list_view_class(self): from .views import CustomSubmissionsListView return CustomSubmissionsListView def get_submission_class(self): return CustomFormPageSubmission def get_data_fields(self): data_fields = [ ("useremail", "User email"), ] data_fields += super().get_data_fields() return data_fields content_panels = [ FieldPanel("title", classname="full title"), FieldPanel("intro", classname="full"), InlinePanel("form_fields", label="Form fields"), FieldPanel("thank_you_text", classname="full"), MultiFieldPanel( [ FieldPanel("to_address", classname="full"), FieldPanel("from_address", classname="full"), FieldPanel("subject", classname="full"), ], "Email", ), ]
class InfoPage(Page): body = RichTextField(blank=True) additional_content = StreamField( [("embed", blocks.RawHTMLBlock())], null=True, blank=True, use_json_field=True, ) search_fields = Page.search_fields + [ index.SearchField("body"), ] content_panels = Page.content_panels + [ FieldPanel("body", classname="full"), FieldPanel("additional_content"), ]
class ManyToManyBlogPage(Page): """ A page type with two different kinds of M2M relation. We don't formally support these, but we don't want them to cause hard breakages either. """ body = RichTextField(blank=True) adverts = models.ManyToManyField(Advert, blank=True) blog_categories = models.ManyToManyField(BlogCategory, through=BlogCategoryBlogPage, blank=True) # make first_published_at editable on this page model settings_panels = Page.settings_panels + [ FieldPanel("first_published_at"), ]
class HomePage(Page): page_ptr = models.OneToOneField(Page, parent_link=True, related_name="+", on_delete=models.CASCADE) body = RichTextField(blank=True) api_fields = ( "body", "carousel_items", "related_links", ) search_fields = Page.search_fields + [ index.SearchField("body"), ] class Meta: verbose_name = "homepage"
class BlogIndexPage(Page): page_ptr = models.OneToOneField(Page, parent_link=True, related_name="+", on_delete=models.CASCADE) intro = RichTextField(blank=True) api_fields = ( "intro", "related_links", ) search_fields = Page.search_fields + [ index.SearchField("intro"), ] def get_blog_entries(self): # Get list of live blog pages that are descendants of this page entries = BlogEntryPage.objects.descendant_of(self).live() # Order by most recent date first entries = entries.order_by("-date") return entries def get_context(self, request): # Get blog entries entries = self.get_blog_entries() # Filter by tag tag = request.GET.get("tag") if tag: entries = entries.filter(tags__name=tag) paginator = Paginator(entries, per_page=10) entries = paginator.get_page(request.GET.get("page")) # Update template context context = super().get_context(request) context["entries"] = entries return context
class ContactPage(Page, ContactFieldsMixin): page_ptr = models.OneToOneField(Page, parent_link=True, related_name="+", on_delete=models.CASCADE) body = RichTextField(blank=True) feed_image = models.ForeignKey( "wagtailimages.Image", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", ) api_fields = ( "body", "feed_image", ) + ContactFieldsMixin.api_fields search_fields = Page.search_fields + [ index.SearchField("body"), ]
class StandardIndexPage(Page): page_ptr = models.OneToOneField(Page, parent_link=True, related_name="+", on_delete=models.CASCADE) intro = RichTextField(blank=True) feed_image = models.ForeignKey( "wagtailimages.Image", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", ) api_fields = ( "intro", "feed_image", "related_links", ) search_fields = Page.search_fields + [ index.SearchField("intro"), ]
class BlogEntryPage(Page): page_ptr = models.OneToOneField(Page, parent_link=True, related_name="+", on_delete=models.CASCADE) body = RichTextField() tags = ClusterTaggableManager(through="BlogEntryPageTag", blank=True) date = models.DateField("Post date") feed_image = models.ForeignKey( "wagtailimages.Image", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", ) api_fields = ( APIField("body"), APIField("tags"), APIField("date"), APIField("feed_image"), APIField( "feed_image_thumbnail", serializer=ImageRenditionField("fill-300x300", source="feed_image"), ), APIField("carousel_items"), APIField("related_links"), ) search_fields = Page.search_fields + [ index.SearchField("body"), ] def get_blog_index(self): # Find closest ancestor which is a blog index return BlogIndexPage.ancestor_of(self).last()
class NewsIndexPage(Page): message = RichTextField(blank=True) content_panels = Page.content_panels + [ FieldPanel("message", classname="full") ] subpage_types = [ "news.NewsPage", # appname.ModelName ] def get_context(self, request): # Update context to include only published posts, ordered by reverse-chron context = super().get_context(request) blogpages = self.get_children().live().order_by("-last_published_at") paginator = Paginator(blogpages, 5) page = request.GET.get("page") try: resources = paginator.page(page) except PageNotAnInteger: resources = paginator.page(1) except EmptyPage: resources = paginator.page(paginator.num_pages) context["blogpages"] = resources return context
class FormPageWithCustomSubmission(AbstractEmailForm): """ This Form page: * Have custom submission model * Have custom related_name (see `FormFieldWithCustomSubmission.page`) * Saves reference to a user * Doesn't render html form, if submission for current user is present """ intro = RichTextField(blank=True) thank_you_text = RichTextField(blank=True) def get_context(self, request, *args, **kwargs): context = super().get_context(request) context["greeting"] = "hello world" return context def get_form_fields(self): return self.custom_form_fields.all() def get_data_fields(self): data_fields = [ ("useremail", "User email"), ] data_fields += super().get_data_fields() return data_fields def get_submission_class(self): return CustomFormPageSubmission def process_form_submission(self, form): form_submission = self.get_submission_class().objects.create( form_data=form.cleaned_data, page=self, user=form.user, ) if self.to_address: addresses = [x.strip() for x in self.to_address.split(",")] content = "\n".join([ x[1].label + ": " + str(form.data.get(x[0])) for x in form.fields.items() ]) send_mail( self.subject, content, addresses, self.from_address, ) # process_form_submission should now return the created form_submission return form_submission def serve(self, request, *args, **kwargs): if (self.get_submission_class().objects.filter( page=self, user__pk=request.user.pk).exists()): return TemplateResponse(request, self.template, self.get_context(request)) return super().serve(request, *args, **kwargs) content_panels = [ FieldPanel("title", classname="full title"), FieldPanel("intro", classname="full"), InlinePanel("custom_form_fields", label="Form fields"), FieldPanel("thank_you_text", classname="full"), MultiFieldPanel( [ FieldPanel("to_address", classname="full"), FieldPanel("from_address", classname="full"), FieldPanel("subject", classname="full"), ], "Email", ), ]
class EventIndex(Page): intro = RichTextField(blank=True) ajax_template = "tests/includes/event_listing.html" def get_events(self): return self.get_children().live().type(EventPage) def get_paginator(self): return Paginator(self.get_events(), 4) def get_context(self, request, page=1): # Pagination paginator = self.get_paginator() try: events = paginator.page(page) except PageNotAnInteger: events = paginator.page(1) except EmptyPage: events = paginator.page(paginator.num_pages) # Update context context = super().get_context(request) context["events"] = events return context def route(self, request, path_components): if self.live and len(path_components) == 1: try: return self.serve(request, page=int(path_components[0])) except (TypeError, ValueError): pass return super().route(request, path_components) def get_static_site_paths(self): # Get page count page_count = self.get_paginator().num_pages # Yield a path for each page for page in range(page_count): yield "/%d/" % (page + 1) # Yield from superclass for path in super().get_static_site_paths(): yield path def get_sitemap_urls(self, request=None): # Add past events url to sitemap return super().get_sitemap_urls( request=request) + [{ "location": self.full_url + "past/", "lastmod": self.latest_revision_created_at, }] def get_cached_paths(self): return super().get_cached_paths() + ["/past/"] content_panels = [ FieldPanel("title", classname="full title"), FieldPanel("intro", classname="full"), ]
class CommitteePage(Page): parent_page_types = ["CommitteesPage"] subpage_types = [] COMMITTEE = "CT" WORKING_GROUP = "WG" CAUCUS = "CU" PRIORITY = "PR" FORMATION_CHOICES = [ (COMMITTEE, "Committee"), (WORKING_GROUP, "Working Group"), (CAUCUS, "Caucus"), (PRIORITY, "Priority"), ] description = RichTextField() formation_type = models.CharField( max_length=2, choices=FORMATION_CHOICES, default="" ) leader = models.ForeignKey( Person, null=True, blank=True, on_delete=models.SET_NULL, related_name="committee_leader", ) leader_name = models.CharField(max_length=30, null=True) email = models.EmailField() people = models.ManyToManyField(Person, related_name="committee_member", blank=True) sign_up_form_endpoint = models.TextField(null=True, blank=True) search_fields = Page.search_fields + [index.SearchField("description")] content_panels = Page.content_panels + [ FieldPanel("formation_type"), FieldPanel("description"), FieldPanel("leader_name"), FieldPanel("email"), FieldPanel("sign_up_form_endpoint"), ] def get_context(self, request): context = super().get_context(request) context["upcoming_events"] = list(self.events.filter(start__gt=datetime.now())) if self.sign_up_form_endpoint: print(self.sign_up_form_endpoint) print(self.slug) print(settings.ACTIONNETWORK_API_KEYS) print(settings.ACTIONNETWORK_API_KEYS.get(self.slug)) embeds = requests.get( f"{self.sign_up_form_endpoint}embed", headers={ "OSDI-API-Token": settings.ACTIONNETWORK_API_KEYS.get(self.slug) }, ).json() print(embeds) embed_code = embeds["embed_standard_layout_only_styles"] else: embed_code = None context["embed_code"] = embed_code return context def __str__(self): return f"{self.title.title()} {self.get_formation_type_display()}"
class NewsPage(Page): main_story_image = models.ForeignKey( "wagtailimages.Image", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", ) main_story_heading = models.CharField(max_length=500, null=True, blank=True) main_story_copy = RichTextField(blank=True) action_network_href = models.URLField(blank=True, null=True) related_stories = StreamField( [("related_story", RelatedStoryBlock())], null=True, blank=True, collapsed=False, default=upcoming_events_as_related_stories, use_json_field=True, ) parent_page_type = ["news.NewsIndexPage"] # appname.ModelName search_fields = Page.search_fields + [ index.SearchField("main_story_copy"), ] content_panels = Page.content_panels + [ FieldPanel("main_story_image", ), FieldPanel("main_story_heading"), FieldPanel("main_story_copy", classname="full"), FieldPanel("related_stories"), ] def save(self, *args, **kwargs): if self.action_network_href: email.edit( self.action_network_href, { "subject": self.title, "body": render_block_to_string("news/news_page.html", "content", context={"page": self}), "from": "STL DSA", "reply_to": "*****@*****.**", }, settings.ACTIONNETWORK_API_KEYS["main"], ) else: response = email.create( self.title, # main_story_html + "".join(related_stories_html), render_block_to_string("news/news_page.html", "content", context={"page": self}), "STL DSA", "*****@*****.**", settings.ACTIONNETWORK_API_KEYS["main"], ) action_network_href = response.json()["_links"]["self"]["href"] self.action_network_href = action_network_href if self.go_live_at and self.go_live_at > timezone.now(): polling.poll( lambda: requests.get( self.action_network_href, headers={ "OSDI-API-Token": settings.ACTIONNETWORK_API_KEYS[ "main"] }, ).json().get("total_targeted", 0) > 0, step=1, timeout=10, step_function=lambda step: step + 2, ) email.schedule( f"{self.action_network_href}/schedule", self.go_live_at, settings.ACTIONNETWORK_API_KEYS["main"], ) super().save(*args, **kwargs) # Call the "real" save() method.
class PageWithRichText(Page): body = RichTextField(max_length=255, blank=True, null=True)