Exemple #1
0
class AbstractBasePage(Page):
    """A base for all page types."""

    heading = models.CharField(max_length=255, null=True, blank=True)
    excerpt = models.TextField(null=True, blank=True)

    translation_fields = ["heading", "excerpt"]
    search_fields = [
        FilterField('live'),
        SearchField('heading'),
        SearchField('excerpt'),
    ]

    class Meta(object):
        """Meta data for the class."""

        abstract = True

    def clean(self):
        """Override clean to remove trailing dashes from slugs with whitespaces."""
        for lang in ['en', 'fr', 'es', 'pt']:
            slug_field = 'slug_{}'.format(lang)
            slug = getattr(self, slug_field)
            if slug:
                slug_clean = re.sub(r'[^\w\s-]', '', slug).strip("-").lower()
                setattr(self, slug_field, slug_clean)
        super(AbstractBasePage, self).clean()
Exemple #2
0
class StandardPage(Page):
    introductory_headline = models.TextField(help_text='Introduce the topic of this page in 1-3 sentences.', blank=True)
    overview = RichTextField(help_text='Give a general overview of what this topic is about. Limit yourself to 3 paragraphs.', blank=True)
    body = StreamField(ContentStreamBlock())
    listing_abstract = models.TextField(help_text='Give a brief blurb (about 1 sentence) of what this topic is about. It will appear on other pages that refer to this one.', blank=True)

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

    search_fields = Page.search_fields + [
        SearchField('introductory_headline'),
        SearchField('overview'),
        SearchField('listing_abstract'),
        SearchField('body')
    ]

    content_panels = Page.content_panels + [
        FieldPanel('listing_abstract'),
        FieldPanel('introductory_headline'),
        FieldPanel('overview'),
        StreamFieldPanel('body'),
    ]

    parent_page_types = [
        'home.HomePage',
        'standard.StandardPage',
        'standard.StandardIndexPage',
        'standard.SectionPage'
    ]

    subpage_types = [
        'standard.StandardPage',
        'standard.SectionPage'
    ]
Exemple #3
0
class Article(Indexed, Model):
    title = CharField(max_length=200)
    body = RichTextField()

    search_fields = [
        SearchField("title", partial_match=True, boost=2),
        SearchField("body"),
    ]
Exemple #4
0
class AbstractBasePage(Page):
    """A base for all page types."""

    heading = models.CharField(max_length=255, null=True, blank=True)
    excerpt = models.TextField(null=True, blank=True)
    social_media_image = models.ForeignKey(
        'wagtailimages.Image', null=True, blank=True,
        on_delete=models.SET_NULL, related_name='+',
        help_text='This image will be used as the image for social media sharing cards.'
    )

    translation_fields = [
        "heading",
        "excerpt"
    ]
    search_fields = [
        FilterField('live'),
        SearchField('heading'),
        SearchField('excerpt'),
    ]

    promote_panels = Page.promote_panels + [
        ImageChooserPanel('social_media_image'),
    ]

    class Meta(object):
        """Meta data for the class."""

        abstract = True

    def get_context(self, request, *args, **kwargs):
        """Override get_context method to check for active language length."""
        context = super(AbstractBasePage, self).get_context(request, *args, **kwargs)
        context['has_multilanguage_support'] = len(settings.ACTIVE_LANGUAGES)
        context['social_twitter_handle'] = settings.TWITTER_HANDLE
        context['social_youtube_url'] = settings.YOUTUBE_CHANNEL_URL
        return context

    def clean(self):
        """Override clean to remove trailing dashes from slugs with whitespaces."""
        for lang in tuple(x[0] for x in settings.LANGUAGES):
            slug_field = 'slug_{}'.format(lang)
            slug = getattr(self, slug_field)
            if slug:
                slug_stripped = re.sub(r'[^\w\s-]', '', slug).strip("-")
                slug_valid = slugify(slug_stripped)
                setattr(self, slug_field, slug_valid)
        super(AbstractBasePage, self).clean()

    @property
    def social_share_image_url(self):
        """Return a default social media image for any page."""
        if self.social_media_image:
            return self.social_media_image.get_rendition('width-320|jpegquality-60').url
        if hasattr(self, 'feed_image'):
            if self.feed_image:
                return self.feed_image.get_rendition('width-320|jpegquality-60').url
        return static(settings.DEFAULT_SHARE_IMAGE_URL)
Exemple #5
0
class Location(Page):
    """
    Geocoded location details.
    """

    region = models.ForeignKey(Region,
                               related_name="locations",
                               null=True,
                               on_delete=models.SET_NULL)
    formatted_address = models.CharField("Full Address",
                                         max_length=255,
                                         blank=True,
                                         null=True)
    lat_lng = models.CharField("Latitude/Longitude",
                               max_length=255,
                               blank=True,
                               null=True)

    @cached_property
    def point(self):
        return geosgeometry_str_to_struct(self.lat_lng)

    @property
    def lat(self):
        return self.point["y"]

    @property
    def lng(self):
        return self.point["x"]

    content_panels = Page.content_panels + [
        FieldPanel("region"),
        MultiFieldPanel(
            [
                FieldPanel("formatted_address"),
                GeoPanel("lat_lng", address_field="formatted_address"),
            ],
            "Geocoded Address",
        ),
    ]

    search_fields = Page.search_fields + [
        SearchField("region", partial_match=True),
        SearchField("formatted_address", partial_match=True),
    ]

    subpage_types = ["Meeting"]

    class Meta:
        indexes = [
            models.Index(fields=["region"]),
            models.Index(fields=["formatted_address"]),
        ]

    def __str__(self):
        return "{0}: {1}".format(self.region, self.title)
Exemple #6
0
    def test_search_searchable_fields(self):
        # Find root page
        root_page = Page.objects.get(id=2)

        # Create a page
        root_page.add_child(instance=SimplePage(
            title="Hi there!", slug='hello-world', content="good morning",
            live=True,
            has_unpublished_changes=False,
        ))

        # Confirm the slug is not being searched
        response = self.get({'q': "hello"})
        self.assertNotContains(response, "There is one matching page")
        search_fields = Page.search_fields

        # Add slug to the search_fields
        Page.search_fields = Page.search_fields + [SearchField('slug', partial_match=True)]

        # Confirm the slug is being searched
        response = self.get({'q': "hello"})
        self.assertContains(response, "There is one matching page")

        # Reset the search fields
        Page.search_fields = search_fields
class StandardIndexPage(Page):
    title_suffix = models.CharField(
        help_text=
        "Additional text to display after the page title e.g. '(Version 3.0)",
        max_length=255,
        blank=True)
    listing_abstract = models.TextField(
        help_text=
        'Give a brief blurb (about 1 sentence) of what this topic is about. It will appear on other pages that refer to this one.',
        blank=True)
    introductory_headline = models.TextField(
        help_text='Introduce the topic of this page in 1-3 sentences.',
        blank=True)
    body = StreamField(
        LimitedStreamBlock(
            required=
            False  # https://github.com/wagtail/wagtail/issues/4306#issuecomment-384099847
        ),
        blank=True)

    @property
    def children(self):
        return self.get_children().specific().live()

    search_fields = Page.search_fields + [SearchField('introductory_headline')]

    content_panels = Page.content_panels + [
        FieldPanel('title_suffix'),
        FieldPanel('listing_abstract'),
        FieldPanel('introductory_headline'),
        StreamFieldPanel('body')
    ]

    parent_page_types = ['home.HomePage']
class CustomIndexPage(Page):
    title_suffix = models.CharField(
        help_text=
        "Additional text to display after the page title e.g. '(Version 3.0)",
        max_length=255,
        blank=True)
    listing_abstract = models.TextField(
        help_text=
        'Give a brief blurb (about 1 sentence) of what this topic is about. It will appear on other pages that refer to this one.',
        blank=True)
    introductory_headline = models.TextField(
        help_text='Introduce the topic of this page in 1-3 sentences.',
        blank=True)
    body = StreamField(ContentStreamBlock(), blank=True)
    custom_sidebar_link = StreamField(CustomSidebarLinkBlock(required=False),
                                      blank=True)

    @property
    def children(self):
        return self.get_children().specific().live()

    search_fields = Page.search_fields + [SearchField('introductory_headline')]

    content_panels = Page.content_panels + [
        FieldPanel('title_suffix'),
        FieldPanel('listing_abstract'),
        FieldPanel('introductory_headline'),
        StreamFieldPanel('custom_sidebar_link'),
        StreamFieldPanel('body')
    ]

    class Meta:
        verbose_name = "Custom Index Page with Streamfield"
Exemple #9
0
class AbstractContentPage(AbstractBasePage):
    """A base for the basic model blocks of all content type pages."""

    content_editor = StreamField(IATIStreamBlock(required=False), null=True, blank=True)

    translation_fields = AbstractBasePage.translation_fields + ["content_editor"]
    search_fields = AbstractBasePage.search_fields + [SearchField('content_editor')]

    class Meta(object):
        """Meta data for the class."""

        abstract = True
Exemple #10
0
class PostPage(Page):
    subtitle = models.CharField(
        verbose_name=_('Subtítulo'),
        help_text=_('O subtítulo da página para os mecanismos de busca.'),
        max_length=255,
        blank=True)
    body = RichTextField(verbose_name=_('Conteúdo'))
    date = models.DateTimeField(
        verbose_name=_('Data de publicação'),
        help_text=_('Data de publicação que será exibida no site.'),
        default=now,
    )
    image = models.ForeignKey(
        'wagtailimages.Image',
        models.SET_NULL,
        '+',
        verbose_name=_('Imagem de destaque'),
        help_text=_('Imagem que servirá de capa para o post.'),
        blank=False,
        null=True,
    )

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

    content_panels = [
        MultiFieldPanel(Page.content_panels + [
            FieldPanel('subtitle'),
            ImageChooserPanel('image'),
        ],
                        heading=_('Dados básicos')),
        FieldPanel('body', classname='full'),
    ]

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

    parent_page_types = ['blog.IndexPage']
    subpage_types = []

    def get_author(self):
        return self.owner.get_full_name

    # TODO: include function to return post excerpt with default length of 10

    class Meta:
        verbose_name = _("post")
Exemple #11
0
class BasicPage(Page):
    body = RichTextField(verbose_name=_('Conteúdo'),
                         help_text=_('Conteúdo que será publicado.'))

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

    content_panels = Page.content_panels + [
        FieldPanel('body', classname='full'),
    ]

    parent_page_types = ['blog.IndexPage']
    subpage_types = []

    class Meta:
        verbose_name = _("página")
Exemple #12
0
class AbstractGithubPage(DefaultPageHeaderImageMixin, AbstractContentPage):
    """A model for abstract reference pages build by Github."""

    class Meta(object):
        """Meta data for the class."""

        abstract = True

    is_creatable = False
    edit_handler = TabbedInterface([])

    parent_page_types = []
    subpage_types = []

    meta_order = models.IntegerField(
        default=0
    )

    ssot_path = models.TextField(
        null=True,
        blank=True,
        help_text='Folder path of SSOT object'
    )

    tag = models.CharField(
        max_length=255,
        help_text='Associated git release tag',
    )

    ssot_root_slug = models.CharField(
        null=True,
        blank=True,
        max_length=255,
        help_text='Slug of the highest parent folder.'
    )

    data = models.TextField(
        null=True,
        blank=True,
        help_text='HTML data for the page'
    )

    publish_date = models.TextField(
        null=True,
        blank=True
    )

    translation_fields = AbstractContentPage.translation_fields + ["data"]
    search_fields = AbstractContentPage.search_fields + [
        SearchField('data'),
    ]

    @cached_property
    def parent_path(self):
        """Return ssot_path of parent object."""
        return "/".join(self.ssot_path.split("/")[:-1])

    @cached_property
    def name(self):
        """Return the last item in the ssot_path as a name."""
        return self.ssot_path.split("/")[-1]

    @cached_property
    def version(self):
        """Return the first item in the ssot_path as a version."""
        return self.ssot_path.split("/")[0]

    def first_paragraph(self):
        """Extract first paragraph snippet."""
        soup = BeautifulSoup(self.data, 'html.parser')
        para = soup.find("p")
        if para:
            first_paragraph = para.text.replace("¶", "")
        else:
            first_paragraph = "Read more about {}.".format(self.title)
            return first_paragraph
        fp_split = first_paragraph.split(" ")
        fp_trunc = ""
        if len(fp_split) >= 50:
            fp_trunc = "..."
        first_paragraph = " ".join(first_paragraph.split(" ")[:50]) + fp_trunc
        return first_paragraph

    def save(self, *args, **kwargs):
        """Overwrite save to automatically update title."""
        soup = BeautifulSoup(self.data, 'html.parser')
        meta_order = soup.find("meta", {"name": "order"})
        if meta_order:
            try:
                self.meta_order = int(meta_order["content"])
            except ValueError:
                self.meta_order = 0
        meta_date = soup.find("meta", {"name": "date"})
        if meta_date:
            self.publish_date = meta_date["content"]
        else:
            self.publish_date = date(datetime.now(), "F d, Y")
        meta_title = soup.find("meta", {"name": "title"})
        if meta_title:
            self.title = meta_title["content"].replace("¶", "")
            self.heading = meta_title["content"].replace("¶", "")
        else:
            title = soup.find("h1")
            if title:
                self.title = title.text.replace("¶", "")
                self.heading = title.text.replace("¶", "")
        meta_description = soup.find("meta", {"name": "description"})
        if meta_description:
            self.excerpt = meta_description["content"].replace("¶", "")
        else:
            self.excerpt = self.first_paragraph()
        meta_guidance_type = soup.find("meta", {"name": "guidance_type"})
        if meta_guidance_type:
            super(AbstractGithubPage, self).save(*args, **kwargs)
            guidance_types = meta_guidance_type["content"].split(",")
            for guidance_type in guidance_types:
                StandardGuidanceTypes.objects.create(page=self, guidance_type=guidance_type.strip())

        self.ssot_root_slug = self.ssot_path.split("/")[0]
        super(AbstractGithubPage, self).save(*args, **kwargs)
class Location(Page):
    """
    Geocoded location details.
    """

    region = models.ForeignKey(
        Region,
        related_name="locations",
        on_delete=models.PROTECT,
        limit_choices_to={"parent__isnull": False},
    )
    formatted_address = models.CharField("Full Address",
                                         max_length=255,
                                         blank=True,
                                         null=True)
    lat_lng = models.CharField("Latitude/Longitude",
                               max_length=255,
                               blank=True,
                               null=True)
    postal_code = models.CharField("Postal Code", max_length=12, blank=True)
    details = models.TextField(
        null=True,
        blank=True,
        help_text=
        "Details specific to the location, not the meeting. For example, "
        "'Located in shopping center behind the bank.'",
    )

    @cached_property
    def point(self):
        return geosgeometry_str_to_struct(self.lat_lng)

    @property
    def lat(self):
        return self.point["y"]

    @property
    def lng(self):
        return self.point["x"]

    content_panels = Page.content_panels + [
        FieldPanel("region"),
        FieldPanel("postal_code"),
        FieldPanel("details"),
        MultiFieldPanel(
            [
                FieldPanel("formatted_address"),
                GeoPanel("lat_lng", address_field="formatted_address"),
            ],
            "Geocoded Address",
        ),
    ]

    search_fields = Page.search_fields + [
        SearchField("region", partial_match=True),
        SearchField("formatted_address", partial_match=True),
    ]

    subpage_types = ["Meeting"]

    class Meta:
        indexes = [
            models.Index(fields=["region"]),
            models.Index(fields=["formatted_address"]),
        ]

    def __str__(self):
        return "{0}: {1}".format(self.region, self.title)
class Meeting(Page):
    """
    Model for storing meeting data.
    """

    SUNDAY = 0
    MONDAY = 1
    TUESDAY = 2
    WEDNESDAY = 3
    THURSDAY = 4
    FRIDAY = 5
    SATURDAY = 6
    DAY_OF_WEEK = (
        (SUNDAY, "Sunday"),
        (MONDAY, "Monday"),
        (TUESDAY, "Tuesday"),
        (WEDNESDAY, "Wednesday"),
        (THURSDAY, "Thursday"),
        (FRIDAY, "Friday"),
        (SATURDAY, "Saturday"),
    )

    INACTIVE = 0
    ACTIVE = 1
    STATUS_CHOICES = (
        (ACTIVE, "Active"),
        (INACTIVE, "Inactive Permanently"),
    )

    group = models.ForeignKey(Group,
                              null=True,
                              blank=True,
                              on_delete=models.SET_NULL,
                              related_name="meetings")
    meeting_location = models.ForeignKey(Location,
                                         related_name="meetings",
                                         null=True,
                                         on_delete=models.SET_NULL)
    start_time = models.TimeField(null=True)
    end_time = models.TimeField(null=True)
    day_of_week = models.SmallIntegerField(default=0, choices=DAY_OF_WEEK)
    status = models.SmallIntegerField(default=1, choices=STATUS_CHOICES)
    details = models.TextField(
        null=True,
        blank=True,
        help_text="Additional details about the meeting.")
    area = models.CharField(max_length=10, blank=True)
    district = models.CharField(max_length=10, blank=True)
    types = ParentalManyToManyField(
        MeetingType,
        related_name="meetings",
        limit_choices_to={"intergroup_code__isnull": False},
    )
    conference_url = models.URLField(
        blank=True,
        verbose_name="Conference URL",
        default="",
        help_text="Example: " \
            "https://zoom.com/j/123456789?pwd=ExzUZMeT091pRU0Omc2QWjErUUUpxS1B",
    )
    conference_phone = models.CharField(
        max_length=255,
        blank=True,
        default="",
        validators=[ConferencePhoneValidator()],
        help_text="Enter a valid conference phone number. The three groups of " \
            "numbers in this example are a Zoom phone number, meeting code, and " \
            "password: +19294362866,,2151234215#,,#,,12341234#",
    )
    venmo = models.TextField(
        max_length=
        31,  # Venmo's max username length is 31 chars with the "@" prefix
        validators=[VenmoUsernameValidator()],
        blank=True,
        verbose_name="Venmo Account",
        default="",
        help_text="Example: @aa-mygroup",
    )
    paypal = models.TextField(
        blank=True,
        verbose_name="PayPal Username",
        default="",
        max_length=255,
        validators=[PayPalUsernameValidator(),
                    MinLengthValidator(3)],
        help_text="Example: aamygroup",
    )
    cashapp = models.TextField(
        max_length=
        31,  # Venmo's max username length is 31 chars with the "@" prefix
        validators=[CashAppUsernameValidator()],
        blank=True,
        verbose_name="CashApp Account",
        default="",
        help_text="Example: $aa-mygroup",
    )

    @property
    def day_sort_order(self):
        """
        Returns 0 for today's day of the week, up to 6 for yesterday's day of the
        week rather than Sunday - Saturday.
        """
        day_sort_order = self.day_of_week - datetime.datetime.today().weekday(
        ) - 1
        if day_sort_order < 0:
            day_sort_order += 7

        return day_sort_order

    content_panels = Page.content_panels + [
        FieldRowPanel([
            FieldPanel("day_of_week"),
            FieldPanel("start_time"),
            FieldPanel("end_time"),
        ], ),
        FieldRowPanel([
            FieldPanel("group"),
            FieldPanel("status"),
        ], ),
        FieldRowPanel([
            FieldPanel("area"),
            FieldPanel("district"),
        ], ),
        FieldRowPanel([
            FieldPanel("venmo"),
            FieldPanel("paypal"),
        ], ),
        FieldRowPanel([
            FieldPanel("conference_url"),
            FieldPanel("conference_phone"),
        ], ),
        FieldPanel("types", widget=CheckboxSelectMultiple),
        FieldPanel("details"),
    ]

    search_fields = Page.search_fields + [
        SearchField("group", partial_match=True),
        SearchField("meeting_location", partial_match=True),
    ]

    parent_page_types = ["Location"]

    class Meta:
        indexes = [
            models.Index(fields=["meeting_location"]),
            models.Index(fields=["day_of_week"]),
        ]

    def save(self, *args, **kwargs):
        """
        Associate the meeting with the Location parent and save. Then
        automatically assign the ONLINE meeting type if the field is
        populated.
        """
        # Associate with the parent meeting location, and save in case this
        # is new, before we change meeting types.
        self.meeting_location = Location.objects.get(pk=self.get_parent().id)

        # Automagically add or remove the online meeting type.
        online_meeting_type = MeetingType.objects.get(spec_code="ONL")
        if self.conference_url:
            self.types.add(online_meeting_type)
        else:
            self.types.remove(online_meeting_type)

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

    def __str__(self):
        return "{0} ({1}): {2} @ {3}".format(self.title, self.group,
                                             self.day_of_week, self.start_time)
Exemple #15
0
class Meeting(Page):
    """
    Model for storing meeting data.
    """

    SUNDAY = 0
    MONDAY = 1
    TUESDAY = 2
    WEDNESDAY = 3
    THURSDAY = 4
    FRIDAY = 5
    SATURDAY = 6
    DAY_OF_WEEK = (
        (SUNDAY, "Sunday"),
        (MONDAY, "Monday"),
        (TUESDAY, "Tuesday"),
        (WEDNESDAY, "Wednesday"),
        (THURSDAY, "Thursday"),
        (FRIDAY, "Friday"),
        (SATURDAY, "Saturday"),
    )

    INACTIVE = 0
    ACTIVE = 1
    STATUS_CHOICES = ((INACTIVE, "Inactive"), (ACTIVE, "Active"))

    group = models.ForeignKey(Group,
                              null=True,
                              blank=True,
                              on_delete=models.SET_NULL,
                              related_name="meetings")
    meeting_location = models.ForeignKey(Location,
                                         related_name="meetings",
                                         null=True,
                                         on_delete=models.SET_NULL)
    start_time = models.TimeField(null=True)
    end_time = models.TimeField(null=True)
    day_of_week = models.SmallIntegerField(default=0, choices=DAY_OF_WEEK)
    status = models.SmallIntegerField(default=1, choices=STATUS_CHOICES)
    meeting_details = models.TextField(
        null=True,
        blank=True,
        help_text="Additional details about the meeting.")
    location_details = models.TextField(
        null=True,
        blank=True,
        help_text=
        "How to find the meeting at the location, I.e.: 'In the basement',"
        " 'In the rear building.'",
    )
    types = ParentalManyToManyField(
        MeetingType,
        related_name="meetings",
        limit_choices_to={"intergroup_code__isnull": False},
    )

    @property
    def day_sort_order(self):
        """
        Returns 0 for today's day of the week, up to 6 for yesterday's day of the
        week rather than Sunday - Saturday.
        """
        day_sort_order = self.day_of_week - datetime.datetime.today().weekday(
        ) - 1
        if day_sort_order < 0:
            day_sort_order += 7

        return day_sort_order

    content_panels = Page.content_panels + [
        FieldPanel("group"),
        FieldRowPanel([
            FieldPanel("day_of_week"),
            FieldPanel("start_time"),
            FieldPanel("end_time"),
        ]),
        FieldPanel("types", widget=CheckboxSelectMultiple),
        FieldPanel("meeting_details"),
        FieldPanel("location_details"),
    ]

    search_fields = Page.search_fields + [
        SearchField("group", partial_match=True),
        SearchField("meeting_location", partial_match=True),
    ]

    parent_page_types = ["Location"]

    class Meta:
        indexes = [
            models.Index(fields=["meeting_location"]),
            models.Index(fields=["day_of_week"]),
        ]

    def save(self, *args, **kwargs):
        self.meeting_location = Location.objects.get(pk=self.get_parent().id)
        super(Meeting, self).save(*args, **kwargs)

    def __str__(self):
        return "{0} ({1}): {2} @ {3}".format(self.title, self.group,
                                             self.day_of_week, self.start_time)