diff --git a/district/models.py b/district/models.py index 3907bea03a9f262d18677fc71f24d189bc0c8324..459fcfd400f80a398c3dc9a5898ab226895d8db3 100644 --- a/district/models.py +++ b/district/models.py @@ -40,7 +40,6 @@ from maps_utils.const import ( from maps_utils.validation import validators as maps_validators from shared.blocks import ( DEFAULT_CONTENT_BLOCKS, - ButtonBlock, ButtonGroupBlock, FigureBlock, YouTubeVideoBlock, @@ -53,7 +52,7 @@ from shared.models import ( MenuMixin, SubpageMixin, ) -from shared.utils import make_promote_panels +from shared.utils import make_promote_panels, strip_all_html_tags, trim_to_length from tuning import admin_help from . import blocks @@ -752,6 +751,18 @@ class DistrictPersonPage( context["random_people"] = context["random_people"][:3] return context + def get_meta_image(self): + return self.search_image or self.profile_photo + + def get_meta_description(self): + if self.search_description: + return self.search_description + + if self.text: + return trim_to_length(strip_all_html_tags(self.text)) + + return None + class DistrictPeoplePage( ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, Page @@ -853,6 +864,11 @@ class DistrictPostElectionStrategyPage( class Meta: verbose_name = "Povolebnà strategie" + def get_meta_description(self): + if self.search_description: + return self.search_description + return self.perex + class DistrictElectionProgramPage( DistrictElectionSubCampaignPageMixin, DistrictElectionBasePage @@ -886,6 +902,14 @@ class DistrictElectionProgramPage( class Meta: verbose_name = "Bod programu voleb" + def get_meta_image(self): + return self.search_image or self.image or self.root_page.get_meta_image() + + def get_meta_description(self): + if self.search_description: + return self.search_description + return self.perex + class DistrictElectionCampaignPage(DistrictElectionBasePage): ### FIELDS @@ -1007,6 +1031,20 @@ class DistrictElectionCampaignPage(DistrictElectionBasePage): def program_points(self): return self.get_children().type(DistrictElectionProgramPage).live().specific() + def get_meta_image(self): + return ( + self.search_image + or self.hero_candidates_image + or self.hero_image + or self.root_page.get_meta_image() + ) + + def get_meta_description(self): + if self.search_description: + return self.search_description + + return self.hero_motto + class DistrictElectionRootPage(RoutablePageMixin, Page): """The election root page only serves as a wrapper for sharing stuff among campaign pages. @@ -1093,6 +1131,11 @@ class DistrictProgramPage( fill_data_from_redmine_for_page(self) return super().save(**kwargs) + def get_meta_description(self): + if self.search_description: + return self.search_description + return self.perex + class DistrictCenterPage( CalendarMixin, ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, Page @@ -1169,6 +1212,26 @@ class DistrictCenterPage( def has_calendar(self): return self.calendar_id is not None + def get_meta_image(self): + return ( + self.search_image + or self.background_photo + or self.root_page.get_meta_image() + ) + + def get_meta_description(self): + if self.search_description: + return self.search_description + + desc = None + + if self.perex: + desc = self.perex + elif self.text: + desc = trim_to_length(strip_all_html_tags(self.text)) + + return desc + class DistrictCrossroadPage( ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, Page @@ -1404,6 +1467,19 @@ class DistrictGeoFeatureCollectionPage( return context + def get_meta_image(self): + return ( + self.search_image + or self.logo_image + or self.image + or self.root_page.get_meta_image() + ) + + def get_meta_description(self): + if self.search_description: + return self.search_description + return self.perex + class DistrictGeoFeatureCollectionCategory(Orderable): name = models.CharField("Název", max_length=100) @@ -1548,13 +1624,11 @@ class DistrictGeoFeatureDetailPage( return cached_index def get_meta_image(self): - return self.image + return self.search_image or self.image def get_meta_description(self): if self.search_description: return self.search_description - if len(self.perex) > 150: - return str(self.perex)[:150] + "..." return self.perex def get_context(self, request): diff --git a/elections2021/parser.py b/elections2021/parser.py index 90584f6d0c668ce9ac2d3e996454505731df0d32..e4db916e42424cba8ba007b7d055cc0f4328f478 100644 --- a/elections2021/parser.py +++ b/elections2021/parser.py @@ -8,6 +8,8 @@ import bleach import bs4 from django.utils.text import slugify +from shared.utils import strip_all_html_tags + from .constants import BENEFITS KNOWN_KEYS = [ @@ -202,10 +204,6 @@ def parse_program_html(fp): } -def strip_html(value): - return bleach.clean(value, tags=[], attributes={}, styles=[], strip=True) - - def strip_div(value): return value.replace("<div>", "").replace("</div>", "") @@ -233,7 +231,7 @@ def clean_point(point): raise ValueError(f"Unknown key: {old_key}") if key in ["nadpis"]: - out[key] = strip_html(val) + out[key] = strip_all_html_tags(val) else: out[key] = replace_tags(val) @@ -259,7 +257,7 @@ def prepare_faq(value): def prepare_horizon(value): - raw = strip_html(value) + raw = strip_all_html_tags(value) m = re.match(r"^(\d+)\s(\w+)$", raw) if m: return None, m.group(1), m.group(2) diff --git a/shared/models.py b/shared/models.py index 230ee10fdc3c0a5c9e34e96a8b924cf0fb09cba2..9e50faed7cff3deff886b6c0a41cd5beed696c7a 100644 --- a/shared/models.py +++ b/shared/models.py @@ -8,24 +8,11 @@ from wagtail.admin.edit_handlers import ( PublishingPanel, StreamFieldPanel, ) -from wagtail.core.blocks import RichTextBlock from wagtail.core.fields import StreamField from wagtail.core.models import Page from wagtail.images.edit_handlers import ImageChooserPanel -from maps_utils.blocks import MapFeatureCollectionBlock, MapPointBlock -from shared.blocks import ( - DEFAULT_CONTENT_BLOCKS, - CardBlock, - FigureBlock, - GalleryBlock, - MenuItemBlock, - MenuParentBlock, - ThreeColumnBlock, - TwoColumnBlock, - YouTubeVideoBlock, -) -from shared.const import RICH_TEXT_DEFAULT_FEATURES +from shared.blocks import DEFAULT_CONTENT_BLOCKS, MenuItemBlock, MenuParentBlock logger = logging.getLogger(__name__) @@ -100,13 +87,13 @@ class ArticleMixin(models.Model): return self.get_parent() def get_meta_image(self): + if hasattr(self, "search_image") and self.search_image: + return self.search_image return self.image def get_meta_description(self): - if self.search_description: + if hasattr(self, "search_description") and self.search_description: return self.search_description - if len(self.perex) > 150: - return str(self.perex)[:150] + "..." return self.perex diff --git a/shared/utils.py b/shared/utils.py index 44972b09c018ca0983ea64172fd9c4894955a354..ad56f316399b23e542bccd4b121c4b6a825370a3 100644 --- a/shared/utils.py +++ b/shared/utils.py @@ -1,6 +1,7 @@ import json import logging +import bleach import requests from django.conf import settings from django.utils.translation import gettext_lazy @@ -63,3 +64,24 @@ def subscribe_to_newsletter(email, news_id, source): "Failed to subscribe!", extra={"payload": payload, "response": response.text}, ) + + +def strip_all_html_tags(value: str): + """Drop all HTML tags from given value. + + :param value: string to sanitize + """ + return bleach.clean( + value, tags=[], attributes=[], styles=[], strip=True, strip_comments=True + ) + + +def trim_to_length(value: str, max_length: int = 150): + """Trim value to max length shall it exceed it. + + :param value: input string + :param max_length: max allowed length + """ + if len(value) > max_length: + return value[:max_length] + "..." + return value