Esempio n. 1
0
class BlogPage(MetadataPageMixin, Page):
    date = models.DateField("Post date")
    # intro = models.CharField(max_length=250)
    body = RichTextField(blank=True)

    # Categories
    node = ParentalManyToManyField('home.Node', blank=True)

    brand = models.CharField(max_length=100, blank=True, null=True)
    model = models.CharField(max_length=100, blank=True, null=True)
    weight = models.CharField(max_length=100, blank=True, null=True)
    color = models.CharField(max_length=50, blank=True, null=True)
    dimensions = models.CharField(max_length=100, blank=True, null=True)

    # Links
    affiliate_name = models.CharField(max_length=100, blank=True, null=True)
    affiliate_link = models.URLField(blank=True, null=True)

    # Social links
    facebook_link = models.URLField(blank=True, null=True)
    twitter_link = models.URLField(blank=True, null=True)
    pinterest_link = models.URLField(blank=True, null=True)
    youtube_link = models.URLField(blank=True, null=True)
    instagram_link = models.URLField(blank=True, null=True)

    # Method returns first element of the image gallery
    @property
    def main_image(self):
        gallery_item = self.gallery_images.first()

        if gallery_item:
            return gallery_item.image
        else:
            return None

    @property
    def categories(self):
        categories = self.node.all()
        return categories

    @property
    def main_category(self):
        categories = self.categories
        if categories:
            category = categories.first()
        else:
            category = None
        return category

    def get_context(self, request):
        context = super().get_context(request)
        context['related_items'] = [
            i.related_page for i in self.related_items.all()
        ]

        return context

    search_fields = Page.search_fields + [
        index.SearchField('body'),
        index.SearchField('brand'),
        index.SearchField('model'),
        index.SearchField('color'),
        index.SearchField('affiliate_name'),
        index.SearchField('node'),
    ]

    content_panels = Page.content_panels + [
        FieldPanel('date'),
        # ImageChooserPanel('main_image'),
        # FieldPanel('intro'),
        FieldPanel('body'),
        FieldPanel('brand'),
        FieldPanel('model'),
        FieldPanel('weight'),
        FieldPanel('color'),
        FieldPanel('dimensions'),
        FieldPanel('affiliate_name'),
        FieldPanel('affiliate_link'),
        FieldPanel('facebook_link'),
        FieldPanel('twitter_link'),
        FieldPanel('pinterest_link'),
        FieldPanel('youtube_link'),
        FieldPanel('instagram_link'),
        InlinePanel('gallery_images', max_num=6, label="Gallery images"),
        FieldPanel('node', widget=forms.CheckboxSelectMultiple),
        InlinePanel('adv_block', max_num=1, label="Adv Side Block"),
        InlinePanel('related_items', max_num=3, label="Related Items"),
    ]
Esempio n. 2
0
class Sessions(models.Model):
    SESSION_TYPE_CHOICES = [('lecture', 'Vorlesung'), ('exercise', 'Übung'),
                            ('study_group', 'Arbeitsgemeinschaft'),
                            ('exam_prep', 'Klausurenkurs'),
                            ('seminar', 'Seminar')]
    SEMESTER_TYPE_CHOICES = [
        ('ss2022', 'Sommersemester 2022'),
        ('ws2022', 'Wintersemester 2022'),
        ('ss2021', 'Sommersemester 2021'),
        ('ws2021', 'Wintersemester 2021'),
        ('ss2020', 'Sommersemester 2020'),
        ('ws2020', 'Wintersemester 2020'),
        ('ss2019', 'Sommersemester 2019'),
        ('ws2019', 'Wintersemester 2019'),
        ('ss2018', 'Sommersemester 2018'),
        ('ws2018', 'Wintersemester 2018'),
    ]

    name = models.CharField(max_length=255)
    subtitle = models.CharField(max_length=255)
    date = RichTextField(blank=True)
    #tags = ClusterTaggableManager(through=SessionTags, blank=True)
    speaker = models.ForeignKey(User,
                                null=True,
                                blank=True,
                                on_delete=models.SET_NULL,
                                related_name='+')
    type = models.CharField(choices=SESSION_TYPE_CHOICES,
                            default='lecture',
                            max_length=255,
                            blank=True)
    semester = models.CharField(choices=SEMESTER_TYPE_CHOICES,
                                default='ws2020',
                                max_length=255,
                                blank=True)
    assessment = RichTextField(blank=True)
    description = RichTextField(blank=True)
    speaker_description = RichTextField(blank=True)
    content = RichTextField(blank=True)
    location = RichTextField(blank=True)
    lat = models.FloatField(null=True, blank=True)
    lon = models.FloatField(null=True, blank=True)

    panels = [
        MultiFieldPanel([
            FieldPanel('name', classname="col-12"),
            FieldPanel('subtitle', classname="col-12"),
        ], "Session"),
        MultiFieldPanel([
            FieldPanel('date', classname="col-12"),
            FieldPanel('type', classname="col-12"),
            FieldPanel('semester', classname="col-12"),
            FieldPanel('description', classname="col-12"),
            FieldPanel('assessment', classname="col-12"),
        ], "Info"),
        MultiFieldPanel([
            FieldPanel('speaker', classname="col-12"),
            FieldPanel('speaker_description', classname="col-12"),
        ], "Speaker"),
        MultiFieldPanel([
            FieldPanel('location', classname="col-12"),
            FieldPanel('lat', classname="col-6"),
            FieldPanel('lon', classname="col-6"),
        ], "Location"),
    ]

    search_fields = [
        index.SearchField('title'),
        index.SearchField('speaker'),
        index.SearchField('description'),
    ]

    class Meta:
        verbose_name = 'Lehrveranstaltung'
        verbose_name_plural = 'Lehrveranstaltungen'
Esempio n. 3
0
class SpotlightIndicator(Page):
    class Meta():
        verbose_name = 'Spotlight Indicator'

    parent_page_types = [SpotlightTheme]

    ddw_id = models.CharField(max_length=255)
    description = models.TextField(help_text='A description of this indicator', null=True, blank=True)
    source = models.TextField(help_text='Where is the data from?', null=True, blank=True)
    color = models.ForeignKey(
        SpotlightColour,
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+'
    )
    start_year = models.IntegerField(blank=True, null=True)
    end_year = models.IntegerField(blank=True, null=True)
    excluded_years = models.TextField(help_text='Comma separated values e.g. 2002,2004,2017', blank=True, null=True)
    DATA_FORMAT_CHOICES = [
        ('percent', 'Percentage'),
        ('plain', 'Plain'),
        ('currency', 'Currency')
    ]
    data_format = models.TextField(
        max_length=100,
        choices=DATA_FORMAT_CHOICES,
        default='plain',
        help_text='Options are plain, currency, percent')
    range = models.CharField(max_length=100, null=True, blank=True, help_text='The range of values shown on the legend')
    value_prefix = models.CharField(max_length=100, null=True, blank=True)
    value_suffix = models.CharField(max_length=100, null=True, blank=True)
    tooltip_template = models.TextField(
        blank=True,
        null=True,
        help_text='Text for the tooltip.Template strings can be used to substitute values e.g. {name}')
    config = StreamField(
        AceEditorStreamBlock(max_num=1, block_counts={'JSON': {'max_num':1}}),
        null=True, blank=True, verbose_name='JSON Config')

    content_panels = Page.content_panels +  [
        FieldPanel('ddw_id'),
        FieldPanel('description'),
        FieldPanel('source'),
        SnippetChooserPanel('color'),
        FieldPanel('start_year'),
        FieldPanel('end_year'),
        FieldPanel('excluded_years'),
        FieldPanel('data_format'),
        FieldPanel('range'),
        FieldPanel('value_prefix'),
        FieldPanel('value_suffix'),
        FieldPanel('tooltip_template'),
        StreamFieldPanel('config')
    ]

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


    def serve(self, request, *args, **kwargs):
        raise Http404()

    def serve_preview(self, request, mode_name):
        return self.get_parent().serve(request)
Esempio n. 4
0
class ConferencePage(PublicBasePage, SocialMediaFields):
    """
    Main page for creating conferences.
    """
    # Generic variables
    hex_regex = RegexValidator(
        regex='^#[a-zA-Z0-9]{6}$',
        message='Please enter a hex color, e.g. #012043'
    )

    # Field definitions
    primary_branding_color = models.CharField(
        validators=[hex_regex], max_length=7, blank=True
    )
    secondary_branding_color = models.CharField(
        validators=[hex_regex], max_length=7, blank=True
    )
    location = models.ForeignKey(
        'public.LocationPage',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='%(app_label)s_%(class)s_related'
    )
    start_date = models.DateTimeField(blank=True, null=True)
    end_date = models.DateTimeField(blank=True, null=True)
    current = models.BooleanField(
        default=True,
        help_text="Uncheck this when the conference has transpired"
    )
    conference_logo = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
    )
    secondary_registration_heading = models.CharField(max_length=40, blank=True)
    secondary_registration_description = models.TextField(blank=True)
    body = StreamField(DefaultBodyFields())

    # Panels and subpage types
    content_panels = Page.content_panels + [
        MultiFieldPanel(
            [
                ImageChooserPanel('banner_image'),
                FieldPanel('banner_title'),
                FieldPanel('banner_subtitle'),
            ],
            heading='Banner'
        ),
        MultiFieldPanel(
            [
                FieldPanel('primary_branding_color'),
                FieldPanel('secondary_branding_color'),
                ImageChooserPanel('conference_logo'),
            ],
            heading='Branding',
        ),
        MultiFieldPanel(
            [
                FieldPanel('start_date'),
                FieldPanel('end_date'),
            ],
            heading='Dates and Times',
        ),
        FieldPanel('location'),
        FieldPanel('current'),
        InlinePanel('main_registration', label='Main Registration Link'),
        MultiFieldPanel(
            [
                FieldPanel('secondary_registration_heading'),
                FieldPanel('secondary_registration_description'),
                InlinePanel(
                    'sub_registration', label='Secondary Registration Link'
                ),
            ],
            heading='Secondary Registration Links',
        ),
        InlinePanel('sponsors', label='Sponsors'),
        InlinePanel('organizers', label='Organizers'),
        StreamFieldPanel('body'),
    ] + SocialMediaFields.panels + PublicBasePage.content_panels

    subpage_types = [
        'conferences.ConferencePage', 'conferences.ConferenceSubPage',
        'redirects.RedirectPage'
    ]

    search_fields = PublicBasePage.search_fields + [
        index.SearchField('banner_image'),
        index.SearchField('primary_branding_color'),
        index.SearchField('secondary_branding_color'),
        index.SearchField('location'),
        index.SearchField('current'),
        index.SearchField('conference_logo'),
        index.SearchField('body'),
    ]
    api_fields = [
        APIField('body'),
    ]

    @property
    def has_right_sidebar(self):
        """
        Test to see if a right sidebar should be
        displayed.

        Returns:
            Boolean
        """
        fields = [
            self.sponsors, self.organizers, self.sub_registration,
            self.secondary_registration_heading,
            self.secondary_registration_description
        ]
        return self.base_has_right_sidebar() or self.has_field(fields)

    def has_conf_banner(self, current_site):
        """
        Used to override the boolean [0] value for PublicBasePage
        get_banner.

        Args:
            current_site: object
        """
        return self.get_banner(current_site)[0] or (
            self.primary_branding_color and self.banner_title
        )

    def get_banner(self, current_site):
        """
        Override the default get_banner method so that
        banners will always display as long as a title
        is present.

        Args:
            current_site: site object.

        Returns:
            See get_banner in PublicBasePage.
        """
        try:
            # Base case
            if self.banner_title:
                return (
                    True, self.banner_image, self.banner_feature,
                    self.banner_title, self.banner_subtitle,
                    self.relative_url(current_site), self.title
                )
            # Recursive case
            else:
                return self.get_parent().specific.get_banner(current_site)
        # Reached the top of the tree (could factor this into an if)
        except (AttributeError):
            return (False, None, None, '', '', '', '')

    # Context

    def get_context(self, request):
        context = super(ConferencePage, self).get_context(request)
        current_site = Site.find_for_request(request)
        main_reg = self.main_registration.all()
        has_sidebar = self.has_left_sidebar(context) or bool(main_reg)
        context['has_left_sidebar'] = has_sidebar
        context['content_div_css'] = self.get_conditional_css_classes(
            'content', has_sidebar
        )
        context['breadcrumb_div_css'] = self.get_conditional_css_classes(
            'breadcrumbs', has_sidebar
        )
        context['has_banner'] = self.has_conf_banner(current_site)
        context['primary_branding_color'] = self.primary_branding_color
        context['secondary_branding_color'] = self.secondary_branding_color
        context['conference_logo'] = self.conference_logo
        context['conference_title'] = self.title
        context['has_social_media'] = self.has_social_media
        context['main_registration'] = main_reg
        context['sponsors'] = self.sponsors.all()
        context['organizers'] = self.organizers.all()
        context['secondary_registration'] = self.sub_registration.all()
        context['secondary_registration_heading'
                ] = self.secondary_registration_heading
        context['secondary_registration_description'
                ] = self.secondary_registration_description
        context['home'] = self.relative_url(current_site)
        return context
Esempio n. 5
0
class MemorialPath(I18nPage):
    """A list of memorials."""

    parent_page_types = ['MemorialPathIndex']

    description = RichTextField(
        features=I18nPage.RICH_TEXT_FEATURES,
        blank=True,
        verbose_name=_(TXT['memorial_path.description']),
        help_text=_(TXT['memorial_path.description.help']),
    )
    description_de = RichTextField(
        features=I18nPage.RICH_TEXT_FEATURES,
        blank=True,
        verbose_name=_(TXT['memorial_path.description']),
        help_text=_(TXT['memorial_path.description.help']),
    )
    description_cs = RichTextField(
        features=I18nPage.RICH_TEXT_FEATURES,
        blank=True,
        verbose_name=_(TXT['memorial_path.description']),
        help_text=_(TXT['memorial_path.description.help']),
    )
    i18n_description = TranslatedField.named('description')

    search_fields = I18nPage.search_fields + [
        index.SearchField('description'),
        index.SearchField('description_de'),
        index.SearchField('description_cs'),
    ]

    api_fields = I18nPage.api_fields + [
        APIField('description'),
        APIField('description_de'),
        APIField('description_cs'),
    ]

    general_panels = [
        FieldPanelTabs(children=[
            FieldPanelTab('title', heading=_(TXT['language.en'])),
            FieldPanelTab('title_de', heading=_(TXT['language.de'])),
            FieldPanelTab('title_cs', heading=_(TXT['language.cs'])),
        ],
                       heading=_(TXT['page.title'])),
        FieldPanelTabs(children=[
            FieldPanelTab('description', heading=_(TXT['language.en'])),
            FieldPanelTab('description_de', heading=_(TXT['language.de'])),
            FieldPanelTab('description_cs', heading=_(TXT['language.cs'])),
        ],
                       heading=_(TXT['memorial_path.description'])),
        MultiFieldPanel(children=[
            InlinePanel('waypoints', panels=[
                PageChooserPanel('memorial'),
            ]),
        ],
                        heading=_(TXT['memorial_path_waypoint.plural'])),
    ]
    meta_panels = I18nPage.meta_panels + [FieldPanel("slug")]
    edit_handler = TabbedInterface([
        ObjectList(general_panels, heading=_(TXT["heading.general"])),
        ObjectList(meta_panels, heading=_(TXT["heading.meta"])),
    ])

    class Meta:
        db_table = DB_TABLE_PREFIX + 'memorial_path'
        verbose_name = _(TXT['memorial_path'])
        verbose_name_plural = _(TXT['memorial_path.plural'])
class ApteanRespondCaseFormPage(BasePage):

    template = "patterns/pages/cases/form_page.html"
    landing_page_template = "patterns/pages/cases/form_page_landing.html"

    form = models.CharField(max_length=255, choices=APTEAN_FORM_CHOICES)

    introduction = RichTextField(
        blank=True,
        help_text="Text displayed before the form",
        features=RICH_TEXT_FEATURES,
    )
    pre_submission_text = RichTextField(
        blank=True,
        help_text="Text displayed after the form, above the submit button",
        features=RICH_TEXT_FEATURES,
        verbose_name="pre-submission text",
    )

    completion_title = models.CharField(
        max_length=255,
        help_text=
        "Heading for the page shown after successful form submission.",
    )
    completion_content = RichTextField(
        blank=True,
        help_text=
        "Text displayed to the user on successful submission of the form",
        features=RICH_TEXT_FEATURES,
    )
    action_text = models.CharField(
        max_length=32,
        blank=True,
        help_text='Form action button text. Defaults to "Submit"',
    )

    search_fields = BasePage.search_fields + [
        index.SearchField("introduction")
    ]

    content_panels = BasePage.content_panels + [
        FieldPanel("form"),
        FieldPanel("introduction"),
        FieldPanel("pre_submission_text"),
        FieldPanel("action_text"),
        MultiFieldPanel(
            [FieldPanel("completion_title"),
             FieldPanel("completion_content")],
            "Confirmation page",
        ),
    ]

    def get_form_class(self):
        return APTEAN_FORM_MAPPING[self.form]

    def get_form(self, *args, **kwargs):
        form_class = self.get_form_class()
        return form_class(*args, **kwargs)

    def serve(self, request, *args, **kwargs):
        try:
            if request.method == "POST":
                form = self.get_form(request.POST, request.FILES)

                if form.is_valid():
                    form, case_reference = self.process_form_submission(form)
                    if form.is_valid():  # still
                        return self.render_landing_page(
                            request, case_reference, *args, **kwargs)
            else:
                form = self.get_form()
        except RespondClientException:
            form = None

        context = self.get_context(request)
        context["form"] = form
        context["form_template"] = form.template_name
        return render(request, self.get_template(request), context)

    def process_form_submission(self, form):
        case_xml = form.get_xml_string()
        client = get_client()
        response = client.create_case(form.webservice, case_xml)
        soup = BeautifulSoup(response.content, "xml")
        if response.status_code != 200:
            reverse_schema_mapping = {
                v: k
                for k, v in form.field_schema_name_mapping.items()
            }
            for error in soup.find_all("failure"):
                if ("schemaName" in error.attrs and error.attrs["schemaName"]
                        in reverse_schema_mapping):
                    form.add_error(
                        reverse_schema_mapping[error.attrs["schemaName"]],
                        error.text)
                elif ("type" in error.attrs
                      and error.attrs["type"] == ATTACHMENT_FAILURE_ERROR):
                    form.add_error(ATTACHMENT_SCHEMA_NAME, error.text)
                else:
                    form.add_error(None, error.text)
            return form, None
        else:
            case_reference = get_case_reference(soup)
            return form, case_reference

    def get_landing_page_template(self, request, *args, **kwargs):
        return self.landing_page_template

    def render_landing_page(self,
                            request,
                            case_reference=None,
                            *args,
                            **kwargs):
        """
        Renders the landing page.
        You can override this method to return a different HttpResponse as
        landing page. E.g. you could return a redirect to a separate page.
        """
        context = self.get_context(request)
        context["case_reference"] = case_reference
        return render(request, self.get_landing_page_template(request),
                      context)
Esempio n. 7
0
class BlogPage(Page):
    """ This is the core of the Blog app. BlogPage are individual articles
    """
    subtitle = models.CharField(blank=True, max_length=255)

    body = MarkdownField(blank=True)

    extended_body = StreamField(
        [('content', MarkdownBlock(template='blog/markdown_block.html')),
         ('newsletter', NewsletterSubscribe()), ('book', BookInline()),
         ('video', EmbedBlock())],
        null=True)

    excerpt = models.TextField(
        blank=True,
        null=True,
        help_text='Short text to display in lists of posts')

    image = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+',
        help_text='Header Image, used also for social sharing')

    image_data = RichTextField(
        blank=True,
        null=True,
        help_text=
        'Information about the header image, to appear after the article')

    video = models.TextField(
        blank=True,
        null=True,
        help_text=
        'If the article has an associated video, it will appear as the header')

    tags = ClusterTaggableManager(through=BlogPageTag, blank=True)
    date_published = models.DateField("Date article published",
                                      blank=True,
                                      null=True)

    allow_comments = models.BooleanField(default=True)

    content_panels = Page.content_panels + [
        FieldPanel('subtitle'),
        StreamFieldPanel('extended_body'),
        FieldPanel('excerpt'),
        MarkdownPanel('body'),
        ImageChooserPanel('image'),
        FieldPanel('image_data'),
        FieldPanel('video'),
        FieldPanel('date_published'),
        InlinePanel('blog_person_relationship',
                    label="Author(s)",
                    panels=None,
                    min_num=1),
        FieldPanel('tags'),
    ]

    promote_panels = Page.promote_panels + [
        FieldPanel('allow_comments'),
    ]

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

    def authors(self):
        """
        Returns the BlogPage's related People. Again note that we are using
        the ParentalKey's related_name from the BlogPeopleRelationship model
        to access these objects. This allows us to access the People objects
        with a loop on the template. If we tried to access the blog_person_
        relationship directly we'd print `blog.BlogPeopleRelationship.None`
        """
        authors = [n.people for n in self.blog_person_relationship.all()]

        return authors

    @property
    def first_author(self):
        return self.authors()[-1]

    @property
    def get_tags(self):
        """
        Similar to the authors function above we're returning all the tags that
        are related to the blog post into a list we can access on the template.
        We're additionally adding a URL to access BlogPage objects with that tag
        """
        tags = self.tags.all()
        for tag in tags:
            tag.url = '/' + '/'.join(
                s.strip('/')
                for s in [self.get_parent().url, 'tags', tag.slug])
        return tags

    # Specifies parent to BlogPage as being BlogIndexPages
    parent_page_types = ['BlogIndexPage']

    # Specifies what content types can exist as children of BlogPage.
    # Empty list means that no child content types are allowed.
    subpage_types = []

    def get_absolute_url(self):
        return self.specific.url

    def get_context(self, request):
        context = super(BlogPage, self).get_context(request)
        context['latest_articles'] = BlogPage.objects.live().order_by(
            '-date_published')[:5]
        context['intro_class'] = 'blog article'
        return context

    @property
    def introduction(self):
        if self.excerpt:
            return self.excerpt

        html = render_markdown(self.body)
        soup = BeautifulSoup(html, "html.parser")
        try:
            introduction = soup.find('p').text
            return introduction
        except AttributeError:
            return None

    class Meta:
        ordering = ['-date_published']
Esempio n. 8
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"),
    ]

    api_fields = [
        APIField('title'),
        APIField('introduction'),
        APIField('image'),
        APIField('body'),
        APIField('address'),
        APIField('lat_long'),
        APIField('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']
Esempio n. 9
0
class BlogPage(Page):
    body = RichTextField(verbose_name=_('body'), blank=True)
    tags = ClusterTaggableManager(through=BlogPageTag, blank=True)
    date = models.DateField(
        _("Post date"),
        default=datetime.datetime.today,
        help_text=_("This date may be displayed on the blog post. It is not "
                    "used to schedule posts to go live at a later date."))
    header_image = models.ForeignKey(get_image_model_string(),
                                     null=True,
                                     blank=True,
                                     on_delete=models.SET_NULL,
                                     related_name='+',
                                     verbose_name=_('Header image'))
    author = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        blank=True,
        null=True,
        limit_choices_to=limit_author_choices,
        verbose_name=_('Author'),
        on_delete=models.SET_NULL,
        related_name='author_pages',
    )

    search_fields = Page.search_fields + [
        index.SearchField('body'),
    ]
    blog_categories = models.ManyToManyField(BlogCategory,
                                             through=BlogCategoryBlogPage,
                                             blank=True)

    settings_panels = [
        MultiFieldPanel([
            FieldRowPanel([
                FieldPanel('go_live_at'),
                FieldPanel('expire_at'),
            ],
                          classname="label-above"),
        ],
                        'Scheduled publishing',
                        classname="publishing"),
        FieldPanel('date'),
        FieldPanel('author'),
    ]

    def save_revision(self, *args, **kwargs):
        if not self.author:
            self.author = self.owner
        return super(BlogPage, self).save_revision(*args, **kwargs)

    def get_absolute_url(self):
        return self.url

    def get_blog_index(self):
        # Find closest ancestor which is a blog index
        return self.get_ancestors().type(BlogIndexPage).last()

    def get_context(self, request, *args, **kwargs):
        context = super(BlogPage, self).get_context(request, *args, **kwargs)
        context['blogs'] = self.get_blog_index().blogindexpage.blogs
        context = get_blog_context(context)
        context['COMMENTS_APP'] = COMMENTS_APP
        return context

    class Meta:
        verbose_name = _('Blog page')
        verbose_name_plural = _('Blog pages')

    parent_page_types = ['blog.BlogIndexPage']
Esempio n. 10
0
class GlossaryTerm(index.Indexed, models.Model):
    name_en = models.CharField(
        max_length=255,
        verbose_name='TERM (ENGLISH)'
    )
    definition_en = RichTextField(
        null=True,
        blank=True,
        verbose_name='DEFINITION (ENGLISH)'
    )
    anchor_en = models.CharField(
        max_length=255,
        null=True,
        blank=True,
        verbose_name='ANCHOR SLUG (ENGLISH)'
    )
    answer_page_en = models.ForeignKey(
        'ask_cfpb.AnswerPage',
        on_delete=models.CASCADE,
        related_name='glossary_terms',
        null=True,
        blank=True,
        verbose_name='ANSWER PAGE (ENGLISH)'
    )
    name_es = models.CharField(
        max_length=255,
        null=True,
        blank=True,
        verbose_name='TERM (SPANISH)'
    )
    definition_es = RichTextField(
        null=True,
        blank=True,
        verbose_name='DEFINITION (SPANISH)'
    )
    anchor_es = models.CharField(
        max_length=255,
        null=True,
        blank=True,
        verbose_name='ANCHOR SLUG (SPANISH)'
    )
    answer_page_es = models.ForeignKey(
        'ask_cfpb.AnswerPage',
        on_delete=models.CASCADE,
        related_name='glossary_terms_es',
        null=True,
        blank=True,
        verbose_name='ANSWER PAGE (SPANISH)'
    )
    portal_topic = ParentalKey(
        'v1.PortalTopic',
        related_name='glossary_terms',
        null=True,
        blank=True
    )
    search_fields = [
        index.SearchField('name_en', partial_match=True),
        index.SearchField('definition_en', partial_match=True),
        index.SearchField('name_es', partial_match=True),
        index.SearchField('definition_es', partial_match=True),
    ]

    def name(self, language='en'):
        if language == 'es':
            return self.name_es
        return self.name_en

    def definition(self, language='en'):
        if language == 'es':
            return self.definition_es
        return self.definition_en

    def answer_page_url(self, language='en'):
        if language == 'es' and self.answer_page_es:
            return self.answer_page_es.url
        if self.answer_page_en:
            return self.answer_page_en.url
        return None

    def anchor(self, language='en'):
        if language == 'es':
            return self.anchor_es
        return self.anchor_en

    def __str__(self):
        return self.name_en

    class Meta:
        unique_together = ['portal_topic', 'name_en']
Esempio n. 11
0
class AnswerPage(CFGOVPage):
    """Page type for Ask CFPB answers."""

    from ask_cfpb.models.django import Answer
    last_edited = models.DateField(
        blank=True,
        null=True,
        help_text="Change the date to today if you make a significant change.")
    question = models.TextField(blank=True)
    statement = models.TextField(
        blank=True,
        help_text=(
            "(Optional) Use this field to rephrase the question title as "
            "a statement. Use only if this answer has been chosen to appear "
            "on a money topic portal (e.g. /consumer-tools/debt-collection)."))
    short_answer = RichTextField(blank=True,
                                 features=['link', 'document-link'],
                                 help_text='Optional answer intro')
    answer_content = StreamField(ask_blocks.AskAnswerContent(),
                                 blank=True,
                                 verbose_name='Answer')
    answer_base = models.ForeignKey(Answer,
                                    blank=True,
                                    null=True,
                                    related_name='answer_pages',
                                    on_delete=models.SET_NULL)
    redirect_to_page = models.ForeignKey(
        'self',
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        related_name='redirect_to_pages',
        help_text="Choose another AnswerPage to redirect this page to")
    featured = models.BooleanField(
        default=False,
        help_text=("Check to make this one of two featured answers "
                   "on the landing page."))
    featured_rank = models.IntegerField(blank=True, null=True)
    category = models.ManyToManyField(
        'Category',
        blank=True,
        help_text=("Categorize this answer. "
                   "Avoid putting into more than one category."))
    search_tags = models.CharField(
        max_length=1000,
        blank=True,
        help_text="Search words or phrases, separated by commas")
    related_resource = models.ForeignKey(RelatedResource,
                                         blank=True,
                                         null=True,
                                         on_delete=models.SET_NULL)
    related_questions = ParentalManyToManyField(
        'self',
        symmetrical=False,
        blank=True,
        related_name='related_question',
        help_text='Maximum of 3 related questions')
    portal_topic = ParentalManyToManyField(
        PortalTopic,
        blank=True,
        help_text='Limit to 1 portal topic if possible')
    primary_portal_topic = ParentalKey(
        PortalTopic,
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        related_name='primary_portal_topic',
        help_text=("Use only if assigning more than one portal topic, "
                   "to control which topic is used as a breadcrumb."))
    portal_category = ParentalManyToManyField(PortalCategory, blank=True)

    user_feedback = StreamField([
        ('feedback', v1_blocks.Feedback()),
    ],
                                blank=True)

    share_and_print = models.BooleanField(
        default=False,
        help_text="Include share and print buttons above answer.")

    content_panels = CFGOVPage.content_panels + [
        MultiFieldPanel([
            FieldPanel('last_edited'),
            FieldPanel('question'),
            FieldPanel('statement'),
            FieldPanel('short_answer')
        ],
                        heading="Page content",
                        classname="collapsible"),
        FieldPanel('share_and_print'),
        StreamFieldPanel('answer_content'),
        MultiFieldPanel([
            SnippetChooserPanel('related_resource'),
            AutocompletePanel('related_questions',
                              target_model='ask_cfpb.AnswerPage')
        ],
                        heading="Related resources",
                        classname="collapsible"),
        MultiFieldPanel([
            FieldPanel('portal_topic', widget=forms.CheckboxSelectMultiple),
            FieldPanel('primary_portal_topic'),
            FieldPanel('portal_category', widget=forms.CheckboxSelectMultiple)
        ],
                        heading="Portal tags",
                        classname="collapsible"),
        MultiFieldPanel([FieldPanel('featured')],
                        heading="Featured answer on Ask landing page",
                        classname="collapsible"),
        MultiFieldPanel([
            AutocompletePanel('redirect_to_page',
                              target_model='ask_cfpb.AnswerPage')
        ],
                        heading="Redirect to another answer",
                        classname="collapsible"),
        MultiFieldPanel([StreamFieldPanel('user_feedback')],
                        heading="User feedback",
                        classname="collapsible collapsed"),
    ]

    sidebar = StreamField([
        ('call_to_action', molecules.CallToAction()),
        ('related_links', molecules.RelatedLinks()),
        ('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)

    sidebar_panels = [
        StreamFieldPanel('sidebar'),
    ]

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

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
        ObjectList(sidebar_panels, heading='Sidebar'),
        ObjectList(CFGOVPage.settings_panels, heading='Configuration'),
    ])

    template = 'ask-cfpb/answer-page.html'

    objects = CFGOVPageManager()

    def get_sibling_url(self):
        if self.answer_base:
            if self.language == 'es':
                sibling = self.answer_base.english_page
            else:
                sibling = self.answer_base.spanish_page
            if sibling and sibling.live and not sibling.redirect_to_page:
                return sibling.url

    def get_context(self, request, *args, **kwargs):
        portal_topic = self.primary_portal_topic or self.portal_topic.first()
        context = super(AnswerPage, self).get_context(request)
        context['related_questions'] = self.related_questions.all()
        context['description'] = (self.short_answer if self.short_answer else
                                  truncate_preview(self.answer_content))
        context['last_edited'] = self.last_edited
        context['portal_page'] = get_portal_or_portal_search_page(
            portal_topic, language=self.language)
        context['breadcrumb_items'] = get_ask_breadcrumbs(
            language=self.language,
            portal_topic=portal_topic,
        )
        context['about_us'] = get_standard_text(self.language, 'about_us')
        context['disclaimer'] = get_standard_text(self.language, 'disclaimer')
        context['sibling_url'] = self.get_sibling_url()
        return context

    def answer_content_text(self):
        raw_text = extract_raw_text(self.answer_content.stream_data)
        return strip_tags(" ".join([self.short_answer, raw_text]))

    def answer_content_data(self):
        return truncate_preview(self.answer_content_text())

    def short_answer_data(self):
        return ' '.join(
            RichTextField.get_searchable_content(self, self.short_answer))

    def text(self):
        short_answer = self.short_answer_data()
        answer_text = self.answer_content_text()
        full_text = f"{short_answer}\n\n{answer_text}\n\n{self.question}"
        return full_text

    def __str__(self):
        if self.answer_base:
            return f"{self.answer_base.id}: {self.title}"
        else:
            return self.title

    @property
    def clean_search_tags(self):
        return [tag.strip() for tag in self.search_tags.split(",")]

    @property
    def status_string(self):
        if self.redirect_to_page:
            if not self.live:
                return ("redirected but not live")
            else:
                return ("redirected")
        else:
            return super(AnswerPage, self).status_string

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

        if not self.category.exists():
            return None

        return self.category.first().category_image

    # Overrides the default of page.id for comparing against split testing
    # clusters. See: core.feature_flags.in_split_testing_cluster
    @property
    def split_test_id(self):
        return self.answer_base.id
Esempio n. 12
0
class GenericPage(Page):
    """
    GenericPage uses a Streamfield with a raw HTML block so is flexible.
    Used for leaf nodes where we don't want to show links to siblings,
    such as tool pages and standalone articles.
    """

    date = models.DateField("Post date", null=True, blank=True)
    short_description = RichTextField(blank=True)
    github_link = models.URLField("Github link", blank=True)
    include_mathjax = models.BooleanField("Include mathjax")
    extra_js_code = models.TextField("Additional JS code", blank=True)
    show_siblings = models.BooleanField(default=False)
    tags = ClusterTaggableManager(through=GenericPageTag, blank=True)

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

    body = StreamField([('heading', blocks.CharBlock(classname="full title")),
                        ('paragraph', blocks.RichTextBlock()),
                        ('html', blocks.RawHTMLBlock()),
                        ('image', ImageChooserBlock()),
                        ('code_block', CodeBlock()),
                        ('two_columns', TwoColumnBlock()),
                        ('link_block', PageLinksBlock())])

    # Inherit search_fields from Page and add more
    search_fields = Page.search_fields + [
        index.SearchField('short_description', boost="1.5"),
        index.SearchField('body'),
    ]

    content_panels = Page.content_panels + [
        FieldPanel('date'),
        FieldPanel('show_siblings'),
        MultiFieldPanel([
            FieldPanel('github_link'),
            FieldPanel('include_mathjax'),
            InlinePanel('css_links', label="CSS links"),
            InlinePanel('js_links', label="JS links"),
            FieldPanel('extra_js_code'),
        ],
                        heading="Additional resources",
                        classname="collapsible collapsed"),
        StreamFieldPanel('body'),
    ]

    promote_panels = [
        MultiFieldPanel(
            [
                ImageChooserPanel('featured_image'),
                FieldPanel('short_description'),
            ],
            heading="Featured content information",
        )
    ] + Page.promote_panels + [FieldPanel('tags')]

    def get_context(self, request):
        context = super(GenericPage, self).get_context(request)
        if self.show_siblings:
            context["previous_page"] = self.get_prev_siblings().live().first()
            context["next_page"] = self.get_next_siblings().live().first()
        return context
Esempio n. 13
0
        all_articles = CoronaArticlePage.objects.all()

        if all_articles.count <= 4:
            return all_articles

        if self.tags.length == 0:
            return all_articles


CoronaArticlePage.content_panels = [
    MultiFieldPanel([
        FieldPanel('title'),
        FieldPanel('author'),
        ImageChooserPanel('cover_image'),
    ],
                    heading='Title & intro'),
    MultiFieldPanel([StreamFieldPanel('content')], heading='Main content'),
    MultiFieldPanel([StreamFieldPanel('sidebar')], heading='Sidebar content'),
    MultiFieldPanel([StreamFieldPanel('fullwidth')],
                    heading='Bottom fullwidth content'),
    MultiFieldPanel([FieldPanel('related_title')], heading='Related articles'),
    MultiFieldPanel([FieldPanel('tags')], heading='Tags')
]

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

CoronaArticlePage.parent_page_types = ['home.CoronaIndexPage']
CoronaArticlePage.subpage_types = []
Esempio n. 14
0
class Report(RoutablePageMixin, Post):
    """
    Report class that inherits from the abstract
    Post model and creates pages for Policy Papers.
    """
    parent_page_types = ['ReportsHomepage']
    subpage_types = []

    sections = StreamField(
        [('section',
          ReportSectionBlock(template="components/report_section_body.html",
                             required=False))],
        null=True,
        blank=True)

    abstract = RichTextField(blank=True, null=True)

    acknowledgements = RichTextField(blank=True, null=True)

    source_word_doc = models.ForeignKey('wagtaildocs.Document',
                                        null=True,
                                        blank=True,
                                        on_delete=models.SET_NULL,
                                        related_name='+',
                                        verbose_name='Source Word Document')

    report_pdf = models.ForeignKey('wagtaildocs.Document',
                                   null=True,
                                   blank=True,
                                   on_delete=models.SET_NULL,
                                   related_name='+',
                                   verbose_name='Report PDF')

    dataviz_src = models.CharField(blank=True,
                                   null=True,
                                   max_length=300,
                                   help_text="")

    overwrite_sections_on_save = models.BooleanField(
        default=False,
        help_text=
        'If checked, sections and endnote fields ⚠ will be overwritten ⚠ with Word document source on save. Use with CAUTION!'
    )
    generate_pdf_on_publish = models.BooleanField(
        'Generate PDF on save',
        default=False,
        help_text=
        '⚠ Save latest content before checking this ⚠\nIf checked, the "Report PDF" field will be filled with a generated pdf. Otherwise, leave this unchecked and upload a pdf to the "Report PDF" field.'
    )
    revising = False

    featured_sections = StreamField([
        ('featured', FeaturedReportSectionBlock(required=False, null=True)),
    ],
                                    null=True,
                                    blank=True)

    endnotes = StreamField([
        ('endnote', EndnoteBlock(required=False, null=True)),
    ],
                           null=True,
                           blank=True)

    report_url = StreamField([
        ('report_url', URLBlock(required=False, null=True)),
    ],
                             null=True,
                             blank=True)

    attachment = StreamField([
        ('attachment', DocumentChooserBlock(required=False, null=True)),
    ],
                             null=True,
                             blank=True)

    partner_logo = models.ForeignKey('home.CustomImage',
                                     null=True,
                                     blank=True,
                                     on_delete=models.SET_NULL,
                                     related_name='+')

    content_panels = [
        MultiFieldPanel([
            FieldPanel('title'),
            FieldPanel('subheading'),
            FieldPanel('date'),
            ImageChooserPanel('story_image'),
        ]),
        InlinePanel('authors', label=("Authors")),
        InlinePanel('programs', label=("Programs")),
        InlinePanel('subprograms', label=("Subprograms")),
        InlinePanel('topics', label=("Topics")),
        InlinePanel('location', label=("Locations")),
        MultiFieldPanel([
            FieldPanel('abstract'),
            StreamFieldPanel('featured_sections'),
            FieldPanel('acknowledgements'),
        ])
    ]

    sections_panels = [StreamFieldPanel('sections')]

    endnote_panels = [StreamFieldPanel('endnotes')]

    settings_panels = Post.settings_panels + [FieldPanel('dataviz_src')]

    promote_panels = Page.promote_panels + [
        FieldPanel('story_excerpt'),
    ]

    pdf_panels = [
        MultiFieldPanel([
            DocumentChooserPanel('source_word_doc'),
            FieldPanel('overwrite_sections_on_save'),
        ],
                        heading='Word Doc Import'),
        MultiFieldPanel([
            FieldPanel('generate_pdf_on_publish'),
            DocumentChooserPanel('report_pdf'),
            StreamFieldPanel('attachment')
        ],
                        heading='PDF Generation'),
        ImageChooserPanel('partner_logo')
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading="Landing"),
        ObjectList(sections_panels, heading="Sections"),
        ObjectList(endnote_panels, heading="Endnotes"),
        ObjectList(promote_panels, heading="Promote"),
        ObjectList(settings_panels, heading='Settings', classname="settings"),
        ObjectList(pdf_panels, heading="PDF Publishing")
    ])

    search_fields = Post.search_fields + [index.SearchField('sections')]

    def get_context(self, request):
        context = super().get_context(request)

        if getattr(request, 'is_preview', False):
            import newamericadotorg.api.report
            revision = PageRevision.objects.filter(
                page=self).last().as_page_object()
            report_data = newamericadotorg.api.report.serializers.ReportDetailSerializer(
                revision).data
            context['initial_state'] = json.dumps(report_data)

        return context

    def save(self, *args, **kwargs):
        super(Report, self).save(*args, **kwargs)

        if not self.overwrite_sections_on_save and not self.generate_pdf_on_publish:
            self.revising = False

        if not self.revising and self.source_word_doc is not None and self.overwrite_sections_on_save:
            self.revising = True
            parse_pdf(self)
            self.overwrite_sections_on_save = False
            self.save_revision()

        if not self.revising and self.generate_pdf_on_publish:
            generate_pdf.apply_async(args=(self.id, ))

    # Extra views

    @route(r'pdf/$')
    def pdf(self, request):
        if not self.report_pdf:
            return self.pdf_render(request)
        url = 'https://s3.amazonaws.com/newamericadotorg/' + self.report_pdf.file.name
        return redirect(url)

    @route(r'pdf/render/$')
    def pdf_render(self, request):
        response = HttpResponse(content_type='application/pdf;')
        response[
            'Content-Disposition'] = 'inline; filename=%s.pdf' % self.title
        response['Content-Transfer-Encoding'] = 'binary'
        protocol = 'https://' if request.is_secure() else 'http://'
        base_url = protocol + request.get_host()

        contents = generate_report_contents(self)
        authors = get_report_authors(self)

        html = loader.get_template('report/pdf.html').render({
            'page': self,
            'contents': contents,
            'authors': authors
        })
        pdf = write_pdf(response, html, base_url)

        return response

    @route(r'print/$')
    def print(self, request):
        contents = generate_report_contents(self)
        authors = get_report_authors(self)

        return render(request,
                      'report/pdf.html',
                      context={
                          'page': self,
                          'contents': contents,
                          'authors': authors
                      })

    @route(r'[a-zA-Z0-9_\.\-]*/$')
    def section(self, request):
        # Serve the whole report, subsection routing is handled by React
        return self.serve(request)

    class Meta:
        verbose_name = 'Report'
Esempio n. 15
0
class MagazineArticle(Page):
    teaser = RichTextField(blank=True)
    body = StreamField(
        [
            ("heading", blocks.CharBlock(classname="full title")),
            (
                "paragraph",
                blocks.RichTextBlock(
                    features=[
                        "h2",
                        "h3",
                        "h4",
                        "bold",
                        "italic",
                        "ol",
                        "ul",
                        "hr",
                        "link",
                        "document-link",
                        "image",
                        "superscript",
                        "superscript",
                        "strikethrough",
                        "blockquote",
                    ]
                ),
            ),
            ("image", ImageChooserBlock()),
            ("pullquote", blocks.BlockQuoteBlock()),
        ]
    )
    body_migrated = models.TextField(
        help_text="Used only for content from old Drupal website.",
        null=True,
        blank=True,
    )

    department = models.ForeignKey(
        MagazineDepartment,
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="articles",
    )

    tags = ClusterTaggableManager(through=MagazineArticleTag, blank=True)

    search_template = "search/magazine_article.html"

    search_fields = Page.search_fields + [index.SearchField("body")]

    content_panels = Page.content_panels + [
        FieldPanel("teaser", classname="full"),
        StreamFieldPanel("body", classname="full"),
        FieldPanel("body_migrated", classname="full"),
        InlinePanel(
            "authors",
            heading="Authors",
            help_text="Select one or more authors, who contributed to this article",
        ),
        MultiFieldPanel(
            [
                PageChooserPanel("department", "magazine.MagazineDepartment"),
                FieldPanel("tags"),
            ],
            heading="Article information",
        ),
    ]

    parent_page_types = ["MagazineIssue"]
    subpage_types = []

    def get_sitemap_urls(self):
        return [
            {
                "location": self.full_url,
                "lastmod": self.latest_revision_created_at,
                "priority": 1,
            }
        ]

    def is_public_access(self):
        """
        Check whether article should be accessible to all readers
        or only subscribers based on issue publication date.
        """
        parent_issue = self.get_parent()

        today = arrow.utcnow()

        six_months_ago = today.shift(months=-6).date()

        # Issues older than six months are public access
        return parent_issue.specific.publication_date <= six_months_ago
Esempio n. 16
0
class EntryPage(Entry, Page):
    # Search
    search_fields = Page.search_fields + [
        index.SearchField('body'),
        index.SearchField('excerpt'),
        index.FilterField('page_ptr_id')
    ]

    # Panels
    content_panels = getattr(Entry, 'content_panels', [])

    promote_panels = Page.promote_panels + getattr(Entry, 'promote_panels', [])

    settings_panels = Page.settings_panels + [
        FieldPanel('date'),
        FieldPanel('owner'),
    ] + getattr(Entry, 'settings_panels', [])

    # Parent and child settings
    parent_page_types = ['puput.BlogPage']
    subpage_types = []

    def get_sitemap_urls(self, request=None):
        from .urls import get_entry_url
        root_url = self.get_url_parts()[1]
        entry_url = get_entry_url(self, self.blog_page.page_ptr, root_url)
        return [{
            'location':
            root_url + entry_url,
            # fall back on latest_revision_created_at if last_published_at is null
            # (for backwards compatibility from before last_published_at was added)
            'lastmod': (self.last_published_at
                        or self.latest_revision_created_at),
        }]

    @property
    def blog_page(self):
        return self.get_parent().specific

    @property
    def related(self):
        return [
            related.entrypage_to
            for related in self.related_entrypage_from.all()
        ]

    @property
    def has_related(self):
        return self.related_entrypage_from.count() > 0

    def get_absolute_url(self):
        return self.full_url

    def get_context(self, request, *args, **kwargs):
        context = super(EntryPage, self).get_context(request, *args, **kwargs)
        context['blog_page'] = self.blog_page
        return context

    class Meta:
        verbose_name = _('Entry')
        verbose_name_plural = _('Entries')
Esempio n. 17
0
class AbstractEmbedVideo(index.Indexed, models.Model):
    title = models.CharField(max_length=255, verbose_name=_('Title'))
    url = EmbedVideoField()
    thumbnail = models.ForeignKey(image_model_name,
                                  verbose_name="Thumbnail",
                                  null=True,
                                  blank=True,
                                  on_delete=models.SET_NULL,
                                  related_name='+')
    created_at = models.DateTimeField(auto_now_add=True)
    uploaded_by_user = models.ForeignKey(settings.AUTH_USER_MODEL,
                                         null=True,
                                         blank=True,
                                         editable=False,
                                         on_delete=models.SET_NULL)

    tags = TaggableManager(help_text=None, blank=True, verbose_name=_('Tags'))

    objects = EmbedVideoQuerySet.as_manager()

    def get_usage(self):
        return get_object_usage(self)

    @property
    def usage_url(self):
        return reverse('wagtail_embed_videos_video_usage', args=(self.id, ))

    search_fields = [
        index.SearchField('title', partial_match=True, boost=10),
        index.RelatedFields('tags', [
            index.SearchField('name', partial_match=True, boost=10),
        ]),
        index.FilterField('uploaded_by_user'),
    ]

    def __str__(self):
        return self.title

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

        super(AbstractEmbedVideo, self).__init__(*args, **kwargs)
        if args:
            if args[3] is None:
                create_thumbnail(self)

    def save(self, *args, **kwargs):
        super(AbstractEmbedVideo, self).save(*args, **kwargs)
        if not self.thumbnail:
            create_thumbnail(self)

    @property
    def default_alt_text(self):
        return self.title

    def is_editable_by_user(self, user):
        if user.has_perm('wagtail_embed_videos.change_embedvideo'):
            # user has global permission to change videos
            return True
        elif user.has_perm('wagtail_embed_videos.add_embedvideo') and\
                self.uploaded_by_user == user:
            # user has video add permission, which also implicitly provides
            # permission to edit their own videos
            return True
        else:
            return False

    class Meta:
        abstract = True
Esempio n. 18
0
class PracticePage(Page):
    parent_page_types = ['practice.PracticeIndexPage']
    subpage_types = []
    full_title = models.TextField("Полное название", max_length=800, null=True)
    raw_subtitle = RichTextField(verbose_name='Подзаголовок', blank=True)
    content = RichTextField(verbose_name='Содержание', blank=True)
    doc = models.ForeignKey(
        'base.CustomDocument', blank=True, null=True,
         on_delete=models.SET_NULL, related_name='+',
         verbose_name= "Документ")
    publish_date = models.DateField("Дата обнародования", null=True)
    category = ForeignKey('practice.CaseCategory', blank=True, null=True, on_delete=models.SET_NULL, verbose_name="Категория дел")
    level = ForeignKey('practice.CourtLevel', blank=True, null=True, on_delete=models.SET_NULL, verbose_name="Уровень суда")
    tags = ClusterTaggableManager(through='practice.PracticePageTag', blank=True)

    @property
    def subtitle(self):
        return richtext(self.raw_subtitle)

    @property
    def text(self):
        return richtext(self.content)

    @property
    def breadcrumbs(self):
        breadcrumbs = []
        for page in self.get_ancestors()[2:]:
            breadcrumbs.append({'title': page.title, 'url': page.url})
        return breadcrumbs

    @property
    def section_title(self):
        return self.get_parent().title

    @property
    def section_url(self):
        return self.get_parent().url

    @property
    def tags_slugs(self):
        return '\n'.join(self.tags.all().values_list('slug', flat=True))
    
    def get_sitemap_urls(self, request):
        return [{
            'location': self.full_url[:-1],
            'lastmod': self.last_published_at,
        }]


    def clean(self):
        super().clean()
        # автоматически создаем слаг и заголовок
        if len(self.full_title) >= 254  :
            self.title = self.full_title[:254]
        else:
            self.title = self.full_title
        if self.slug == 'default-blank-slug':
            if self.doc:
                dot_index = self.doc.filename.rfind('.')
                filename = self.doc.filename[:dot_index]
                self.slug = slugify(filename)
            elif len(self.title) > 80:
                self.slug = slugify(self.title[:80])
            else:
                self.slug = slugify(self.title)
        if '--' in self.slug:
            self.slug = self.slug.replace('--','-')
        if self.slug.endswith('-'):
            self.slug = self.slug[:-1]

    @property
    def clean_preview(self):
        h = html2text.HTML2Text()
        h.ignore_links = True
        h.ignore_emphasis = True
        h.ignore_images = True
        if self.subtitle:
            raw_text = h.handle(self.subtitle)
        else:
            raw_text = h.handle(self.raw_content)
        if len(raw_text) > 310:
            raw_text = raw_text[:310]
        space_index = raw_text.rfind(' ')
        raw_text = raw_text[:space_index] + '...'
        return raw_text.replace('\n', ' ').strip()

    search_fields = Page.search_fields + [
    index.SearchField('full_title'),
    index.SearchField('subtitle'),
    index.SearchField('content'),
    index.SearchField('tags'),
    index.RelatedFields('tags', [
        index.SearchField('slug'),
        index.FilterField('slug'),
        index.FilterField('name'),
        index.SearchField('name')]),
    
    index.SearchField('tags_slugs', partial_match=True),
    index.SearchField('category'),
    index.FilterField('level'),
    index.FilterField('category'),
    index.RelatedFields('category', [
        index.FilterField('name'),
    ]),
]   

    api_fields = [
        APIField('full_title'),
        APIField('subtitle'),
        APIField('text'),
        APIField('doc', serializer=base_serializers.DocSerializer()),
        APIField('breadcrumbs'),
        APIField('section_title'),
        APIField('section_url'),
        APIField('publish_date', serializer=base_serializers.DateSerializer()),
        APIField('category', serializer=serializers.CategorySerializer()),
        APIField('level', serializer=serializers.LevelSerializer()),
        APIField('tags', serializer=base_serializers.TagSerializer()),
        APIField('related_docs', serializer=serializers.RelatedDocsSerializer()),
        APIField('clean_preview'),
    ]

    content_panels =  [
        FieldPanel('full_title'),
        FieldPanel('raw_subtitle'),
        FieldPanel('publish_date'),
        DocumentChooserPanel('doc'),
        FieldPanel('category', widget=forms.RadioSelect),
        FieldPanel('level', widget=forms.RadioSelect),
        FieldPanel('tags'),
        MultiFieldPanel([InlinePanel("related_docs", label='Документ')
            ], heading='Связанные документы'
            ),
        FieldPanel('content'),
        ]

    class Meta:
        verbose_name = 'Страница судебной практики'
        verbose_name_plural = 'Страницы судебной практики'
Esempio n. 19
0
class AbstractDocument(CollectionMember, index.Indexed, models.Model):
    title = models.CharField(max_length=255, verbose_name=_('title'))
    file = models.FileField(upload_to='documents', verbose_name=_('file'))
    created_at = models.DateTimeField(verbose_name=_('created at'), auto_now_add=True)
    uploaded_by_user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        verbose_name=_('uploaded by user'),
        null=True,
        blank=True,
        editable=False,
        on_delete=models.SET_NULL
    )

    tags = TaggableManager(help_text=None, blank=True, verbose_name=_('tags'))

    file_size = models.PositiveIntegerField(null=True, editable=False)
    # A SHA-1 hash of the file contents
    file_hash = models.CharField(max_length=40, blank=True, editable=False)

    objects = DocumentQuerySet.as_manager()

    search_fields = CollectionMember.search_fields + [
        index.SearchField('title', partial_match=True, boost=10),
        index.AutocompleteField('title'),
        index.FilterField('title'),
        index.RelatedFields('tags', [
            index.SearchField('name', partial_match=True, boost=10),
            index.AutocompleteField('name'),
        ]),
        index.FilterField('uploaded_by_user'),
    ]

    def is_stored_locally(self):
        """
        Returns True if the image is hosted on the local filesystem
        """
        try:
            self.file.path

            return True
        except NotImplementedError:
            return False

    @contextmanager
    def open_file(self):
        # Open file if it is closed
        close_file = False
        f = self.file

        if f.closed:
            # Reopen the file
            if self.is_stored_locally():
                f.open('rb')
            else:
                # Some external storage backends don't allow reopening
                # the file. Get a fresh file instance. #1397
                storage = self._meta.get_field('file').storage
                f = storage.open(f.name, 'rb')

            close_file = True

        # Seek to beginning
        f.seek(0)

        try:
            yield f
        finally:
            if close_file:
                f.close()

    def get_file_size(self):
        if self.file_size is None:
            try:
                self.file_size = self.file.size
            except Exception:
                # File doesn't exist
                return

            self.save(update_fields=['file_size'])

        return self.file_size

    def _set_file_hash(self, file_contents):
        self.file_hash = hashlib.sha1(file_contents).hexdigest()

    def get_file_hash(self):
        if self.file_hash == '':
            with self.open_file() as f:
                self._set_file_hash(f.read())

            self.save(update_fields=['file_hash'])

        return self.file_hash

    def __str__(self):
        return self.title

    @property
    def filename(self):
        return os.path.basename(self.file.name)

    @property
    def file_extension(self):
        return os.path.splitext(self.filename)[1][1:]

    @property
    def url(self):
        return reverse('wagtaildocs_serve', args=[self.id, self.filename])

    def get_usage(self):
        return get_object_usage(self)

    @property
    def usage_url(self):
        return reverse('wagtaildocs:document_usage',
                       args=(self.id,))

    def is_editable_by_user(self, user):
        from wagtail.documents.permissions import permission_policy
        return permission_policy.user_has_permission_for_instance(user, 'change', self)

    class Meta:
        abstract = True
        verbose_name = _('document')
        verbose_name_plural = _('documents')
Esempio n. 20
0
class BrowsePage(CFGOVPage):
    header = StreamField([
        ('text_introduction', molecules.TextIntroduction()),
        ('featured_content', organisms.FeaturedContent()),
    ], blank=True)

    content = StreamField([
        ('full_width_text', organisms.FullWidthText()),
        ('info_unit_group', organisms.InfoUnitGroup()),
        ('expandable_group', organisms.ExpandableGroup()),
        ('expandable', organisms.Expandable()),
        ('well', organisms.Well()),
        ('video_player', organisms.VideoPlayer()),
        ('snippet_list', organisms.ResourceList()),
        ('table_block', organisms.AtomicTableBlock(
            table_options={'renderer': 'html'}
        )),
        ('feedback', v1_blocks.Feedback()),
        ('raw_html_block', blocks.RawHTMLBlock(
            label='Raw HTML block'
        )),
        ('conference_registration_form', ConferenceRegistrationForm()),
        ('chart_block', organisms.ChartBlock()),
        ('mortgage_chart_block', organisms.MortgageChartBlock()),
        ('mortgage_map_block', organisms.MortgageMapBlock()),
        ('mortgage_downloads_block', MortgageDataDownloads()),
        ('data_snapshot', organisms.DataSnapshot()),
        ('job_listing_table', JobListingTable()),
        ('bureau_structure', organisms.BureauStructure()),
        ('yes_checklist', YESChecklist()),
    ], blank=True)

    secondary_nav_exclude_sibling_pages = models.BooleanField(default=False)

    # General content tab
    content_panels = CFGOVPage.content_panels + [
        StreamFieldPanel('header'),
        StreamFieldPanel('content'),
    ]

    sidefoot_panels = CFGOVPage.sidefoot_panels + [
        FieldPanel('secondary_nav_exclude_sibling_pages'),
    ]

    # Tab handler interface
    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='General Content'),
        ObjectList(sidefoot_panels, heading='Sidebar'),
        ObjectList(CFGOVPage.settings_panels, heading='Configuration'),
    ])

    template = 'browse-basic/index.html'

    objects = PageManager()

    search_fields = CFGOVPage.search_fields + [
        index.SearchField('content'),
        index.SearchField('header')
    ]

    @property
    def page_js(self):
        return (
            super(BrowsePage, self).page_js + ['secondary-navigation.js']
        )

    def get_context(self, request, *args, **kwargs):
        context = super(BrowsePage, self).get_context(request, *args, **kwargs)
        context.update({
            'get_secondary_nav_items': get_secondary_nav_items
        })
        return context
Esempio n. 21
0
class ConferenceSubPage(PublicBasePage):
    """
    Subpages for conferences. These inherit
    most of their template "goodness" from
    parent ConferencePage.
    """
    body = StreamField(DefaultBodyFields())

    content_panels = Page.content_panels + [
        StreamFieldPanel('body'),
    ] + PublicBasePage.content_panels

    subpage_types = ['conferences.ConferenceSubPage']

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

    api_fields = [
        APIField('body'),
    ]

    @property
    def has_right_sidebar(self):
        """
        Override default test to see if a right
        sidebar should be displayed.

        Returns:
            Boolean
        """
        parent = self.get_parent_of_type('conference page')
        return parent.has_right_sidebar

    @property
    def has_social_media(self):
        """
        Override default test for social media.

        Returns:
            Boolean
        """
        parent = self.get_parent_of_type('conference page')
        return parent.has_social_media

    # Context
    def get_context(self, request):
        context = super(ConferenceSubPage, self).get_context(request)
        current_site = Site.find_for_request(request)
        parent = self.get_parent_of_type('conference page')

        # Set social media fields dynamically and
        # get all the values from the parent page.
        # This doesn't seem like a good practice
        # How else can this be done?
        social_media_fields = [
            f.name for f in SocialMediaFields._meta.get_fields()
        ]
        for field in social_media_fields:
            exec('self.' + field + ' = ' + 'parent.' + field)

        context['primary_branding_color'] = parent.primary_branding_color
        context['secondary_branding_color'] = parent.secondary_branding_color
        context['conference_logo'] = parent.conference_logo
        context['conference_title'] = parent.title
        context['has_social_media'] = parent.has_social_media
        context['main_registration'] = parent.main_registration.all()
        context['sponsors'] = parent.sponsors.all()
        context['organizers'] = parent.organizers.all()
        context['secondary_registration'] = parent.sub_registration.all()
        context['secondary_registration_heading'
                ] = parent.secondary_registration_heading
        context['secondary_registration_description'
                ] = parent.secondary_registration_description
        context['home'] = parent.relative_url(current_site)
        return context
Esempio n. 22
0
class EventCalendar(RoutablePageMixin, Page):
    """
    Base calendar class which actually displays the calendar.
    """
    heading = models.CharField(max_length=65)
    subheading = models.CharField(max_length=65)
    
    description = RichTextField(blank=True, help_text=_('Description of the calendar'), verbose_name=_('Description'))
    """ Short description of the calendar."""

    default_image = models.ForeignKey('wagtailimages.Image',
                                      null=True,
                                      blank=True,
                                      on_delete=models.SET_NULL,
                                      related_name='+',
                                      verbose_name=_('Default Image'),
                                      help_text=_('Default image to be used for calendar entries')
                                      )
    """ Default image to be used for calendar entries """

    content_panels = Page.content_panels + [
        FieldPanel('description'),
        ImageChooserPanel('default_image'),
        FieldPanel('heading', classname="full"),
        FieldPanel('subheading', classname="full"),
    ]

    subpage_types = ['events.EventCalPage']

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

    class Meta:
        verbose_name = _("Calendar Root Page")
    
    parent_page_types = ["home.TeamPage"]

    @route(r"^events/$")
    def getEvents(self, request: django.http.HttpRequest) -> django.http.HttpResponse:
        """
        Route that returns the events. Is accessed by fullcalender as the api to call

        :param request: Django request
        :return: JSON of the events ard their details
        """

        def filterForPeriod(request: django.http.HttpRequest) -> models.QuerySet:
            """
            Filter for the specific time frame being queried by FullCalendar

            :param request: Django request
            :return: Queryset of EventCalPage objects
            """

            # TODO make sure start and end points work properly
            events = EventCalPage.objects.filter(start_dt__range=[request.GET['start'], request.GET['end']]).live()
            return events

        if request.is_ajax():
            result = [
                {
                    'title': event.title,
                    'start': event.start_dt.astimezone(pytz.timezone(request.GET['timezone'])).isoformat(),
                    'end': event.end_dt.astimezone(pytz.timezone(request.GET['timezone'])).isoformat(),
                    'url': event.url
                } for event in filterForPeriod(request)
            ]
            json_output = json.dumps(result)
            return HttpResponse(json_output)
        else:
            return super(EventCalendar, self).serve(request)

    @route(r"^category/(?P<category>[\w\-]+)/$")
    def viewByCategory(self, request: django.http.HttpRequest, **kwargs) -> django.http.HttpResponse:
        """
        View calendar by a specific category

        :param request: Django request
        :param kwargs: Django request kwargs
        :return: HttpResponse that shows a calendar filtered by a category
        """

        return render(request, "events/event_calendar_category.html",
                      {'self': self, 'page': self, 'category': kwargs['category']})

    @route(r"^category/(?P<category>[\w\-]+)/events/$")
    def getEventsByCategory(self, request: django.http.HttpRequest, **kwargs: dict) -> django.http.HttpResponse:
        """
        Gets the events for a specific category for a specific timeframe. Is accessed by fullcalender.js

        :param request: Django request
        :param kwargs: Django request kwargs
        :return: HttpResponse
        """

        categ = kwargs['category']

        def filterForPeriod(request: django.http.HttpRequest, categ: str) -> django.http.HttpResponse:
            """
            Filters for a period taking into account the specific category

            :param request:
            :param categ:
            :return:
            """
            # TODO make sure start and end points work properly
            events = EventCalPage.objects.filter(start_dt__range=[request.GET['start'], request.GET['end']]).live()
            events = events.filter(categories__name__iexact=categ)

            return events

        if request.is_ajax():
            result = [
                {
                    'title': event.title,
                    'start': event.start_dt.astimezone(pytz.timezone(request.GET['timezone'])).isoformat(),
                    'end': event.end_dt.astimezone(pytz.timezone(request.GET['timezone'])).isoformat(),
                    'url': event.url
                } for event in filterForPeriod(request, categ)
            ]
            json_output = json.dumps(result)
            return HttpResponse(json_output)
        else:
            return render(request, "events/event_calendar_category.html",
                          {'self': self, 'page': self, 'category': kwargs['category']})

    @route(r'^ical/$')
    def icalView(self, request: django.http.HttpRequest, *args, **kwargs: dict) -> django.http.HttpResponse:
        """
        Route that produces the ical files requested by clients.

        :param request: Django request
        :param args: Django request args
        :param kwargs: Django request kwargs
        :return: HttpResponse containing an ical file
        """

        cal = Calendar()
        cal.add('prodid', '-//Calendar Event event//mxm.dk//')
        cal.add('version', '2.0')
        for entry in EventCalPage.objects.live():
            event = Event()
            event.add('summary', entry.title)
            event.add('dtstart', entry.start_dt)
            event.add('dtend', entry.end_dt)
            event.add('dtstamp', timezone.now())
            event.add('uid', str(entry.pk))
            event['location'] = vText(entry.location)
            cal.add_component(event)
        return HttpResponse(cal.to_ical(), content_type="text/calendar")

    @route(r"^category/(?P<category>[\w\-]+)/ical/$")
    def icalViewCategory(self, request: django.http.HttpRequest, *args, **kwargs: dict) -> django.http.HttpResponse:
        """
        Route that produces the ical files requested by clients, but filtered for a specific category

        :param request: Django HttpRequest
        :param args: Django request args
        :param kwargs: Django request kwargs
        :return: HttpResponse containing an ical file
        """
        cal = Calendar()
        cal.add('prodid', '-//Calendar Event event//mxm.dk//')
        cal.add('version', '2.0')
        print(kwargs['category'])
        for entry in EventCalPage.objects.filter(categories__name__iexact=kwargs['category']).live():
            event = Event()
            event.add('summary', entry.title)
            event.add('dtstart', entry.start_dt)
            event.add('dtend', entry.end_dt)
            event.add('dtstamp', timezone.now())
            event.add('uid', str(entry.pk))
            event['location'] = vText(entry.location)
            cal.add_component(event)
        return HttpResponse(cal.to_ical(), content_type="text/calendar")

    @property
    def get_categories(self) -> models.QuerySet:
        """
        Gets the calendar categories that currently exist

        :return: Returns a Queryset of Category objects
        """
        return Category.objects.all()

    @property
    def get_url(self) -> str:
        """
        Gets the url of the calendar page

        :return: Url of the calendar page
        """
        return self.url

    def get_context(self, request):
        context = super().get_context(request)
        next_event = EventCalPage.objects.filter(start_dt__gte=timezone.now()).order_by('start_dt').first()
        context['nextevent'] = next_event
        return context
Esempio n. 23
0
class JobOfferIndexPage(BasePage):
    subpage_types = ["JobOfferPage"]
    max_count_per_parent = 1

    heading = models.CharField("Überschrift", max_length=255, blank=True)
    highlight_in_heading = models.CharField(
        "Hervorhebungen in der Überschrift",
        help_text="Wiederhole Text aus der Überschrift der farblich hervorgehoben werden soll",
        blank=True,
        max_length=255,
    )
    subtitle = models.CharField("Untertitel", max_length=255, blank=True)

    before_jobs = StreamField(
        StandardStreamBlock,
        blank=True,
        verbose_name="Intro-Text (wenn Jobs vorhanden)",
        help_text="Wird als Text vor der Liste der Stellenanzeigen angezeigt. Aber nur wenn es auch Stellenanzeigen gibt.",
    )

    after_jobs = StreamField(
        StandardStreamBlock,
        blank=True,
        verbose_name="Outro-Text (wenn Jobs vorhanden)",
        help_text="Wird als Text nach der Liste der Stellenanzeigen angezeigt. Aber nur wenn es auch Stellenanzeigen gibt.",
    )

    empty = StreamField(
        StandardStreamBlock(),
        blank=True,
        null=True,
        verbose_name="Wenn keine Jobs",
        help_text="Wird angezeigt, wenn es keine Stellenanzeigen gibt.",
    )

    def get_context(self, request):
        context = super().get_context(request)
        context["jobs"] = JobOfferPage.objects.all().live()
        return context

    search_fields = BasePage.search_fields + [
        index.SearchField("heading"),
        index.SearchField("subtitle"),
        index.SearchField("before_jobs"),
        index.SearchField("after_jobs"),
    ]

    content_panels = [
        MultiFieldPanel(
            [
                FieldPanel("title"),
                FieldPanel("heading"),
                FieldPanel("highlight_in_heading"),
                FieldPanel("subtitle"),
            ],
            "Kopf",
        ),
        StreamFieldPanel("before_jobs"),
        HelpPanel(
            template="jobs/admin_add_job_button.html",
            heading="Stellenauschreibung erstellen",
        ),
        StreamFieldPanel("after_jobs"),
        StreamFieldPanel("empty"),
    ]

    class Meta:
        verbose_name = "Auflistung von Stellenausschreibungen"
        verbose_name_plural = "Auflistungen von Stellenausschreibungen"
Esempio n. 24
0
class EventCalPage(RoutablePageMixin, Page):
    """
    Calendar entry/ an even base model.
    """

    categories = models.ManyToManyField('events.Category', through='events.CategoryEventPage', blank=True,
                                        help_text=_('Categories this event belongs to'), verbose_name=_('Categories'))
    """Optional category that a specific calendar entry may belong to"""

    instruments = models.ManyToManyField('Instrument', through='events.instrumentEventPage', blank=True,
                                        help_text=_('Instruments this event belongs to'), verbose_name=_('Instruments'))
    

    description = RichTextField(blank=True, help_text=_('Description of event'), verbose_name=_('Description'))
    """Required. Description of the event/calendar entry"""

    image = models.ForeignKey('wagtailimages.Image',
                              null=True,
                              blank=True,
                              on_delete=models.SET_NULL,
                              related_name='+',
                              verbose_name=_('Image'),
                              )
    """Optional image to associate with a calendar entry. Only really useful for the website"""

    start_dt = models.DateTimeField(help_text=_('Starting time of event'),
                                    verbose_name=_('Start of Event'))
    """Required. Start datetime of the event/calender entry"""

    end_dt = models.DateTimeField(help_text=_('End time of event. Does not need to be same day.'),
                                  verbose_name=_('End of Event'))
    """Required. End datetime of the event/calender entry. Must be after start_dt else it raises a Validation Error"""

    location = models.CharField(max_length=255, blank=True, help_text=_('Location of event'),
                                verbose_name=_('Location'))
    """Optional location information"""

    omap = PlainLocationField(based_fields=['location'], zoom=7)

    problem_status = models.BooleanField(default=False, help_text=_('Whether there is a problem with the event'),
                                         verbose_name=_('Problem Status'))
    """Optional true/false indicating whether there is an issue with an event. It is important to both the ical files and the website"""

    problem_text = models.TextField(blank=True, null=True, help_text=_('Text that describes the problem. Keep brief.'),
                                    verbose_name=_('Problem Description'))
    """Optional text that describes what is wrong. Used in conjunction with problem_status. Requires problem_status = true to work at all."""


    content_panels = Page.content_panels + [
        FieldPanel('description'),
        ImageChooserPanel('image'),
        FieldPanel('start_dt'),
        FieldPanel('end_dt'),
        FieldPanel('location'),
        FieldPanel('omap'),
        MultiFieldPanel([
            InlinePanel("event_categories", label=_("Categories"))
        ]),
        MultiFieldPanel([
            InlinePanel("event_instruments", label=_("Instruments"))
        ]),
    ]

    settings_panels = Page.settings_panels + [
        FieldPanel('problem_status'),
        FieldPanel('problem_text')
    ]

    parent_page_types = ["events.EventCalendar"]

    search_fields = Page.search_fields + [
        index.SearchField('description'),
        index.RelatedFields('categories', [
            index.SearchField('name'),
            index.SearchField('description'),
        ])
    ]

    class Meta:
        verbose_name = _("Calendar Event")
    
    def __str__(self):
        return self.title

    def clean(self):
        """
        Checks that the end date and time occurs after the start date and date
        """

        if self.start_dt > self.end_dt:
            raise ValidationError(_('Start date and time must be before end date and time'))

    def save(self, *args, **kwargs):
        """
        Overloads the save method of the Page model. It applies the default image to a calendar entry/event if it doesn't already have one.
        """
        if not self.image:
            self.image = EventCalendar.objects.live()[0].default_image

        super(EventCalPage, self).save()
    
       
    def get_context(self, request):
        context = super().get_context(request)


        # Add extra variables and return the updated context
        instrumentpresences = Instrument.objects.annotate(num_participations=Count('instrumentparticipations',filter=Q(instrumentparticipations__event_page=self.pk, instrumentparticipations__choice='OUI')))
        presences = Participation.objects.filter(event_page=self.pk, choice='OUI')
        absences = Participation.objects.filter(event_page=self.pk, choice='NON')
        questions = Participation.objects.filter(event_page=self.pk, choice='PEUT-ETRE')
        instrumentmaxs = InstrumentEventPage.objects.filter(page=self.pk)
        context['instrumentpresences'] = instrumentpresences
        context['presences'] = presences
        context['absences'] = absences
        context['questions'] = questions
        context['instrumentmaxs'] = instrumentmaxs
        return context  

    @property
    def get_categories(self) -> models.QuerySet:
        """
        Gets all the event categories.

        :return: Queryset containing the Categories objects
        """
        return Category.objects.filter(eventcalpage=self.pk)

    @property
    def get_status_text(self) -> Union[str, bool]:
        """
        Shows the status text of a calender entry/event

        :return: Str if the event is finished, or begun but not yet completed else false.
        """
        if self.end_dt < timezone.now():
            return _("Event Finished")
        elif self.problem_status:
            return self.problem_text
        elif self.start_dt < timezone.now() < self.end_dt:
            return _("Event has begun")  # TODO  better time format @ {self.start_dt.isoformat()}
        else:
            return False

    @route(r'^ical/$')
    def icalView(self, request: django.http.HttpRequest, *args, **kwargs: dict) -> django.http.HttpResponse:
        """
        Route that returns an ical file for a specific event.

        :param request: Django HttpRequest
        :param args: Normal request args
        :param kwargs: Normal request kwargs
        :return: ical file as part of HttpResponse with only the details of a specific event
        """
        cal = Calendar()
        cal.add('prodid', '-//Calendar Event event//mxm.dk//')
        cal.add('version', '2.0')
        event = Event()
        event.add('summary', self.title)
        event.add('dtstart', self.start_dt)
        event.add('dtend', self.end_dt)
        event.add('dtstamp', timezone.now())
        event.add('uid', str(self.pk))
        event['location'] = vText(self.location)
        cal.add_component(event)
        return HttpResponse(cal.to_ical(), content_type="text/calendar")
Esempio n. 25
0
class AbstractDocument(CollectionMember, index.Indexed, models.Model):
    title = models.CharField(max_length=255, verbose_name=_('title'))
    file = models.FileField(upload_to='documents', verbose_name=_('file'))
    created_at = models.DateTimeField(verbose_name=_('created at'),
                                      auto_now_add=True)
    uploaded_by_user = models.ForeignKey(settings.AUTH_USER_MODEL,
                                         verbose_name=_('uploaded by user'),
                                         null=True,
                                         blank=True,
                                         editable=False,
                                         on_delete=models.SET_NULL)

    tags = TaggableManager(help_text=None, blank=True, verbose_name=_('tags'))

    file_size = models.PositiveIntegerField(null=True, editable=False)
    # A SHA-1 hash of the file contents
    file_hash = models.CharField(max_length=40, blank=True, editable=False)

    objects = DocumentQuerySet.as_manager()

    search_fields = CollectionMember.search_fields + [
        index.SearchField('title', partial_match=True, boost=10),
        index.AutocompleteField('title'),
        index.FilterField('title'),
        index.RelatedFields('tags', [
            index.SearchField('name', partial_match=True, boost=10),
            index.AutocompleteField('name'),
        ]),
        index.FilterField('uploaded_by_user'),
    ]

    def clean(self):
        """
        Checks for WAGTAILDOCS_EXTENSIONS and validates the uploaded file
        based on allowed extensions that were specified.
        Warning : This doesn't always ensure that the uploaded file is valid
        as files can be renamed to have an extension no matter what
        data they contain.

        More info : https://docs.djangoproject.com/en/3.1/ref/validators/#fileextensionvalidator
        """
        allowed_extensions = getattr(settings, "WAGTAILDOCS_EXTENSIONS", None)
        if allowed_extensions:
            validate = FileExtensionValidator(allowed_extensions)
            validate(self.file)

    def is_stored_locally(self):
        """
        Returns True if the image is hosted on the local filesystem
        """
        try:
            self.file.path

            return True
        except NotImplementedError:
            return False

    @contextmanager
    def open_file(self):
        # Open file if it is closed
        close_file = False
        f = self.file

        if f.closed:
            # Reopen the file
            if self.is_stored_locally():
                f.open('rb')
            else:
                # Some external storage backends don't allow reopening
                # the file. Get a fresh file instance. #1397
                storage = self._meta.get_field('file').storage
                f = storage.open(f.name, 'rb')

            close_file = True

        # Seek to beginning
        f.seek(0)

        try:
            yield f
        finally:
            if close_file:
                f.close()

    def get_file_size(self):
        if self.file_size is None:
            try:
                self.file_size = self.file.size
            except Exception:
                # File doesn't exist
                return

            self.save(update_fields=['file_size'])

        return self.file_size

    def _set_file_hash(self, file_contents):
        self.file_hash = hashlib.sha1(file_contents).hexdigest()

    def get_file_hash(self):
        if self.file_hash == '':
            with self.open_file() as f:
                self._set_file_hash(f.read())

            self.save(update_fields=['file_hash'])

        return self.file_hash

    def __str__(self):
        return self.title

    @property
    def filename(self):
        return os.path.basename(self.file.name)

    @property
    def file_extension(self):
        return os.path.splitext(self.filename)[1][1:]

    @property
    def url(self):
        if getattr(settings, 'WAGTAILDOCS_SERVE_METHOD', None) == 'direct':
            try:
                return self.file.url
            except NotImplementedError:
                # backend does not provide a url, so fall back on the serve view
                pass

        return reverse('wagtaildocs_serve', args=[self.id, self.filename])

    def get_usage(self):
        return get_object_usage(self)

    @property
    def usage_url(self):
        return reverse('wagtaildocs:document_usage', args=(self.id, ))

    def is_editable_by_user(self, user):
        from wagtail.documents.permissions import permission_policy
        return permission_policy.user_has_permission_for_instance(
            user, 'change', self)

    @property
    def content_type(self):
        content_types_lookup = getattr(settings, 'WAGTAILDOCS_CONTENT_TYPES',
                                       {})
        return (content_types_lookup.get(self.file_extension.lower())
                or guess_type(self.filename)[0] or 'application/octet-stream')

    @property
    def content_disposition(self):
        inline_content_types = getattr(settings,
                                       'WAGTAILDOCS_INLINE_CONTENT_TYPES',
                                       ['application/pdf'])
        if self.content_type in inline_content_types:
            return 'inline'
        else:
            return "attachment; filename={0}; filename*=UTF-8''{0}".format(
                urllib.parse.quote(self.filename))

    class Meta:
        abstract = True
        verbose_name = _('document')
        verbose_name_plural = _('documents')
Esempio n. 26
0
class AbstractImage(ImageFileMixin, CollectionMember, index.Indexed,
                    models.Model):
    title = models.CharField(max_length=255, verbose_name=_('title'))
    file = models.ImageField(verbose_name=_('file'),
                             upload_to=get_upload_to,
                             width_field='width',
                             height_field='height')
    width = models.IntegerField(verbose_name=_('width'), editable=False)
    height = models.IntegerField(verbose_name=_('height'), editable=False)
    created_at = models.DateTimeField(verbose_name=_('created at'),
                                      auto_now_add=True,
                                      db_index=True)
    uploaded_by_user = models.ForeignKey(settings.AUTH_USER_MODEL,
                                         verbose_name=_('uploaded by user'),
                                         null=True,
                                         blank=True,
                                         editable=False,
                                         on_delete=models.SET_NULL)

    tags = TaggableManager(help_text=None, blank=True, verbose_name=_('tags'))

    focal_point_x = models.PositiveIntegerField(null=True, blank=True)
    focal_point_y = models.PositiveIntegerField(null=True, blank=True)
    focal_point_width = models.PositiveIntegerField(null=True, blank=True)
    focal_point_height = models.PositiveIntegerField(null=True, blank=True)

    file_size = models.PositiveIntegerField(null=True, editable=False)
    # A SHA-1 hash of the file contents
    file_hash = models.CharField(max_length=40, blank=True, editable=False)

    objects = ImageQuerySet.as_manager()

    def _set_file_hash(self, file_contents):
        self.file_hash = hashlib.sha1(file_contents).hexdigest()

    def get_file_hash(self):
        if self.file_hash == '':
            with self.open_file() as f:
                self._set_file_hash(f.read())

            self.save(update_fields=['file_hash'])

        return self.file_hash

    def get_upload_to(self, filename):
        folder_name = 'original_images'
        filename = self.file.field.storage.get_valid_name(filename)

        # do a unidecode in the filename and then
        # replace non-ascii characters in filename with _ , to sidestep issues with filesystem encoding
        filename = "".join(
            (i if ord(i) < 128 else '_') for i in string_to_ascii(filename))

        # Truncate filename so it fits in the 100 character limit
        # https://code.djangoproject.com/ticket/9893
        full_path = os.path.join(folder_name, filename)
        if len(full_path) >= 95:
            chars_to_trim = len(full_path) - 94
            prefix, extension = os.path.splitext(filename)
            filename = prefix[:-chars_to_trim] + extension
            full_path = os.path.join(folder_name, filename)

        return full_path

    def get_usage(self):
        return get_object_usage(self)

    @property
    def usage_url(self):
        return reverse('wagtailimages:image_usage', args=(self.id, ))

    search_fields = CollectionMember.search_fields + [
        index.SearchField('title', partial_match=True, boost=10),
        index.AutocompleteField('title'),
        index.FilterField('title'),
        index.RelatedFields('tags', [
            index.SearchField('name', partial_match=True, boost=10),
            index.AutocompleteField('name'),
        ]),
        index.FilterField('uploaded_by_user'),
    ]

    def __str__(self):
        return self.title

    def get_rect(self):
        return Rect(0, 0, self.width, self.height)

    def get_focal_point(self):
        if self.focal_point_x is not None and \
           self.focal_point_y is not None and \
           self.focal_point_width is not None and \
           self.focal_point_height is not None:
            return Rect.from_point(
                self.focal_point_x,
                self.focal_point_y,
                self.focal_point_width,
                self.focal_point_height,
            )

    def has_focal_point(self):
        return self.get_focal_point() is not None

    def set_focal_point(self, rect):
        if rect is not None:
            self.focal_point_x = rect.centroid_x
            self.focal_point_y = rect.centroid_y
            self.focal_point_width = rect.width
            self.focal_point_height = rect.height
        else:
            self.focal_point_x = None
            self.focal_point_y = None
            self.focal_point_width = None
            self.focal_point_height = None

    def get_suggested_focal_point(self):
        with self.get_willow_image() as willow:
            faces = willow.detect_faces()

            if faces:
                # Create a bounding box around all faces
                left = min(face[0] for face in faces)
                top = min(face[1] for face in faces)
                right = max(face[2] for face in faces)
                bottom = max(face[3] for face in faces)
                focal_point = Rect(left, top, right, bottom)
            else:
                features = willow.detect_features()
                if features:
                    # Create a bounding box around all features
                    left = min(feature[0] for feature in features)
                    top = min(feature[1] for feature in features)
                    right = max(feature[0] for feature in features)
                    bottom = max(feature[1] for feature in features)
                    focal_point = Rect(left, top, right, bottom)
                else:
                    return None

        # Add 20% to width and height and give it a minimum size
        x, y = focal_point.centroid
        width, height = focal_point.size

        width *= 1.20
        height *= 1.20

        width = max(width, 100)
        height = max(height, 100)

        return Rect.from_point(x, y, width, height)

    @classmethod
    def get_rendition_model(cls):
        """ Get the Rendition model for this Image model """
        return cls.renditions.rel.related_model

    def get_rendition(self, filter):
        if isinstance(filter, str):
            filter = Filter(spec=filter)

        cache_key = filter.get_cache_key(self)
        Rendition = self.get_rendition_model()

        try:
            rendition_caching = True
            cache = caches['renditions']
            rendition_cache_key = Rendition.construct_cache_key(
                self.id, cache_key, filter.spec)
            cached_rendition = cache.get(rendition_cache_key)
            if cached_rendition:
                return cached_rendition
        except InvalidCacheBackendError:
            rendition_caching = False

        try:
            rendition = self.renditions.get(
                filter_spec=filter.spec,
                focal_point_key=cache_key,
            )
        except Rendition.DoesNotExist:
            # Generate the rendition image
            try:
                logger.debug(
                    "Generating '%s' rendition for image %d",
                    filter.spec,
                    self.pk,
                )

                start_time = time.time()
                generated_image = filter.run(self, BytesIO())

                logger.debug("Generated '%s' rendition for image %d in %.1fms",
                             filter.spec, self.pk,
                             (time.time() - start_time) * 1000)
            except:  # noqa:B901,E722
                logger.debug("Failed to generate '%s' rendition for image %d",
                             filter.spec, self.pk)
                raise

            # Generate filename
            input_filename = os.path.basename(self.file.name)
            input_filename_without_extension, input_extension = os.path.splitext(
                input_filename)

            # A mapping of image formats to extensions
            FORMAT_EXTENSIONS = {
                'jpeg': '.jpg',
                'png': '.png',
                'gif': '.gif',
                'webp': '.webp',
            }

            output_extension = filter.spec.replace(
                '|', '.') + FORMAT_EXTENSIONS[generated_image.format_name]
            if cache_key:
                output_extension = cache_key + '.' + output_extension

            # Truncate filename to prevent it going over 60 chars
            output_filename_without_extension = input_filename_without_extension[:(
                59 - len(output_extension))]
            output_filename = output_filename_without_extension + '.' + output_extension

            rendition, created = self.renditions.get_or_create(
                filter_spec=filter.spec,
                focal_point_key=cache_key,
                defaults={
                    'file': File(generated_image.f, name=output_filename)
                })

        if rendition_caching:
            cache.set(rendition_cache_key, rendition)

        return rendition

    def is_portrait(self):
        return (self.width < self.height)

    def is_landscape(self):
        return (self.height < self.width)

    @property
    def filename(self):
        return os.path.basename(self.file.name)

    @property
    def default_alt_text(self):
        # by default the alt text field (used in rich text insertion) is populated
        # from the title. Subclasses might provide a separate alt field, and
        # override this
        return self.title

    def is_editable_by_user(self, user):
        from wagtail.images.permissions import permission_policy
        return permission_policy.user_has_permission_for_instance(
            user, 'change', self)

    class Meta:
        abstract = True
Esempio n. 27
0
    class ProductProxy(index.Indexed, get_model("catalogue", "Product")):
        def popularity(self):
            months_to_run = settings.OSCAR_SEARCH.get(
                "MONTHS_TO_RUN_ANALYTICS", 3)
            orders_above_date = timezone.now() - relativedelta(
                months=months_to_run)

            Line = get_model("order", "Line")

            return Line.objects.filter(
                product=self,
                order__date_placed__gte=orders_above_date).count()

        def price(self):
            selector = get_class("partner.strategy", "Selector")
            strategy = selector().strategy()
            if self.is_parent:
                return strategy.fetch_for_parent(self).price.incl_tax

            return strategy.fetch_for_product(self).price.incl_tax

        def string_attrs(self):
            return [str(a.value_as_text) for a in self.attribute_values.all()]

        def attrs(self):
            values = self.attribute_values.all().select_related("attribute")
            result = {}
            for value in values:
                at = value.attribute
                if at.type == at.OPTION:
                    result[value.attribute.code] = value.value.option
                elif at.type == at.MULTI_OPTION:
                    result[value.attribute.code] = [
                        a.option for a in value.value
                    ]
                elif es_type_for_product_attribute(at) != "text":
                    result[value.attribute.code] = value.value

            if self.is_parent:
                for child in ProductProxy.objects.filter(parent=self):
                    result = merge_dicts(result, child.attrs())

            return result

        def object(self):
            "Mimic a haystack search result"
            return self

        def category_id(self):
            return self.categories.values_list("id", flat=True)

        def category_name(self):
            return list(self.categories.values_list("name", flat=True))

        @classmethod
        def get_search_fields(cls):  # hook extra_product_fields for overriding
            return process_product_fields(super().get_search_fields())

        search_fields = [
            index.FilterField("id"),
            index.SearchField("title", partial_match=True, boost=2),
            index.AutocompleteField("title"),
            index.AutocompleteField("upc", es_extra={"analyzer": "keyword"}),
            index.FilterField("upc"),
            index.SearchField("upc", boost=3, es_extra={"analyzer":
                                                        "keyword"}),
            index.SearchField("description", partial_match=True),
            index.FilterField("popularity"),
            index.FilterField("price", es_extra={"type": "double"}),
            index.FilterField("category_id"),
            index.SearchField("category_name", partial_match=True),
            index.AutocompleteField("category_name"),
            index.RelatedFields(
                "categories",
                [
                    index.SearchField("description", partial_match=True),
                    index.SearchField("slug"),
                    index.SearchField("full_name"),
                    index.SearchField("get_absolute_url"),
                ],
            ),
            index.RelatedFields(
                "stockrecords",
                [
                    index.FilterField("price_currency"),
                    index.SearchField("partner_sku"),
                    index.SearchField("price_excl_tax"),
                    index.FilterField("partner"),
                    index.FilterField("num_in_stock"),
                ],
            ),
            index.FilterField("parent_id"),
            index.FilterField("structure"),
            index.FilterField("is_standalone"),
            index.FilterField("slug"),
            index.FilterField("rating"),
            index.FilterField("date_created"),
            index.FilterField("date_updated"),
            index.SearchField("string_attrs"),
            index.FilterField("attrs",
                              es_extra=product_attributes_es_config()),
        ]

        class Meta:
            proxy = True
Esempio n. 28
0
class AbstractFlexPage(Page):

    # commented image_block because of https://luminousweb.atlassian.net/browse/YOUG-16
    # for now it is not necessary and creates misunderstanding with image_content_block

    # commented accordion_block,  person_block & RTE because of https://luminousweb.atlassian.net/browse/YOUG-234
    # as a duplicate module to accordion_list_block and not used person_block and Full RTE

    content = StreamField(
        [
            # ('accordion_block', blocks.AccordionBlock()),
            ('accordion_list_block', blocks.AccordionListBlock()),
            ('advisor_analyst_block', blocks.AdvisorsBlock()),
            ('turquoise_block', blocks.TurquoiseListBlocks()),
            ('callout_module_block', blocks.CalloutsModuleBlock()),
            ('three_column_callout_module_block',
             blocks.ThreeColumnCalloutsModuleBlock()),
            ('contact_block', blocks.ContactInfoBlock()),
            ('download_block', blocks.DownloadBlock()),
            ('icon_block', blocks.IconsListBlock()),
            ('iframe_block', blocks.IframeBlock()),
            ('image_content_block', blocks.ImageContentBlock()),
            ('image_full_bleed_block', blocks.ImageFullBleedBlock()),
            # ('image_person_block', blocks.ImagePersonBlock()),
            ('image_people_block', blocks.ImagePeopleBlock()),
            ('newsfeed_block', blocks.NewsFeedModuleBlock()),
            ('number_block', blocks.NumberingListBlock()),
            ('event_module_block', blocks.EventListBlock()),
            ('form_module_block', blocks.FormModuleBlock()),
            ("full_rte_editor", blocks.RichTextBlockFull()),
            ('hero_banner_block', blocks.HeroBannerBlock()),
            ('padding_block', blocks.PaddingBlock()),
            ("quotation_block", blocks.QuotationBlock()),
            ('table_block', blocks.TableModuleBlock()),
            ('timeline_block', blocks.TimeLineModuleBlock()),
            ("title_and_text", blocks.TitleAndTextBlock()),
            ('two_columns_block', blocks.TwoColumnModuleBlock()),
            ("video_block", blocks.VideoBlock()),
            ('widget_block', blocks.WidgetChooserBlock()),
            ('right_widget_block', blocks.RightWidgetChooserBlock()),
            # ("full_rich_text", blocks.RichTextBlock()),
            ("rss_block", blocks.RssBlock()),
            ("rss_news_block", blocks.RssNewsBlock()),
            ("back_page_block", blocks.BackPageLinkBlock()),
            ("Logos_block", blocks.LogosListBlock()),
            # ('image_block', blocks.ImageBlock()),
        ],
        null=True,
        blank=True,
    )

    subtitle = models.CharField(max_length=100, null=True, blank=True)
    is_timestamp_displayed = models.BooleanField(
        default=False,
        blank=True,
        null=True,
        verbose_name='Display published date')

    content_panels = Page.content_panels + [
        FieldPanel("subtitle"),
        FieldPanel("is_timestamp_displayed"),
        StreamFieldPanel("content")
    ]

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

    class Meta:
        abstract = True
Esempio n. 29
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')
    ]

    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
Esempio n. 30
0
class FormPage(AbstractEmailForm):
    landing_page_template = 'pages/form_page_confirmation.html'
    subpage_types = []

    top_content = StreamField(BASE_BLOCKS + COLUMNS_BLOCKS, blank=True)
    confirmation_text = models.TextField(
        default='Your registration was submitted')
    send_confirmation_email = models.BooleanField(
        default=False, verbose_name='Send confirmation email?')
    confirmation_email_subject = models.CharField(
        default='ISIMIP Form submission confirmation.',
        max_length=500,
        verbose_name='Email subject',
        null=True,
        blank=True)
    confirmation_email_text = models.TextField(
        default=
        'The form was submitted successfully. We will get back to you soon.',
        verbose_name='Email text',
        null=True,
        blank=True)
    bottom_content = StreamField(BASE_BLOCKS + COLUMNS_BLOCKS, blank=True)

    button_name = models.CharField(max_length=500,
                                   verbose_name='Button name',
                                   default='Submit')

    content_panels = AbstractEmailForm.content_panels + [
        StreamFieldPanel('top_content'),
        StreamFieldPanel('bottom_content')
    ]
    form_content_panels = [
        InlinePanel('form_fields', label="Form fields"),
        FieldPanel('button_name'),
        FieldPanel('confirmation_text', classname="full"),
        MultiFieldPanel([
            FieldPanel('send_confirmation_email'),
            FieldPanel('confirmation_email_subject'),
            FieldPanel('confirmation_email_text', classname="full"),
        ], "Confirmation email"),
        MultiFieldPanel([
            FieldPanel('to_address', classname="full"),
            FieldPanel('from_address', classname="full"),
            FieldPanel('subject', classname="full"),
        ], "Email"),
    ]
    search_fields = Page.search_fields + [
        index.SearchField('top_content'),
        index.SearchField('bottom_content'),
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
        ObjectList(form_content_panels, heading='Form Builder'),
        ObjectList(AbstractEmailForm.promote_panels, heading='Promote'),
        ObjectList(AbstractEmailForm.settings_panels,
                   heading='Settings',
                   classname="settings"),
    ])

    def get_context(self, request, *args, **kwargs):
        context = super().get_context(request, *args, **kwargs)
        message = {'tags': 'success', 'text': self.confirmation_text}
        context['confirmation_messages'] = [message]
        return context

    def serve(self, request, *args, **kwargs):
        context = self.get_context(request)
        if request.method == 'POST':
            form = self.get_form(request.POST, page=self, user=request.user)

            if form.is_valid():
                self.process_form_submission(form)

                # render the landing_page
                # TODO: It is much better to redirect to it
                return render(request, self.get_landing_page_template(request),
                              self.get_context(request))
            else:
                context[
                    'form_error'] = 'One of the fields below has not been filled out correctly. Please correct and resubmit.'
        else:
            form = self.get_form(page=self, user=request.user)

        context['form'] = form
        return render(request, self.get_template(request), context)

    def process_form_submission(self, form):
        self.get_submission_class().objects.create(form_data=json.dumps(
            form.cleaned_data, cls=DjangoJSONEncoder),
                                                   page=self)
        if self.to_address:
            self.send_mail(form)
        if self.send_confirmation_email:
            # quick hack sending a confirmation email to the user
            confirmation_email_address = None
            # check for confirmation email address and filter headings
            for field in form:
                if isinstance(field.field.widget, EmailInput):
                    confirmation_email_address = field.value()
                    break
            if confirmation_email_address:
                extra_content = [
                    '',
                ]
                for field in form:
                    value = field.value()
                    if isinstance(value, list):
                        value = ', '.join(value)
                    extra_content.append('{}: {}'.format(field.label, value))
                extra_content = '\n'.join(extra_content)
                send_mail(
                    self.confirmation_email_subject,
                    self.confirmation_email_text + extra_content,
                    [
                        confirmation_email_address,
                    ],
                    self.from_address,
                )