Ejemplo n.º 1
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, "
            f"max. {DESCRIPTION_MAX_LENGTH} characters"
        ),
        max_length=DESCRIPTION_MAX_LENGTH,
    )
    body = CustomStreamField(
        help_text=(
            "Main page body content. Supports rich text, images, embed via URL, "
            "embed via HTML, and inline code snippets"
        )
    )

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

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

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

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

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

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

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

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

    # Search config
    search_fields = BasePage.search_fields + [  # Inherit search_fields from Page
        # "title" is already specced in BasePage
        index.SearchField("description"),
        index.SearchField("body"),
        index.SearchField("sidebar"),
        # Add FilterFields for things we may be filtering on (eg topics)
        index.FilterField("slug"),
    ]
Ejemplo n.º 2
0
class DefaultRichBlockFieldPage(Page):
    body = StreamField([
        ('rich_text', RichTextBlock()),
    ])

    content_panels = Page.content_panels + [StreamFieldPanel('body')]
Ejemplo n.º 3
0
class BlogPage(RoutablePageMixin, Page):
    description = models.CharField(
        max_length=255,
        blank=True,
    )

    herocarrousel = StreamField([
        ('academic', HeroAcademic()),
        ('mini_photo', HeroWithMiniFoto()),
        ('solo_text', HeroSoloText()),
        ('photo_or_letters', HeroWithFotoOrLetter()),
    ],
                                null=True,
                                blank=True)

    herobanner = StreamField([
        ('large_banner', Banner()),
        ('circle_banner',
         blocks.StaticBlock(label="Middle Circle Apply", icon="site")),
        ('circle_banner_half',
         blocks.StaticBlock(label="Middle Circle Apply Half", icon="site")),
        ('mini_banner',
         blocks.StaticBlock(label="Mini Circle Apply", icon="site")),
        ('only_text', HeroBannerCircText(label="Only Text Circle",
                                         icon="site")),
        ('circle_talk',
         blocks.StaticBlock(label="Middle Circle Talk", icon="site")),
        ('circle_talk_half',
         blocks.StaticBlock(label="Middle Circle Talk Half", icon="site")),
    ],
                             null=True,
                             blank=True)

    content_panels = Page.content_panels + [
        FieldPanel('description', classname="full"),
        MultiFieldPanel([
            StreamFieldPanel('herocarrousel'),
            StreamFieldPanel('herobanner'),
        ],
                        heading="Hero",
                        classname="collapsible collapsed"),
    ]

    def get_posts(self):
        return PostPage.objects.descendant_of(self).live().order_by('-date')

    def use_paginator(self, request, post_t):
        page_n = request.GET.get('page')
        if not page_n:
            page_n = "1"
        #post_t = PostPage.objects.descendant_of(self).live().order_by('-date')
        paginator = Paginator(post_t, 6)
        posts_page = paginator.get_page(page_n)
        return posts_page

    def get_context(self, request, *args, **kwargs):
        context = super(BlogPage, self).get_context(request, *args, **kwargs)
        context['posts'] = self.posts
        #context['posts'] = self.get_posts
        #context['posts'] = PostPage.objects.descendant_of(self).live()
        context['blog_page'] = self
        context['showing_all'] = self.showing_all
        return context

    @route(r'^view_all/')
    def view_all(self, request, *args, **kwargs):
        self.posts = self.get_posts()
        self.showing_all = True
        return Page.serve(self, request, *args, **kwargs)

    @route(r'^tag/(?P<tag>[-\w]+)/$')
    def post_by_tag(self, request, tag, *args, **kwargs):
        self.showing_all = False
        self.search_type = 'tag'
        self.search_term = tag
        posts_p = self.get_posts().filter(tags__slug=tag)
        self.posts = self.use_paginator(request, posts_p)
        return Page.serve(self, request, *args, **kwargs)

    @route(r'^category/(?P<category>[-\w]+)/$')
    def post_by_category(self, request, category, *args, **kwargs):
        self.showing_all = False
        self.search_type = 'category'
        self.search_term = category
        posts_p = self.get_posts().filter(categories__slug=category)
        self.posts = self.use_paginator(request, posts_p)
        return Page.serve(self, request, *args, **kwargs)

    @route(r'^author/(?P<author>[-\w]+)/$')
    def post_by_author(self, request, author, *args, **kwargs):
        self.showing_all = False
        self.search_type = 'author'
        self.search_term = author
        posts_p = self.get_posts().filter(author__slug=author)
        self.posts = self.use_paginator(request, posts_p)
        #self.posts = self.get_posts()
        return Page.serve(self, request, *args, **kwargs)

    @route(r'^(\d{4})/$')
    @route(r'^(\d{4})/(\d{2})/$')
    @route(r'^(\d{4})/(\d{2})/(\d{2})/$')
    def post_by_date(self,
                     request,
                     year,
                     month=None,
                     day=None,
                     *args,
                     **kwargs):
        self.showing_all = False
        self.posts = self.get_posts().filter(date__year=year)
        if month:
            self.posts = self.posts.filter(date__month=month)
            df = DateFormat(date(int(year), int(month), 1))
            self.search_term = df.format('F Y')
        if day:
            self.posts = self.posts.filter(date__day=day)
            self.search_term = date_format(
                date(int(year), int(month), int(day)))
        return Page.serve(self, request, *args, **kwargs)

    @route(r'^(\d{4})/(\d{2})/(\d{2})/(.+)/$')
    def post_by_date_slug(self, request, year, month, day, slug, *args,
                          **kwargs):
        post_page = self.get_posts().filter(slug=slug).first()
        self.showing_all = False
        if not post_page:
            raise Http404
        return Page.serve(post_page, request, *args, **kwargs)

    @route(r'^$')
    def post_list(self, request, *args, **kwargs):
        #page_n = request.GET.get('page')
        #if not page_n:
        #    page_n = "1"
        #post_t = self.get_posts()
        #paginator = Paginator(post_t, 3)
        #posts_page = paginator.get_page(page_n)
        #self.posts = posts_page
        #self.posts = self.get_posts()
        self.showing_all = False
        posts_p = self.get_posts()
        self.posts = self.use_paginator(request, posts_p)

        return Page.serve(self, request, *args, **kwargs)
Ejemplo n.º 4
0
class CallToActionSnippet(models.Model):
    title = models.CharField(max_length=255)
    summary = RichTextField(blank=True, max_length=255)
    image = models.ForeignKey('images.CustomImage',
                              null=True,
                              blank=True,
                              on_delete=models.SET_NULL,
                              related_name='+')

    link = StreamField(blocks.StreamBlock([
        ('external_link',
         blocks.StructBlock([
             ('url', blocks.URLBlock()),
             ('title', blocks.CharBlock()),
         ],
                            icon='link')),
        (
            'internal_link',
            blocks.StructBlock([
                ('page', blocks.PageChooserBlock()),
                ('title', blocks.CharBlock(required=False)),
            ],
                               icon='link'),
        ),
    ],
                                          max_num=1,
                                          required=True),
                       blank=True)

    panels = [
        FieldPanel('title'),
        FieldPanel('summary'),
        ImageChooserPanel('image'),
        StreamFieldPanel('link'),
    ]

    def get_link_text(self):
        # Link is required, so we should always have
        # an element with index 0
        block = self.link[0]

        title = block.value['title']
        if block.block_type == 'external_link':
            return title

        # Title is optional for internal_link
        # so fallback to page's title, if it's empty
        return title or block.value['page'].title

    def get_link_url(self):
        # Link is required, so we should always have
        # an element with index 0
        block = self.link[0]

        if block.block_type == 'external_link':
            return block.value['url']

        return block.value['page'].get_url()

    def __str__(self):
        return self.title
Ejemplo n.º 5
0
class Video(BasePage):
    resource_type = "video"
    parent_page_types = ["Videos"]
    subpage_types = []
    template = "video.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 = RichTextField(
        blank=True,
        default="",
        features=RICH_TEXT_FEATURES,
        help_text=(
            "Optional body content. Supports rich text, images, embed via URL, "
            "embed via HTML, and inline code snippets"),
    )
    related_links_mdn = StreamField(
        StreamBlock([("link", ExternalLinkBlock())], required=False),
        null=True,
        blank=True,
        help_text="Optional links to MDN Web Docs for further reading",
        verbose_name="Related MDN links",
    )
    image = ForeignKey(
        "mozimages.MozImage",
        null=True,
        blank=True,
        on_delete=SET_NULL,
        related_name="+",
    )
    types = CharField(max_length=14, choices=VIDEO_TYPE, default="conference")
    duration = CharField(
        max_length=30,
        blank=True,
        null=True,
        help_text=
        ("Optional video duration in MM:SS format e.g. “12:34”. Shown when the "
         "video is displayed as a card"),
    )
    transcript = RichTextField(
        blank=True,
        default="",
        features=RICH_TEXT_FEATURES,
        help_text="Optional text transcript of the video, supports rich text",
    )
    video_url = StreamField(
        StreamBlock([("embed", EmbedBlock())],
                    min_num=1,
                    max_num=1,
                    required=True),
        help_text=
        ("Embed URL for the video e.g. https://www.youtube.com/watch?v=kmk43_2dtn0"
         ),
    )
    speakers = StreamField(
        StreamBlock(
            [("speaker", PageChooserBlock(target_model="people.Person"))],
            required=False,
        ),
        blank=True,
        null=True,
        help_text=
        "Optional list of people associated with or starring in the video",
    )

    # Card fields
    card_title = CharField("Title", max_length=140, blank=True, default="")
    card_description = TextField("Description",
                                 max_length=400,
                                 blank=True,
                                 default="")
    card_image = ForeignKey(
        "mozimages.MozImage",
        null=True,
        blank=True,
        on_delete=SET_NULL,
        related_name="+",
        verbose_name="Image",
    )

    # Meta fields
    date = DateField("Upload date", default=datetime.date.today)
    keywords = ClusterTaggableManager(through=VideoTag, blank=True)

    # Content panels
    content_panels = BasePage.content_panels + [
        FieldPanel("description"),
        ImageChooserPanel("image"),
        StreamFieldPanel("video_url"),
        FieldPanel("body"),
        StreamFieldPanel("related_links_mdn"),
        FieldPanel("transcript"),
    ]

    # Card panels
    card_panels = [
        FieldPanel("card_title"),
        FieldPanel("card_description"),
        ImageChooserPanel("card_image"),
    ]

    # Meta panels
    meta_panels = [
        FieldPanel("date"),
        StreamFieldPanel("speakers"),
        MultiFieldPanel(
            [InlinePanel("topics")],
            heading="Topics",
            help_text=
            ("These are the topic pages the video will appear on. The first topic "
             "in the list will be treated as the primary topic and will be shown "
             "in the page’s related content."),
        ),
        FieldPanel("duration"),
        MultiFieldPanel(
            [FieldPanel("types")],
            heading="Type",
            help_text="Choose a video type to help people search for the video",
        ),
        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 = [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 primary_topic(self):
        """Return the first (primary) topic specified for the video."""
        video_topic = self.topics.first()
        return video_topic.topic if video_topic else None

    @property
    def read_time(self):
        return str(readtime.of_html(str(self.body)))

    @property
    def related_resources(self):
        """Returns resources that are related to the current resource, i.e. live,
        public articles and videos which have the same topics."""
        topic_pks = [topic.topic.pk for topic in self.topics.all()]
        return get_combined_articles_and_videos(
            self, topics__topic__pk__in=topic_pks)

    def has_speaker(self, person):
        for speaker in self.speakers:  # pylint: disable=not-an-iterable
            if str(speaker.value) == str(person.title):
                return True
        return False
Ejemplo n.º 6
0
class ProjectsPage(Page):
    headers = StreamField([
        ('h_hero', _H_HeroBlock(icon='image')),
        # ('code', blocks.RawHTMLBlock(null=True, blank=True, classname="full", icon='code'))
    ])

    price_min = models.IntegerField(
        verbose_name="Preis der billigsten Einheit", null=True, blank=False)
    price_max = models.IntegerField(
        verbose_name=
        "Preis der teuersten Einheit (leer lassen wenn nur eine Einheit vorhanden ist)",
        null=True,
        blank=True)
    buy_available = models.BooleanField(verbose_name="Kaufmöglichkeit")
    rent_available = models.BooleanField(verbose_name="Mietmöglichkeit")
    location_name = models.CharField(
        verbose_name="Ortsname (z.B. Villach-Landskron)",
        null=True,
        blank=True,
        max_length=255)
    coordinates = models.CharField(
        verbose_name=
        "Standpunkt (Koordinaten, z.B. Beispiel: 46.6120061,13.916085)",
        null=True,
        blank=True,
        max_length=255)

    sections = StreamField(
        [
            ('s_info', _S_InfoBlock(icon='fa-info')),
            ('s_contentcenter', _S_ContentCenter(icon='fa-info')),
            ('s_contentright', _S_ContentRight(icon='fa-info')),
            ('s_contentleft', _S_ContentLeft(icon='fa-info')),
            # ('s_imagegallery', _S_ImageGallery(icon='fa-info'))
        ],
        null=True,
        blank=True)

    gallery = StreamField([('g_gallery', _G_GalleryBlock(icon='fa-info'))],
                          null=True,
                          blank=True)

    flats = StreamField([('f_flats', _F_FlatsBlock(icon='fa-info'))],
                        verbose_name="Wohneinheiten-Pages",
                        null=True,
                        blank=True)

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

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

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

    main_content_panels = [
        StreamFieldPanel('headers'),
        FieldPanel('price_min'),
        FieldPanel('price_max'),
        FieldPanel('buy_available'),
        FieldPanel('rent_available'),
        FieldPanel('location_name'),
        FieldPanel('coordinates'),
        StreamFieldPanel('sections'),
        StreamFieldPanel('gallery'),
        StreamFieldPanel('flats')
        # StreamFieldPanel('footers')
    ]

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

    preview_modes = []
Ejemplo n.º 7
0
class CFGOVPage(Page):
    authors = ClusterTaggableManager(through=CFGOVAuthoredPages,
                                     blank=True,
                                     verbose_name='Authors',
                                     help_text='A comma separated list of ' +
                                     'authors.',
                                     related_name='authored_pages')
    tags = ClusterTaggableManager(through=CFGOVTaggedPages,
                                  blank=True,
                                  related_name='tagged_pages')
    language = models.CharField(choices=ref.supported_languagues,
                                default='en',
                                max_length=2)
    social_sharing_image = models.ForeignKey(
        'v1.CFGOVImage',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        help_text=(
            'Optionally select a custom image to appear when users share this '
            'page on social media websites. Recommended size: 1200w x 630h. '
            'Maximum size: 4096w x 4096h.'))
    schema_json = JSONField(
        null=True,
        blank=True,
        verbose_name='Schema JSON',
        help_text=mark_safe(
            'Enter structured data for this page in JSON-LD format, '
            'for use by search engines in providing rich search results. '
            '<a href="https://developers.google.com/search/docs/guides/'
            'intro-structured-data">Learn more.</a> '
            'JSON entered here will be output in the '
            '<code>&lt;head&gt;</code> of the page between '
            '<code>&lt;script type="application/ld+json"&gt;</code> and '
            '<code>&lt;/script&gt;</code> tags.'),
    )
    force_breadcrumbs = models.BooleanField(
        "Force breadcrumbs on child pages",
        default=False,
        blank=True,
        help_text=(
            "Normally breadcrumbs don't appear on pages one or two levels "
            "below the homepage. Check this option to force breadcrumbs to "
            "appear on all children of this page no matter how many levels "
            "below the homepage they are (for example, if you want "
            "breadcrumbs to appear on all children of a top-level campaign "
            "page)."),
    )

    # This is used solely for subclassing pages we want to make at the CFPB.
    is_creatable = False

    objects = CFGOVPageManager()

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

    # These fields show up in either the sidebar or the footer of the page
    # depending on the page type.
    sidefoot = StreamField([
        ('call_to_action', molecules.CallToAction()),
        ('related_links', molecules.RelatedLinks()),
        ('related_posts', organisms.RelatedPosts()),
        ('related_metadata', molecules.RelatedMetadata()),
        ('email_signup', organisms.EmailSignUp()),
        ('sidebar_contact', organisms.SidebarContactInfo()),
        ('rss_feed', molecules.RSSFeed()),
        ('social_media', molecules.SocialMedia()),
        ('reusable_text', v1_blocks.ReusableTextChooserBlock(ReusableText)),
    ],
                           blank=True)

    # Panels
    promote_panels = Page.promote_panels + [
        ImageChooserPanel('social_sharing_image'),
        FieldPanel('force_breadcrumbs', 'Breadcrumbs'),
    ]

    sidefoot_panels = [
        StreamFieldPanel('sidefoot'),
    ]

    settings_panels = [
        MultiFieldPanel(promote_panels, 'Settings'),
        InlinePanel('categories', label="Categories", max_num=2),
        FieldPanel('tags', 'Tags'),
        FieldPanel('authors', 'Authors'),
        FieldPanel('schema_json', 'Structured Data'),
        MultiFieldPanel(Page.settings_panels, 'Scheduled Publishing'),
        FieldPanel('language', 'language'),
    ]

    # Tab handler interface guide because it must be repeated for each subclass
    edit_handler = TabbedInterface([
        ObjectList(Page.content_panels, heading='General Content'),
        ObjectList(sidefoot_panels, heading='Sidebar/Footer'),
        ObjectList(settings_panels, heading='Configuration'),
    ])

    def clean(self):
        super(CFGOVPage, self).clean()
        validate_social_sharing_image(self.social_sharing_image)

    def get_authors(self):
        """ Returns a sorted list of authors. Default is alphabetical """
        return self.alphabetize_authors()

    def alphabetize_authors(self):
        """
        Alphabetize authors of this page by last name,
        then first name if needed
        """
        # First sort by first name
        author_names = self.authors.order_by('name')
        # Then sort by last name
        return sorted(author_names, key=lambda x: x.name.split()[-1])

    def related_metadata_tags(self):
        # Set the tags to correct data format
        tags = {'links': []}
        filter_page = self.get_filter_data()
        for tag in self.specific.tags.all():
            tag_link = {'text': tag.name, 'url': ''}
            if filter_page:
                relative_url = filter_page.relative_url(filter_page.get_site())
                param = '?topics=' + tag.slug
                tag_link['url'] = relative_url + param
            tags['links'].append(tag_link)
        return tags

    def get_filter_data(self):
        for ancestor in self.get_ancestors().reverse().specific():
            if ancestor.specific_class.__name__ in [
                    'BrowseFilterablePage', 'SublandingFilterablePage',
                    'EventArchivePage', 'NewsroomLandingPage'
            ]:
                return ancestor
        return None

    def get_breadcrumbs(self, request):
        ancestors = self.get_ancestors().specific()
        for i, ancestor in enumerate(ancestors):
            if ancestor.is_child_of(request.site.root_page):
                if ancestor.specific.force_breadcrumbs:
                    return ancestors[i:]
                return ancestors[i + 1:]
        return []

    def get_appropriate_descendants(self, inclusive=True):
        return CFGOVPage.objects.live().descendant_of(self, inclusive)

    def get_appropriate_siblings(self, inclusive=True):
        return CFGOVPage.objects.live().sibling_of(self, inclusive)

    def get_context(self, request, *args, **kwargs):
        context = super(CFGOVPage, self).get_context(request, *args, **kwargs)

        for hook in hooks.get_hooks('cfgovpage_context_handlers'):
            hook(self, request, context, *args, **kwargs)

        # Add any banners that are enabled and match the current request path
        # to a context variable.
        context['banners'] = Banner.objects \
            .filter(enabled=True) \
            .annotate(
                # This annotation creates a path field in the QuerySet
                # that we can use in the filter below to compare with
                # the url_pattern defined on each enabled banner.
                path=Value(request.path, output_field=models.CharField())) \
            .filter(path__regex=F('url_pattern'))

        if self.schema_json:
            context['schema_json'] = self.schema_json

        return context

    def serve(self, request, *args, **kwargs):
        """
        If request is ajax, then return the ajax request handler response, else
        return the super.
        """
        if request.method == 'POST':
            return self.serve_post(request, *args, **kwargs)

        # Force the page's language on the request
        translation.activate(self.language)
        request.LANGUAGE_CODE = translation.get_language()
        return super(CFGOVPage, self).serve(request, *args, **kwargs)

    def _return_bad_post_response(self, request):
        if request.is_ajax():
            return JsonResponse({'result': 'error'}, status=400)

        return HttpResponseBadRequest(self.url)

    def serve_post(self, request, *args, **kwargs):
        """Handle a POST to a specific form on the page.

        Attempts to retrieve form_id from the POST request, which must be
        formatted like "form-name-index" where the "name" part is the name of a
        StreamField on the page and the "index" part refers to the index of the
        form element in the StreamField.

        If form_id is found, it returns the response from the block method
        retrieval.

        If form_id is not found, or if form_id is not a block that implements
        get_result() to process the POST, it returns an error response.
        """
        form_module = None
        form_id = request.POST.get('form_id', None)

        if form_id:
            form_id_parts = form_id.split('-')

            if len(form_id_parts) == 3:
                streamfield_name = form_id_parts[1]
                streamfield = getattr(self, streamfield_name, None)

                if streamfield is not None:
                    try:
                        streamfield_index = int(form_id_parts[2])
                    except ValueError:
                        streamfield_index = None

                    if streamfield_index is not None:
                        try:
                            form_module = streamfield[streamfield_index]
                        except IndexError:
                            form_module = None

        try:
            result = form_module.block.get_result(self, request,
                                                  form_module.value, True)
        except AttributeError:
            return self._return_bad_post_response(request)

        if isinstance(result, HttpResponse):
            return result

        context = self.get_context(request, *args, **kwargs)
        context['form_modules'][streamfield_name].update(
            {streamfield_index: result})

        return TemplateResponse(request,
                                self.get_template(request, *args, **kwargs),
                                context)

    class Meta:
        app_label = 'v1'

    def parent(self):
        parent = self.get_ancestors(inclusive=False).reverse()[0].specific
        return parent

    # To be overriden if page type requires JS files every time
    @property
    def page_js(self):
        return []

    @property
    def streamfield_js(self):
        js = []

        block_cls_names = get_page_blocks(self)
        for block_cls_name in block_cls_names:
            block_cls = import_string(block_cls_name)
            if hasattr(block_cls, 'Media') and hasattr(block_cls.Media, 'js'):
                js.extend(block_cls.Media.js)

        return js

    # Returns the JS files required by this page and its StreamField blocks.
    @property
    def media(self):
        return sorted(set(self.page_js + self.streamfield_js))

    # Returns an image for the page's meta Open Graph tag
    @property
    def meta_image(self):
        return self.social_sharing_image

    @property
    def post_preview_cache_key(self):
        return 'post_preview_{}'.format(self.id)
Ejemplo n.º 8
0
class LivePageMixin(models.Model):
    """
    Base class for pages using Wagtail Live.

    Attributes:
        channel_id (str):
            Id of the corresponding channel in a messaging app.
        last_updated_at (DateTime):
            Date and time of the last update of this page.
        live_posts (StreamField):
            StreamField containing all the posts/messages published
            respectively on this page/channel.
    """

    channel_id = models.CharField(
        help_text="Channel ID",
        max_length=255,
        blank=True,
        unique=True,
    )
    last_updated_at = models.DateTimeField(
        help_text="Last update of this page",
        blank=True,
        default=timezone.now,
    )

    live_posts = StreamField(
        [
            ("live_post", LivePostBlock()),
        ],
        blank=True,
    )

    panels = [
        FieldPanel("channel_id"),
        StreamFieldPanel("live_posts"),
    ]

    @property
    def last_update_timestamp(self):
        """Timestamp of the last update of this page."""

        # Live posts are saved using a json format.
        # We strip the microseconds here to follow that format.
        microsecond = (self.last_updated_at.microsecond // 1000) * 1000
        return self.last_updated_at.replace(
            microsecond=microsecond).timestamp()

    def save(self, sync=True, *args, **kwargs):
        """Update live page on save depending on the `WAGTAIL_LIVE_SYNC_WITH_ADMIN` setting."""

        sync_changes = sync and getattr(settings,
                                        "WAGTAIL_LIVE_SYNC_WITH_ADMIN", True)
        has_changed = False
        if sync_changes and self.id:
            renders, seen = [], set()
            previous_posts = {
                live_post.id: live_post
                for live_post in self.__class__.objects.get(
                    id=self.id).live_posts
            }
            now = timezone.now()

            for i, post in enumerate(self.live_posts):  # New posts
                post_id = post.id
                if post_id in previous_posts:
                    seen.add(post_id)

                    # Check if the post has been modified.
                    previous_post = previous_posts[post_id]
                    identic = compare_live_posts_values(
                        post.value, previous_post.value)
                    if not identic:
                        post.value["modified"] = now
                        renders.append(i)

                else:
                    # This is a new post.
                    # Force the value of `created` here to keep it synchronized with the
                    # `last_updated_at` property.
                    # This is mostly to avoid missing new updates with the polling publishers.
                    post.value["created"] = now
                    renders.append(i)

            removals = list(set(previous_posts.keys()).difference(seen))

            has_changed = bool(renders or removals)
            if has_changed:
                self.last_updated_at = now

        result = super().save(*args, **kwargs)

        if sync_changes and has_changed:
            # Reverse renders so the latest posts, which are in the start of the list,
            # are processed later in the front end.
            renders.reverse()
            renders = list(map(self.get_live_post_by_index, renders))

            # Send signal.
            live_page_update.send(
                sender=self.__class__,
                channel_id=self.channel_id,
                renders=renders,
                removals=removals,
            )

        return result

    def _get_live_post_index(self, message_id):
        for i, post in enumerate(self.live_posts):
            if post.value["message_id"] == message_id:
                return i

    def get_live_post_index(self, message_id):
        """
        Retrieves the index of a live post.

        Args:
            message_id (str): ID of the message corresponding to a live post.

        Returns:
            int: Index of the live post if found else -1
        """

        return self._get_live_post_index(message_id=message_id)

    def get_live_post_by_index(self, live_post_index):
        """
        Retrieves a live post by its index.

        Args:
            live_post_index (int): Index of the live post to look for.

        Returns:
            LivePostBlock: The live post instance

        Raises:
            IndexError: if a live post with the given index doesn't exist.
        """

        return self.live_posts[live_post_index]

    def get_live_post_by_message_id(self, message_id):
        """
        Retrieves a live post by its ID.

        Args:
            message_id (str):
                ID of the message corresponding to a live post.

        Returns:
            LivePostBlock: The live post instance

        Raises:
            KeyError: if a live post with the given ID doesn't exist.
        """

        live_post_index = self.get_live_post_index(message_id=message_id)
        if live_post_index is None:
            raise KeyError
        return self.get_live_post_by_index(live_post_index)

    def add_live_post(self, live_post):
        """
        Adds a new live post to live page.

        Args:
            live_post (LivePostBlock):
                live post to add
        """

        posts = self.live_posts
        lp_index = 0
        post_created_at = live_post["created"]
        while lp_index < len(posts):
            if posts[lp_index].value["created"] < post_created_at:
                break
            lp_index += 1

        # Insert to keep posts sorted by time
        self.live_posts.insert(lp_index, ("live_post", live_post))

        self.last_updated_at = post_created_at
        self.save(sync=False)

        live_post = self.get_live_post_by_index(lp_index)
        live_page_update.send(
            sender=self.__class__,
            channel_id=self.channel_id,
            renders=[live_post],
            removals=[],
        )

    def update_live_post(self, live_post):
        """
        Updates a live post when it has been edited.

        Args:
            live_post (livePostBlock):
                Live post to update.
        """

        live_post.value["modified"] = self.last_updated_at = timezone.now()
        self.save(sync=False)

        live_page_update.send(
            sender=self.__class__,
            channel_id=self.channel_id,
            renders=[live_post],
            removals=[],
        )

    def delete_live_post(self, message_id):
        """
        Deletes the live post corresponding to message_id.

        Args:
            message_id (str):
                ID of the message corresponding to a live post.
        Raises:
            KeyError: if live post containing message with message_id doesn't exist.
        """

        live_post_index = self.get_live_post_index(message_id=message_id)
        if live_post_index is None:
            raise KeyError

        live_post_id = self.live_posts[live_post_index].id
        del self.live_posts[live_post_index]

        self.last_updated_at = timezone.now()
        self.save(sync=False)

        live_page_update.send(
            sender=self.__class__,
            channel_id=self.channel_id,
            renders={},
            removals=[live_post_id],
        )

    def get_updates_since(self, last_update_ts):
        """
        Retrieves new updates since a given timestamp value.

        Args:
            last_update_ts (DateTime):
                Timestamp of the last update.

        Returns:
            (list, dict):
                a tuple containing the current live posts
                and the updated posts since `last_update_ts`.
        """

        # Reverse posts list so that latest updates are processed later by the client side.
        posts = reversed(self.live_posts)
        current_posts, updated_posts = [], {}
        for post in posts:
            post_id = post.id
            current_posts.append(post_id)

            created = post.value["created"]
            if created >= last_update_ts:  # This is a new post
                updated_posts[post_id] = {
                    "show": post.value["show"],
                    "content": post.render(context={"block_id": post.id}),
                }
                continue

            last_modified = post.value["modified"]
            if last_modified and last_modified >= last_update_ts:
                # This is an edited post
                updated_posts[post_id] = {
                    "show": post.value["show"],
                    "content": post.render(context={"block_id": post.id}),
                }

        return (updated_posts, current_posts)

    class Meta:
        abstract = True
Ejemplo n.º 9
0
class LabPage(BasePage):
    subpage_types = ['RFPPage']
    parent_page_types = ['LabIndex']

    introduction = models.TextField(blank=True)
    icon = models.ForeignKey(
        'images.CustomImage',
        null=True,
        blank=True,
        related_name='+',
        on_delete=models.SET_NULL
    )
    lab_type = models.ForeignKey(
        'wagtailcore.Page',
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        related_name='lab_public',
    )
    lab_link = models.CharField(blank=True, max_length=255, verbose_name=_('External link'), validators=[MailToAndURLValidator()])
    link_text = models.CharField(max_length=255, help_text=_('Text to display on the button for external links'), blank=True)
    body = StreamField(LabBlock())

    search_fields = BasePage.search_fields + [
        index.SearchField('introduction'),
        index.SearchField('body')
    ]

    content_panels = BasePage.content_panels + [
        ImageChooserPanel('icon'),
        FieldPanel('introduction'),
        MultiFieldPanel([
            PageChooserPanel('lab_type', 'funds.LabType'),
            FieldRowPanel([
                FieldPanel('lab_link'),
                FieldPanel('link_text'),
            ]),
        ], heading=_('Link for lab application')),
        StreamFieldPanel('body'),
        InlinePanel('related_pages', label=_('Related pages')),
    ]

    def get_context(self, request):
        context = super().get_context(request)
        context['rfps'] = self.get_children().live().public()
        return context

    can_open = True

    @property
    def is_open(self):
        try:
            return bool(self.lab_type.specific.open_round)
        except AttributeError:
            return bool(self.lab_link)

    def clean(self):
        if self.lab_type and self.lab_link:
            raise ValidationError({
                'lab_type': _('Cannot link to both a Lab page and external link'),
                'lab_link': _('Cannot link to both a Lab page and external link'),
            })

        if not self.lab_type and not self.lab_link:
            raise ValidationError({
                'lab_type': _('Please provide a way for applicants to apply'),
                'lab_link': _('Please provide a way for applicants to apply'),
            })

        if self.lab_type and self.link_text:
            raise ValidationError({
                'link_text': _('Cannot customise the text for internal lab pages, leave blank'),
            })

        if self.lab_link and not self.link_text:
            raise ValidationError({
                'link_text': _('Please provide some text for the link button'),
            })
Ejemplo n.º 10
0
class StreamFieldEntryPage(EntryPage):
    streamfield = StreamField(MyStreamBlock(), blank=True)
    content_panels = EntryPage.content_panels + [
        StreamFieldPanel('streamfield')
    ]
Ejemplo n.º 11
0
class AboutPage(AbstractEmailForm):
    max_count = 1

    page_heading = models.CharField(max_length=100, blank=False, null=True)
    header_image = models.ForeignKey("wagtailimages.Image",
                                     null=True,
                                     blank=True,
                                     on_delete=models.SET_NULL,
                                     related_name="+")
    brand_name = models.CharField(max_length=100, blank=False, null=True)
    parent_company = models.CharField(max_length=100, blank=False, null=True)
    company_details = RichTextField(blank=False, null=True)
    team_heading = models.CharField(max_length=40, blank=True, null=True)
    team_cards = StreamField(
        [("emp_cards", EmployeeCardBlock(label="Employee"))],
        null=True,
        blank=True)

    form_heading = models.CharField(max_length=40, blank=True, null=True)
    form_success_text = models.CharField(
        max_length=200,
        blank=False,
        default=
        'Thank you for getting in touch! We will reply to you as soon as possible.'
    )

    content_panels = AbstractEmailForm.content_panels + [
        MultiFieldPanel([
            FieldPanel("page_heading"),
            ImageChooserPanel("header_image"),
            FieldPanel("brand_name"),
            FieldPanel("parent_company"),
            FieldPanel("company_details"),
        ],
                        heading="Company Summary"),
        MultiFieldPanel(
            [FieldPanel("team_heading"),
             StreamFieldPanel("team_cards")],
            heading="Team Details"),
        MultiFieldPanel([
            FieldPanel('form_heading'),
            InlinePanel('form_fields', label='Form Fields'),
            FieldPanel('form_success_text'),
            MultiFieldPanel([
                FieldRowPanel([
                    FieldPanel('from_address', classname='col6'),
                    FieldPanel('to_address', classname='col6'),
                ]),
                FieldPanel("subject"),
            ],
                            heading="Email Settings")
        ],
                        heading="Contact Form")
    ]

    def get_context(self, request):
        context = super().get_context(request)
        context["addresses"] = BusinessAddress.objects.all()
        context["numbers"] = BusinessContact.objects.all()
        return context

    def save(self, *args, **kwargs):

        key = make_template_fragment_key("about")
        cache.delete(key)

        return super().save(*args, **kwargs)
Ejemplo n.º 12
0
class LeftSidebarPage(MenuPage):
    body = StreamField(MyStreamBlock(), blank=True)

    content_panels = Page.content_panels + [
        StreamFieldPanel('body'),
    ]
Ejemplo n.º 13
0
class FullWidthPage(MenuPage):
    body = StreamField(MyStreamBlock(), blank=True)

    content_panels = Page.content_panels + [
        StreamFieldPanel('body'),
    ]
Ejemplo n.º 14
0
class MoloSurveyPage(TranslatablePageMixinNotRoutable,
                     surveys_models.AbstractSurvey):
    parent_page_types = [
        'surveys.SurveysIndexPage', 'core.SectionPage', 'core.ArticlePage'
    ]
    subpage_types = []
    language = models.ForeignKey(
        'core.SiteLanguage',
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
    )
    translated_pages = models.ManyToManyField("self", blank=True)

    form_builder = SurveysFormBuilder

    base_form_class = MoloSurveyForm

    introduction = TextField(blank=True)
    homepage_introduction = TextField(blank=True)
    image = models.ForeignKey('wagtailimages.Image',
                              null=True,
                              blank=True,
                              on_delete=models.SET_NULL,
                              related_name='+')
    description = StreamField([
        ('heading', blocks.CharBlock(classname="full title")),
        ('paragraph', MarkDownBlock()),
        ('image', ImageChooserBlock()),
        ('list', blocks.ListBlock(blocks.CharBlock(label="Item"))),
        ('numbered_list', blocks.ListBlock(blocks.CharBlock(label="Item"))),
        ('page', blocks.PageChooserBlock()),
    ],
                              null=True,
                              blank=True)
    thank_you_text = TextField(blank=True)
    submit_text = TextField(blank=True)
    homepage_button_text = TextField(blank=True)
    allow_anonymous_submissions = BooleanField(
        default=False,
        help_text='Check this to allow users who are NOT logged in to complete'
        ' surveys.')
    allow_multiple_submissions_per_user = BooleanField(
        default=False,
        help_text='Check this to allow users to complete a survey more than'
        ' once.')

    show_results = BooleanField(
        default=False,
        help_text='Whether to show the survey results to the user after they'
        ' have submitted their answer(s).')
    show_results_as_percentage = BooleanField(
        default=False,
        help_text='Whether to show the survey results to the user after they'
        ' have submitted their answer(s) as a percentage or as'
        ' a number.')

    multi_step = BooleanField(
        default=False,
        verbose_name='Multi-step',
        help_text='Whether to display the survey questions to the user one at'
        ' a time, instead of all at once.')

    display_survey_directly = BooleanField(
        default=False,
        verbose_name='Display Question Directly',
        help_text='This is similar to polls, in which the questions are '
        'displayed directly on the page, instead of displaying '
        'a link to another page to complete the survey.')
    your_words_competition = BooleanField(
        default=False,
        verbose_name='Is YourWords Competition',
        help_text='This will display the correct template for yourwords')
    extra_style_hints = models.TextField(
        default='',
        null=True,
        blank=True,
        help_text=_("Styling options that can be applied to this page "
                    "and all its descendants"))
    content_panels = surveys_models.AbstractSurvey.content_panels + [
        ImageChooserPanel('image'),
        FieldPanel('introduction', classname='full'),
        FieldPanel('homepage_introduction', classname='full'),
        FieldPanel('homepage_button_text', classname='full'),
        StreamFieldPanel('description'),
        InlinePanel('survey_form_fields', label='Form fields'),
        FieldPanel('submit_text', classname='full'),
        FieldPanel('thank_you_text', classname='full'),
        InlinePanel('terms_and_conditions', label="Terms and Conditions"),
    ]

    settings_panels = surveys_models.AbstractSurvey.settings_panels + [
        MultiFieldPanel([
            FieldPanel('allow_anonymous_submissions'),
            FieldPanel('allow_multiple_submissions_per_user'),
            FieldPanel('show_results'),
            FieldPanel('show_results_as_percentage'),
            FieldPanel('multi_step'),
            FieldPanel('display_survey_directly'),
            FieldPanel('your_words_competition'),
        ],
                        heading='Survey Settings'),
        MultiFieldPanel([
            FieldRowPanel([FieldPanel('extra_style_hints')],
                          classname="label-above")
        ], "Meta")
    ]

    def get_effective_extra_style_hints(self):
        return self.extra_style_hints

    def get_effective_image(self):
        return self.image

    def get_data_fields(self):
        data_fields = [
            ('username', _('Username')),
            ('created_at', _('Submission Date')),
        ]
        data_fields += [(field.clean_name, field.admin_label)
                        for field in self.get_form_fields()]
        return data_fields

    def get_submission_class(self):
        return MoloSurveySubmission

    def get_parent_section(self):
        return SectionPage.objects.all().ancestor_of(self).last()

    def process_form_submission(self, form):
        user = form.user if not form.user.is_anonymous() else None

        self.get_submission_class().objects.create(form_data=json.dumps(
            form.cleaned_data, cls=DjangoJSONEncoder),
                                                   page=self,
                                                   user=user)

    def has_user_submitted_survey(self, request, survey_page_id):
        if 'completed_surveys' not in request.session:
            request.session['completed_surveys'] = []

        if request.user.pk is not None \
            and self.get_submission_class().objects.filter(
                page=self, user__pk=request.user.pk
            ).exists() \
                or survey_page_id in request.session['completed_surveys']:
            return True
        return False

    def set_survey_as_submitted_for_session(self, request):
        if 'completed_surveys' not in request.session:
            request.session['completed_surveys'] = []
        request.session['completed_surveys'].append(self.id)
        request.session.modified = True

    def get_form(self, *args, **kwargs):
        prevent_required = kwargs.pop('prevent_required', False)
        form = super(MoloSurveyPage, self).get_form(*args, **kwargs)
        if prevent_required:
            for field in form.fields.values():
                field.required = False
        return form

    def get_form_class_for_step(self, step):
        return self.form_builder(step.object_list).get_form_class()

    @property
    def session_key_data(self):
        return 'survey_data-{}'.format(self.pk)

    def load_data(self, request):
        return json.loads(request.session.get(self.session_key_data, '{}'))

    def save_data(self, request, data):
        request.session[self.session_key_data] = json.dumps(
            data, cls=DjangoJSONEncoder)

    def serve_questions(self, request):
        """
        Implements a simple multi-step form.

        Stores each step in the session.
        When the last step is submitted correctly, the whole form is saved in
        the DB.
        """
        context = self.get_context(request)
        # this will only return a page if there is a translation
        page = get_translation_for([context['page']],
                                   locale=request.LANGUAGE_CODE,
                                   site=request.site)
        if page:
            page = page[0]
            if not page.language.is_main_language:
                # if there is a translation, redirect to the translated page
                return redirect(page.url)
        survey_data = self.load_data(request)

        paginator = SkipLogicPaginator(
            self.get_form_fields(),
            request.POST,
            survey_data,
        )

        is_last_step = False
        step_number = request.GET.get('p', 1)

        try:
            step = paginator.page(step_number)
        except PageNotAnInteger:
            step = paginator.page(1)
        except EmptyPage:
            step = paginator.page(paginator.num_pages)
            is_last_step = True

        if request.method == 'POST':
            # The first step will be submitted with step_number == 2,
            # so we need to get a from from previous step
            # Edge case - submission of the last step
            prev_step = step if is_last_step else paginator.page(
                int(step_number) - 1)

            # Create a form only for submitted step
            prev_form_class = self.get_form_class_for_step(prev_step)
            prev_form = prev_form_class(
                paginator.new_answers,
                page=self,
                user=request.user,
            )
            if prev_form.is_valid():
                # If data for step is valid, update the session
                survey_data.update(prev_form.cleaned_data)
                self.save_data(request, survey_data)

                if prev_step.has_next():
                    # Create a new form for a following step, if the following
                    # step is present
                    form_class = self.get_form_class_for_step(step)
                    form = form_class(page=self, user=request.user)
                else:
                    # If there is no more steps, create form for all fields
                    data = self.load_data(request)
                    form = self.get_form(data,
                                         page=self,
                                         user=request.user,
                                         prevent_required=True)

                    if form.is_valid():
                        # Perform validation again for whole form.
                        # After successful validation, save data into DB,
                        # and remove from the session.
                        self.set_survey_as_submitted_for_session(request)

                        # We fill in the missing fields which were skipped with
                        # a default value
                        for question in self.get_form_fields():
                            if question.clean_name not in data:
                                form.cleaned_data[question.clean_name] = SKIP

                        self.process_form_submission(form)
                        del request.session[self.session_key_data]

                        return prev_step.success(self.slug)

            else:
                # If data for step is invalid
                # we will need to display form again with errors,
                # so restore previous state.
                step = prev_step
                form = prev_form
        else:
            # Create empty form for non-POST requests
            form_class = self.get_form_class_for_step(step)
            form = form_class(page=self, user=request.user)

        context['form'] = form
        context['fields_step'] = step
        context['is_intermediate_step'] = step.possibly_has_next()

        return render(request, self.template, context)

    @cached_property
    def has_page_breaks(self):
        return any(field.has_skipping or field.page_break
                   for field in self.get_form_fields())

    def serve(self, request, *args, **kwargs):
        if (not self.allow_multiple_submissions_per_user
                and self.has_user_submitted_survey(request, self.id)):
            if not (request.method == 'POST' and 'ajax' in request.POST):
                return render(request, self.template,
                              self.get_context(request))

        if ((self.has_page_breaks or self.multi_step)
                and not self.display_survey_directly):
            return self.serve_questions(request)

        if request.method == 'POST':
            form = self.get_form(request.POST, page=self, user=request.user)

            if form.is_valid():
                # check if the post is made via ajax call
                if 'ajax' in request.POST and \
                        request.POST['ajax'] == 'True':
                    # check if a submission exists for this question and user
                    submission = self.get_submission_class().objects.filter(
                        page=self, user__pk=request.user.pk)
                    if submission.exists():
                        # currently for submissions via ajax calls
                        # user should be able to update their submission
                        submission.delete()

                self.set_survey_as_submitted_for_session(request)
                self.process_form_submission(form)

                # render the landing_page
                return redirect(
                    reverse('molo.surveys:success', args=(self.slug, )))

        return super(MoloSurveyPage, self).serve(request, *args, **kwargs)
Ejemplo n.º 15
0
class ArticlePage(Page):
    parent_page_types = [
        "ArticleIndexPage",
    ]
    subpage_types = []

    author = models.ForeignKey('AuthorPage',
                               null=True,
                               on_delete=models.SET_NULL,
                               related_name='+')
    main_image = models.ForeignKey('images.CustomImage',
                                   null=True,
                                   blank=True,
                                   on_delete=models.SET_NULL,
                                   related_name='+')
    date = models.DateField("Post date")
    intro = models.TextField(
        max_length=480,
        help_text=
        'This will only appear in article previews, not with the full article. '
        + md_format_help)
    body = StreamField([
        ('text', blocks.RichTextBlock(features=DEFAULT_RICHTEXT_FEATURES)),
        ('captioned_image', CaptionedImageBlock()),
        ('embed', EmbedBlock(icon='media')),
        ('block_quote', QuoteBlock()),
        ('table', TableBlock(template='blog/table_block.html')),
        ('media', OptionsMediaBlock()),
        ('code', CodeBlock()),
    ])
    notes = models.TextField(
        null=True,
        blank=True,
        help_text="This text will not appear on the page.")
    tags = ClusterTaggableManager(through=ArticlePageTag, blank=True)

    content_panels = Page.content_panels + [
        PageChooserPanel('author', ),
        FieldPanel('date'),
        ImageChooserPanel('main_image'),
        InlinePanel('article_audiences', label='Audiences'),
        FieldPanel('tags'),
        FieldPanel('intro'),
        StreamFieldPanel('body'),
        FieldPanel('notes'),
    ]

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

    class Meta:
        verbose_name = "Article"

    def __unicode__(self):
        return self.title

    def subject(self):
        # Find closest ancestor which is article index page
        return self.get_ancestors().type(ArticleIndexPage).last()
Ejemplo n.º 16
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")
    ]

    def __str__(self):
        return self.title
Ejemplo n.º 17
0
class BlogPage(HeadlessPreviewMixin, Page):
    date = models.DateField("Post date")
    advert = models.ForeignKey(
        "home.Advert",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="+",
    )
    cover = models.ForeignKey(
        "wagtailimages.Image",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="+",
    )
    book_file = models.ForeignKey(
        "wagtaildocs.Document",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="+",
    )
    featured_media = models.ForeignKey(
        "wagtailmedia.Media",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="+",
    )
    author = models.ForeignKey(
        AuthorPage,
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="+",
    )
    body = StreamField(StreamFieldBlock())

    content_panels = Page.content_panels + [
        FieldPanel("date"),
        ImageChooserPanel("cover"),
        StreamFieldPanel("body"),
        InlinePanel("related_links", label="Related links"),
        InlinePanel("authors", label="Authors"),
        FieldPanel("author"),
        SnippetChooserPanel("advert"),
        DocumentChooserPanel("book_file"),
        MediaChooserPanel("featured_media"),
    ]

    @property
    def copy(self):
        return self

    graphql_fields = [
        GraphQLString("heading"),
        GraphQLString("date"),
        GraphQLStreamfield("body"),
        GraphQLCollection(
            GraphQLForeignKey, "related_links", "home.blogpagerelatedlink"
        ),
        GraphQLCollection(GraphQLString, "related_urls", source="related_links.url"),
        GraphQLCollection(GraphQLString, "authors", source="authors.person.name"),
        GraphQLSnippet("advert", "home.Advert"),
        GraphQLImage("cover"),
        GraphQLDocument("book_file"),
        GraphQLMedia("featured_media"),
        GraphQLForeignKey("copy", "home.BlogPage"),
        GraphQLPage("author"),
    ]
Ejemplo n.º 18
0
class ContentPage(Page):
    article_date = models.DateField("Article date (only for blog)",
                                    null=True,
                                    blank=True)

    article_image = models.ForeignKey(
        "cms.CustomImage",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="+",
    )

    article_image_caption = models.TextField(
        "Article image caption (only for blog)", null=True, blank=True)

    article_teaser = models.TextField("Article teaser (only for blog)",
                                      null=True,
                                      blank=True)

    layout = models.CharField(
        max_length=2,
        choices=PageLayout.choices,
        default=PageLayout.CENTERED_MIDDLE,
    )

    body = StreamField([
        ("heading", blocks.CharBlock(classname="full title")),
        ("paragraph", blocks.RichTextBlock()),
        ("quote", QuoteBlock()),
        ("image", ImageChooserBlock()),
        ("two_columns", TwoColumnBlock()),
        ("centered_column", CenteredColumnBlock()),
        ("raw_html", blocks.RawHTMLBlock()),
        ("list_child_pages", blocks.PageChooserBlock(can_choose_root=False)),
    ])

    content_panels = Page.content_panels + [
        StreamFieldPanel("body"),
        FieldPanel("article_date"),
        FieldPanel("article_teaser"),
        FieldPanel("article_image_caption"),
        FieldPanel("layout"),
    ]

    promote_panels = [
        MultiFieldPanel(Page.promote_panels, "Common page configuration"),
        ImageChooserPanel("article_image", ),
    ]

    # Export fields over the API
    api_fields = [
        APIField("body"),
        APIField("article_date"),
        APIField("article_teaser"),
        APIField("article_image"),
        APIField("article_image_caption"),
        APIField("layout"),
        APIField(
            "article_image_thumbnail",
            serializer=ImageRenditionField("fill-1200x600",
                                           source="article_image"),
        ),
    ]
Ejemplo n.º 19
0
class FlatPage(Page):
    headers = StreamField([
        ('h_hero', _H_HeroBlock(icon='image')),
        # ('code', blocks.RawHTMLBlock(null=True, blank=True, classname="full", icon='code'))
    ])

    price = models.DecimalField(verbose_name="Preis",
                                max_digits=11,
                                decimal_places=2,
                                null=True,
                                blank=False)

    br_choices = (
        ('RE', 'Miete'),
        ('BU', 'Kauf'),
    )

    buy_or_rent = models.CharField(verbose_name="Mieten oder Kaufen?",
                                   max_length=4,
                                   choices=br_choices,
                                   default='RE')
    lead = models.CharField(null=True, blank=True, max_length=512)
    available = models.BooleanField(verbose_name="Verfügbar")
    # ground_plan = ImageChooserBlock(
    #     required=True, blank=False, help_text="Raumplan")

    sections = StreamField(
        [('s_contentcenter', _S_ContentCenter(icon='fa-info')),
         ('s_contentright', _S_ContentRight(icon='fa-info')),
         ('s_contentleft', _S_ContentLeft(icon='fa-info'))],
        null=True,
        blank=True)

    gallery = StreamField([('g_gallery', _G_GalleryBlock(icon='fa-info'))],
                          null=True,
                          blank=True)

    ground_plan = StreamField(
        [('p_groundplan', _P_GroundPlanBlock(icon='fa-info'))],
        null=True,
        blank=True,
        verbose_name="Grundrissplan")

    main_content_panels = [
        StreamFieldPanel('headers'),
        FieldPanel('price'),
        FieldPanel('buy_or_rent'),
        FieldPanel('lead'),
        FieldPanel('available'),
        # FieldPanel('ground_plan'),
        StreamFieldPanel('ground_plan'),
        StreamFieldPanel('sections'),
        StreamFieldPanel('gallery'),
        # StreamFieldPanel('footers')
    ]

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

    preview_modes = []
Ejemplo n.º 20
0
class ContactFormPage(AbstractEmailForm):
    """
    Contact form page that uses wagtail fields. Note that fields
    are locked to first_names, last_names, email, phone & message.
    Users can still build custom forms by using the FormPage model.
    """
    image = models.ForeignKey('wagtailimages.Image',
                              null=True,
                              blank=True,
                              on_delete=models.SET_NULL,
                              related_name='+')
    body = StreamField(BaseStreamBlock())
    thank_you_text = RichTextField(blank=True)

    form_builder = CaptchaFormBuilder

    content_panels = AbstractEmailForm.content_panels + [
        ImageChooserPanel('image'),
        StreamFieldPanel('body'),
        # InlinePanel('form_fields', label="Form fields"),
        FieldPanel('thank_you_text', classname="full"),
        MultiFieldPanel([
            FieldRowPanel([
                FieldPanel('from_address', classname="col6"),
                FieldPanel('to_address', classname="col6"),
            ]),
        ], "Email"),
    ]

    def get_submission_class(self):
        return ContactMessage

    def process_bilingual_form_data(self, form):
        """
        Returns a cleaned_data object with all english keys.
        wagtail-modeltranslation is delivering a cleaned_data object
        with translated-keys.
        """
        keys = list(form.cleaned_data.keys())  # Original keys
        fields = [
            ['name', keys[0]],
            ['email', keys[1]],
            ['phone', keys[2]],
            ['subject', keys[3]],
            ['message', keys[4]],
            ['captcha', keys[5]],
        ]
        res = []
        for f in fields:
            try:
                res.append(
                    form.cleaned_data[f[0]])  # Attempt english retrieval first
            except KeyError:
                res.append(form.cleaned_data[f[1]])  # Translated language
        return {f[0]: r
                for f, r in zip(fields, res)}  # Return dict with english keys

    def process_form_submission(self, form):
        # import pdb; pdb.set_trace()
        cleaned_data = self.process_bilingual_form_data(form)
        try:
            contact = Contact.objects.get(email=cleaned_data['email'])
        except Contact.DoesNotExist:
            contact = Contact.objects.create(
                names=cleaned_data['name'],
                email=cleaned_data['email'],
                phone=cleaned_data['phone'],
            )
        submission = self.get_submission_class().objects.create(
            form_data=json.dumps(cleaned_data, cls=DjangoJSONEncoder),
            page=self,
            contact=contact,
        )
        if self.to_address:
            self.send_mail(form)
        return submission

    def send_mail(self, form):
        cleaned_data = self.process_bilingual_form_data(form)
        addresses = [x.strip() for x in self.to_address.split(',')]
        content = []
        for field in form:
            value = field.value()
            if isinstance(value, list):
                value = ', '.join(value)
            content.append('{}: {}'.format(field.label, value))

        content = '\n'.join(content)
        subject = "Message from: %(name)s <%(email)s>" % {
            'name': cleaned_data['name'],
            'email': cleaned_data['email'],
        }
        send_mail(
            subject,
            content,
            addresses,
            self.from_address,
        )

    def save(self, *args, **kwargs):
        super().save()
        field_list = list(self.form_fields.all().values_list('label',
                                                             flat=True))
        if CONTACT_CONST['name']['label'] not in field_list:
            ContactCaptchaFormField.objects.create(
                label=CONTACT_CONST['name']['label'],
                label_es=CONTACT_CONST['name']['label_es'],
                placeholder=CONTACT_CONST['name']['placeholder'],
                placeholder_es=CONTACT_CONST['name']['placeholder_es'],
                field_type='singleline',
                additional_attrs=CONTACT_CONST['name']['attrs'],
                required=True,
                page=self)
        if CONTACT_CONST['email']['label'] not in field_list:
            ContactCaptchaFormField.objects.create(
                label=CONTACT_CONST['email']['label'],
                label_es=CONTACT_CONST['email']['label_es'],
                placeholder=CONTACT_CONST['email']['placeholder'],
                placeholder_es=CONTACT_CONST['email']['placeholder_es'],
                additional_attrs=CONTACT_CONST['email']['attrs'],
                field_type='email',
                required=True,
                page=self,
            )
        if CONTACT_CONST['phone']['label'] not in field_list:
            ContactCaptchaFormField.objects.create(
                label=CONTACT_CONST['phone']['label'],
                label_es=CONTACT_CONST['phone']['label_es'],
                placeholder=CONTACT_CONST['phone']['placeholder'],
                placeholder_es=CONTACT_CONST['phone']['placeholder_es'],
                additional_attrs=CONTACT_CONST['phone']['attrs'],
                field_type='singleline',
                required=False,
                page=self,
            )
        if CONTACT_CONST['sub']['label'] not in field_list:
            ContactCaptchaFormField.objects.create(
                label=CONTACT_CONST['sub']['label'],
                label_es=CONTACT_CONST['sub']['label_es'],
                placeholder=CONTACT_CONST['sub']['placeholder'],
                placeholder_es=CONTACT_CONST['sub']['placeholder_es'],
                additional_attrs=CONTACT_CONST['sub']['attrs'],
                field_type='singleline',
                required=True,
                page=self,
            )
        if CONTACT_CONST['msg']['label'] not in field_list:
            ContactCaptchaFormField.objects.create(
                label=CONTACT_CONST['msg']['label'],
                label_es=CONTACT_CONST['msg']['label_es'],
                placeholder=CONTACT_CONST['msg']['placeholder'],
                placeholder_es=CONTACT_CONST['msg']['placeholder_es'],
                additional_attrs=CONTACT_CONST['msg']['attrs'],
                field_type='multiline',
                required=True,
                page=self,
            )
        if 'recaptchav2' not in list(self.form_fields.all().values_list(
                'field_type', flat=True)):
            ContactCaptchaFormField.objects.create(
                label='captcha',
                label_es='captcha',
                field_type='recaptchav2',
                required=True,
                page=self,
            )

        super().save(*args, **kwargs)
Ejemplo n.º 21
0
class ArticlePage(Page):

    preview_modes = [('', 'English'), ('Spanish', 'Spanish')]

    def get_sitemap_urls(self, request = None):
        pages = []
        for language in translation.trans_real.settings.LANGUAGES:
            translation.activate(language[0])
            if self.is_published:
                pages.append(super().get_sitemap_urls()[0])
        return pages

    def serve_preview(self, request, mode_name):
        if (mode_name == 'Spanish'):
            translation.activate("es")
        print(translation.get_language())
        return super().serve(request)

    
    def serve(self, request):
        if ( not self.is_published ):
            raise Http404("Not implemented yet!", translation.get_language())
        return super().serve(request)

    translated_title = TranslatedField()
    title_en, title_es = translated_title.init(
        models.CharField,
        ('title_en', 'title_es'),
        max_length=255, blank=True)

    def get_title():
        return str(translated_title)

    is_published = TranslatedField()
    is_published_en, is_published_es = is_published.init(
        models.BooleanField,
        ('is_published_en', 'is_published_es'),
        blank=True, default=False)


    sub_title = TranslatedField()
    sub_title_en, sub_title_es = sub_title.init(
        models.CharField,
        ('sub_title_en', 'sub_title_es'),
        max_length=255, blank=True)

    blurb = TranslatedField()
    blurb_en, blurb_es = blurb.init(
        RichTextField,
        ('blurb_en', 'blurb_es'),
        blank=True)

    intro = TranslatedField()
    intro_en, intro_es = intro.init(
        RichTextField,
        ('intro_en', 'intro_es'),
        blank=True)

    colour = ColorField(default='#6c6c1c')
    cover_image = models.ForeignKey(
        CustomImage,
        null=True,
        # blank=True,
        on_delete=models.SET_NULL,
        related_name='+'
    )

    translated_seo_title = TranslatedField()
    seo_title_en, seo_title_es = translated_seo_title.init(
        models.TextField,
        ('seo_title_en', 'seo_title_es'),
        blank=True)
    translated_seo_description = TranslatedField()
    seo_description_en, seo_description_es = translated_seo_description.init(
        models.TextField,
        ('seo_description_en', 'seo_description_es'),
        blank=True)

    intro_extras = StreamField([
        ('code_snippet', CodeSnippetBlock("code_snippets.CodeSnippet"))
    ], blank=True)

    content_panels = Page.content_panels + [
        MultiFieldPanel([
            FieldPanel("seo_title_en"),
            FieldPanel("seo_description_en"),
            FieldPanel("blurb_en"),
            FieldPanel("is_published_en"),
        ], heading="Promote"),
        MultiFieldPanel([
            FieldPanel('title_en'),
            FieldPanel('sub_title_en'),
            FieldPanel('colour'),
            ImageChooserPanel('cover_image'),
        ], heading="Cover"),
        FieldPanel('intro_en'),
        InlinePanel('things_to_take_en', label="Things to take"),
        StreamFieldPanel('intro_extras'),
        InlinePanel('sections_en', label="Section")
    ]

    es_content_panels = [
        MultiFieldPanel([
            FieldPanel("seo_title_es"),
            FieldPanel("seo_description_es"),
            FieldPanel("blurb_es"),
            FieldPanel("is_published_es"),
        ], heading="Promote"),
        FieldPanel('title_es'),
        FieldPanel('sub_title_es'),
        FieldPanel('intro_es'),
        InlinePanel('things_to_take_es'),
        InlinePanel('sections_es', label="Sección"),
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='EN Content'),
        ObjectList(es_content_panels, heading='ES content'),
        ObjectList(Page.promote_panels, heading='Promote'),
        ObjectList(Page.settings_panels, heading='Settings',
                   classname="settings"),
    ])

    search_fields = Page.search_fields + [
        index.SearchField('title_en'),
        index.SearchField('title_es'),
        index.SearchField('sub_title_en'),
        index.SearchField('sub_title_es'),

    ]

    def __str__(self):
        return self.title

    sections = TranslatedField()
    sections.init(None, ('sections_en', 'sections_es'))

    things_to_take = TranslatedField()
    things_to_take.init(None, ('things_to_take_en', 'things_to_take_es'))

    @property
    def related_names(self):
        return self.sub_title.split(", ")

    @property
    def things_to_take_en(self):
        tips = self.things_to_take_en.all()
        return tips

    @property
    def sections_en(self):
        article = self.sections_en.all()
        return article

    @property
    def things_to_take_es(self):
        tips = self.things_to_take_es.all()
        return tips

    @property
    def sections_es(self):
        article = self.sections_es.all()
        return article
Ejemplo n.º 22
0
class BaseResumePage(MetadataMixin, Page):
    page_ptr = models.OneToOneField(
        Page, parent_link=True, related_name="+", on_delete=models.CASCADE
    )
    is_creatable = False

    font = models.CharField(max_length=100, null=True, blank=True)
    background_color = models.CharField(max_length=100, null=True, blank=True)

    full_name = models.CharField(max_length=100, null=True, blank=True)

    role = models.CharField(max_length=100, null=True, blank=True)
    about = MarkdownField(max_length=2500, null=True, blank=True)
    photo = models.ForeignKey(
        Image, null=True, blank=True, on_delete=models.SET_NULL, related_name="+"
    )
    social_links = fields.StreamField(
        [
            (
                "social_link",
                blocks.StructBlock(
                    [
                        ("text", blocks.TextBlock()),
                        ("url", blocks.URLBlock()),
                        ("logo", ImageChooserBlock()),
                    ],
                    icon="group",
                ),
            ),
        ],
        null=True,
        blank=True,
    )

    resume = fields.StreamField(
        [
            ("work_experience", WorkExperienceBlock()),
            ("contributions", ContributionsBlock()),
            ("writing", WritingsBlock()),
            ("education", EducationBlock()),
        ],
        null=True,
        blank=True,
    )

    content_panels = Page.content_panels + [
        MultiFieldPanel(
            [
                FieldPanel("font"),
                FieldPanel("background_color"),
            ],
            heading="Customization",
        ),
        MultiFieldPanel(
            [
                FieldPanel("full_name"),
                FieldPanel("role"),
                MarkdownPanel("about"),
                ImageChooserPanel("photo"),
                StreamFieldPanel("social_links"),
            ],
            heading="Personal details",
        ),
        StreamFieldPanel("resume"),
    ]

    def get_template(self, request):  # pylint: disable=arguments-differ
        return "wagtail_resume/resume_page.html"

    def get_meta_title(self):
        return self.full_name

    def get_meta_description(self):
        about = Truncator(self.about).words(35)
        return f"{self.full_name} - {self.role}. {about}"

    def get_meta_image(self):
        return self.photo

    def get_meta_url(self):
        return self.get_full_url()

    def get_meta_twitter_card_type(self):
        return self.photo
Ejemplo n.º 23
0
class NewsletterPage(Page):
    body = StreamField(
        [
            ('advertisement_block', AdvertisementBlock()),
            ('content_block', ContentBlock()),
            ('featured_content_block', FeaturedContentBlock()),
            ('social_block', SocialBlock()),
            ('text_block', TextBlock()),
        ],
        blank=True,
    )
    html_file = models.ForeignKey(
        'wagtaildocs.Document',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
    )

    # Reference field for the Drupal-Wagtail migrator. Can be removed after.
    drupal_node_id = models.IntegerField(blank=True, null=True)

    content_panels = Page.content_panels + [
        MultiFieldPanel(
            [
                StreamFieldPanel('body'),
            ],
            heading='Body',
            classname='collapsible collapsed',
        ),
        MultiFieldPanel(
            [
                DocumentChooserPanel('html_file'),
            ],
            heading='HTML File',
            classname='collapsible collapsed',
        ),
    ]

    def html_string(self):
        def in_line_tracking(href, title):
            tracking = f'utm_source=cigi_newsletter&utm_medium=email&utm_campaign={slugify(title)}'
            if '?' in href:
                return f'{href}&{tracking}'
            else:
                return f'{href}?{tracking}'

        text_soup = BeautifulSoup(
            render_to_string('newsletters/newsletter_html.html', {
                'self': self,
                'page': self,
                'is_html_string': True
            }), 'html.parser')

        for link in text_soup.findAll('a'):
            try:
                link['href'] = in_line_tracking(link['href'], self.title)
            except KeyError:
                pass

        return str(text_soup)

    parent_page_types = ['newsletters.NewsletterListPage']
    subpage_types = []
    templates = 'newsletters/newsletter_page.html'

    class Meta:
        verbose_name = 'Newsletter'
        verbose_name_plural = 'Newsletters'
Ejemplo n.º 24
0
class ProjectPage(Page):

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

    subtitle = models.CharField(max_length=255, blank=True)
    subtitle_de = models.CharField(max_length=255, blank=True)

    teaser = RichTextField(blank=True)
    teaser_de = RichTextField(blank=True)

    BODY_BLOCKS = [
        ('richtext', RichTextBlock()),
        ('html', RawHTMLBlock()),
        ('image', ImageChooserBlock()),
    ]

    body = StreamField(BODY_BLOCKS, blank=True)
    body_de = StreamField(BODY_BLOCKS, blank=True)

    collaboration = RichTextField(blank=True)
    collaboration_de = RichTextField(blank=True)

    publications = RichTextField(blank=True)
    publications_de = RichTextField(blank=True)

    references = RichTextField(blank=True)
    references_de = RichTextField(blank=True)

    start_date = models.DateField(blank=True, null=True)
    end_date = models.DateField(blank=True, null=True)

    STATUS_IN_PROGRESS = 'IN_PROGRESS'
    STATUS_COMPLETED = 'COMPLETED'
    STATUS_CHOICES = ((STATUS_IN_PROGRESS, _('in progress')),
                      (STATUS_COMPLETED, _('completed')))

    status = models.CharField(max_length=32,
                              choices=STATUS_CHOICES,
                              default=STATUS_IN_PROGRESS)

    trans_title = TranslatedTextField('title')
    trans_subtitle = TranslatedTextField('subtitle')
    trans_teaser = TranslatedTextField('teaser')
    trans_body = TranslatedStreamField('body')
    trans_collaboration = TranslatedTextField('collaboration')
    trans_publications = TranslatedTextField('publications')
    trans_references = TranslatedTextField('references')

    search_fields = Page.search_fields + [
        index.SearchField('title_de'),
        index.SearchField('subtitle'),
        index.SearchField('subtitle_de'),
        index.SearchField('teaser'),
        index.SearchField('teaser_de'),
        index.SearchField('body'),
        index.SearchField('body_de'),
    ]

    content_panels = [
        FieldPanel('title', classname="full title"),
        FieldPanel('subtitle', classname="full title"),
        FieldPanel('teaser', classname='full'),
        StreamFieldPanel('body', classname='full'),
        FieldPanel('collaboration', classname='full'),
        FieldPanel('publications', classname='full'),
        FieldPanel('references', classname='full'),
    ]
    content_de_panels = [
        FieldPanel('title_de', classname="full title"),
        FieldPanel('subtitle_de', classname="full title"),
        FieldPanel('teaser_de', classname='full'),
        StreamFieldPanel('body_de', classname='full'),
        FieldPanel('collaboration_de', classname='full'),
        FieldPanel('publications_de', classname='full'),
        FieldPanel('references_de', classname='full'),
    ]
    misc_panels = [
        FieldPanel('status'),
        FieldPanel('start_date'),
        FieldPanel('end_date'),
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
        ObjectList(content_de_panels, heading='Content DE'),
        ObjectList(misc_panels, heading='Misc'),
        ObjectList(Page.promote_panels, heading='Promote'),
        ObjectList(Page.settings_panels,
                   heading='Settings',
                   classname='settings'),
    ])

    parent_page_types = ['projects.ProjectIndexPage']

    @property
    def color(self):
        return self.get_parent().specific.color
Ejemplo n.º 25
0
class LandingPage(
        BasePost
):  # a special type of post page (I intend to use it for game devlog)

    Page = TranslatablePage
    project_overview = models.BooleanField(default=False)
    banner = models.ForeignKey('wagtailimages.Image',
                               on_delete=models.SET_NULL,
                               related_name='+',
                               blank=True,
                               null=True)
    title_color = ColorField(default='#FFFFFF')
    tilable_banner = models.BooleanField(default=False)
    intro = models.CharField(
        max_length=255,
        blank=True,
    )

    body = StreamField([
        ('heading', blocks.CharBlock(classname="full title")),
        ('paragraph', blocks.RichTextBlock()),
        ('code', blocks.TextBlock()),
        ('code_output', blocks.TextBlock()),
        ('image', ImageChooserBlock(icon="image")),
        ('two_columns', TwoColumnBlock()),
        ('embedded_video', EmbedBlock(icon="media")),
        ('custom_html', blocks.TextBlock(icon='plus-inverse')),
    ],
                       null=True,
                       blank=True)

    content_panels = Page.content_panels + [
        MultiFieldPanel([
            ImageChooserPanel('banner'),
            FieldPanel('tilable_banner'),
            FieldPanel('title_color')
        ],
                        heading='Banner'),
        FieldPanel('categories', widget=forms.CheckboxSelectMultiple),
        FieldPanel('intro'),
        StreamFieldPanel('body'),
        PageChooserPanel(
            'related_page1',
            ['blog.PostPage', 'blog.LandingPage', 'blog.LandingPost']),
        PageChooserPanel(
            'related_page2',
            ['blog.PostPage', 'blog.LandingPage', 'blog.LandingPost']),
        # InlinePanel('related_links', label="Related Links"),
    ]
    settings_panels = Page.settings_panels + [
        FieldPanel('date'),
        FieldPanel('project_overview'),
        ImageChooserPanel('thumbnail'),
        MultiFieldPanel([
            FieldPanel('is_series'),
            FieldPanel('series_name'),
            FieldPanel('series_id')
        ],
                        heading='SeriesSetting',
                        classname="collapsible collapsed"),
    ]

    def get_context(self, request, *args, **kwargs):
        context = super().get_context(request, *args, **kwargs)
        return context
Ejemplo n.º 26
0
class EnforcementActionPage(AbstractFilterPage):
    public_enforcement_action = models.CharField(max_length=150, blank=True)
    initial_filing_date = models.DateField(null=True, blank=True)
    settled_or_contested_at_filing = models.CharField(max_length=10,
                                                      choices=[('Settled',
                                                                'Settled'),
                                                               ('Contested',
                                                                'Contested')],
                                                      blank=True)
    court = models.CharField(default='', max_length=150, blank=True)

    content = StreamField([
        ('full_width_text', organisms.FullWidthText()),
        ('expandable', organisms.Expandable()),
        ('expandable_group', organisms.ExpandableGroup()),
        ('notification', molecules.Notification()),
        ('table_block',
         organisms.AtomicTableBlock(table_options={'renderer': 'html'})),
        ('feedback', v1_blocks.Feedback()),
    ],
                          blank=True)

    content_panels = [StreamFieldPanel('header'), StreamFieldPanel('content')]

    metadata_panels = [
        FieldPanel('public_enforcement_action'),
        FieldPanel('initial_filing_date'),
        InlinePanel('defendant_types', label='Defendant/Respondent Type'),
        InlinePanel('categories', label="Forum", min_num=1, max_num=2),
        FieldPanel('court'),
        InlinePanel('docket_numbers', label="Docket Number", min_num=1),
        FieldPanel('settled_or_contested_at_filing'),
        InlinePanel('statuses', label="Status", min_num=1),
        InlinePanel('products', label="Products"),
        InlinePanel('at_risk_groups', label="At Risk Groups"),
        InlinePanel('statutes', label="Statutes/Regulations"),
        InlinePanel('enforcement_dispositions', label='Final Disposition'),
    ]

    settings_panels = [
        MultiFieldPanel(CFGOVPage.promote_panels, 'Settings'),
        MultiFieldPanel([
            FieldPanel('preview_title'),
            FieldPanel('preview_subheading'),
            FieldPanel('preview_description'),
            FieldPanel('secondary_link_url'),
            FieldPanel('secondary_link_text'),
            ImageChooserPanel('preview_image'),
        ],
                        heading='Page Preview Fields',
                        classname='collapsible'),
        FieldPanel('authors', 'Authors'),
        MultiFieldPanel([
            FieldPanel('date_published'),
            FieldPanel('comments_close_by'),
        ],
                        'Relevant Dates',
                        classname='collapsible'),
        MultiFieldPanel(Page.settings_panels, 'Scheduled Publishing'),
        FieldPanel('language', 'Language'),
        MultiFieldPanel(CFGOVPage.archive_panels, 'Archive'),
    ]

    edit_handler = TabbedInterface([
        ObjectList(AbstractFilterPage.content_panels + content_panels,
                   heading='General Content'),
        ObjectList(metadata_panels, heading='Metadata'),
        ObjectList(CFGOVPage.sidefoot_panels, heading='Sidebar'),
        ObjectList(settings_panels, heading='Configuration')
    ])

    template = 'enforcement-action/index.html'

    objects = PageManager()

    search_fields = AbstractFilterPage.search_fields + [
        index.SearchField('content')
    ]

    @classmethod
    def all_actions(cls):
        # Return the collection of all Enforcement Action Pages.
        # Exclude any pages in the Trash or otherwise not a child of the
        # EnforcementActionsFilterPage.
        try:
            # TODO: find a less hacky way to get only the pages in the
            # correct part of the page tree
            pg_id = 1327
            parent_page = Page.objects.get(id=pg_id)
            query = cls.objects.child_of(parent_page)
        except (Page.DoesNotExist):
            query = cls.objects

        query = query.filter(initial_filing_date__isnull=False)
        query = query.live().order_by('-initial_filing_date')
        return query

    def get_context(self, request):
        context = super(EnforcementActionPage, self).get_context(request)
        dispositions = self.enforcement_dispositions.all()

        context.update({
            'total_consumer_relief':
            sum(disp.final_order_consumer_redress +
                disp.final_order_other_consumer_relief
                for disp in dispositions),
            'total_cmp':
            sum(disp.final_order_civil_money_penalty for disp in dispositions),
            'defendant_types': [
                d.get_defendant_type_display()
                for d in self.defendant_types.all()
            ],
            'statutes': [s.statute for s in self.statutes.all()],
            'products': [p.product for p in self.products.all()],
            'at_risk_groups':
            [g.at_risk_group for g in self.at_risk_groups.all()]
        })

        return context
Ejemplo n.º 27
0
class LocationPage(Page):
    """
    Detail for a specific bakery location.
    """
    introduction = models.TextField(help_text='Text to describe the page',
                                    blank=True)
    image = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        help_text=
        'Landscape mode only; horizontal width between 1000px and 3000px.')
    body = StreamField(BaseStreamBlock(), verbose_name="Page body", blank=True)
    address = models.TextField()
    lat_long = models.CharField(
        max_length=36,
        help_text="Comma separated lat/long. (Ex. 64.144367, -21.939182) \
                   Right click Google Maps and select 'What\'s Here'",
        validators=[
            RegexValidator(
                regex=r'^(\-?\d+(\.\d+)?),\s*(\-?\d+(\.\d+)?)$',
                message=
                'Lat Long must be a comma-separated numeric lat and long',
                code='invalid_lat_long'),
        ])

    # Search index configuration
    search_fields = Page.search_fields + [
        index.SearchField('address'),
        index.SearchField('body'),
    ]

    # Fields to show to the editor in the admin view
    content_panels = [
        FieldPanel('title', classname="full"),
        FieldPanel('introduction', classname="full"),
        ImageChooserPanel('image'),
        StreamFieldPanel('body'),
        FieldPanel('address', classname="full"),
        FieldPanel('lat_long'),
        InlinePanel('hours_of_operation', label="Hours of Operation"),
    ]

    def __str__(self):
        return self.title

    @property
    def operating_hours(self):
        hours = self.hours_of_operation.all()
        return hours

    # Determines if the location is currently open. It is timezone naive
    def is_open(self):
        now = datetime.now()
        current_time = now.time()
        current_day = now.strftime('%a').upper()
        try:
            self.operating_hours.get(day=current_day,
                                     opening_time__lte=current_time,
                                     closing_time__gte=current_time)
            return True
        except LocationOperatingHours.DoesNotExist:
            return False

    # Makes additional context available to the template so that we can access
    # the latitude, longitude and map API key to render the map
    def get_context(self, request):
        context = super(LocationPage, self).get_context(request)
        context['lat'] = self.lat_long.split(",")[0]
        context['long'] = self.lat_long.split(",")[1]
        context['google_map_api_key'] = settings.GOOGLE_MAP_API_KEY
        return context

    # Can only be placed under a LocationsIndexPage object
    parent_page_types = ['LocationsIndexPage']
Ejemplo n.º 28
0
class UseCasePage(Page):

    category = models.CharField(max_length=2, choices=CATEGORY_CHOICES)

    image = models.ForeignKey(
        'a4_candy_cms_images.CustomImage',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        verbose_name="Use Case Header Image",
        help_text="The Image that is shown on the use case item page " +
        "and the use case index page")

    title_de = models.CharField(max_length=250,
                                blank=True,
                                verbose_name="German Title")
    title_en = models.CharField(max_length=250,
                                blank=True,
                                verbose_name="English Title")

    body_streamfield_de = fields.StreamField(
        [('paragraph', blocks.RichTextBlock()),
         ('html', blocks.RawHTMLBlock()), ('examples', ExampleBlock())],
        blank=True)

    body_streamfield_en = fields.StreamField(
        [('paragraph', blocks.RichTextBlock()),
         ('html', blocks.RawHTMLBlock()), ('examples', ExampleBlock())],
        blank=True)

    subtitle = TranslatedField('title_de', 'title_en')

    teaser = TranslatedField('teaser_de', 'teaser_en')

    body = TranslatedField('body_streamfield_de', 'body_streamfield_en')

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

        if category:
            try:
                use_cases = UseCasePage.objects\
                    .filter(category=category)\
                    .exclude(id=self.id)
            except ValueError:
                use_cases = []

        context = super().get_context(request)
        context['other_use_cases'] = use_cases
        return context

    en_content_panels = [
        FieldPanel('title_en'),
        StreamFieldPanel('body_streamfield_en')
    ]

    de_content_panels = [
        FieldPanel('title_de'),
        StreamFieldPanel('body_streamfield_de')
    ]

    common_panels = [
        FieldPanel('title'),
        ImageChooserPanel('image'),
        FieldPanel('slug'),
        FieldPanel('category')
    ]

    edit_handler = TabbedInterface([
        ObjectList(common_panels, heading='Common'),
        ObjectList(en_content_panels, heading='English'),
        ObjectList(de_content_panels, heading='German')
    ])

    subpage_types = []
Ejemplo n.º 29
0
class TableBlockStreamPage(Page):
    table = StreamField([('table', TableBlock())])

    content_panels = [StreamFieldPanel('table')]
Ejemplo n.º 30
0
class CybergatewayHomePage(Page):
    """
    The Cybergateway themed template Page
    """

    # Hero section of HomePage
    site_logo = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        help_text='Site Logo Image'
    )

    site_link = models.CharField(
        max_length=255,
        default="#",
        help_text='Give a site redirect link',
    )

    site_text = models.CharField(
        max_length=50,
        default="#",
        help_text='Give a Site Name',
    )

    site_header = models.CharField(
        max_length=70,
        default="#",
        help_text='Give a Site Header Name',
    )

    site_link1 = models.CharField(
        max_length=70,
        default="#",
        help_text='Give a Site Nav Link [1]',
    )

    site_link_text1 = models.CharField(
        max_length=70,
        help_text='Give a Site Nav Link Text [1]',
    )

    site_link2 = models.CharField(
        max_length=70,
        default='#',
        help_text='Give a Site Nav Link [2]',
    )

    site_link_text2 = models.CharField(
        max_length=70,
        help_text='Give a Site Nav Link Text [2]',
    )

    site_link3 = models.CharField(
        max_length=70,
        default="#",
        help_text='Give a Site Nav Link [3]',
    )

    site_link_text3 = models.CharField(
        max_length=70,
        help_text='Give a Site Nav Link Text [3]',
    )

    contact = StreamField(
        BaseStreamBlock(),
        verbose_name="Contact Info Block",
        blank=True,
        null=True)

    footer = StreamField(
        BaseStreamBlock(),
        verbose_name="Footer Content Block",
        blank=True,
        null=True)

    boolean_choices = (
        ("yes", "Yes"),
        ("no", "No")
    )

    show_navbar = models.CharField(
        choices=boolean_choices,
        max_length=5,
        help_text="Choose yes if you want to display the navbar on home page and no if you don't want to.",
        default="yes")

    show_nav_extra = models.CharField(
        choices=boolean_choices,
        max_length=5,
        help_text="Choose yes if you want the secondary navbar to show on home page or no if you don't want to",
        default="yes")

    show_footer = models.CharField(
        choices=boolean_choices,
        max_length=5,
        help_text="Choose yes if you want the Footer to show on home page or no if you don't want to",
        default="yes")

    content_panels = Page.content_panels + [
        MultiFieldPanel([
            ImageChooserPanel('site_logo'),
            FieldPanel('site_link'),
            FieldPanel('site_text'),
            FieldPanel('site_header'),
            FieldPanel('site_link1'),
            FieldPanel('site_link_text1'),
            FieldPanel('site_link2'),
            FieldPanel('site_link_text2'),
            FieldPanel('site_link3'),
            FieldPanel('site_link_text3'),
        ], heading="Navbar Section"),
        InlinePanel("row", label="row"),
        StreamFieldPanel('contact'),
        StreamFieldPanel('footer'),
    ]

    customization_panels = [
        FieldPanel('show_navbar'),
        FieldPanel('show_nav_extra'),
        FieldPanel('show_footer'),
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
        ObjectList(customization_panels, heading='Customization'),
        ObjectList(Page.promote_panels, heading='Promote'),
        ObjectList(Page.settings_panels, heading='Settings',
                   classname="settings"),
    ])

    def __str__(self):
        return self.title