Пример #1
0
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
Пример #2
0
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)
Пример #3
0
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
Пример #4
0
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'),
    ]
Пример #5
0
class ImportantLinks(BaseSetting):
    terms_pdf = models.ForeignKey('wagtaildocs.Document',
                                  null=True,
                                  blank=True,
                                  related_name='+')

    panels = [
        DocumentChooserPanel('terms_pdf'),
    ]
Пример #6
0
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'),
    ]
Пример #7
0
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
Пример #8
0
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')
Пример #9
0
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'),
    ]
Пример #10
0
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'),
    ]
Пример #11
0
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
Пример #12
0
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'),
    ]
Пример #13
0
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'),
    ]
Пример #14
0
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")
Пример #15
0
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
Пример #16
0
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
Пример #17
0
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']
Пример #18
0
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')
Пример #19
0
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
Пример #20
0
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 = []
Пример #21
0
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
Пример #22
0
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 '#'
Пример #23
0
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
Пример #24
0
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))
Пример #25
0
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"
Пример #26
0
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()
Пример #27
0
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"
Пример #28
0
                                          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')
Пример #29
0
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"),
    ])
Пример #30
0
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"),
    ])