class HomePage(Page):
    intro = RichTextField(blank=True)

    content_panels = Page.content_panels + [
        FieldPanel('intro', classname="full")
    ]
Exemplo n.º 2
0
class AdvancedChartPage(InstructionsMixin, EChartOptionsMixin, D3OptionsMixin,
                        PlotlyOptionsMixin, FallbackImageMixin,
                        RoutablePageMixin, Page):
    """
    A code based chart page for advanced users
    """
    parent_page_types = [VisualisationsPage]
    subpage_types = []

    subtitle = models.TextField(
        blank=True,
        null=True,
        help_text="Optional: subtitle to appear underneath the chart title.")

    html = AceEditorField(options={'mode': 'html'},
                          blank=True,
                          default='{% load wagtailcore_tags %}')
    javascript = AceEditorField(options={'mode': 'javascript'},
                                blank=True,
                                default='"use strict";')
    css = AceEditorField(options={'mode': 'css'},
                         blank=True,
                         default='/* CSS goes here */')

    caption = RichTextField(
        null=True,
        blank=True,
        help_text='Optional: caption text and link(s) for the chart',
        features=INSTRUCTIONS_RICHTEXT_FEATURES + SIMPLE_RICHTEXT_FEATURES)

    content_panels = Page.content_panels + [
        FieldPanel('subtitle'),
        PlotlyOptionsPanel(),
        D3OptionsPanel(),
        EChartOptionsPanel(),
        FieldPanel('html', classname='collapsible'),
        FieldPanel('javascript', classname='collapsible'),
        # FieldPanel('css', classname='collapsible'), TODO: add CSS support - may work best in an iFrame
        FallbackImagePanel(),
        InstructionsPanel(),
        FieldPanel('caption'),
    ]

    @cached_property
    def parent(self):
        return self.get_parent().specific

    @cached_property
    def instructions_text(self):
        if self.instructions:
            return self.instructions

        return ''

    def get_sitemap_urls(self, request):
        return []

    @route(r'^$')
    def chart(self, request):
        if request.user.is_authenticated:
            return super().serve(request)
        raise Http404()

    class Meta:
        verbose_name = 'Advanced Chart Page'
Exemplo n.º 3
0
class EventIndexPage(Page):
    UPCOMING = 'gte'
    PAST = 'lt'
    EVENT_LIST_CHOICES = (
        (UPCOMING, 'Upcoming Events'),
        (PAST, 'Past Events'),
    )
    show_events = models.CharField(max_length=3,
                                   choices=EVENT_LIST_CHOICES,
                                   default=UPCOMING)

    intro = RichTextField(blank=True)
    intro_homepage = models.TextField(
        help_text=
        'Describe the events index page. This is displayed on the homepage.',
        blank=True)

    parent_page_types = ['home.HomePage']
    subpage_types = ['events.EventPage']

    search_fields = Page.search_fields + [index.SearchField('intro')]

    content_panels = Page.content_panels + [
        FieldPanel('intro'), FieldPanel('intro_homepage')
    ]

    @property
    def events(self):
        events = EventPage.objects.child_of(self).live()
        kwargs = {
            '{0}__{1}'.format('date_from', self.show_events): date.today(),
        }
        events = events.filter(**kwargs)
        events = events.order_by('date_from')
        return events

    @property
    def past_events(self):
        events = EventPage.objects.child_of(self).live().filter(
            date_from__lt=date.today())
        events = events.order_by('-date_from')
        events = events.all()  # [:10]
        return events

        class Meta:
            verbose_name = "Event List"

    @property
    def past_event(self):
        event = EventPage.objects.child_of(self).live().filter(
            date_from__lt=date.today())
        return event

    def get_context(self, request):
        past_events = self.past_events

        # Pagination
        page = request.GET.get('page', 1)
        paginator = Paginator(past_events, 20)

        try:
            past_events = paginator.page(page)
        except PageNotAnInteger:
            past_events = paginator.page(1)
        except EmptyPage:
            past_events = paginator.page(paginator.num_pages)

        context = super(EventIndexPage, self).get_context(request)

        # Hide upcoming events if past page 1
        if int(page) == 1:
            context['events'] = self.events

        context['past_events'] = past_events
        return context
Exemplo n.º 4
0
class DisclaimerPage(Page):
    template = "disclaimer/disclaimer_page.html"
    summary = RichTextField(default='Input here...')
    content_panels = Page.content_panels + [
        FieldPanel("summary"),
    ]
Exemplo n.º 5
0
class ParticipatePage2(PrimaryPage):
    parent_page_types = ['Homepage']
    template = 'wagtailpages/static/participate_page2.html'

    ctaHero = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='primary_hero_participate',
        verbose_name='Primary Hero Image',
    )

    ctaHeroHeader = models.TextField(
        blank=True,
    )

    ctaHeroSubhead = RichTextField(
        features=[
            'bold', 'italic', 'link',
        ],
        blank=True,
    )

    ctaCommitment = models.TextField(
        blank=True,
    )

    ctaButtonTitle = models.CharField(
        verbose_name='Button Text',
        max_length=250,
        blank=True,
    )

    ctaButtonURL = models.TextField(
        verbose_name='Button URL',
        blank=True,
    )

    ctaHero2 = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='primary_hero_participate2',
        verbose_name='Primary Hero Image',
    )

    ctaHeroHeader2 = models.TextField(
        blank=True,
    )

    ctaHeroSubhead2 = RichTextField(
        features=[
            'bold', 'italic', 'link',
        ],
        blank=True,
    )

    ctaCommitment2 = models.TextField(
        blank=True,
    )

    ctaButtonTitle2 = models.CharField(
        verbose_name='Button Text',
        max_length=250,
        blank=True,
    )

    ctaButtonURL2 = models.TextField(
        verbose_name='Button URL',
        blank=True,
    )

    ctaHero3 = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='primary_hero_participate3',
        verbose_name='Primary Hero Image',
    )

    ctaHeroHeader3 = models.TextField(
        blank=True,
    )

    ctaHeroSubhead3 = RichTextField(
        features=[
            'bold', 'italic', 'link',
        ],
        blank=True,
    )

    ctaCommitment3 = models.TextField(
        blank=True,
    )

    ctaFacebook3 = models.TextField(
        blank=True,
    )

    ctaTwitter3 = models.TextField(
        blank=True,
    )

    ctaEmailShareBody3 = models.TextField(
        blank=True,
    )

    ctaEmailShareSubject3 = models.TextField(
        blank=True,
    )

    h2 = models.TextField(
        blank=True,
    )

    h2Subheader = models.TextField(
        blank=True,
        verbose_name='H2 Subheader',
    )

    content_panels = Page.content_panels + [
        MultiFieldPanel([
            ImageChooserPanel('ctaHero'),
            FieldPanel('ctaHeroHeader'),
            FieldPanel('ctaHeroSubhead'),
            FieldPanel('ctaCommitment'),
            FieldPanel('ctaButtonTitle'),
            FieldPanel('ctaButtonURL'),
        ], heading="Primary CTA"),
        FieldPanel('h2'),
        FieldPanel('h2Subheader'),
        InlinePanel('featured_highlights', label='Highlights Group 1', max_num=3),
        MultiFieldPanel([
            ImageChooserPanel('ctaHero2'),
            FieldPanel('ctaHeroHeader2'),
            FieldPanel('ctaHeroSubhead2'),
            FieldPanel('ctaCommitment2'),
            FieldPanel('ctaButtonTitle2'),
            FieldPanel('ctaButtonURL2'),
        ], heading="CTA 2"),
        InlinePanel('featured_highlights2', label='Highlights Group 2', max_num=6),
        MultiFieldPanel([
            ImageChooserPanel('ctaHero3'),
            FieldPanel('ctaHeroHeader3'),
            FieldPanel('ctaHeroSubhead3'),
            FieldPanel('ctaCommitment3'),
            FieldPanel('ctaFacebook3'),
            FieldPanel('ctaTwitter3'),
            FieldPanel('ctaEmailShareSubject3'),
            FieldPanel('ctaEmailShareBody3'),
        ], heading="CTA 3"),
        InlinePanel('cta4', label='CTA Group 4', max_num=3),
    ]
class Highlight(TranslatableMixin, SortableMixin):
    """
    An data type to highlight things like pulse
    projects, custom pages, etc
    Especially on the homepage under "Get Involved"
    """
    title = models.CharField(
        max_length=300,
        help_text='Title of the higlight',
    )
    description = models.TextField(
        max_length=5000,
        help_text='Description of the higlight',
    )
    link_label = models.CharField(
        max_length=300,
        help_text='Text to show that links to this higlight\'s '
        'details page',
    )
    link_url = models.URLField(
        max_length=2048,
        help_text='Link to this higlight\'s details page',
    )
    image = models.ForeignKey(
        'wagtailimages.Image',
        on_delete=models.SET_NULL,
        blank=False,
        null=True,
        related_name='+',
    )
    footer = RichTextField(
        "footer",
        help_text="Content to appear after description (view more projects "
        "link or something similar)",
        null=True,
    )
    publish_after = models.DateTimeField(
        help_text='Make this highlight visible only '
        'after this date and time (UTC)',
        null=True,
    )
    expires = models.DateTimeField(
        help_text='Hide this highlight after this date and time (UTC)',
        default=None,
        null=True,
        blank=True,
    )
    order = models.PositiveIntegerField(
        default=0,
        editable=False,
        db_index=True,
    )

    panels = [
        FieldPanel("title"),
        FieldPanel("description"),
        FieldPanel("link_label"),
        FieldPanel("link_url"),
        ImageChooserPanel("image"),
        FieldPanel("footer"),
        FieldPanel("publish_after"),
        FieldPanel("expires"),
    ]

    translatable_fields = [
        TranslatableField('title'),
        TranslatableField('description'),
        TranslatableField('link_label'),
        TranslatableField('footer'),
    ]

    objects = HighlightQuerySet.as_manager()

    class Meta(TranslatableMixin.Meta):
        verbose_name_plural = 'highlights'
        ordering = ('order', )

    def __str__(self):
        return str(self.title)
Exemplo n.º 7
0
class SimpleGalleryIndex(RoutablePageMixin, Page):
    intro_title = models.CharField(
        verbose_name=_('Intro title'),
        max_length=250,
        blank=True,
        help_text=_('Optional H1 title for the gallery page.'))
    intro_text = RichTextField(
        blank=True,
        verbose_name=_('Intro text'),
        help_text=_('Optional text to go with the intro text.'))
    collection = models.ForeignKey(
        'wagtailcore.Collection',
        verbose_name=_('Collection'),
        null=True,
        blank=False,
        on_delete=models.SET_NULL,
        related_name='+',
        help_text=_('Show images in this collection in the gallery view.'))
    images_per_page = models.IntegerField(
        default=8,
        verbose_name=_('Images per page'),
        help_text=_('How many images there should be on one page.'))
    use_lightbox = models.BooleanField(
        verbose_name=_('Use lightbox'),
        default=True,
        help_text=_(
            'Use lightbox to view larger images when clicking the thumbnail.'))
    order_images_by = models.IntegerField(choices=IMAGE_ORDER_TYPES, default=1)

    content_panels = Page.content_panels + [
        FieldPanel('intro_title', classname='full title'),
        FieldPanel('intro_text', classname='full title'),
        FieldPanel('collection'),
        FieldPanel('images_per_page', classname='full title'),
        FieldPanel('use_lightbox'),
        FieldPanel('order_images_by'),
    ]

    @property
    def images(self, tags=None):
        return get_gallery_images(self.collection.name, self)

    @property
    def tags(self):
        return self.get_gallery_tags()

    def get_context(self, request):
        images = self.images
        tags = self.tags
        context = super(SimpleGalleryIndex, self).get_context(request)
        page = request.GET.get('page')
        paginator = Paginator(images, self.images_per_page)
        try:
            images = paginator.page(page)
        except PageNotAnInteger:
            images = paginator.page(1)
        except EmptyPage:
            images = paginator.page(paginator.num_pages)
        context['gallery_images'] = images
        context['gallery_tags'] = tags
        return context

    @route('^tags/$', name='tag_archive')
    @route('^tags/([\w-]+)/$', name='tag_archive')
    def tag_archive(self, request, tag=None):
        try:
            tag = Tag.objects.get(slug=tag)
        except Tag.DoesNotExist:
            if tag:
                msg = 'There are no blog posts tagged with "{}"'.format(tag)
                messages.add_message(request, messages.INFO, msg)
            return redirect(self.url)
        try:
            taglist.append(tag)
        except NameError:
            taglist = []
            taglist.append(tag)

        images = get_gallery_images(self.collection.name, self, tags=taglist)
        tags = self.get_gallery_tags(tags=taglist)
        paginator = Paginator(images, self.images_per_page)
        page = request.GET.get('page')
        try:
            images = paginator.page(page)
        except PageNotAnInteger:
            images = paginator.page(1)
        except EmptyPage:
            images = paginator.page(paginator.num_pages)
        context = self.get_context(request)
        context['gallery_images'] = images
        context['gallery_tags'] = tags
        context['current_tag'] = tag
        return render(request,
                      'wagtail_simple_gallery/simple_gallery_index.html',
                      context)

    class Meta:
        verbose_name = _('Gallery index')
        verbose_name_plural = _('Gallery indices')

    template = getattr(settings, 'SIMPLE_GALLERY_TEMPLATE',
                       'wagtail_simple_gallery/simple_gallery_index.html')

    def get_gallery_tags(self, tags=[]):
        images = get_gallery_images(self.collection.name, self, tags=tags)
        for img in images:
            tags += img.tags.all()
        tags = sorted(set(tags))
        return tags
Exemplo n.º 8
0
class People(BasePage):

    RESOURCES_PER_PAGE = 8

    parent_page_types = ["home.HomePage", "content.ContentPage"]
    subpage_types = ["Person"]
    template = "people.html"

    # Content fields
    description = RichTextField(
        blank=True,
        default="",
        features=RICH_TEXT_FEATURES_SIMPLE,
        help_text="Optional short text description, max. 400 characters",
        max_length=400,
    )

    # Meta fields
    nav_description = TextField("Navigation description",
                                max_length=400,
                                blank=True,
                                default="")
    keywords = ClusterTaggableManager(through=PeopleTag, blank=True)
    icon = FileField(
        upload_to="people/icons",
        blank=True,
        default="",
        help_text=("MUST be a black-on-transparent SVG icon ONLY, "
                   "with no bitmap embedded in it."),
        validators=[check_for_svg_file],
    )

    # Content panels
    content_panels = BasePage.content_panels + [FieldPanel("description")]

    # Meta panels
    meta_panels = [
        FieldPanel(
            "nav_description",
            help_text=
            "Text to display in the navigation with the title for this page.",
        ),
        MultiFieldPanel(
            [FieldPanel("icon")],
            heading="Theme",
            help_text=
            ("This icon will be used if, for example, this page is shown in a Menu"
             ),
        ),
        MultiFieldPanel(
            [
                FieldPanel("seo_title"),
                FieldPanel("search_description"),
                ImageChooserPanel("social_image"),
                FieldPanel("keywords"),
            ],
            heading="SEO",
            help_text=(
                "Optional fields to override the default title and description "
                "for SEO purposes"),
        ),
    ]

    # Settings panels
    settings_panels = BasePage.settings_panels + [
        FieldPanel("slug"),
        FieldPanel("show_in_menus"),
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading="Content"),
        ObjectList(meta_panels, heading="Meta"),
        ObjectList(settings_panels, heading="Settings", classname="settings"),
    ])

    class Meta:
        verbose_name_plural = "People"

    @classmethod
    def can_create_at(cls, parent):
        # Allow only one instance of this page type
        return super().can_create_at(parent) and not cls.objects.exists()

    def get_context(self, request):
        context = super().get_context(request)
        context["filters"] = self.get_filters()
        context["people"] = self.get_people(request)
        return context

    def get_people(self, request):

        countries = request.GET.getlist(LOCATION_QUERYSTRING_KEY)
        roles = request.GET.getlist(ROLE_QUERYSTRING_KEY)
        topics = request.GET.getlist(TOPIC_QUERYSTRING_KEY)

        countries_q = Q(country__in=countries) if countries else Q()
        roles_q = Q(role__in=roles) if roles else Q()
        topics_q = Q(topics__topic__slug__in=topics) if topics else Q()

        combined_q = Q()
        if countries_q:
            combined_q.add(countries_q, Q.AND)
        if roles_q:
            combined_q.add(roles_q, Q.AND)
        if topics_q:
            combined_q.add(topics_q, Q.AND)

        people = Person.published_objects.filter(combined_q).order_by("title")

        people = paginate_resources(
            people,
            page_ref=request.GET.get(PAGINATION_QUERYSTRING_KEY),
            per_page=self.RESOURCES_PER_PAGE,
        )

        return people

    def get_relevant_countries(self):
        # Relevant here means a country that a published person is in
        raw_countries = (person.country
                         for person in Person.published_objects.distinct(
                             "country").order_by("country") if person.country)

        return [{
            "code": country.code,
            "name": country.name
        } for country in raw_countries]

    def get_filters(self):
        from ..topics.models import Topic

        return {
            "countries": self.get_relevant_countries(),
            "roles": ROLE_CHOICES,
            "topics": Topic.published_objects.order_by("title"),
        }
Exemplo n.º 9
0
class HomePage(Page):
    """
    The Home Page. This looks slightly more complicated than it is. You can
    see if you visit your site and edit the homepage that it is split between
    a:
    - Hero area
    - Body area
    - A promotional area
    - Moveable featured site sections
    """

    # Hero section of HomePage
    image = models.ForeignKey('wagtailimages.Image',
                              null=True,
                              blank=True,
                              on_delete=models.SET_NULL,
                              related_name='+',
                              help_text='Homepage image')
    hero_text = models.CharField(
        max_length=255, help_text='Write an introduction for the bakery')
    hero_cta = models.CharField(verbose_name='Hero CTA',
                                max_length=255,
                                help_text='Text to display on Call to Action')
    hero_cta_link = models.ForeignKey(
        'wagtailcore.Page',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        verbose_name='Hero CTA link',
        help_text='Choose a page to link to for the Call to Action')

    # Body section of the HomePage
    body = StreamField(BaseStreamBlock(),
                       verbose_name="Home content block",
                       blank=True)

    # Promo section of the HomePage
    promo_image = models.ForeignKey('wagtailimages.Image',
                                    null=True,
                                    blank=True,
                                    on_delete=models.SET_NULL,
                                    related_name='+',
                                    help_text='Promo image')
    promo_title = models.CharField(
        null=True,
        blank=True,
        max_length=255,
        help_text='Title to display above the promo copy')
    promo_text = RichTextField(null=True,
                               blank=True,
                               help_text='Write some promotional copy')

    # Featured sections on the HomePage
    # You will see on templates/base/home_page.html that these are treated
    # in different ways, and displayed in different areas of the page.
    # Each list their children items that we access via the children function
    # that we define on the individual Page models e.g. BlogIndexPage
    featured_section_1_title = models.CharField(
        null=True,
        blank=True,
        max_length=255,
        help_text='Title to display above the promo copy')
    featured_section_1 = models.ForeignKey(
        'wagtailcore.Page',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        help_text='First featured section for the homepage. Will display up to '
        'three child items.',
        verbose_name='Featured section 1')

    featured_section_2_title = models.CharField(
        null=True,
        blank=True,
        max_length=255,
        help_text='Title to display above the promo copy')
    featured_section_2 = models.ForeignKey(
        'wagtailcore.Page',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        help_text='Second featured section for the homepage. Will display up to '
        'three child items.',
        verbose_name='Featured section 2')

    featured_section_3_title = models.CharField(
        null=True,
        blank=True,
        max_length=255,
        help_text='Title to display above the promo copy')
    featured_section_3 = models.ForeignKey(
        'wagtailcore.Page',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        help_text='Third featured section for the homepage. Will display up to '
        'six child items.',
        verbose_name='Featured section 3')

    content_panels = Page.content_panels + [
        MultiFieldPanel([
            ImageChooserPanel('image'),
            FieldPanel('hero_text', classname="full"),
            MultiFieldPanel([
                FieldPanel('hero_cta'),
                PageChooserPanel('hero_cta_link'),
            ]),
        ],
                        heading="Hero section"),
        MultiFieldPanel([
            ImageChooserPanel('promo_image'),
            FieldPanel('promo_title'),
            FieldPanel('promo_text'),
        ],
                        heading="Promo section"),
        StreamFieldPanel('body'),
        MultiFieldPanel([
            MultiFieldPanel([
                FieldPanel('featured_section_1_title'),
                PageChooserPanel('featured_section_1'),
            ]),
            MultiFieldPanel([
                FieldPanel('featured_section_2_title'),
                PageChooserPanel('featured_section_2'),
            ]),
            MultiFieldPanel([
                FieldPanel('featured_section_3_title'),
                PageChooserPanel('featured_section_3'),
            ]),
        ],
                        heading="Featured homepage sections",
                        classname="collapsible")
    ]

    api_fields = [
        APIField('image'),
        APIField('body'),
    ]

    def __str__(self):
        return self.title
Exemplo n.º 10
0
class HomePage(Page):
    city = models.CharField(null=True, blank=False, max_length=255)
    zip_code = models.CharField(null=True, blank=False, max_length=255)
    address = models.CharField(null=True, blank=False, max_length=255)
    telephone = models.CharField(blank=True, max_length=255)
    vat_number = models.CharField(blank=True, max_length=255)
    email = models.CharField(blank=True, max_length=255)

    copyrightholder = models.CharField(blank=True, max_length=255)

    about = RichTextField(blank=True)
    privacy = RichTextField(blank=True)
    shipping = RichTextField(blank=True)
    gtc = RichTextField(blank=True)
    cancellation_policy = RichTextField(blank=True)

    sociallinks = StreamField([
        ('link',
         blocks.URLBlock(
             help_text="Important! Format https://www.domain.tld/xyz"))
    ])

    array = []

    def sociallink_company(self):
        for link in self.sociallinks:
            self.array.append(str(link).split(".")[1])
        return self.array

    headers = StreamField([
        ('h_hero', _H_HeroBlock(icon='image')),
        # ('code', blocks.RawHTMLBlock(null=True, blank=True, classname="full", icon='code'))
    ])

    sections = StreamField(
        [('s_feature', _S_FeatureBlock(icon='fa-info')),
         ('s_maps', _S_MapsBlock(icon='fa-info')),
         ('s_partners', _S_PartnersBlock(icon='fa-info')),
         ('s_references', _S_ReferencesBlock(icon='fa-info')),
         ('s_about', _S_AboutBlock(icon='fa-info')),
         ('s_news', _S_NewsBlock(icon='fa-info')),
         ('s_projects', _S_ProjectsBlock(icon='fa-info')),
         ('s_contentcenter', _S_ContentCenter(icon='fa-info')),
         ('s_contentright', _S_ContentRight(icon='fa-info')),
         ('s_contentleft', _S_ContentLeft(icon='fa-info'))],
        null=True,
        blank=True)

    # footers = StreamField([
    # ], null=True, blank=False)

    token = models.CharField(null=True, blank=True, max_length=255)

    #graphql_fields = [
    #    GraphQLStreamfield("headers"),
    #    GraphQLStreamfield("sections"),
    #]

    main_content_panels = [
        StreamFieldPanel('headers'),
        StreamFieldPanel('sections'),
        # StreamFieldPanel('footers')
    ]

    imprint_panels = [
        MultiFieldPanel(
            [
                FieldPanel('city'),
                FieldPanel('zip_code'),
                FieldPanel('address'),
                FieldPanel('telephone'),
                FieldPanel('email'),
                FieldPanel('copyrightholder')
            ],
            heading="contact",
        ),
        MultiFieldPanel(
            [FieldPanel('vat_number')],
            heading="legal",
        ),
        StreamFieldPanel('sociallinks'),
        MultiFieldPanel(
            [
                FieldPanel('about'),
                FieldPanel('privacy'),
                FieldPanel('shipping'),
                FieldPanel('gtc'),
                FieldPanel('cancellation_policy'),
            ],
            heading="terms",
        )
    ]

    token_panel = [FieldPanel('token')]

    edit_handler = TabbedInterface([
        ObjectList(Page.content_panels + main_content_panels, heading='Main'),
        ObjectList(imprint_panels, heading='Imprint'),
        ObjectList(Page.promote_panels + token_panel + Page.settings_panels,
                   heading='Settings',
                   classname="settings")
    ])

    preview_modes = []
Exemplo n.º 11
0
class Person(BasePage):
    resource_type = "person"
    parent_page_types = ["People"]
    subpage_types = []
    template = "person.html"

    # Content fields
    nickname = CharField(max_length=250, null=True, blank=True)
    job_title = CharField(max_length=250)
    role = CharField(max_length=250, choices=ROLE_CHOICES, default="staff")
    description = RichTextField(
        "About",
        blank=True,
        default="",
        features=RICH_TEXT_FEATURES_SIMPLE,
        help_text="Optional ‘About me’ section content, supports rich text",
    )

    # Card fields
    card_title = CharField("Title", max_length=140, blank=True, default="")
    card_description = TextField("Description",
                                 max_length=400,
                                 blank=True,
                                 default="")
    card_image = ForeignKey(
        "mozimages.MozImage",
        null=True,
        blank=True,
        on_delete=SET_NULL,
        related_name="+",
        verbose_name="Image",
        help_text="An image in 16:9 aspect ratio",
    )

    # Meta
    city = CharField(max_length=250, blank=True, default="")
    country = CountryField(verbose_name="Country or Region",
                           blank=True,
                           default="")
    twitter = CharField(max_length=250, blank=True, default="")
    facebook = CharField(max_length=250, blank=True, default="")
    linkedin = CharField(max_length=250, blank=True, default="")
    github = CharField(max_length=250, blank=True, default="")
    email = CharField(max_length=250, blank=True, default="")
    websites = StreamField(
        StreamBlock([("website", PersonalWebsiteBlock())],
                    max_num=3,
                    required=False),
        null=True,
        blank=True,
        help_text="Optional links to any other personal websites",
    )
    keywords = ClusterTaggableManager(through=PersonTag, blank=True)

    # Content panels
    content_panels = [
        MultiFieldPanel(
            [
                CustomLabelFieldPanel("title", label="Full name"),
                FieldPanel("nickname"),
                FieldPanel("job_title"),
                FieldPanel("role"),
            ],
            heading="Details",
        ),
        FieldPanel("description"),
    ]

    # Card panels
    card_panels = [
        FieldPanel("card_title"),
        FieldPanel("card_description"),
        MultiFieldPanel(
            [ImageChooserPanel("card_image")],
            heading="16:9 Image",
            help_text=(
                "Image used for representing this page as a Card. "
                "Should be 16:9 aspect ratio. "
                "If not specified a fallback will be used. "
                "This image is also shown when sharing this page via social "
                "media unless a social image is specified."),
        ),
    ]

    # Meta panels
    meta_panels = [
        MultiFieldPanel(
            [FieldPanel("city"), FieldPanel("country")],
            heading="Location",
            help_text=("Location fields. The country field is also filterable "
                       "via the people directory page."),
        ),
        MultiFieldPanel([InlinePanel("topics")],
                        heading="Topics this person specializes in"),
        MultiFieldPanel(
            [
                FieldPanel("twitter"),
                FieldPanel("facebook"),
                FieldPanel("linkedin"),
                FieldPanel("github"),
                FieldPanel("email"),
            ],
            heading="Profiles",
            help_text="",
        ),
        StreamFieldPanel("websites"),
        MultiFieldPanel(
            [
                FieldPanel("seo_title"),
                FieldPanel("search_description"),
                ImageChooserPanel("social_image"),
                FieldPanel("keywords"),
            ],
            heading="SEO",
            help_text=(
                "Optional fields to override the default title and description "
                "for SEO purposes"),
        ),
    ]

    # Settings panels
    settings_panels = BasePage.settings_panels + [FieldPanel("slug")]

    # Tabs
    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading="Content"),
        ObjectList(card_panels, heading="Card"),
        ObjectList(meta_panels, heading="Meta"),
        ObjectList(settings_panels, heading="Settings", classname="settings"),
    ])

    @property
    def display_title(self):
        """
        Return the display title for profile pages. Adds a nickname to the
        person's full name when one is provided.
        """
        return f'{self.title} aka "{self.nickname}"' if self.nickname else self.title

    @property
    def events(self):
        """
        Return upcoming events where this person is a speaker,
        ordered by start date
        """
        from ..events.models import Event

        upcoming_events = Event.published_objects.filter(
            start_date__gte=get_past_event_cutoff())

        speaker_events = Event.published_objects.none()

        for event in upcoming_events.all():
            # add the event to the list if the current person is a speaker
            if event.has_speaker(self):
                speaker_events = speaker_events | Event.published_objects.page(
                    event)

        return speaker_events.order_by("start_date")

    @property
    def articles(self):
        """
        Return articles and external articles where this person is (one of) the authors,
        ordered by article date, most recent first
        """
        from ..articles.models import Article
        from ..externalcontent.models import ExternalArticle

        articles = Article.published_objects.none()
        external_articles = ExternalArticle.published_objects.none()

        all_articles = Article.published_objects.all()
        all_external_articles = ExternalArticle.published_objects.all()

        for article in all_articles:
            if article.has_author(self):
                articles = articles | Article.published_objects.page(article)

        for external_article in all_external_articles:
            if external_article.has_author(self):
                external_articles = external_articles | (
                    ExternalArticle.published_objects.page(external_article))

        return sorted(chain(articles, external_articles),
                      key=attrgetter("date"),
                      reverse=True)

    @property
    def videos(self):
        """
        Return the most recent videos and external videos where this person is (one of)
        the speakers.
        """
        from ..videos.models import Video
        from ..externalcontent.models import ExternalVideo

        videos = Video.published_objects.none()
        external_videos = ExternalVideo.published_objects.none()

        all_videos = Video.published_objects.all()
        all_external_videos = ExternalVideo.published_objects.all()

        for video in all_videos:
            if video.has_speaker(self):
                videos = videos | Video.published_objects.page(video)

        for external_video in all_external_videos:
            if external_video.has_speaker(self):
                external_videos = external_videos | (
                    ExternalVideo.published_objects.page(external_video))

        return sorted(chain(videos, external_videos),
                      key=attrgetter("date"),
                      reverse=True)

    @property
    def role_group(self):
        return {
            "slug": self.role,
            "title": dict(ROLE_CHOICES).get(self.role, "")
        }

    @property
    def country_group(self):
        return ({
            "slug": self.country.code.lower(),
            "title": self.country.name
        } if self.country else {
            "slug": ""
        })

    def get_topics(self) -> List:
        """Return the live/published Topic pages associated with this Person"""

        # Note that we do this in Python because django-modelcluster won't support
        # `filter(topic__live=True)` when _previewing_ pages (even tho it'll work
        # on saved ones)
        topics = [pt.topic for pt in self.topics.all()]
        return [t for t in topics if t.live]
Exemplo n.º 12
0
class CoursePage(Page):

    parent_page_type = ["CourseIndexPage"]
    intro = models.CharField('one line summary', max_length=250)
    summary = RichTextField('full summary')
    # start_date = models.DateTimeField(blank=False)
    address_details = models.CharField('address details',
                                       max_length=250,
                                       blank=True)
    formatted_address = models.CharField(max_length=255)
    # latlng_address = models.CharField(max_length=255)

    course_image = models.ForeignKey('wagtailimages.Image',
                                     null=True,
                                     blank=True,
                                     on_delete=models.SET_NULL,
                                     related_name='+')

    course_flyer = StreamField([
        ('course_flyer', DocumentChooserBlock()),
    ],
                               blank=True)

    organiser_name = models.CharField('name', max_length=250, blank=True)
    organiser_email = models.EmailField('email', blank=True)
    organiser_number = models.CharField('number', max_length=250, blank=True)

    course_programme = StreamField([
        ('table', TableBlock()),
    ], blank=True)

    class Meta:
        verbose_name = "Courses & Conferences Page"

    search_fields = Page.search_fields + [
        index.SearchField('intro'),
        index.SearchField('summary'),
    ]

    content_panels = Page.content_panels + [
        FieldPanel('intro', classname="full"),
        FieldPanel('summary', classname="full"),
        ImageChooserPanel('course_image'),
        InlinePanel('related_links', label="Course Links"),
        InlinePanel('related_dates', label="Course Dates"),
        StreamFieldPanel('course_programme'),
        StreamFieldPanel('course_flyer'),
        MultiFieldPanel(
            [
                FieldPanel('address_details', classname="full"),
                MapFieldPanel('formatted_address'),
            ],
            heading="Location",
            # classname="collapsible collapsed"
        ),
        MultiFieldPanel(
            [
                FieldPanel('organiser_name', classname="full"),
                FieldPanel('organiser_email', classname="full"),
                FieldPanel('organiser_number', classname="full"),
            ],
            heading="Organiser Details",
            # classname="collapsible collapsed"
        ),

        # MapFieldPanel('latlng_address', latlng=True),
    ]

    # parent_page_types = ['CourseIndexPage']

    def get_context(self, request):
        context = super().get_context(request)
        relateddates = self.related_dates.order_by('date')
        #the [:1] returns the first result from the list e.g. the start date.
        context['relateddates'] = relateddates
        return context
Exemplo n.º 13
0
class PublicationPage(FoundationMetadataPageMixin, Page):
    """
    This is the root page of a publication.

    From here the user can browse to the various sections (called chapters).
    It will have information on the publication, its authors, and metadata from it's children

    Publications are collections of Articles
    Publications can also be broken down into Chapters, which are really just child publication pages
    Each of those Chapters may have several Articles
    An Article can only belong to one Chapter/Publication Page
    """

    subpage_types = ['ArticlePage', 'PublicationPage']

    toc_thumbnail_image = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='toc_thumbnail_image',
        verbose_name='Table of Content Thumbnail',
        help_text=
        'Thumbnail image to show on table of content. Use square image of 320×320 pixels or larger.',
    )

    hero_image = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='publication_hero_image',
        verbose_name='Publication Hero Image',
    )
    subtitle = models.CharField(
        blank=True,
        max_length=250,
    )
    secondary_subtitle = models.CharField(
        blank=True,
        max_length=250,
    )
    publication_date = models.DateField("Publication date",
                                        null=True,
                                        blank=True)
    publication_file = models.ForeignKey(
        'wagtaildocs.Document',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
    )
    additional_author_copy = models.CharField(
        help_text="Example: with contributing authors",
        max_length=100,
        blank=True,
    )
    intro_notes = RichTextField(blank=True,
                                features=['link', 'bold', 'italic', 'h4'])
    notes = RichTextField(
        blank=True, features=['link', 'bold', 'italic', 'h4', 'ol', 'ul'])
    contents_title = models.CharField(
        blank=True,
        default="Table of Contents",
        max_length=250,
    )
    content_panels = Page.content_panels + [
        MultiFieldPanel(
            [
                FieldPanel('subtitle'),
                FieldPanel('secondary_subtitle'),
                FieldPanel('publication_date'),
                ImageChooserPanel('toc_thumbnail_image'),
                ImageChooserPanel('hero_image'),
                DocumentChooserPanel('publication_file'),
                InlinePanel('authors', label='Author'),
                FieldPanel('additional_author_copy'),
            ],
            heading='Hero',
        ),
        FieldPanel('intro_notes'),
        FieldPanel('contents_title'),
        FieldPanel('notes'),
    ]

    @property
    def is_chapter_page(self):
        """
        A PublicationPage nested under a PublicationPage is considered to be a
        "ChapterPage". The templates used very similar logic and structure, and
        all the fields are the same.
        """
        parent = self.get_parent().specific
        return parent.__class__ is PublicationPage

    @property
    def next_page(self):
        """
        Only applies to Chapter Publication (sub-Publication Pages).
        Returns a Page object or None.
        """
        next_page = self.get_parent()
        if self.is_chapter_page:
            sibling = self.get_siblings().filter(path__gt=self.path,
                                                 live=True).first()
            if sibling:
                # If there is no more chapters. Return the parent page.
                next_page = sibling
        return next_page

    @property
    def prev_page(self):
        """
        Only applies to Chapter Publication (sub-Publication Pages).
        Returns a Page object or None.
        """
        prev_page = self.get_parent()
        if self.is_chapter_page:
            sibling = self.get_siblings().filter(path__lt=self.path,
                                                 live=True).reverse().first()
            if sibling:
                # If there is no more chapters. Return the parent page.
                prev_page = sibling
        return prev_page

    @property
    def zen_nav(self):
        return True

    def breadcrumb_list(self):
        """
        Get all the parent PublicationPages and return a QuerySet
        """
        return Page.objects.ancestor_of(self).type(PublicationPage).live()

    def get_context(self, request, *args, **kwargs):
        context = super().get_context(request, *args, **kwargs)
        pages = []
        for page in self.get_children():
            if request.user.is_authenticated:
                # User is logged in, and can preview a page. Get all pages, even drafts.
                pages.append({
                    'child': page,
                    'grandchildren': page.get_children()
                })
            elif page.live:
                # User is not logged in AND this page is live. Only fetch live grandchild pages.
                pages.append({
                    'child': page,
                    'grandchildren': page.get_children().live()
                })
        context['child_pages'] = pages
        return set_main_site_nav_information(self, context, 'Homepage')
Exemplo n.º 14
0
class CalendarPage(RoutablePageMixin, Page):
    """CalendarPage displays all the events which are in the same site."""
    class Meta:
        verbose_name = _("calendar page")
        verbose_name_plural = _("calendar pages")

    EventsPerPage = getattr(settings, "JOYOUS_EVENTS_PER_PAGE", 25)
    holidays = Holidays()
    subpage_types = [
        'joyous.SimpleEventPage', 'joyous.MultidayEventPage',
        'joyous.RecurringEventPage', 'joyous.MultidayRecurringEventPage'
    ]
    base_form_class = CalendarPageForm

    intro = RichTextField(_("intro"),
                          blank=True,
                          help_text=_("Introductory text."))
    view_choices = MultipleSelectField(_("view choices"),
                                       blank=True,
                                       default=["L", "W", "M"],
                                       choices=EVENTS_VIEW_CHOICES)
    default_view = models.CharField(_("default view"),
                                    default="M",
                                    max_length=15,
                                    choices=EVENTS_VIEW_CHOICES)

    search_fields = Page.search_fields[:]
    content_panels = Page.content_panels + [
        FieldPanel('intro', classname="full"),
    ]
    settings_panels = Page.settings_panels + [
        MultiFieldPanel(
            [FieldPanel('view_choices'),
             FieldPanel('default_view')],
            heading=_("View Options")),
    ]

    @route(r"^$")
    @route(r"^{YYYY}/$".format(**DatePictures))
    def routeDefault(self, request, year=None):
        """Route a request to the default calendar view."""
        eventsView = request.GET.get('view', self.default_view)
        if eventsView in ("L", "list"):
            return self.serveUpcoming(request)
        elif eventsView in ("W", "weekly"):
            return self.serveWeek(request, year)
        else:
            return self.serveMonth(request, year)

    @route(r"^{YYYY}/{Mon}/$(?i)".format(**DatePictures))
    def routeByMonthAbbr(self, request, year, monthAbbr):
        """Route a request with a month abbreviation to the monthly view."""
        month = (DatePictures['Mon'].index(monthAbbr.lower()) // 4) + 1
        return self.serveMonth(request, year, month)

    @route(r"^month/$")
    @route(r"^{YYYY}/{MM}/$".format(**DatePictures))
    def serveMonth(self, request, year=None, month=None):
        """Monthly calendar view."""
        myurl = self.get_url(request)

        def myUrl(urlYear, urlMonth):
            if 1900 <= urlYear <= 2099:
                return myurl + self.reverse_subpage('serveMonth',
                                                    args=[urlYear, urlMonth])

        today = timezone.localdate()
        if year is None: year = today.year
        if month is None: month = today.month
        year = int(year)
        month = int(month)
        lastDay = dt.date(year, month, calendar.monthrange(year, month)[1])

        if year == today.year and month == today.month:
            weekNum = gregorian_to_week_date(today)[1]
        else:
            weekNum = gregorian_to_week_date(dt.date(year, month, 7))[1]
        weeklyUrl = myurl + self.reverse_subpage('serveWeek',
                                                 args=[year, weekNum])
        listUrl = myurl + self.reverse_subpage('serveUpcoming')

        prevMonth = month - 1
        prevMonthYear = year
        if prevMonth == 0:
            prevMonth = 12
            prevMonthYear -= 1

        nextMonth = month + 1
        nextMonthYear = year
        if nextMonth == 13:
            nextMonth = 1
            nextMonthYear += 1

        cxt = self._getCommonContext(request)
        cxt.update({
            'year': year,
            'month': month,
            'yesterday': today - dt.timedelta(1),
            'lastweek': today - dt.timedelta(7),
            'lastDay': lastDay,
            'prevMonthUrl': myUrl(prevMonthYear, prevMonth),
            'nextMonthUrl': myUrl(nextMonthYear, nextMonth),
            'prevYearUrl': myUrl(year - 1, month),
            'nextYearUrl': myUrl(year + 1, month),
            'weeklyUrl': weeklyUrl,
            'listUrl': listUrl,
            'thisMonthUrl': myUrl(today.year, today.month),
            'monthName': MONTH_NAMES[month],
            'weekdayAbbr': weekday_abbr,
            'events': self._getEventsByWeek(request, year, month)
        })
        cxt.update(self._getExtraContext("month"))
        return TemplateResponse(request, "joyous/calendar_month.html", cxt)

    @route(r"^week/$")
    @route(r"^{YYYY}/W{WW}/$".format(**DatePictures))
    def serveWeek(self, request, year=None, week=None):
        """Weekly calendar view."""
        myurl = self.get_url(request)

        def myUrl(urlYear, urlWeek):
            if (urlYear < 1900 or urlYear > 2099
                    or urlYear == 2099 and urlWeek == 53):
                return None
            if urlWeek == 53 and num_weeks_in_year(urlYear) == 52:
                urlWeek = 52
            return myurl + self.reverse_subpage('serveWeek',
                                                args=[urlYear, urlWeek])

        today = timezone.localdate()
        thisYear, thisWeekNum, dow = gregorian_to_week_date(today)
        if year is None: year = thisYear
        if week is None: week = thisWeekNum
        year = int(year)
        week = int(week)

        firstDay, lastDay, prevYearNumWeeks, yearNumWeeks = week_info(
            year, week)
        if week == 53 and yearNumWeeks == 52:
            raise Http404("Only 52 weeks in {}".format(year))

        eventsInWeek = self._getEventsByDay(request, firstDay, lastDay)
        if firstDay.year >= 1900:
            monthlyUrl = myurl + self.reverse_subpage(
                'serveMonth', args=[firstDay.year, firstDay.month])
        else:
            monthlyUrl = myurl + self.reverse_subpage('serveMonth',
                                                      args=[1900, 1])
        listUrl = myurl + self.reverse_subpage('serveUpcoming')

        prevWeek = week - 1
        prevWeekYear = year
        if prevWeek == 0:
            prevWeek = prevYearNumWeeks
            prevWeekYear -= 1

        nextWeek = week + 1
        nextWeekYear = year
        if nextWeek > yearNumWeeks:
            nextWeek = 1
            nextWeekYear += 1

        cxt = self._getCommonContext(request)
        cxt.update({
            'year': year,
            'week': week,
            'yesterday': today - dt.timedelta(1),
            'prevWeekUrl': myUrl(prevWeekYear, prevWeek),
            'nextWeekUrl': myUrl(nextWeekYear, nextWeek),
            'prevYearUrl': myUrl(year - 1, week),
            'nextYearUrl': myUrl(year + 1, week),
            'thisWeekUrl': myUrl(thisYear, thisWeekNum),
            'monthlyUrl': monthlyUrl,
            'listUrl': listUrl,
            'weekName': _("Week {weekNum}").format(weekNum=week),
            'weekdayAbbr': weekday_abbr,
            'events': [eventsInWeek]
        })
        cxt.update(self._getExtraContext("week"))
        return TemplateResponse(request, "joyous/calendar_week.html", cxt)

    @route(r"^day/$")
    @route(r"^{YYYY}/{MM}/{DD}/$".format(**DatePictures))
    def serveDay(self, request, year=None, month=None, dom=None):
        """The events of the day list view."""
        myurl = self.get_url(request)
        today = timezone.localdate()
        if year is None: year = today.year
        if month is None: month = today.month
        if dom is None: dom = today.day
        year = int(year)
        month = int(month)
        dom = int(dom)
        day = dt.date(year, month, dom)

        daysEvents = self._getEventsOnDay(request, day).all_events
        # FIXME: Does include Cancellations - should it?
        if len(daysEvents) == 1:
            event = daysEvents[0].page
            return redirect(event.get_url(request))
        eventsPage = self._paginate(request, daysEvents)

        monthlyUrl = myurl + self.reverse_subpage('serveMonth',
                                                  args=[year, month])
        weekNum = gregorian_to_week_date(today)[1]
        weeklyUrl = myurl + self.reverse_subpage('serveWeek',
                                                 args=[year, weekNum])
        listUrl = myurl + self.reverse_subpage('serveUpcoming')

        cxt = self._getCommonContext(request)
        cxt.update({
            'year': year,
            'month': month,
            'dom': dom,
            'day': day,
            'monthlyUrl': monthlyUrl,
            'weeklyUrl': weeklyUrl,
            'listUrl': listUrl,
            'monthName': MONTH_NAMES[month],
            'weekdayName': WEEKDAY_NAMES[day.weekday()],
            'events': eventsPage
        })
        cxt.update(self._getExtraContext("day"))
        return TemplateResponse(request, "joyous/calendar_list_day.html", cxt)

    @route(r"^upcoming/$")
    def serveUpcoming(self, request):
        """Upcoming events list view."""
        myurl = self.get_url(request)
        today = timezone.localdate()
        monthlyUrl = myurl + self.reverse_subpage(
            'serveMonth', args=[today.year, today.month])
        weekNum = gregorian_to_week_date(today)[1]
        weeklyUrl = myurl + self.reverse_subpage('serveWeek',
                                                 args=[today.year, weekNum])
        listUrl = myurl + self.reverse_subpage('servePast')
        upcomingEvents = self._getUpcomingEvents(request)
        # FIXME: Does not include Cancellations - should it?
        eventsPage = self._paginate(request, upcomingEvents)

        cxt = self._getCommonContext(request)
        cxt.update({
            'weeklyUrl': weeklyUrl,
            'monthlyUrl': monthlyUrl,
            'listUrl': listUrl,
            'events': eventsPage
        })
        cxt.update(self._getExtraContext("upcoming"))
        return TemplateResponse(request, "joyous/calendar_list_upcoming.html",
                                cxt)

    @route(r"^past/$")
    def servePast(self, request):
        """Past events list view."""
        myurl = self.get_url(request)
        today = timezone.localdate()
        monthlyUrl = myurl + self.reverse_subpage(
            'serveMonth', args=[today.year, today.month])
        weekNum = gregorian_to_week_date(today)[1]
        weeklyUrl = myurl + self.reverse_subpage('serveWeek',
                                                 args=[today.year, weekNum])
        listUrl = myurl + self.reverse_subpage('serveUpcoming')
        pastEvents = self._getPastEvents(request)
        # FIXME: Does not include Cancellations - should it?
        eventsPage = self._paginate(request, pastEvents)

        cxt = self._getCommonContext(request)
        cxt.update({
            'weeklyUrl': weeklyUrl,
            'monthlyUrl': monthlyUrl,
            'listUrl': listUrl,
            'events': eventsPage
        })
        cxt.update(self._getExtraContext("past"))
        return TemplateResponse(request, "joyous/calendar_list_past.html", cxt)

    @route(r"^mini/{YYYY}/{MM}/$".format(**DatePictures))
    def serveMiniMonth(self, request, year=None, month=None):
        """Serve data for the MiniMonth template tag."""
        if not request.is_ajax():
            raise Http404("/mini/ is for ajax requests only")

        today = timezone.localdate()
        if year is None: year = today.year
        if month is None: month = today.month
        year = int(year)
        month = int(month)

        cxt = self._getCommonContext(request)
        cxt.update({
            'year': year,
            'month': month,
            'calendarUrl': self.get_url(request),
            'monthName': MONTH_NAMES[month],
            'weekdayInfo': zip(weekday_abbr, weekday_name),
            'events': self._getEventsByWeek(request, year, month)
        })
        cxt.update(self._getExtraContext("mini"))
        return TemplateResponse(request, "joyous/includes/minicalendar.html",
                                cxt)

    @classmethod
    def can_create_at(cls, parent):
        return super().can_create_at(parent) and cls._allowAnotherAt(parent)

    @classmethod
    def _allowAnotherAt(cls, parent):
        """You can only create one of these pages per site."""
        site = parent.get_site()
        if site is None:
            return False
        return not cls.peers().descendant_of(site.root_page).exists()

    @classmethod
    def peers(cls):
        """Return others of the same concrete type."""
        contentType = ContentType.objects.get_for_model(cls)
        return cls.objects.filter(content_type=contentType)

    def _getCommonContext(self, request):
        cxt = self.get_context(request)
        cxt.update({
            'version': __version__,
            'themeCSS': getattr(settings, "JOYOUS_THEME_CSS", ""),
            'today': timezone.localdate()
        })
        return cxt

    def _getExtraContext(self, route):
        return {}

    def _getEventsOnDay(self, request, day):
        """Return all the events in this site for a given day."""
        return self._getEventsByDay(request, day, day)[0]

    def _getEventsByDay(self, request, firstDay, lastDay):
        """
        Return the events in this site for the dates given, grouped by day.
        """
        home = request.site.root_page
        return getAllEventsByDay(request,
                                 firstDay,
                                 lastDay,
                                 home=home,
                                 holidays=self.holidays)

    def _getEventsByWeek(self, request, year, month):
        """
        Return the events in this site for the given month grouped by week.
        """
        home = request.site.root_page
        return getAllEventsByWeek(request,
                                  year,
                                  month,
                                  home=home,
                                  holidays=self.holidays)

    def _getUpcomingEvents(self, request):
        """Return the upcoming events in this site."""
        home = request.site.root_page
        return getAllUpcomingEvents(request, home=home)

    def _getPastEvents(self, request):
        """Return the past events in this site."""
        home = request.site.root_page
        return getAllPastEvents(request, home=home)

    def _getEventFromUid(self, request, uid):
        """Try and find an event with the given UID in this site."""
        event = getEventFromUid(request, uid)  # might raise exception
        home = request.site.root_page
        if event.get_ancestors().filter(id=home.id).exists():
            # only return event if it is in the same site
            return event

    def _getAllEvents(self, request):
        """Return all the events in this site."""
        home = request.site.root_page
        return getAllEvents(request, home=home)

    def _paginate(self, request, events):
        paginator = Paginator(events, self.EventsPerPage)
        try:
            eventsPage = paginator.page(request.GET.get('page'))
        except PageNotAnInteger:
            eventsPage = paginator.page(1)
        except EmptyPage:
            eventsPage = paginator.page(paginator.num_pages)
        return eventsPage
Exemplo n.º 15
0
class HomePage(Page):
    city = models.CharField(null=True, blank=False, max_length=255)
    zip_code = models.CharField(null=True, blank=False, max_length=255)
    address = models.CharField(null=True, blank=False, max_length=255)
    telephone = models.CharField(null=True, blank=False, max_length=255)
    telefax = models.CharField(null=True, blank=False, max_length=255)
    vat_number = models.CharField(null=True, blank=False, max_length=255)
    whatsapp_telephone = models.CharField(null=True, blank=True, max_length=255)
    whatsapp_contactline = models.CharField(null=True, blank=True, max_length=255)
    tax_id = models.CharField(null=True, blank=False, max_length=255)
    trade_register_number = models.CharField(null=True, blank=False, max_length=255)
    court_of_registry = models.CharField(null=True, blank=False, max_length=255)
    place_of_registry = models.CharField(null=True, blank=False, max_length=255)
    trade_register_number = models.CharField(null=True, blank=False, max_length=255)
    ownership = models.CharField(null=True, blank=False, max_length=255)
    email = models.CharField(null=True, blank=False, max_length=255)

    copyrightholder = models.CharField(null=True, blank=False, max_length=255)

    about = RichTextField(null=True, blank=False)
    privacy = RichTextField(null=True, blank=False)

    sociallinks = StreamField([
        ('link', blocks.URLBlock(help_text="Important! Format https://www.domain.tld/xyz"))
    ])

    array = []
    def sociallink_company(self):
        for link in self.sociallinks:
            self.array.append(str(link).split(".")[1])
        return self.array


    headers = StreamField([
        ('h_hero', _H_HeroBlock(null=True, blank=False, icon='image')),
        ('code', blocks.RawHTMLBlock(null=True, blank=True, classname="full", icon='code'))
    ], null=True, blank=False)

    sections = StreamField([
        ('s_why', _S_WhyBlock(null=True, blank=False, icon='group')),
        ('s_about', _S_AboutBlock(null=True, blank=False, icon='fa-quote-left')),
        ('s_instagram', _S_InstagramBlock(null=True, blank=False, icon='fa-instagram')),
        ('s_steps', _S_StepsBlock(null=True, blank=False, icon='fa-list-ul')),
        ('s_shop', _S_ShopBlock(null=True, blank=False, icon='home')),
        ('s_trusted', _S_TrustedBlock(null=True, blank=False, icon='fa-list-ul')),
        ('s_wolf', _S_WolfBlock(null=True, blank=False, icon='fa-list-ul')),
        ('s_faq', _S_FAQBlock(null=True, blank=False, icon='home')),
        ('code', blocks.RawHTMLBlock(null=True, blank=True, classname="full", icon='code'))
    ], null=True, blank=False)

    token = models.CharField(null=True, blank=True, max_length=255)

    #graphql_fields = [
    #    GraphQLStreamfield("headers"),
    #    GraphQLStreamfield("sections"),
    #]

    main_content_panels = [
        StreamFieldPanel('headers'),
        StreamFieldPanel('sections')
    ]

    imprint_panels = [
        MultiFieldPanel(
            [
            FieldPanel('city'),
            FieldPanel('zip_code'),
            FieldPanel('address'),
            FieldPanel('telephone'),
            FieldPanel('telefax'),
            FieldPanel('whatsapp_telephone'),
            FieldPanel('whatsapp_contactline'),
            FieldPanel('email'),
            FieldPanel('copyrightholder')
            ],
            heading="contact",
        ),
        MultiFieldPanel(
            [
            FieldPanel('vat_number'),
            FieldPanel('tax_id'),
            FieldPanel('trade_register_number'),
            FieldPanel('court_of_registry'),
            FieldPanel('place_of_registry'),
            FieldPanel('trade_register_number'),
            FieldPanel('ownership')
            ],
            heading="legal",
        ),
        StreamFieldPanel('sociallinks'),
        MultiFieldPanel(
            [
            FieldPanel('about'),
            FieldPanel('privacy')
            ],
            heading="privacy",
        )
    ]

    token_panel = [
        FieldPanel('token')
    ]

    edit_handler = TabbedInterface([
        ObjectList(Page.content_panels + main_content_panels, heading='Main'),
        ObjectList(imprint_panels, heading='Imprint'),
        ObjectList(Page.promote_panels + token_panel + Page.settings_panels, heading='Settings', classname="settings")
    ])
Exemplo n.º 16
0
class SubscriptionPage(RoutablePageMixin, BannerPage):
    template = "subscription/subscribe.html"
    subpage_types = []
    parent_page_type = [
        "home.HomePage",
    ]
    max_count = 1

    email_intro_text = RichTextField(
        blank=True,
        verbose_name="Intro Text",
    )
    email_success_text = RichTextField(
        blank=True,
        verbose_name="Success Text",
    )
    telegram_intro_text = RichTextField(
        blank=True,
        verbose_name="Intro Text",
    )

    content_panels = BannerPage.content_panels + [
        MultiFieldPanel(
            [
                FieldPanel("email_intro_text"),
                FieldPanel("email_success_text"),
            ],
            heading="Email",
        ),
        MultiFieldPanel([FieldPanel("telegram_intro_text")],
                        heading="Telegram"),
    ]

    def get_context(self, request, *args, **kwargs):
        context = super().get_context(request, *args, **kwargs)
        context["hide_newsletter_info"] = True
        return context

    @route(r"^$")
    @route(r"^subscribe/$", name="subscribe")
    def subscribe(self, request, *args, **kwargs):
        from subscription.forms import NewsletterSubscriptionForm

        context = self.get_context(request, *args, **kwargs)
        if request.method == "POST":
            form = NewsletterSubscriptionForm(request.POST)
            if form.is_valid():
                subscriber = form.save(commit=False)
                if NewsletterSubscription.objects.filter(
                        email=subscriber.email).exists():
                    context[
                        "error_message"] = """This email address is already subscribed. 
                    You will continue to receive updates."""
                else:
                    subscriber.save()
                return render(request, "subscription/subscribe_landing.html",
                              context)
        else:
            form = NewsletterSubscriptionForm()

        context["form"] = form
        return render(request, "subscription/subscribe.html", context)

    @route(r"^unsubscribe/([0-9a-f]{32})/$", name="unsubscribe")
    def unsubscribe(self, request, uuid, *args, **kwargs):
        context = self.get_context(request, *args, **kwargs)
        subscription = NewsletterSubscription.objects.filter(uuid=uuid).first()
        if subscription:
            subscription.delete()
            context["email"] = subscription.email
        return render(request, "subscription/cancellation.html", context)

    def get_unsubscribe_url_for(self, subscription):
        return self.get_full_url() + self.reverse_subpage(
            "unsubscribe", args=(subscription.uuid, ))
Exemplo n.º 17
0
class PublicationPage(BasePage, CanonicalMixin):
    # standard approach appears to be
    # (via https://docs.wagtail.io/en/latest/topics/streamfield.html)
    # author = models.Charfield(max_length=255)
    # which also gives subcategories for StreamField

    # next two lines would make the post less available but by commenting out
    # instead we make it available anywhere that doesn't exclude it
    # parent_page_types = ["PublicationIndexPage"]
    # subpage_types = []

    updated_at = models.DateTimeField(
        editable=True,
        null=True,
        blank=True,
        verbose_name="Updated at (leave blank for initial publication)",
    )
    history = RichTextField(
        blank=True,
        verbose_name="Version history (leave blank for initial publication)")

    content_panels = [
        *Page.content_panels,
        FieldPanel("first_published_at"),
        FieldPanel("updated_at"),
        StreamFieldPanel("body"),
        FieldPanel("history"),
    ]

    settings_panels = CanonicalMixin.panels + BasePage.settings_panels

    def replace_headers(self, html):
        """Given a html string, return a html string where the header tags
        have id attributes so they can be used as anchors, and list the ids
        and text of those tags so we can make a table of contents."""
        header_tags = ["h2"]
        toc = []
        root = lxml.html.fromstring(html)
        for tag_name in header_tags:
            for tag in root.xpath(f"//{tag_name}"):
                # create a slugged name for the tag of the form tag-text-3
                header_name = tag.text
                bare_slug = slugify(header_name, allow_unicode=True)
                self.slug_count[bare_slug] += 1
                slug = bare_slug + slug_count_text(self.slug_count[bare_slug])
                # modify the tag and record the details
                tag.set("id", slug)
                toc.append((header_name, slug))
        return lxml.html.tostring(root).decode("utf-8"), toc

    def get_context(self, request):
        """Pass the html of each richtext node within the streamfield to
        replace_headers, creating a page-wide table of contents. We use
        self.slug_count to preserve the list of slugs seen so far across
        multiple rich_text blocks."""
        context = super().get_context(request)
        context["toc"] = []  # table of contents
        self.slug_count = Counter({"contents": 1})
        for i, block in enumerate(self.body._raw_data):
            if block["type"] == "rich_text":
                replacement_html, new_toc = self.replace_headers(
                    block["value"])
                context["toc"].extend(new_toc)
                self.body._raw_data[i]["value"] = replacement_html
        return context
Exemplo n.º 18
0
class BlogPage(RoutablePageMixin, Page):
    subtitle = models.CharField(max_length=255, null=True, blank=True)
    main_image = models.ForeignKey('wagtailimages.Image',
                                   null=True,
                                   blank=True,
                                   on_delete=models.SET_NULL,
                                   related_name='+')
    date = models.DateField("Post date", null=True, blank=True)
    intro = models.CharField(max_length=250, null=True, blank=True)
    body = StreamField([
        ('heading', CharBlock(classname="full title", icon='title')),
        ('paragraph', RichTextBlock(icon='pilcrow')),
        ('image', ImageChooserBlock(icon='image')),
        ('codeblock', TextBlock(icon='cogs')),
        ('markdown', MarkDownBlock()),
        ('real_codeblock', CodeBlock()),
    ],
                       blank=True,
                       null=True)
    tags = ClusterTaggableManager(through=BlogPageTag, blank=True)
    listing_intro = RichTextField(null=True, blank=True)
    listing_image = models.ForeignKey('wagtailimages.Image',
                                      null=True,
                                      blank=True,
                                      on_delete=models.SET_NULL,
                                      related_name='+')

    search_fields = Page.search_fields + [
        index.SearchField('intro'),
        index.SearchField('body'),
    ]

    content_panels = Page.content_panels + [
        FieldPanel('subtitle'),
        ImageChooserPanel('main_image'),
        FieldPanel('date'),
        FieldPanel('intro'),
        StreamFieldPanel('body'),
        FieldPanel('tags'),
    ]

    promote_panels = Page.promote_panels + [
        FieldPanel('listing_intro'),
        ImageChooserPanel('listing_image'),
    ]

    @property
    def home_page(self):
        return self.get_parent()

    @property
    def next_blog(self):
        blogs = BlogPage.objects.filter(live=True).order_by('-date')
        current_index = blogs.index(self)

    def get_absolute_url(self):
        return self.full_url

    @route(r'^$', name='normal_blog')
    def normal_blog(self, request):
        site_root = self.get_parent()

        return render(request, self.template, {
            'self': self,
        })

    @route(r'^amp/$', name='amp_blog')
    def amp_blog(self, request):
        context = self.get_context(request)
        context['is_amp'] = True
        context['base_template'] = 'base_amp.html'
        response = TemplateResponse(request, self.template, context)
        return response
Exemplo n.º 19
0
class People(models.Model):
    STATUS_CHOICES = [
        ('former', 'Ehemalig'),
        ('current', 'Aktuell'),
    ]

    ROLE_CHOICES = [
        ('chairholder', 'Lehstuhlinhaber'),
        ('office-management', 'Office Management'),
        ('academic-staff-male', 'Wiss. Mitarbeiter'),
        ('academic-staff-female', 'Wiss. Mitarbeiterin'),
        ('academic-assistant', 'Wiss. Hilfskraft'),
        ('student-assistant', 'Stud. Hilfskraft'),
        ('jurcoach-law-team', 'Jurcoach Jura-Team'),
        ('jurcoach-evaluation-team', 'Jurcoach Evaluations-Team'),
        ('jurcoach-web-team', 'Jurcoach Informatik-Team'),
        ('webmaster', 'Webmaster'),
        ('associate-professor', 'Privatdozent'),
    ]

    user = models.ForeignKey(User,
                             null=True,
                             blank=True,
                             on_delete=models.SET_NULL,
                             related_name='+')
    first_name = models.CharField("Vorname", max_length=255)
    last_name = models.CharField("Nachname", max_length=255)
    status = models.CharField(
        choices=STATUS_CHOICES,
        default='former',
        max_length=255,
        blank=True,
    )
    role = models.CharField(
        choices=ROLE_CHOICES,
        max_length=255,
        blank=True,
    )
    telephone = models.CharField("Telefonnummer", max_length=255, blank=True)
    email = models.CharField("Mailadresse", max_length=255, blank=True)
    room = models.CharField("Raumnummer", max_length=255, blank=True)
    description = RichTextField(blank=True)
    image = models.ForeignKey('wagtailimages.Image',
                              null=True,
                              blank=True,
                              on_delete=models.SET_NULL,
                              related_name='+')

    panels = [
        MultiFieldPanel([
            FieldRowPanel([
                FieldPanel('user', classname="col-12"),
            ]),
            FieldRowPanel([
                FieldPanel('first_name', classname="col-6"),
                FieldPanel('last_name', classname="col-6"),
            ], "Name"),
            FieldRowPanel([
                FieldPanel('telephone', classname="col-4"),
                FieldPanel('email', classname="col-4"),
            ], "Contact"),
            FieldRowPanel([
                FieldPanel('status', classname="col-6"),
                FieldPanel('role', classname="col-6"),
            ]),
            FieldRowPanel([
                FieldPanel('room', classname="col-4"),
            ], "Room"),
        ], "Information"),
        MultiFieldPanel([
            FieldRowPanel([
                FieldPanel('description', classname="col-12"),
            ]),
        ], "Weitere Informationen"),
        ImageChooserPanel('image')
    ]

    search_fields = [
        index.SearchField('first_name'),
        index.SearchField('last_name'),
    ]

    @property
    def thumb_image(self):
        try:
            return self.image.get_rendition('fill-120x120').img_tag()
        except:
            return ''

    def __str__(self):
        return '{} {}'.format(self.first_name, self.last_name)

    class Meta:
        verbose_name = 'Person'
        verbose_name_plural = 'Personen'
Exemplo n.º 20
0
class BlogIndexPage(Page):
    intro = RichTextField(blank=True)

    content_panels = Page.content_panels + [
        InlinePanel('related_links', label="Related links"),
    ]
Exemplo n.º 21
0
class FormSection(SectionBase, SectionTitleBlock, ButtonAction,
                  AbstractEmailForm):

    form_submit_button_text = models.CharField(
        blank=True,
        max_length=100,
        verbose_name='Submit button text',
        default='Submit',
        # help_text="Leave field empty to hide.",
    )
    # intro = RichkTextField(blank=True)
    thank_you_text = RichTextField(blank=True)

    # basic tab panels
    basic_panels = AbstractEmailForm.content_panels + [
        SectionBase.section_content_panels,
        SectionBase.section_layout_panels,
        SectionBase.section_design_panels,
    ]

    # advanced tab panels
    advanced_panels = (SectionTitleBlock.title_basic_panels,
                       ) + ButtonAction.button_action_panels

    # form tab panels
    form_panels = [
        InlinePanel('form_fields', label="Form fields"),
        FieldPanel('thank_you_text', classname="full"),
        MultiFieldPanel([
            FieldRowPanel([
                FieldPanel('from_address', classname="col6"),
                FieldPanel('to_address', classname="col6"),
            ]),
            FieldPanel('subject'),
        ], "Email"),
    ]

    # Register Tabs
    edit_handler = TabbedInterface([
        ObjectList(basic_panels, heading="Basic"),
        ObjectList(form_panels, heading="Form"),
        ObjectList(advanced_panels, heading="Plus+"),
    ])

    # Page settings
    template = 'sections/form_section_preview.html'
    parent_page_types = ['home.HomePage']
    subpage_types = []

    # Overriding Methods
    # def get_form(self, *args, **kwargs):
    #     form = super().get_form(*args, **kwargs)
    #     # Get form and update attributes
    #     # https://stackoverflow.com/questions/48321770/how-to-modify-attributes-of-the-wagtail-form-input-fields
    #     return form

    def serve(self, request):
        if request.is_ajax():
            print('IS AXJAX')
        return super(FormSection, self).serve(request)

    def __str__(self):
        if self.title:
            return self.title + " (Form Section)"
        else:
            return super(AbstractEmailForm, self).__str__()

    class Meta:
        verbose_name = 'Form Section'
        verbose_name_plural = 'Form Sections'
Exemplo n.º 22
0
class ContentPage(BasePage):
    parent_page_types = [
        "home.HomePage", "content.ContentPage", "topics.Topic"
    ]
    subpage_types = ["people.People", "content.ContentPage"]
    template = "content.html"

    # Content fields
    description = RichTextField(
        blank=True,
        default="",
        features=RICH_TEXT_FEATURES_SIMPLE,
        help_text="Optional short text description, max. 400 characters",
        max_length=400,
    )
    body = CustomStreamField(help_text=(
        "Main page body content. Supports rich text, images, embed via URL, "
        "embed via HTML, and inline code snippets"))

    sidebar = CustomStreamField(
        null=True,
        blank=True,
        help_text=
        ("Sidebar page body content (narrower than main body). Rendered to the "
         "right of the main body content in desktop and below it in mobile."
         "Supports rich text, images, embed via URL, "
         "embed via HTML, and inline code snippets"),
    )

    # Card fields
    card_title = CharField("Title", max_length=140, blank=True, default="")
    card_description = TextField("Description",
                                 max_length=140,
                                 blank=True,
                                 default="")
    card_image = ForeignKey(
        "mozimages.MozImage",
        null=True,
        blank=True,
        on_delete=SET_NULL,
        related_name="+",
        verbose_name="Image",
        help_text="An image in 16:9 aspect ratio",
    )
    card_image = ForeignKey(
        "mozimages.MozImage",
        null=True,
        blank=True,
        on_delete=SET_NULL,
        related_name="+",
        verbose_name="Image",
        help_text="An image in 16:9 aspect ratio",
    )
    card_image_3_2 = ForeignKey(
        "mozimages.MozImage",
        null=True,
        blank=True,
        on_delete=SET_NULL,
        related_name="+",
        verbose_name="Image",
        help_text="An image in 3:2 aspect ratio",
    )

    # Meta fields
    nav_description = TextField("Navigation description",
                                max_length=400,
                                blank=True,
                                default="")
    icon = FileField(
        upload_to="contentpage/icons",
        blank=True,
        default="",
        help_text=("MUST be a black-on-transparent SVG icon ONLY, "
                   "with no bitmap embedded in it."),
        validators=[check_for_svg_file],
    )
    keywords = ClusterTaggableManager(through=ContentPageTag, blank=True)

    # Editor panel configuration
    content_panels = BasePage.content_panels + [
        FieldPanel("description"),
        StreamFieldPanel("body"),
        StreamFieldPanel("sidebar"),
    ]

    # Card panels
    card_panels = [
        FieldPanel(
            "card_title",
            help_text=("Title displayed when this page is "
                       "represented by a card in a list of items. "
                       "If blank, the page's title is used."),
        ),
        FieldPanel(
            "card_description",
            help_text=("Summary text displayed when this page is "
                       "represented by a card in a list of items. "
                       "If blank, the page's description is used."),
        ),
        MultiFieldPanel(
            [ImageChooserPanel("card_image")],
            heading="16:9 Image",
            help_text=(
                "Image used for representing this page as a Card. "
                "Should be 16:9 aspect ratio. "
                "If not specified a fallback will be used. "
                "This image is also shown when sharing this page via social "
                "media unless a social image is specified."),
        ),
        MultiFieldPanel(
            [ImageChooserPanel("card_image_3_2")],
            heading="3:2 Image",
            help_text=("Image used for representing this page as a Card. "
                       "Should be 3:2 aspect ratio. "
                       "If not specified a fallback will be used. "),
        ),
    ]

    # Meta panels
    meta_panels = [
        FieldPanel(
            "nav_description",
            help_text=
            "Text to display in the navigation with the title for this page.",
        ),
        MultiFieldPanel(
            [FieldPanel("icon")],
            heading="Theme",
            help_text=
            ("This icon will be used if, for example, this page is shown in a Menu"
             ),
        ),
        MultiFieldPanel(
            [
                FieldPanel("seo_title"),
                FieldPanel("search_description"),
                ImageChooserPanel("social_image"),
                FieldPanel("keywords"),
            ],
            heading="SEO",
            help_text=("Optional fields to override the default title and "
                       "description for SEO purposes"),
        ),
    ]

    # Settings panels
    settings_panels = BasePage.settings_panels + [
        FieldPanel("slug"),
        FieldPanel("show_in_menus"),
    ]

    # Tabs
    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading="Content"),
        ObjectList(card_panels, heading="Card"),
        ObjectList(meta_panels, heading="Meta"),
        ObjectList(settings_panels, heading="Settings", classname="settings"),
    ])
Exemplo n.º 23
0
class Homepage(FoundationMetadataPageMixin, Page):
    hero_headline = models.CharField(
        max_length=140,
        help_text='Hero story headline',
        blank=True,
    )

    hero_story_description = RichTextField(
        features=[
            'bold', 'italic', 'link',
        ]
    )

    hero_image = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='hero_image'
    )

    hero_button_text = models.CharField(
        max_length=50,
        blank=True
    )

    hero_button_url = models.URLField(
        blank=True
    )

    content_panels = Page.content_panels + [
        MultiFieldPanel([
            FieldPanel('hero_headline'),
            FieldPanel('hero_story_description'),
            FieldRowPanel([
                FieldPanel('hero_button_text'),
                FieldPanel('hero_button_url'),
            ]),
            ImageChooserPanel('hero_image'),
        ],
            heading='hero',
            classname='collapsible'
        ),
        InlinePanel('featured_highlights', label='Highlights', max_num=5),
        InlinePanel('featured_news', label='News', max_num=4),
    ]

    subpage_types = [
        'PrimaryPage',
        'PeoplePage',
        'InitiativesPage',
        'Styleguide',
        'NewsPage',
        'ParticipatePage',
        'ParticipatePage2',
        'MiniSiteNameSpace',
        'RedirectingPage',
        'OpportunityPage',
        'BanneredCampaignPage',
    ]

    def get_context(self, request):
        # We need to expose MEDIA_URL so that the s3 images will show up properly
        # due to our custom image upload approach pre-wagtail
        context = super(Homepage, self).get_context(request)
        context['MEDIA_URL'] = settings.MEDIA_URL
        return context
Exemplo n.º 24
0
class JanisBasePage(Page):
    """
    This is base page class made for our pages to inherit from.
    It is abstract, which for Django means that it isn't stored as it's own table
    in the DB.
    We use it to add functionality that we know will be desired by all other pages,
    such as setting the preview fields and urls for janis stuff to make our headless
    setup work smoothly
    """

    parent_page_types = ['base.HomePage']
    subpage_types = []
    search_fields = Page.search_fields + [
        index.RelatedFields('owner', [
            index.SearchField('last_name', partial_match=True),
            index.FilterField('last_name'),
        ])
    ]

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    author_notes = RichTextField(
        # max_length=DEFAULT_MAX_LENGTH,
        features=['ul', 'ol', 'link'],
        blank=True,
        verbose_name=
        'Notes for authors (Not visible on the resident facing site)')

    notes_content_panel = [FieldPanel('author_notes')]

    coa_global = models.BooleanField(default=False,
                                     verbose_name='Make this a top level page')

    def janis_url(self):
        """
        This function parses various attributes of related content types to construct the
        expected url structure for janis.

        For attributes with multiple relations, it ONLY takes the FIRST one.
        """
        try:
            """
             These use ternary operators with some appropriate conditionals
             the idea is: return this value in these cases or tell use you got
             nothing (see the privacy policy info page for example).

             'None' responses get filtered out and removed from the URL path.

             TODO:
             make this more abstract(potentially not by content type)
             further check if the order of conditionals affects performance
             Better utilization of querysets may be possible for better performance
            """
            page_slug = self.slug or None
            has_no_theme = [
                'service page', 'topic page', 'information page',
                'department page', 'guide page', 'official document page',
                'form container', 'location page'
            ]
            has_no_topic_collection = has_no_theme

            has_no_topic = [
                'topic page', 'topic collection page', 'department page',
                'location page'
            ]

            theme_slug = (self.theme.slug if self.content_type.name
                          not in has_no_theme else None)
            # https://docs.djangoproject.com/en/2.2/ref/models/querysets/#first
            topic_collection_slug = (
                self.topiccollections.first().topiccollection.slug if
                (self.content_type.name not in has_no_topic_collection and
                 # https://docs.djangoproject.com/en/2.2/ref/models/querysets/#exists
                 self.topiccollections.exists()) else None)
            topic_slug = (self.topics.first().topic.slug if
                          (self.content_type.name not in has_no_topic
                           and self.topics.exists()) else None)

            # add hardcoded language path to base url
            base_url = f"{self.janis_url_base('publish_janis_branch')}/en"

            # Quick location page exception
            if self.content_type.name == 'location page':
                location_url = base_url + '/location/' + page_slug
                return location_url

            # attributes for the url are needed by not discovered yet lets fetch them
            # looking for missing elements, deducing content type from what works and what dosen't
            # this is pretty ugly and ought to be cleaned up
            if theme_slug is None and self.content_type.name != 'department page':
                try:
                    theme_slug = self.topics.first(
                    ).topic.topiccollections.first(
                    ).topiccollection.theme.slug or None
                    topic_collection_slug = self.topics.first(
                    ).topic.topiccollections.first(
                    ).topiccollection.slug or None
                except AttributeError as e:
                    try:
                        theme_slug = self.topiccollections.first(
                        ).topiccollection.theme.slug or None
                        topic_collection_slug = self.topiccollections.first(
                        ).topiccollection.slug or None
                    except AttributeError as e:
                        # this is for pages just under departments
                        theme_slug = self.related_departments.all(
                        )[0].related_department.slug or None
                    finally:
                        paths_list = [
                            base_url, theme_slug, topic_collection_slug,
                            topic_slug, page_slug
                        ]
                        janis_url = '/'.join(filter(None, (paths_list)))
                        return janis_url
            # collect all our path elements
            paths_list = [
                base_url, theme_slug, topic_collection_slug, topic_slug,
                page_slug
            ]
            # join them together, filtering out empty ones

            janis_url = '/'.join(filter(None, (paths_list)))
            return janis_url
        except Exception as e:
            # right now this is a catch-all,
            print("!janis url error!:", self.title, e)
            print(traceback.format_exc())
            return "#"
            pass

    def janis_preview_url_end(self, revision=None):
        """
            Optional "revision" parameter to get the janis_preview_url for a specific revision
            Otherwise, it will return the janis_preview_url for the latest revision
        """
        if revision:
            url_page_type = revision.page.janis_url_page_type
        else:
            revision = self.get_latest_revision()
            url_page_type = self.janis_url_page_type
        try:
            global_id = graphene.Node.to_global_id('PageRevisionNode',
                                                   revision.id)
            url_end = f"preview/{url_page_type}/{global_id}"
        except AttributeError:
            # TODO: make previews work for test fixture pages that may not have revisions/global_ids
            url_end = f"preview/{url_page_type}/"

        if settings.ISSTAGING or settings.ISPRODUCTION:
            return url_end
        else:
            # Pass address of CMS_API if we are not running on Staging or Production
            # Janis will query from its default CMS_API if a param is not provided
            return url_end + f"?CMS_API={settings.CMS_API}"

    def janis_url_base(self, janis_branch):
        """
        returns a valid url of the base URL in janis:
            Use hardcoded JANIS_URL for staging and prod
            Otherwise, use configurable branch setting

        TODO: this and url_base in site settings could probably
        be revisited for semantics to be less confusing
        """
        if settings.ISSTAGING or settings.ISPRODUCTION:
            return os.getenv("JANIS_URL")
        else:
            branch_settings = JanisBranchSettings.objects.first()
            return branch_settings.url_base(janis_branch)

    # alias for url base function
    janis_preview_url_start = janis_url_base

    # TODO this function and preview_url_data are pretty similar, we can probably consolidate them
    def janis_preview_url(self, revision=None, lang="en"):
        return f"{self.janis_preview_url_start('preview_janis_branch')}/{lang}/{self.janis_preview_url_end(revision=revision)}"

    # data needed to construct preview URLs for any language
    # [janis_preview_url_start]/[lang]/[janis_preview_url_end]
    # ex: http://localhost:3000/es/preview/information/UGFnZVJldmlzaW9uTm9kZToyMjg=
    def preview_url_data(self, revision=None):
        return {
            "janis_preview_url_start":
            self.janis_preview_url_start('preview_janis_branch'),
            "janis_preview_url_end":
            self.janis_preview_url_end(revision=revision),
        }

    @property
    def status_string(self):
        """
        override wagtail default
        see https://github.com/wagtail/wagtail/blob/f44d27642b4a6932de73273d8320bbcb76330c21/wagtail/core/models.py#L1010
        """
        if not self.live:
            if self.expired:
                return ("Expired")
            elif self.approved_schedule:
                return ("Scheduled")
            else:
                return ("Draft")
        else:
            if self.approved_schedule:
                return ("Live + Scheduled")
            elif self.has_unpublished_changes:
                return ("Live + Draft")
            else:
                return ("Live")

    @cached_classmethod
    def get_edit_handler(cls):
        if hasattr(cls, 'edit_handler'):
            return cls.edit_handler.bind_to(model=cls)

        editor_panels = [
            ObjectList(cls.content_panels + [
                AdminOnlyFieldPanel('coa_global', classname="admin-only-field")
            ],
                       heading='Content'),
            ObjectList(cls.notes_content_panel, heading='Notes')
        ]

        try:
            if flag_enabled('SHOW_EXTRA_PANELS'):
                editor_panels += (PermissionObjectList(cls.promote_panels,
                                                       heading='SEO'),
                                  PermissionObjectList(cls.settings_panels,
                                                       heading='Settings'))
        except ProgrammingError as e:
            print("some problem, maybe with flags")
            pass

        edit_handler = TabbedInterface(editor_panels)

        return edit_handler.bind_to(model=cls)

    class Meta:
        abstract = True
        # https://docs.djangoproject.com/en/2.2/topics/auth/customizing/#custom-permissions
        permissions = [
            ("view_extra_panels", "Can view extra panels"),
            ("view_snippets", "Can view snippets"),
            ("add_snippets", "Can add snippet"),
            ("delete_snippets", "Can delete snippet"),
        ]
Exemplo n.º 25
0
class HomePage(Page):
    body = RichTextField(blank=True)

    content_panels = Page.content_panels + [
        FieldPanel('body', classname="full"),
    ]
Exemplo n.º 26
0
class HomePage(RoutablePageMixin, Page):
    banner_title = models.CharField(max_length=100, blank=False, null=True)
    banner_subtitle = RichTextField(features=['bold', 'italic'])
    banner_image = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=False,
        on_delete=models.SET_NULL,
        related_name='+',
    )
    banner_cta = models.ForeignKey(
        'wagtailcore.Page',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
    )

    content = StreamField([
        ('cta', blocks.CTABlock()),
    ],
                          null=True,
                          blank=True)

    # only one on site
    max_count = 1
    # parent_page_type = ['wagtailcore.Page']
    subpage_types = [
        'blog.BlogListingPage', 'contact.ContactPage', 'flex.FlexPage',
        'behance.BehanceProjectListingPage'
    ]

    # search_fields = Page.search_fields + [

    # ]

    api_fields = [
        APIField('banner_title'),
        APIField('banner_subtitle'),
        APIField('banner_image'),
        APIField('banner_cta'),
        APIField('carousel_images'),
        APIField('content'),
    ]

    content_panels = Page.content_panels + [
        MultiFieldPanel([
            InlinePanel('carousel_images', max_num=5, min_num=1,
                        label='Image'),
        ],
                        heading='Carousel Images'),
        StreamFieldPanel('content'),
    ]

    # promote_panels = []
    # settings_panels = []

    banner_panels = [
        MultiFieldPanel([
            FieldPanel('banner_title'),
            FieldPanel('banner_subtitle'),
            ImageChooserPanel('banner_image'),
            PageChooserPanel('banner_cta'),
        ],
                        heading='Banners Options'),
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
        ObjectList(banner_panels, heading='Banner'),
        ObjectList(Page.promote_panels, heading='Promote'),
        ObjectList(Page.settings_panels, heading='Settings'),
    ])

    class Meta:
        verbose_name = 'Home Page'
        verbose_name_plural = 'Home Pages'

    @route(r'^subscribe/$')
    def the_subscribe_page(self, request, *args, **kwargs):
        context = self.get_context(request, *args, **kwargs)
        context['a_special_text'] = 'Hello world 123123'
        return render(request, 'home/subscribe.html', context)
Exemplo n.º 27
0
class ChartPage(ChartOptionsMixin, SpecificInstructionsMixin,
                FallbackImageMixin, RoutablePageMixin, Page):
    """
    Individual chart page
    """
    parent_page_types = [VisualisationsPage]
    subpage_types = []

    class Meta:
        verbose_name = 'Plotly Studio Chart Page'

    subtitle = models.TextField(
        blank=True,
        null=True,
        help_text="Optional: subtitle to appear underneath the chart title.")

    chart_json = JSONField(
        verbose_name="Chart JSON",
        help_text=
        'Paste exported Chart Studio JSON here. To preserve data integretity, the JSON data should not be edited in Wagtail'
    )
    caption = RichTextField(
        null=True,
        blank=True,
        help_text='Optional: caption text and link(s) for the chart',
        features=MINIMAL_RICHTEXT_FEATURES)

    content_panels = Page.content_panels + [
        FieldPanel('subtitle'),
        FieldPanel('chart_json'),
        FallbackImagePanel(),
        ChartOptionsPanel(),
        SpecificInstructionsPanel(),
        FieldPanel('caption')
    ]

    @cached_property
    def parent(self):
        return self.get_parent().specific

    @cached_property
    def instructions_text(self):
        if self.instructions:
            return self.instructions

        elif self.display_general_instructions and self.parent.instructions:
            return self.parent.instructions

        return ''

    def get_sitemap_urls(self, request):
        return []

    @route(r'^$')
    def chart(self, request):
        if request.user.is_authenticated:
            return super().serve(request)
        raise Http404()

    @route(r'^data/$')
    def data(self, request):
        return JsonResponse(self.chart_json)
Exemplo n.º 28
0
class SingleQuestion(models.Model):
    question = RichTextField()
    image = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+'
    )

    ans1 = models.CharField(
        max_length=255,
        null=True,
        blank=True,
    )

    ans2 = models.CharField(
        max_length=255,
        null=True,
        blank=True,
    )

    ans3 = models.CharField(
        max_length=255,
        null=True,
        blank=True,
    )

    ans4 = models.CharField(
        max_length=255,
        null=True,
        blank=True,
    )

    ans5 = models.CharField(
        max_length=255,
        null=True,
        blank=True,
    )

    POSSIBLE_CHOICES = (
        (1, 'A'),
        (2, 'B'),
        (3, 'C'),
        (4, 'D'),
        (5, 'E')
    )

    correct_ans = models.IntegerField(
        max_length=2,
        choices=POSSIBLE_CHOICES,
    )

    QUESTION_CATEGORIES = (
        ('logic', 'Logic'),
        ('math', 'Math'),
        ('pattern', 'Pattern'),
        ('spacial', 'Spacial'),
        ('language', 'Language'),
        ('reasoning', 'Reasoning'),

    )

    category = models.CharField(
        max_length=25,
        choices=QUESTION_CATEGORIES,
    )

    panels = [
        FieldPanel('question'),
        ImageChooserPanel('image'),
        FieldPanel('ans1'),
        FieldPanel('ans2'),
        FieldPanel('ans3'),
        FieldPanel('ans4'),
        FieldPanel('ans5'),
        FieldPanel('correct_ans'),
        FieldPanel('category'),
    ]

    @property
    def question_text(self):
        return strip_tags(self.question)
Exemplo n.º 29
0
class EventPage(Page):
    date_from = models.DateField(
        "Start date",
        help_text=
        "Required for us to know if the event is in the future or past")
    date_to = models.DateField(
        "End date",
        null=True,
        blank=True,
        help_text="Not required if event is on a single day")
    time_from = models.TimeField("Start time", null=True, blank=True)
    time_to = models.TimeField("End time", null=True, blank=True)

    meta_information = RichTextField(
        blank=True,
        help_text=
        "Meta information about the event e.g. Haverford College, Department of economics"
    )
    description = RichTextField(
        blank=True,
        help_text="The description written as though in the future")
    description_past = RichTextField(
        blank=True, help_text="The description written as though in the past")
    parent_page_types = ['EventIndexPage']
    subpage_types = []

    search_fields = Page.search_fields + [index.SearchField('description')]

    content_panels = Page.content_panels + [
        FieldRowPanel([
            FieldPanel('date_from'),
            FieldPanel('date_to'),
        ], ),
        FieldPanel('meta_information'),
        MultiFieldPanel([
            FieldPanel('description'),
            FieldPanel('description_past'),
        ],
                        heading="Description of event"),
    ]

    @property
    def event_index(self):
        # Find closest ancestor which is an event index
        return self.get_ancestors().type(EventIndexPage).last()

    # This is a legacy from PromptWorks. It introduces an ical download on the event page
    def serve(self, request):
        if "format" in request.GET:
            if request.GET['format'] == 'ical':
                # Export to ical format
                response = HttpResponse(
                    export_event(self, 'ical'),
                    content_type='text/calendar',
                )
                response[
                    'Content-Disposition'] = 'attachment; filename=' + self.slug + '.ics'
                return response
            else:
                # Unrecognised format error
                message = 'Could not export event\n\nUnrecognised format: ' + request.GET[
                    'format']
                return HttpResponse(message, content_type='text/plain')
        else:
            # Display event page as usual
            return super(EventPage, self).serve(request)

    class Meta:
        verbose_name = "Event"
Exemplo n.º 30
0
class ContactPage(Page):
    # ---- General Page information ------
    title_sv = models.CharField(max_length=255)
    translated_title = TranslatedField('title', 'title_sv')

    contact_point = StreamField([('person', ContactCardBlock())], )

    other_contacts = StreamField(
        [('contact',
          blocks.StructBlock([
              ('person', ContactCardBlock()),
              ('english_groups',
               blocks.CharBlock(
                   help_text=_('Comma separated list of English group names.'),
                   required=False,
               )),
              ('swedish_groups',
               blocks.CharBlock(
                   help_text=_('Comma separated list of Swedish group names.'),
                   required=False,
               )),
          ],
                             icon='user'))], )

    map_location = models.CharField(
        max_length=255,
        verbose_name=_('Map Location'),
        help_text=_('Enter comma separated coordinates'),
        blank=True,
    )

    location_description = RichTextField(
        verbose_name=_('Location Description'),
        help_text=_('Enter the text to show on the map'),
        blank=True,
    )

    def get_context(self, request, *args, **kwargs):
        contacts = {}
        for contact in self.other_contacts:
            if request.LANGUAGE_CODE == 'sv':
                groups = contact.value.get('swedish_groups', '')
            else:
                groups = contact.value.get('english_groups', '')
            groups = groups.split(',')

            for group in groups:
                group = group.strip()
                list = contacts.get(group, [])
                list.append(contact.value['person'])
                contacts[group] = list

        context = super(ContactPage, self).get_context(request, *args,
                                                       **kwargs)
        context['contacts'] = contacts
        return context

    content_panels = Page.content_panels + [
        FieldPanel('title_sv', classname="full title"),
        FieldPanel('map_location'),
        FieldPanel('location_description'),
        StreamFieldPanel('contact_point'),
        StreamFieldPanel('other_contacts'),
    ]