class LinkFields(LinkFieldUrl, LinkFieldPage, LinkFieldDocument, LinkFieldImage): link_text = models.CharField(max_length=40, blank=True) @property def link(self): if self.link_page: return self.link_page elif self.link_document: return self.link_document elif self.link_image: return self.link_image else: return self.link_external panels = [ FieldPanel('link_text'), FieldPanel('link_external'), PageChooserPanel('link_page'), DocumentChooserPanel('link_document'), ImageChooserPanel('link_image') ] class Meta: abstract = True
class RedirectPage(PublicBasePage, LinkFields): """ Page object for setting up redirects that need to appear in the sidebar in a given section of the site. """ subpage_types = [] content_panels = Page.content_panels + [ MultiFieldPanel([ PageChooserPanel('link_page'), FieldPanel('link_external'), DocumentChooserPanel('link_document'), ], heading='Redirect to') ] + PublicBasePage.content_panels search_fields = PublicBasePage.search_fields + [ index.SearchField('link_page', partial_match=True), index.SearchField('link_external', partial_match=True), ] def serve(self, request): """ Override the serve method to create a redirect. """ return redirect(self.link, permanent=True)
class LinkFields(models.Model): link_external = models.URLField("External link", blank=True) link_page = models.ForeignKey( 'wagtailcore.Page', null=True, blank=True, related_name='+' ) link_document = models.ForeignKey( 'wagtaildocs.Document', null=True, blank=True, related_name='+' ) @property def link(self): if self.link_page: return self.link_page.url elif self.link_document: return self.link_document.url else: return self.link_external panels = [ FieldPanel('link_external'), PageChooserPanel('link_page'), DocumentChooserPanel('link_document'), ] class Meta: abstract = True
class FacultyResources(models.Model): resource = models.ForeignKey( Resource, null=True, help_text="Manage resources through snippets.", related_name='+') link_external = models.URLField("External link", blank=True) link_page = models.ForeignKey('wagtailcore.Page', null=True, blank=True, related_name='+') link_document = models.ForeignKey('wagtaildocs.Document', null=True, blank=True, related_name='+') api_fields = ( 'resource', 'link_external', 'link_page', 'link_document', ) panels = [ SnippetChooserPanel('resource', Resource), FieldPanel('link_external'), PageChooserPanel('link_page'), DocumentChooserPanel('link_document'), ]
class ImportantLinks(BaseSetting): terms_pdf = models.ForeignKey('wagtaildocs.Document', null=True, blank=True, related_name='+') panels = [ DocumentChooserPanel('terms_pdf'), ]
class StudentResources(models.Model): resource = models.ForeignKey( StudentResource, null=True, help_text="Manage resources through snippets.", related_name='+') def get_resource_heading(self): return self.resource.heading resource_heading = property(get_resource_heading) def get_resource_description(self): return self.resource.description resource_description = property(get_resource_description) link_external = models.URLField("External link", blank=True) link_page = models.ForeignKey('wagtailcore.Page', null=True, blank=True, related_name='+') link_document = models.ForeignKey('wagtaildocs.Document', null=True, blank=True, related_name='+') def get_link_document(self): return build_document_url(self.link_document.url) link_document_url = property(get_link_document) def get_document_title(self): return self.link_document.title link_document_title = property(get_document_title) link_text = models.CharField(max_length=255, help_text="Call to Action Text") api_fields = ( 'resource_heading', 'resource_description', 'link_external', 'link_page', 'link_document_url', 'link_document_title', 'link_text', ) panels = [ SnippetChooserPanel('resource'), FieldPanel('link_external'), PageChooserPanel('link_page'), DocumentChooserPanel('link_document'), FieldPanel('link_text'), ]
class HomePageSection(Page): main_image = models.ForeignKey(CustomImage, null=True, blank=True, on_delete=models.SET_NULL, related_name='+') orientation = models.CharField(max_length=50, choices=(('left', 'LEFT'), ('right', 'RIGHT'), ('centre', 'CENTRE'), ('freeform', 'FREEFORM')), default='left') link_page = models.ForeignKey('wagtailcore.Page', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') button_text = models.CharField(max_length=255, help_text="Button title", null=True, blank=True, default="button") sectionClassName = models.SlugField(max_length=100, help_text="no spaces", default="homepage-section") body = RichTextField(blank=True) streamBody = StreamField([ ('heading', blocks.CharBlock(classname="full-title")), ('paragraph', blocks.RichTextBlock()), ('image', ImageChooserBlock()), ('markup', RawHTMLBlock()), ], null=True, blank=True) sectionBackgroundSelector = models.ForeignKey('wagtaildocs.Document', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') content_panels = Page.content_panels + [ FieldPanel('orientation'), FieldPanel('sectionClassName'), MultiFieldPanel(IndexLink.panels, "Related index page"), ImageChooserPanel('main_image'), DocumentChooserPanel('sectionBackgroundSelector'), FieldPanel('body', classname="section-body"), StreamFieldPanel('streamBody'), InlinePanel('related_links', label="Section link items"), ] def get_context(self, request): context = super(HomePageSection, self).get_context(request) return context
class ArticleDocumentItem(Orderable, models.Model): document = models.ForeignKey( 'wagtaildocs.Document', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', verbose_name=_('document') ) panels = [ DocumentChooserPanel('document', ), ] page = ParentalKey('aroe.ArticlePage', related_name='document_items')
class NoCommentPage(Page): date = models.DateField("Post date") intro = RichTextField(blank=True) body = RichTextField(blank=True) category = models.ForeignKey('news.CategoryPage', null=True, blank=True, related_name='+', on_delete=models.SET_NULL) link_document = models.ForeignKey( 'wagtaildocs.Document', null=True, blank=True, related_name='+', on_delete=models.SET_NULL, ) @property def categories(self): # Get list of live news pages that are descendants of this page categories = CategoryPage.objects.live() return categories @property def news_item(self): # Get list of live news pages that are descendants of this page news_item = NoCommentPage.objects.live().descendant_of( self.get_parent()) # Order by most recent date first news_item = news_item.order_by('-date')[:5] return news_item def get_context(self, request): context = super(NoCommentPage, self).get_context(request) context['news'] = self.news_item return context search_fields = Page.search_fields + ( index.SearchField('intro'), index.SearchField('body'), ) content_panels = Page.content_panels + [ FieldPanel('date'), PageChooserPanel('category'), FieldPanel('intro', classname="full"), FieldPanel('body', classname="full"), DocumentChooserPanel('link_document'), ]
class ResourcePageRelatedDocument(Orderable, models.Model): """ Class used to store multiple documents related to a specific ResourcePage """ document = models.ForeignKey('wagtaildocs.Document', null=True, blank=True, related_name='+') page = ParentalKey('resources.ResourcePage', related_name='related_documents') panels = [ DocumentChooserPanel('document'), ]
class LinkFieldDocument(models.Model): link_document = models.ForeignKey('wagtaildocs.Document', null=True, blank=True, related_name='+') @property def link(self): return self.link_document panels = [ DocumentChooserPanel('link_document'), ] class Meta: abstract = True
class RelatedDocument(Orderable, models.Model): title = models.CharField(max_length=255, help_text="Document name") document = models.ForeignKey('wagtaildocs.Document', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', help_text="Please upload related documents") class Meta: abstract = True ordering = ['sort_order'] panels = [ FieldPanel('title'), DocumentChooserPanel('document'), ]
class EventPageRelatedMedia(Orderable): page = ParentalKey('wagtailmedia_tests.EventPage', related_name='related_media') title = models.CharField(max_length=255, help_text="Link title") link_media = models.ForeignKey('wagtailmedia.Media', null=True, blank=True, related_name='+') @property def link(self): return self.link_media.url panels = [ FieldPanel('title'), DocumentChooserPanel('link_media'), ]
class MemberPage(Page): membership_fr = RichTextField(blank=False, verbose_name='Conditions d\'admission FR', default='') membership_en = RichTextField(blank=False, verbose_name='Conditions d\'admission EN', default='') file = models.ForeignKey('wagtaildocs.Document', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') officers = StreamField(MemberOfficerBlock(), blank=True, verbose_name='Bureau') members = StreamField(MemberMembersBlock(), blank=True, verbose_name='Membres') search_fields = Page.search_fields + [ index.SearchField('membership_fr'), index.SearchField('membership_en'), index.SearchField('file'), index.SearchField('members'), index.SearchField('officers'), ] content_panels = [ FieldPanel('title', classname="full title"), MultiFieldPanel([ FieldPanel('membership_fr', classname='full title'), FieldPanel('membership_en', classname='full title'), DocumentChooserPanel('file'), ], heading=_("Membership")), MultiFieldPanel([ StreamFieldPanel('officers'), StreamFieldPanel('members'), ], heading=_("Members")), ] promote_panels = Page.promote_panels class Meta: verbose_name = _("Member Page")
class LinkFields(models.Model): # h/t https://github.com/torchbox/wagtaildemo/blob/e73170a1ac4eb30f1a071e81542b21c136ced1cd/demo/models.py#L85 link_external = models.URLField( verbose_name=_('external link'), blank=True ) link_page = models.ForeignKey( 'wagtailcore.Page', null=True, blank=True, related_name='+', verbose_name=_('linked page'), ) link_document = models.ForeignKey( 'wagtaildocs.Document', null=True, blank=True, related_name='+', verbose_name=_('linked document'), ) @property def link(self): """ Get the actual link URL for this object. :return: URL string (might be empty) :rtype: str """ if self.link_page: return self.link_page.url elif self.link_document: return self.link_document.url else: return self.link_external panels = [ FieldPanel('link_external'), PageChooserPanel('link_page'), DocumentChooserPanel('link_document'), ] class Meta: abstract = True
class LinkFields(models.Model): link_external = models.URLField( "External link", blank=True, help_text= 'Add if you want this item to link to an external url. Add only one of External Link, Link Page, or Link Document.' ) link_page = models.ForeignKey( 'wagtailcore.Page', null=True, blank=True, related_name='+', help_text= 'Add if you want this item to link to a page on this site. Add only one of External Link, Link Page, or Link Document.' ) link_document = models.ForeignKey( 'wagtaildocs.Document', null=True, blank=True, related_name='+', help_text= 'Add if you want this item to link to a document. Add only one of External Link, Link Page, or Link Document.' ) @property def link(self): if self.link_page: return self.link_page.url elif self.link_document: return self.link_document.url else: return self.link_external panels = [ FieldPanel('link_external'), PageChooserPanel('link_page'), DocumentChooserPanel('link_document'), ] class Meta: abstract = True
class NewsIndex(Page): intro = RichTextField(blank=True) press_kit = models.ForeignKey('wagtaildocs.Document', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') @property def articles(self): articles = NewsArticle.objects.live().child_of(self) article_data = {} for article in articles: article_data['news/{}'.format(article.slug)] = { 'date': article.date, 'heading': article.heading, 'subheading': article.subheading, 'pin_to_top': article.pin_to_top, 'article_image': article.article_image, 'author': article.author, 'tags': [tag.name for tag in article.tags.all()], } return article_data content_panels = Page.content_panels + [ FieldPanel('intro', classname="full"), DocumentChooserPanel('press_kit'), ] api_fields = ( 'intro', 'press_kit', 'articles', 'slug', 'seo_title', 'search_description', ) subpage_types = ['news.NewsArticle'] parent_page_types = ['pages.HomePage']
class ArtworkPage(Page): description = RichTextField(blank=True, null=True) date = models.DateField("Post date", default=datetime.date.today) image = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) video = models.ForeignKey( 'wagtaildocs.Document', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) content_panels = Page.content_panels + [ FieldPanel('description'), FieldPanel('date'), ImageChooserPanel('image'), DocumentChooserPanel('video'), InlinePanel('artworkartistlink', label="Artist Attributes", panels = [ PageChooserPanel('artist', 'artists.ArtistProfilePage'), ] ), ] api_fields = ['slug', 'title', 'description', 'date', 'image', 'video', 'artwork_preview_url'] subpage_types = [] @property def artwork_preview_url(self): if self.image: return generate_image_url(self.image, 'width-800')
class Book(Page): parent_page_types = ['books.BookIndex'] author = CharField(max_length=200, blank=False, null=False) description = RichTextField(blank=True) language = models.ForeignKey('books.Language', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') lessons = ManyToManyField('lessons.Lesson', through='lessons.LessonBookRelationship') tags = TaggableManager(through=RuTaggedItem, blank=True) file = models.ForeignKey('wagtaildocs.Document', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') search_fields = Page.search_fields + [ index.SearchField('description'), ] content_panels = Page.content_panels + [ FieldPanel('author'), FieldPanel('description', classname="full"), SnippetChooserPanel('language'), DocumentChooserPanel('file'), InlinePanel('techs', label="Mentioned technologies"), InlinePanel('students', label="Students"), FieldPanel('tags'), ] def __str__(self): return self.name
class EventDetailPage(Page): # event_title = TextField() long_desc = RichTextField() image = models.ForeignKey('wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') doc = models.ForeignKey('wagtaildocs.Document', blank=True, null=True, on_delete=models.SET_NULL, related_name='+') content_panels = Page.content_panels + [ # FieldPanel('title'), DocumentChooserPanel('doc'), ImageChooserPanel('image'), RichTextFieldPanel('long_desc') # FieldPanel('long_desc') ] parent_page_types = ['EventListPage'] subpage_types = []
class MenuItems(models.Model): """ Represents a link to an external page, a document or a Wagtail page """ link_external = models.URLField( "External link", blank=True, null=True, help_text= 'Set an external link if you want the link to point somewhere outside the CMS.' ) link_page = models.ForeignKey( 'wagtailcore.Page', null=True, on_delete=models.SET_NULL, blank=True, related_name='+', help_text= 'Choose an existing page if you want the link to point somewhere inside the CMS.' ) link_document = models.ForeignKey( 'wagtaildocs.Document', null=True, on_delete=models.SET_NULL, blank=True, related_name='+', help_text= 'Choose an existing document if you want the link to open a document.') panels = [ FieldPanel('link_external'), PageChooserPanel('link_page'), DocumentChooserPanel('link_document') ] class Meta: abstract = True
class LinkField(models.Model): """Model field to handle link item """ link_external = models.URLField(verbose_name='External Link', blank=True, null=True, help_text='Set external link if you want your \ link points somewher outside this CMS System') link_page = models.ForeignKey('wagtailcore.Page', verbose_name='Internal Page', on_delete=models.SET_NULL, blank=True, null=True, help_text='Choose existing from existing page') link_document = models.ForeignKey('wagtaildocs.Document', verbose_name='Link to Document', on_delete=models.SET_NULL, blank=True, null=True) link_email = models.EmailField(verbose_name='Email Link', blank=True, null=True) panels = [ MultiFieldPanel([ PageChooserPanel('link_page'), FieldPanel('link_external'), DocumentChooserPanel('link_document'), FieldPanel('link_email'), ], heading="Link Type", classname="full"), ] class Meta: abstract = True @property def link(self): if self.link_page: return self.link_page.url elif self.link_external: return self.link_external elif self.link_document: return self.link_document.url elif self.link_email: return 'mailto:{}'.format(self.link_email) else: return '#'
class LinkFields(models.Model): """ Represents a link to an external page, a document or a fellow page """ link_external = models.URLField( "External link", blank=True, null=True, help_text= 'Set an external link if you want the link to point somewhere outside the CMS.' ) link_page = models.ForeignKey( 'wagtailcore.Page', null=True, on_delete=models.SET_NULL, blank=True, related_name='+', help_text= 'Choose an existing page if you want the link to point somewhere inside the CMS.' ) link_document = models.ForeignKey( 'wagtaildocs.Document', null=True, on_delete=models.SET_NULL, blank=True, related_name='+', help_text= 'Choose an existing document if you want the link to open a document.') link_email = models.EmailField( blank=True, null=True, help_text= 'Set the recipient email address if you want the link to send an email.' ) link_phone = models.CharField( max_length=20, blank=True, null=True, help_text='Set the number if you want the link to dial a phone number.' ) @property def link(self): if self.link_page: return self.link_page.url elif self.link_external: return self.link_external elif self.link_document: return self.link_document.url elif self.link_email: return 'mailto:%s' % self.link_email elif self.link_phone: return 'tel:%s' % self.link_phone.strip() else: return "#" panels = [ MultiFieldPanel([ PageChooserPanel('link_page'), FieldPanel('link_external'), DocumentChooserPanel('link_document'), FieldPanel('link_email'), FieldPanel('link_phone'), ], "Link"), ] class Meta: abstract = True
class EventPage(AbstractFilterPage): # General content fields body = RichTextField('Subheading', blank=True) archive_body = RichTextField(blank=True) live_body = RichTextField(blank=True) future_body = RichTextField(blank=True) start_dt = models.DateTimeField("Start", blank=True, null=True) end_dt = models.DateTimeField("End", blank=True, null=True) future_body = RichTextField(blank=True) archive_image = models.ForeignKey('wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') video_transcript = models.ForeignKey('wagtaildocs.Document', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') speech_transcript = models.ForeignKey('wagtaildocs.Document', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') flickr_url = models.URLField("Flickr URL", blank=True) youtube_url = models.URLField( "Youtube URL", blank=True, help_text="Format: https://www.youtube.com/embed/video_id. " "It can be obtained by clicking on Share > " "Embed on Youtube.", validators=[ RegexValidator(regex='^https?:\/\/www\.youtube\.com\/embed\/.*$') ]) live_stream_availability = models.BooleanField("Streaming?", default=False, blank=True) live_stream_url = models.URLField( "URL", blank=True, help_text="Format: https://www.youtube.com/embed/video_id.") live_stream_date = models.DateTimeField("Go Live Date", blank=True, null=True) # Venue content fields venue_name = models.CharField(max_length=100, blank=True) venue_street = models.CharField(max_length=100, blank=True) venue_suite = models.CharField(max_length=100, blank=True) venue_city = models.CharField(max_length=100, blank=True) venue_state = USStateField(blank=True) venue_zip = models.IntegerField(blank=True, null=True) agenda_items = StreamField([('item', AgendaItemBlock())], blank=True) objects = CFGOVPageManager() # General content tab content_panels = CFGOVPage.content_panels + [ FieldPanel('body', classname="full"), FieldRowPanel([ FieldPanel('start_dt', classname="col6"), FieldPanel('end_dt', classname="col6"), ]), MultiFieldPanel([ FieldPanel('archive_body', classname="full"), ImageChooserPanel('archive_image'), DocumentChooserPanel('video_transcript'), DocumentChooserPanel('speech_transcript'), FieldPanel('flickr_url'), FieldPanel('youtube_url'), ], heading='Archive Information'), FieldPanel('live_body', classname="full"), FieldPanel('future_body', classname="full"), MultiFieldPanel([ FieldPanel('live_stream_availability'), FieldPanel('live_stream_url'), FieldPanel('live_stream_date'), ], heading='Live Stream Information'), ] # Venue content tab venue_panels = [ FieldPanel('venue_name'), MultiFieldPanel([ FieldPanel('venue_street'), FieldPanel('venue_suite'), FieldPanel('venue_city'), FieldPanel('venue_state'), FieldPanel('venue_zip'), ], heading='Venue Address'), ] # Agenda content tab agenda_panels = [ StreamFieldPanel('agenda_items'), ] # Promotion panels promote_panels = [ MultiFieldPanel(AbstractFilterPage.promote_panels, "Page configuration"), ] # Tab handler interface edit_handler = TabbedInterface([ ObjectList(content_panels, heading='General Content'), ObjectList(venue_panels, heading='Venue Information'), ObjectList(agenda_panels, heading='Agenda Information'), ObjectList(AbstractFilterPage.sidefoot_panels, heading='Sidebar'), ObjectList(AbstractFilterPage.settings_panels, heading='Configuration'), ]) template = 'events/event.html' @property def page_js(self): return super(EventPage, self).page_js + ['video-player.js'] def location_image_url(self, scale='2', size='276x155', zoom='12'): center = 'Washington, DC' if self.venue_city: center = self.venue_city if self.venue_state: center = center + ', ' + self.venue_state options = { 'center': center, 'scale': scale, 'size': size, 'zoom': zoom } url = 'https://maps.googleapis.com/maps/api/staticmap?' return '{url}{options}'.format(url=url, options=urlencode(options))
class HomePage(Page, LocalorePromoteFields): site_intro = RichTextField(blank=True) related_content_title = models.CharField( verbose_name="title", max_length=255, ) related_content_page = models.ForeignKey('wagtailcore.Page', verbose_name="page to link to", null=True, blank=True, on_delete=models.SET_NULL, related_name='+') related_content_subtitle = models.CharField(verbose_name="subtitle", max_length=255, blank=True, default="Across America") video_poster_image = models.ForeignKey('localore_admin.LocaloreImage', verbose_name="poster image", null=True, on_delete=models.SET_NULL, related_name='+') video_poster_image_mobile = models.ForeignKey( 'localore_admin.LocaloreImage', verbose_name="poster image (mobile)", null=True, on_delete=models.SET_NULL, related_name='+') video_mp4 = models.ForeignKey('wagtaildocs.Document', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') video_webm = models.ForeignKey('wagtaildocs.Document', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') video_ogv = models.ForeignKey('wagtaildocs.Document', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') video_youtube_id = models.CharField( verbose_name="YouTube video ID", max_length=12, default="j6IIjLK-8fU", help_text=format_html( "The part in bold: " "https://www.youtube.com/watch?v=<b>j6IIjLK-8fU</b>"), ) video_is_360 = models.BooleanField( "360˚ video", default=False, help_text="This is a 360-degree video.", ) view_more_title = models.CharField( verbose_name='"View more" link title', max_length=255, help_text='For example, "View more connections"', ) view_more_page = models.ForeignKey('wagtailcore.Page', verbose_name="Page to link to", null=True, on_delete=models.SET_NULL, related_name='+') content_panels = Page.content_panels + [ FieldPanel('site_intro', classname="full"), MultiFieldPanel([ FieldPanel('related_content_title'), FieldPanel('related_content_subtitle'), PageChooserPanel('related_content_page'), ], "Featured content"), MultiFieldPanel([ ImageChooserPanel('video_poster_image'), ImageChooserPanel('video_poster_image_mobile'), DocumentChooserPanel('video_mp4'), DocumentChooserPanel('video_webm'), DocumentChooserPanel('video_ogv'), ], "Hero section"), MultiFieldPanel([ FieldPanel('video_youtube_id'), FieldPanel('video_is_360'), FieldPanel('view_more_title'), PageChooserPanel('view_more_page'), ], "Fullscreen video"), InlinePanel( 'featured_pages', label="Featured Pages", min_num=3, max_num=3, ), ] promote_panels = LocalorePromoteFields.promote_panels parent_page_types = [] @property def video_poster_image_file_extension(self): return self.video_poster_image.file.url.split('.')[-1] @property def preview_modes(self): return super(HomePage, self).preview_modes + [ ('no-video', 'Preview poster image'), ] def serve_preview(self, request, mode_name): if mode_name == 'no-video': self.video_mp4 = None return super(HomePage, self).serve_preview(request, mode_name) class Meta: verbose_name = "Homepage"
class SignUpFormPage(Page): formatted_title = models.CharField( max_length=255, blank=True, help_text="This is the title displayed on the page, not the document " "title tag. HTML is permitted. Be careful.") intro = RichTextField() call_to_action_text = models.CharField( max_length=255, help_text="Displayed above the email submission form.") call_to_action_image = models.ForeignKey('torchbox.TorchboxImage', null=True, blank=True, on_delete=models.SET_NULL, related_name='+') form_button_text = models.CharField(max_length=255) thank_you_text = models.CharField( max_length=255, help_text="Displayed on successful form submission.") email_subject = models.CharField(max_length=100, verbose_name='subject') email_body = models.TextField(verbose_name='body') email_attachment = models.ForeignKey( 'wagtaildocs.Document', null=True, related_name='+', on_delete=models.SET_NULL, verbose_name='attachment', ) email_from_address = models.EmailField( verbose_name='from address', help_text="Anything ending in @torchbox.com is good.") content_panels = [ MultiFieldPanel([ FieldPanel('title', classname="title"), FieldPanel('formatted_title'), ], 'Title'), FieldPanel('intro', classname="full"), InlinePanel('bullet_points', label="Bullet points"), InlinePanel('logos', label="Logos"), InlinePanel('quotes', label="Quotes"), MultiFieldPanel([ FieldPanel('call_to_action_text'), ImageChooserPanel('call_to_action_image'), FieldPanel('form_button_text'), FieldPanel('thank_you_text'), ], 'Form'), MultiFieldPanel([ FieldPanel('email_subject'), FieldPanel('email_body'), DocumentChooserPanel('email_attachment'), FieldPanel('email_from_address'), ], 'Email'), ] def get_context(self, request): form = SignUpFormPageForm() context = super(SignUpFormPage, self).get_context(request) context['form'] = form return context def serve(self, request, *args, **kwargs): if request.is_ajax() and request.method == "POST": form = SignUpFormPageForm(request.POST) if form.is_valid(): form.save() self.send_email_response(form.cleaned_data['email']) return render( request, 'torchbox/includes/sign_up_form_page_landing.html', { 'page': self, 'form': form }) else: return render(request, 'torchbox/includes/sign_up_form_page_form.html', { 'page': self, 'form': form }) else: return super(SignUpFormPage, self).serve(request) def send_email_response(self, to_address): email_message = EmailMessage( subject=self.email_subject, body=self.email_body, from_email=self.email_from_address, to=[to_address], ) email_message.attach_file(self.email_attachment.file.path) email_message.send()
class ActivityPage(CFGOVPage): """ A model for the Activity Detail page. """ # Allow Activity pages to exist under the ActivityIndexPage or the Trash parent_page_types = [ActivityIndexPage, HomePage] subpage_types = [] objects = CFGOVPageManager() date = models.DateField('Updated', default=timezone.now) summary = models.TextField('Summary', blank=False) big_idea = RichTextField('Big idea', blank=False) essential_questions = RichTextField('Essential questions', blank=False) objectives = RichTextField('Objectives', blank=False) what_students_will_do = RichTextField('What students will do', blank=False) # noqa: E501 activity_file = models.ForeignKey('wagtaildocs.Document', null=True, blank=False, on_delete=models.SET_NULL, related_name='+', verbose_name='Teacher guide') # TODO: to figure out how to use Document choosers on ManyToMany fields handout_file = models.ForeignKey('wagtaildocs.Document', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', verbose_name='Student file 1') handout_file_2 = models.ForeignKey('wagtaildocs.Document', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', verbose_name='Student file 2') handout_file_3 = models.ForeignKey('wagtaildocs.Document', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', verbose_name='Student file 3') building_block = ParentalManyToManyField( 'teachers_digital_platform.ActivityBuildingBlock', blank=False) # noqa: E501 school_subject = ParentalManyToManyField( 'teachers_digital_platform.ActivitySchoolSubject', blank=False) # noqa: E501 topic = ParentalTreeManyToManyField( 'teachers_digital_platform.ActivityTopic', blank=False) # noqa: E501 # Audience grade_level = ParentalManyToManyField( 'teachers_digital_platform.ActivityGradeLevel', blank=False) # noqa: E501 age_range = ParentalManyToManyField( 'teachers_digital_platform.ActivityAgeRange', blank=False) # noqa: E501 student_characteristics = ParentalManyToManyField( 'teachers_digital_platform.ActivityStudentCharacteristics', blank=True) # noqa: E501 # Activity Characteristics activity_type = ParentalManyToManyField( 'teachers_digital_platform.ActivityType', blank=False) # noqa: E501 teaching_strategy = ParentalManyToManyField( 'teachers_digital_platform.ActivityTeachingStrategy', blank=False) # noqa: E501 blooms_taxonomy_level = ParentalManyToManyField( 'teachers_digital_platform.ActivityBloomsTaxonomyLevel', blank=False) # noqa: E501 activity_duration = models.ForeignKey( ActivityDuration, blank=False, on_delete=models.PROTECT) # noqa: E501 # Standards taught jump_start_coalition = ParentalManyToManyField( 'teachers_digital_platform.ActivityJumpStartCoalition', blank=True, verbose_name='Jump$tart Coalition', ) council_for_economic_education = ParentalManyToManyField( 'teachers_digital_platform.ActivityCouncilForEconEd', blank=True, verbose_name='Council for Economic Education', ) content_panels = CFGOVPage.content_panels + [ FieldPanel('date'), FieldPanel('summary'), FieldPanel('big_idea'), FieldPanel('essential_questions'), FieldPanel('objectives'), FieldPanel('what_students_will_do'), MultiFieldPanel( [ DocumentChooserPanel('activity_file'), DocumentChooserPanel('handout_file'), DocumentChooserPanel('handout_file_2'), DocumentChooserPanel('handout_file_3'), ], heading="Download activity", ), FieldPanel('building_block', widget=forms.CheckboxSelectMultiple), FieldPanel('school_subject', widget=forms.CheckboxSelectMultiple), FieldPanel('topic', widget=forms.CheckboxSelectMultiple), MultiFieldPanel( [ FieldPanel('grade_level', widget=forms.CheckboxSelectMultiple), # noqa: E501 FieldPanel('age_range', widget=forms.CheckboxSelectMultiple), FieldPanel('student_characteristics', widget=forms.CheckboxSelectMultiple), # noqa: E501 ], heading="Audience", ), MultiFieldPanel( [ FieldPanel('activity_type', widget=forms.CheckboxSelectMultiple), # noqa: E501 FieldPanel('teaching_strategy', widget=forms.CheckboxSelectMultiple), # noqa: E501 FieldPanel('blooms_taxonomy_level', widget=forms.CheckboxSelectMultiple), # noqa: E501 FieldPanel('activity_duration'), ], heading="Activity characteristics", ), MultiFieldPanel( [ FieldPanel('council_for_economic_education', widget=forms.CheckboxSelectMultiple), # noqa: E501 FieldPanel('jump_start_coalition', widget=forms.CheckboxSelectMultiple), # noqa: E501 ], heading="National standards", ), ] edit_handler = TabbedInterface([ ObjectList(content_panels, heading='General Content'), ObjectList(CFGOVPage.sidefoot_panels, heading='Sidebar/Footer'), ObjectList(CFGOVPage.settings_panels, heading='Configuration'), ]) # admin use only search_fields = Page.search_fields + [ index.SearchField('summary'), index.SearchField('big_idea'), index.SearchField('essential_questions'), index.SearchField('objectives'), index.SearchField('what_students_will_do'), index.FilterField('date'), index.FilterField('building_block'), index.FilterField('school_subject'), index.FilterField('topic'), index.FilterField('grade_level'), index.FilterField('age_range'), index.FilterField('student_characteristics'), index.FilterField('activity_type'), index.FilterField('teaching_strategy'), index.FilterField('blooms_taxonomy_level'), index.FilterField('activity_duration'), index.FilterField('jump_start_coalition'), index.FilterField('council_for_economic_education'), ] def get_topics_list(self, parent=None): """ Get a hierarchical list of this activity's topics. parent: ActivityTopic """ if parent: descendants = set(parent.get_descendants()) & set( self.topic.all()) # noqa: E501 children = parent.get_children() children_list = [] # If this parent has descendants in self.topic, add its children. if descendants: for child in children: if set(child.get_descendants()) & set(self.topic.all()): children_list.append(self.get_topics_list(child)) elif child in self.topic.all(): children_list.append(child.title) if children_list: return parent.title + " (" + ', '.join( children_list) + ")" # noqa: E501 # Otherwise, just add the parent. else: return parent.title else: # Build root list of topics and recurse their children. topic_list = [] topic_ids = [topic.id for topic in self.topic.all()] ancestors = ActivityTopic.objects.filter( id__in=topic_ids).get_ancestors(True) # noqa: E501 roots = ActivityTopic.objects.filter(parent=None) & ancestors for root_topic in roots: topic_list.append(self.get_topics_list(root_topic)) if topic_list: return ', '.join(topic_list) else: return '' class Meta: verbose_name = "TDP Activity page"
blank=True, on_delete=models.SET_NULL) @property def casestudy_index(self): # Find closest ancestor which is a casestudy index return self.get_ancestors().type(CaseStudyIndexPage).last() CaseStudyPage.content_panels = [ FieldPanel('title', classname="full title"), FieldPanel('date'), FieldPanel('summary', classname="full"), FieldPanel('marketplace_entry', classname="full"), MultiFieldPanel(TopImage.panels, "hero image"), DocumentChooserPanel('downloadable_package'), InlinePanel(CaseStudyPage, 'focus_areas', label="Focus Areas"), InlinePanel(CaseStudyPage, 'regions', label="Regions"), InlinePanel(CaseStudyPage, 'countries', label="Countries"), InlinePanel(CaseStudyPage, 'organizations', label="Organisations"), FieldPanel('submitter_email'), ] # The following children of Case Study use through-model # Desribed in M2M issue work-around # https://github.com/torchbox/wagtail/issues/231 class CountryCaseStudy(Orderable, models.Model): country = models.ForeignKey(Country, related_name="+") page = ParentalKey(CaseStudyPage, related_name='countries')
class SeriesPage(ThemeablePage, FeatureStyleFields, Promotable, ShareLinksMixin, PageLayoutOptions, VideoDocumentMixin): subtitle = RichTextField(blank=True, default="") short_description = RichTextField(blank=True, default="") body = article_fields.BodyField(blank=True, default="") main_image = models.ForeignKey( 'images.AttributedImage', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) feature_image = models.ForeignKey( 'images.AttributedImage', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) primary_topic = models.ForeignKey( 'articles.Topic', null=True, blank=True, on_delete=models.SET_NULL, related_name='series' ) project = models.ForeignKey( "projects.ProjectPage", null=True, blank=True, on_delete=models.SET_NULL, ) search_fields = Page.search_fields + [ index.SearchField('subtitle', partial_match=True), index.SearchField('body', partial_match=True), index.SearchField('get_primary_topic_name', partial_match=True), index.SearchField('get_topic_names', partial_match=True), ] number_of_related_articles = models.PositiveSmallIntegerField(default=6, verbose_name="Number of Related Articles to Show") def get_primary_topic_name(self): if self.primary_topic: return self.primary_topic.name else: "" def get_topic_names(self): return '\n'.join([topic.name if topic else "" for topic in self.topics]) def get_author_names(self): return '\n'.join([author.full_name if author else "" for author in self.authors]) @property def articles(self): article_list = [] for article_link in self.related_article_links.all(): if article_link.article: article_link.article.override_text = article_link.override_text article_link.article.override_image = article_link.override_image article_list.append(article_link.article) return article_list @property def authors(self): author_list = [] for article_link in self.related_article_links.all(): if article_link.article: if article_link.article: for author_link in article_link.article.author_links.all(): if author_link.author: if author_link.author not in author_list: author_list.append(author_link.author) author_list.sort(key=attrgetter('last_name')) return author_list @property def topics(self): all_topics = [] if self.primary_topic: all_topics.append(self.primary_topic) for article_link in self.related_article_links.all(): if article_link.article: all_topics.extend(article_link.article.topics) all_topics = list(set(all_topics)) if all_topics: all_topics.sort(key=attrgetter('name')) return all_topics @property def related_series(self): related_series_list = [] if self.project: related_series_list = self.project.get_related_series(self) return related_series_list def related_articles(self, number): articles = [] if self.primary_topic: articles = list(ArticlePage.objects.live().filter(primary_topic=self.primary_topic).distinct().order_by( '-first_published_at')[:number]) current_total = len(articles) if current_total < number: for article in self.articles: articles.extend(list(article.related_articles(number))) articles = list(set(articles))[:number] current_total = len(articles) if current_total >= number: return articles return articles content_panels = Page.content_panels + [ FieldPanel('subtitle'), FieldPanel('short_description'), PageChooserPanel('project'), ImageChooserPanel('main_image'), ImageChooserPanel('feature_image'), DocumentChooserPanel('video_document'), StreamFieldPanel('body'), InlinePanel('related_article_links', label="Articles"), SnippetChooserPanel('primary_topic'), ] promote_panels = Page.promote_panels + [ MultiFieldPanel( [ FieldPanel('sticky'), FieldPanel('sticky_for_type_section'), FieldPanel('slippery'), FieldPanel('slippery_for_type_section'), FieldPanel('editors_pick'), FieldPanel('feature_style'), FieldPanel('title_size'), FieldPanel('fullbleed_feature'), FieldPanel('image_overlay_opacity'), ], heading="Featuring Settings" ) ] style_panels = ThemeablePage.style_panels + [ MultiFieldPanel( [ FieldPanel('include_main_image'), FieldPanel('include_main_image_overlay'), FieldPanel('full_bleed_image_size'), FieldPanel('include_caption_in_footer'), ], heading="Main Image" ), MultiFieldPanel( [ FieldPanel('number_of_related_articles'), ], heading="Sections" ) ] edit_handler = TabbedInterface([ ObjectList(content_panels, heading='Content'), ObjectList(style_panels, heading='Page Style Options'), ObjectList(promote_panels, heading='Promote'), ObjectList(Page.settings_panels, heading='Settings', classname="settings"), ])
class ArticlePage(ThemeablePage, FeatureStyleFields, Promotable, ShareLinksMixin, PageLayoutOptions, VideoDocumentMixin): excerpt = RichTextField(blank=True, default="") body = article_fields.BodyField() chapters = article_fields.ChapterField(blank=True, null=True) table_of_contents_heading = models.TextField(blank=True, default="Table of Contents") citations_heading = models.TextField(blank=True, default="Works Cited") endnotes_heading = models.TextField(blank=True, default="End Notes") endnote_identifier_style = models.CharField( max_length=20, default="roman-lower", choices=( ('roman-lower', 'Roman Numerals - Lowercase'), ('roman-upper', 'Roman Numerals - Uppercase'), ('numbers', 'Numbers') ) ) main_image = models.ForeignKey( 'images.AttributedImage', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) feature_image = models.ForeignKey( 'images.AttributedImage', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) primary_topic = models.ForeignKey( 'articles.Topic', null=True, blank=True, on_delete=models.SET_NULL, related_name='articles' ) category = models.ForeignKey( 'articles.ArticleCategory', related_name='%(class)s', on_delete=models.SET_NULL, null=True, default=1 ) include_author_block = models.BooleanField(default=True) visualization = models.BooleanField(default=False) interview = models.BooleanField(default=False) video = models.BooleanField(default=False) number_of_related_articles = models.PositiveSmallIntegerField(default=6, verbose_name="Number of Related Articles to Show") json_file = article_fields.WagtailFileField(max_length=255, blank=True, null=True, verbose_name='JSON file', help_text="Only provide if you know your template will be filled with the contents of a JSON data file.") project = models.ForeignKey( "projects.ProjectPage", null=True, blank=True, on_delete=models.SET_NULL, ) _response_to = False search_fields = Page.search_fields + [ index.SearchField('excerpt', partial_match=True), index.SearchField('body', partial_match=True), index.SearchField('chapters', partial_match=True), index.SearchField('get_primary_topic_name', partial_match=True), index.SearchField('get_category_name', partial_match=True), index.SearchField('get_topic_names', partial_match=True), index.SearchField('get_author_names', partial_match=True), ] def get_primary_topic_name(self): if self.primary_topic: return self.primary_topic.name return "" def get_category_name(self): if self.category: return self.category.name return "" def get_topic_names(self): return '\n'.join([link.topic.name if link.topic else "" for link in self.topic_links.all()]) def get_author_names(self): return '\n'.join( [author_link.author.full_name if author_link.author else "" for author_link in self.author_links.all()]) @property def authors(self): author_list = [] for link in self.author_links.all(): if link.author: author_list.append((link.author)) return author_list @property def series_articles(self): related_series_data = [] for link in self.series_links.all(): series_page = link.series series_articles = series_page.articles series_articles.remove(self) related_series_data.append((series_page, series_articles)) return related_series_data @property def topics(self): primary_topic = self.primary_topic all_topics = [link.topic for link in self.topic_links.all()] if primary_topic: all_topics.append(primary_topic) all_topics = list(set(all_topics)) if len(all_topics) > 0: all_topics.sort(key=attrgetter('name')) return all_topics @property def response_to(self): if self._response_to is False: response_to_count = self.response_to_links.count() if response_to_count > 1: logger.warning( 'ArticlePage(pk={0}) appears to be a response to multiple articles. Only the first one is being returned.'.format( self.pk ) ) if response_to_count != 0: self._response_to = self.response_to_links.first().response_to else: self._response_to = None return self._response_to @property def is_response(self): return self.response_to is not None def responses(self): return [link.response for link in self.response_links.all()] def related_articles(self, number): included = [self.id] article_list = [] if self.primary_topic: articles = ArticlePage.objects.live().filter(primary_topic=self.primary_topic).exclude( id=self.id).distinct().order_by('-first_published_at')[:number] article_list.extend(articles.all()) included.extend([article.id for article in articles.all()]) current_total = len(article_list) if current_total < number: # still don't have enough, so pick using secondary topics topics = Topic.objects.filter(article_links__article=self) if topics: additional_articles = ArticlePage.objects.live().filter(primary_topic__in=topics).exclude( id__in=included).distinct().order_by('-first_published_at')[:number - current_total] article_list.extend(additional_articles.all()) current_total = len(article_list) included.extend([article.id for article in additional_articles.all()]) if current_total < number: authors = ContributorPage.objects.live().filter(article_links__article=self) if authors: additional_articles = ArticlePage.objects.live().filter(author_links__author__in=authors).exclude( id__in=included).distinct().order_by('-first_published_at')[:number - current_total] article_list.extend(additional_articles.all()) current_total = len(article_list) included.extend([article.id for article in additional_articles.all()]) if current_total < number: # still don't have enough, so just pick the most recent additional_articles = ArticlePage.objects.live().exclude(id__in=included).order_by('-first_published_at')[:number - current_total] article_list.extend(additional_articles.all()) return article_list content_panels = Page.content_panels + [ FieldPanel('excerpt'), InlinePanel('author_links', label="Authors"), PageChooserPanel('project'), ImageChooserPanel('main_image'), ImageChooserPanel('feature_image'), DocumentChooserPanel('video_document'), StreamFieldPanel('body'), SnippetChooserPanel('primary_topic'), InlinePanel('topic_links', label="Secondary Topics"), InlinePanel('response_links', label="Responses"), ] advanced_content_panels = [ FieldPanel('json_file'), MultiFieldPanel( [ FieldPanel('table_of_contents_heading'), StreamFieldPanel('chapters'), ], heading="Chapters Section" ), MultiFieldPanel( [ FieldPanel('endnotes_heading'), FieldPanel('endnote_identifier_style'), InlinePanel('endnote_links', label="End Notes"), ], heading="End Notes Section" ), MultiFieldPanel( [ FieldPanel('citations_heading'), InlinePanel('citation_links', label="Citations"), ], heading="Citations Section" ), ] promote_panels = Page.promote_panels + [ MultiFieldPanel( [ FieldPanel('sticky'), FieldPanel('sticky_for_type_section'), FieldPanel('slippery'), FieldPanel('slippery_for_type_section'), FieldPanel('editors_pick'), FieldPanel('feature_style'), FieldPanel('title_size'), FieldPanel('fullbleed_feature'), FieldPanel('image_overlay_opacity'), ], heading="Featuring Settings" ), ] style_panels = ThemeablePage.style_panels + [ MultiFieldPanel( [ FieldPanel('include_main_image'), FieldPanel('include_main_image_overlay'), FieldPanel('full_bleed_image_size'), FieldPanel('include_caption_in_footer'), ], heading="Main Image" ), MultiFieldPanel( [ InlinePanel('background_image_links', label="Background Images"), ], heading="Background Images" ), MultiFieldPanel( [ FieldPanel('include_author_block'), FieldPanel('number_of_related_articles') ], heading="Sections" ), MultiFieldPanel( [ FieldPanel('interview'), FieldPanel('video'), FieldPanel('visualization'), ], heading="Categorization" ) ] edit_handler = TabbedInterface([ ObjectList(content_panels, heading='Content'), ObjectList(advanced_content_panels, heading='Advanced Content'), ObjectList(style_panels, heading='Page Style Options'), ObjectList(promote_panels, heading='Promote'), ObjectList(Page.settings_panels, heading='Settings', classname="settings"), ])