From f331981c2bdaefae400fb9631fa18d1bc522a4d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Valenta?= <git@imaniti.org> Date: Wed, 3 Jan 2024 20:09:17 +0100 Subject: [PATCH] share main models --- main/blocks.py | 68 +--- ...5_alter_mainhomepage_footer_other_links.py | 41 ++ main/models.py | 203 +--------- shared/blocks/__init__.py | 2 + shared/{blocks.py => blocks/base.py} | 0 shared/blocks/main.py | 90 +++++ {main => shared}/feeds.py | 36 +- shared/models/__init__.py | 2 + shared/{models.py => models/base.py} | 0 shared/models/main.py | 375 ++++++++++++++++++ 10 files changed, 553 insertions(+), 264 deletions(-) create mode 100644 main/migrations/0065_alter_mainhomepage_footer_other_links.py create mode 100644 shared/blocks/__init__.py rename shared/{blocks.py => blocks/base.py} (100%) create mode 100644 shared/blocks/main.py rename {main => shared}/feeds.py (57%) create mode 100644 shared/models/__init__.py rename shared/{models.py => models/base.py} (100%) create mode 100644 shared/models/main.py diff --git a/main/blocks.py b/main/blocks.py index a489dfe8..faac594d 100644 --- a/main/blocks.py +++ b/main/blocks.py @@ -12,7 +12,7 @@ from wagtail.blocks import ( from wagtail.documents.blocks import DocumentChooserBlock from wagtail.images.blocks import ImageChooserBlock -from shared.blocks import CardLinkBlockMixin, CardLinkWithHeadlineBlockMixin +from shared.blocks import CardLinkBlockMixin, CardLinkWithHeadlineBlockMixin, CTAMixin PROGRAM_RICH_TEXT_FEATURES = [ "h3", @@ -56,21 +56,6 @@ class CardLinkBlock(CardLinkBlockMixin): label = "Karta s odkazem" -class CTAMixin(StructBlock): - button_link = URLBlock(label="Odkaz tlačítka") - button_text = CharBlock(label="Text tlačítka") - - class Meta: - icon = "doc-empty" - label = "Výzva s odkazem" - - -class NavbarMenuItemBlock(CTAMixin): - class Meta: - label = "Tlačítko" - template = "main/includes/molecules/navbar/additional_button.html" - - class ProgramGroupBlockMixin(StructBlock): title = CharBlock(label="Titulek části programu") # point_list = ListBlock(ProgramBlock(), label="Jednotlivé články programu") @@ -254,19 +239,6 @@ class RegionsBlock(StructBlock): label = "Články pro regiony" -class PersonContactBlock(StructBlock): - position = CharBlock(label="Název pozice", required=False) - # email, phone? - person = PageChooserBlock( - label="Osoba", - page_type=["main.MainPersonPage"], - ) - - class Meta: - icon = "user" - label = "Osoba s volitelnou pozicí" - - class PersonContactBoxBlock(StructBlock): title = CharBlock(label="Titulek") image = ImageChooserBlock(label="Ikona") @@ -277,38 +249,6 @@ class PersonContactBoxBlock(StructBlock): label = "Kontakty" -# Footer -class LinkBlock(StructBlock): - text = CharBlock(label="Název") - link = URLBlock(label="Odkaz") - - class Meta: - icon = "link" - label = "Odkaz" - - -class OtherLinksBlock(StructBlock): - title = CharBlock(label="Titulek") - list = ListBlock(LinkBlock, label="Seznam odkazů s titulkem") - - class Meta: - icon = "link" - label = "Odkazy" - template = "main/blocks/article_quote_block.html" - - -class SocialLinkBlock(LinkBlock): - icon = CharBlock( - label="Ikona", - help_text="Seznam ikon - https://styleguide.pirati.cz/latest/?p=viewall-atoms-icons <br/>" - "Název ikony zadejte bez tečky na začátku", - ) # TODO CSS class name or somthing better? - - class Meta: - icon = "link" - label = "Odkaz" - - # ARTICLE BLOCKS class ArticleQuoteBlock(StructBlock): quote = CharBlock(label="Citace") @@ -408,3 +348,9 @@ class TeamBlock(StructBlock): value = super().get_prep_value(value) value["slug"] = slugify(value["title"]) return value + + +# --- TODO: Remove legacy blocks used in migrations only + +class LinkBlock(StructBlock): + pass diff --git a/main/migrations/0065_alter_mainhomepage_footer_other_links.py b/main/migrations/0065_alter_mainhomepage_footer_other_links.py new file mode 100644 index 00000000..787c9193 --- /dev/null +++ b/main/migrations/0065_alter_mainhomepage_footer_other_links.py @@ -0,0 +1,41 @@ +# Generated by Django 4.1.10 on 2024-01-03 19:04 + +from django.db import migrations +import shared.blocks.main +import wagtail.blocks +import wagtail.fields + + +class Migration(migrations.Migration): + dependencies = [ + ("main", "0064_alter_mainhomepage_content"), + ] + + operations = [ + migrations.AlterField( + model_name="mainhomepage", + name="footer_other_links", + field=wagtail.fields.StreamField( + [ + ( + "other_links", + wagtail.blocks.StructBlock( + [ + ("title", wagtail.blocks.CharBlock(label="Titulek")), + ( + "list", + wagtail.blocks.ListBlock( + shared.blocks.main.LinkBlock, + label="Seznam odkazů s titulkem", + ), + ), + ] + ), + ) + ], + blank=True, + use_json_field=True, + verbose_name="Odkazy v zápatí webu", + ), + ), + ] diff --git a/main/models.py b/main/models.py index 34b23630..65cb63fa 100644 --- a/main/models.py +++ b/main/models.py @@ -40,6 +40,7 @@ from shared.models import ( # MenuMixin, SubpageMixin, ) from shared.utils import make_promote_panels, subscribe_to_newsletter +from shared import blocks as shared_blocks from tuning import admin_help from . import blocks @@ -48,29 +49,10 @@ from .forms import JekyllImportForm from .menu import MenuMixin, PageInMenuMixin -class MainHomePage( - MenuMixin, - RoutablePageMixin, - ExtendedMetadataHomePageMixin, - MetadataPageMixin, - ArticlesMixin, - Page, -): - # header +from shared.models import MainHomePageMixin - menu_button_name = models.CharField( - verbose_name="Text na tlačítku pro zapojení", max_length=16 - ) - - menu_button_content = StreamField( - [ - ("navbar_menu_item", blocks.NavbarMenuItemBlock()), - ], - verbose_name="Obsah menu pro zapojení se", - blank=True, - use_json_field=True, - ) +class MainHomePage(MainHomePageMixin): # content content = StreamField( [ @@ -85,25 +67,6 @@ class MainHomePage( blank=True, use_json_field=True, ) - - # footer - footer_other_links = StreamField( - [ - ("other_links", blocks.OtherLinksBlock()), - ], - verbose_name="Odkazy v zápatí webu", - blank=True, - use_json_field=True, - ) - - footer_person_list = StreamField( - [("person", blocks.PersonContactBlock())], - verbose_name="Osoby v zápatí webu", - blank=True, - max_num=6, - use_json_field=True, - ) - # settings gdpr_and_cookies_page = models.ForeignKey( "main.MainSimplePage", @@ -113,48 +76,6 @@ class MainHomePage( null=True, ) - matomo_id = models.IntegerField( - "Matomo ID pro sledování návštěvnosti", blank=True, null=True - ) - - social_links = StreamField( - [ - ("social_links", blocks.SocialLinkBlock()), - ], - verbose_name="Odkazy na sociální sítě", - blank=True, - use_json_field=True, - ) - - content_panels = Page.content_panels + [ - FieldPanel("content"), - FieldPanel("footer_other_links"), - FieldPanel("footer_person_list"), - ] - - promote_panels = make_promote_panels(admin_help.build(admin_help.IMPORTANT_TITLE)) - - settings_panels = [ - FieldPanel("menu_button_name"), - FieldPanel("menu_button_content"), - PageChooserPanel("gdpr_and_cookies_page"), - FieldPanel("social_links"), - FieldPanel("matomo_id"), - ] - - ### EDIT HANDLERS - - edit_handler = TabbedInterface( - [ - ObjectList(content_panels, heading="Obsah"), - ObjectList(promote_panels, heading="Propagovat"), - ObjectList(settings_panels, heading="Nastavení"), - ObjectList(MenuMixin.menu_panels, heading="Menu"), - ] - ) - - ### RELATIONS - subpage_types = [ "main.MainArticlesPage", "main.MainProgramPage", @@ -172,122 +93,30 @@ class MainHomePage( class Meta: verbose_name = "HomePage pirati.cz" - @cached_property - def gdpr_and_cookies_url(self): - if self.gdpr_and_cookies_page: - return self.gdpr_and_cookies_page.url - return "#" - - @staticmethod - def get_404_response(request): - return render(request, "main/404.html", status=404) - - def get_context(self, request, *args, **kwargs): - context = super().get_context(request, args, kwargs) - - context["article_data_list"] = self.materialize_shared_articles_query( - self.append_all_shared_articles_query(MainArticlePage.objects.live().all()) - .live() - .order_by("-union_date")[:3] - ) - - return context - - def get_region_response(self, request): - context = {} - if request.GET.get("region", None) == "VSK": - sorted_article_qs = MainArticlePage.objects.filter( - region__isnull=False - ).order_by("-date") - context = {"article_data_list": sorted_article_qs[:3]} - else: - sorted_article_qs = MainArticlePage.objects.filter( - region=request.GET.get("region", None) - )[:3] - context = {"article_data_list": sorted_article_qs[:3]} - - data = { - "html": render( - request, "main/includes/small_article_preview.html", context - ).content.decode("utf-8") - } - - return JsonResponse(data=data, safe=False) - - def serve(self, request, *args, **kwargs): - if request.META.get("HTTP_X_REQUESTED_WITH") == "XMLHttpRequest": - if "region" in request.GET: - return self.get_region_response(request) - - return super().serve(request, *args, **kwargs) - - @cached_property - def newsletter_subscribe_url(self): - newsletter_subscribe = self.reverse_subpage("newsletter_subscribe") - return ( - self.url + newsletter_subscribe - if self.url is not None - else newsletter_subscribe - ) # preview fix + @property + def article_page_model(self): + return MainArticlePage @property - def articles_page(self): - return self._first_subpage_of_type(MainArticlesPage) + def articles_page_model(self): + return MainArticlesPage @property - def people_page(self): - return self._first_subpage_of_type(MainPeoplePage) + def contact_page_model(self): + return MainContactPage @property - def contact_page(self): - return self._first_subpage_of_type(MainContactPage) + def search_page_model(self): + return MainSearchPage @property - def search_page(self): - return self._first_subpage_of_type(MainSearchPage) + def people_page(self): + return self._first_subpage_of_type(MainPeoplePage) @property def root_page(self): return self - @route(r"^prihlaseni-k-newsletteru/$") - def newsletter_subscribe(self, request): - if request.method == "POST": - form = SubscribeForm(request.POST) - if form.is_valid(): - subscribe_to_newsletter( - form.cleaned_data["email"], settings.PIRATICZ_NEWSLETTER_CID - ) - - messages.success( - request, - "Zkontroluj si prosím schránku, poslali jsme ti potvrzovací email.", - ) - - try: - page = ( - Page.objects.filter(id=form.cleaned_data["return_page_id"]) - .live() - .first() - ) - return HttpResponseRedirect(page.full_url) - except Page.DoesNotExist: - return HttpResponseRedirect(self.url) - - messages.error( - request, - "Tvůj prohlížeč nám odeslal špatná data. Prosím, zkus to znovu.", - ) - - return HttpResponseRedirect(self.url) - - @route(r"^feeds/atom/$") - def view_feed(self, request): - # Avoid circular import - from .feeds import LatestArticlesFeed # noqa - - return LatestArticlesFeed()(request, self.articles_page.id) - def _first_subpage_of_type(self, page_type) -> Page or None: try: return self.get_descendants().type(page_type).live().specific()[0] @@ -765,7 +594,7 @@ class MainPersonPage( social_links = StreamField( [ - ("social_links", blocks.SocialLinkBlock()), + ("social_links", shared_blocks.SocialLinkBlock()), ], verbose_name="Odkazy na sociální sítě", blank=True, @@ -886,7 +715,7 @@ class MainContactPage( ### FIELDS contact_people = StreamField( - [("item", blocks.PersonContactBlock())], + [("item", shared_blocks.PersonContactBlock())], verbose_name="Kontaktní osoby", blank=True, use_json_field=True, diff --git a/shared/blocks/__init__.py b/shared/blocks/__init__.py new file mode 100644 index 00000000..d31603b8 --- /dev/null +++ b/shared/blocks/__init__.py @@ -0,0 +1,2 @@ +from .base import * +from .main import * diff --git a/shared/blocks.py b/shared/blocks/base.py similarity index 100% rename from shared/blocks.py rename to shared/blocks/base.py diff --git a/shared/blocks/main.py b/shared/blocks/main.py new file mode 100644 index 00000000..9cce950f --- /dev/null +++ b/shared/blocks/main.py @@ -0,0 +1,90 @@ +from wagtail import blocks + +from wagtail.blocks import ( + CharBlock, + ListBlock, + PageChooserBlock, + RichTextBlock, + StructBlock, + URLBlock, +) + +from .base import MenuItemBlock as MenuItemBlockBase + + +# Mixins (or used as such) + +class CTAMixin(StructBlock): + button_link = URLBlock(label="Odkaz tlačítka") + button_text = CharBlock(label="Text tlačítka") + + class Meta: + icon = "doc-empty" + label = "Výzva s odkazem" + + +class LinkBlock(StructBlock): + text = CharBlock(label="Název") + link = URLBlock(label="Odkaz") + + class Meta: + icon = "link" + label = "Odkaz" + + +# Navbar + +class MainMenuItemBlock(MenuItemBlockBase): + title = blocks.CharBlock( + label="Titulek", + help_text="Pokud není odkazovaná stránka na Majáku, použij možnost zadání samotné adresy níže.", + required=True, + ) + + class Meta: + label = "Položka v menu" + + +class NavbarMenuItemBlock(CTAMixin): + class Meta: + label = "Tlačítko" + template = "main/includes/molecules/navbar/additional_button.html" + + +class SocialLinkBlock(LinkBlock): + icon = CharBlock( + label="Ikona", + help_text="Seznam ikon - https://styleguide.pirati.cz/latest/?p=viewall-atoms-icons <br/>" + "Název ikony zadejte bez tečky na začátku", + ) # TODO CSS class name or somthing better? + + class Meta: + icon = "link" + label = "Odkaz" + + +# People + +class PersonContactBlock(StructBlock): + position = CharBlock(label="Název pozice", required=False) + # email, phone? + person = PageChooserBlock( + label="Osoba", + page_type=["main.MainPersonPage"], + ) + + class Meta: + icon = "user" + label = "Osoba s volitelnou pozicí" + + +# Footer + +class OtherLinksBlock(StructBlock): + title = CharBlock(label="Titulek") + list = ListBlock(LinkBlock, label="Seznam odkazů s titulkem") + + class Meta: + icon = "link" + label = "Odkazy" + template = "main/blocks/article_quote_block.html" diff --git a/main/feeds.py b/shared/feeds.py similarity index 57% rename from main/feeds.py rename to shared/feeds.py index 54438673..7165433b 100644 --- a/main/feeds.py +++ b/shared/feeds.py @@ -5,39 +5,43 @@ from django.contrib.syndication.views import Feed from django.template.loader import render_to_string from django.urls import reverse -from .models import MainArticlePage, MainArticlesPage - class LatestArticlesFeed(Feed): - def get_object(self, request, id: int) -> MainArticlesPage: - return MainArticlesPage.objects.get(id=id) + def __init__(self, articles_page_model, article_page_model, *args, **kwargs): + self.articles_page_model = articles_page_model + self.article_page_model = article_page_model + + return super().__init__(*args, **kwargs) + + def get_object(self, request, id: int): + return self.articles_page_model.objects.get(id=id) - def title(self, obj: MainArticlesPage) -> str: + def title(self, obj) -> str: return obj.title - def link(self, obj: MainArticlesPage) -> str: + def link(self, obj) -> str: return obj.get_full_url() - def description(self, obj: MainArticlesPage) -> str: + def description(self, obj) -> str: return obj.perex - def items(self, obj: MainArticlesPage) -> list: + def items(self, obj) -> list: return obj.materialize_shared_articles_query( obj.append_all_shared_articles_query(MainArticlePage.objects.child_of(obj))[ :32 ] ) - def item_title(self, item: MainArticlePage) -> str: + def item_title(self, item) -> str: return item.title - def item_description(self, item: MainArticlePage) -> str: + def item_description(self, item) -> str: return render_to_string( "main/feed_item_description.html", {"item": item}, ) - def item_pubdate(self, item: MainArticlePage) -> datetime: + def item_pubdate(self, item) -> datetime: return datetime( item.date.year, item.date.month, @@ -46,7 +50,7 @@ class LatestArticlesFeed(Feed): 0, ) - def item_author_name(self, item: MainArticlePage) -> str: + def item_author_name(self, item) -> str: if item.author: return item.author @@ -55,13 +59,13 @@ class LatestArticlesFeed(Feed): return "" - def item_categories(self, item: MainArticlePage) -> list: + def item_categories(self, item) -> list: return item.get_tags - def item_link(self, item: MainArticlePage) -> str: + def item_link(self, item) -> str: return item.get_full_url() - def item_enclosure_url(self, item: MainArticlePage) -> typing.Union[None, str]: + def item_enclosure_url(self, item) -> typing.Union[None, str]: if item.image is None: return None @@ -69,5 +73,5 @@ class LatestArticlesFeed(Feed): item_enclosure_mime_type = "image/webp" - def item_enclosure_length(self, item: MainArticlePage) -> int: + def item_enclosure_length(self, item) -> int: return item.image.file_size diff --git a/shared/models/__init__.py b/shared/models/__init__.py new file mode 100644 index 00000000..d31603b8 --- /dev/null +++ b/shared/models/__init__.py @@ -0,0 +1,2 @@ +from .base import * +from .main import * diff --git a/shared/models.py b/shared/models/base.py similarity index 100% rename from shared/models.py rename to shared/models/base.py diff --git a/shared/models/main.py b/shared/models/main.py new file mode 100644 index 00000000..fd3d0c52 --- /dev/null +++ b/shared/models/main.py @@ -0,0 +1,375 @@ +import datetime +from functools import cached_property + +from dateutil.relativedelta import relativedelta +from django.conf import settings +from django.contrib import messages +from django.core.paginator import Paginator +from django.db import models +from django.forms.models import model_to_dict +from django.http import HttpResponseRedirect, JsonResponse +from django.shortcuts import render +from django.utils import timezone +from modelcluster.contrib.taggit import ClusterTaggableManager +from modelcluster.fields import ParentalKey +from taggit.models import Tag, TaggedItemBase +from wagtail.admin.panels import ( + FieldPanel, + HelpPanel, + MultiFieldPanel, + ObjectList, + PageChooserPanel, + TabbedInterface, +) +from wagtail.blocks import PageChooserBlock, RichTextBlock +from wagtail.contrib.routable_page.models import RoutablePageMixin, route +from wagtail.fields import RichTextField, StreamField +from wagtail.models import Page +from wagtail.search import index +from wagtailmetadata.models import MetadataPageMixin + +from calendar_utils.models import CalendarMixin +from shared.forms import SubscribeForm +from .base import ( # MenuMixin, + ArticleMixin, + ArticlesMixin, + ArticlesPageMixin, + ExtendedMetadataHomePageMixin, + ExtendedMetadataPageMixin, + SharedTaggedMainArticle, + SubpageMixin, +) +from shared.utils import make_promote_panels, subscribe_to_newsletter +from tuning import admin_help + +from wagtail.models import Page + +from django.db import models +from wagtail.admin.panels import FieldPanel, MultiFieldPanel +from wagtail.fields import StreamField +from wagtail.models import Page + +from .base import MenuMixin as MenuMixinBase +from shared.blocks import MainMenuItemBlock, NavbarMenuItemBlock, OtherLinksBlock, PersonContactBlock, SocialLinkBlock + + +class MainMenuMixin(MenuMixinBase): + important_item_name = models.CharField( + verbose_name="Jméno", + help_text="Pokud není odkazovaná stránka na Majáku, použij možnost zadání samotné adresy níže.", + max_length=16, + blank=True, + null=True, + ) + + important_item_page = models.ForeignKey( + Page, + verbose_name="Stránka", + null=True, + blank=True, + related_name="+", + on_delete=models.PROTECT, + ) + + important_item_url = models.URLField( + verbose_name="Adresa", + blank=True, + null=True, + ) + + menu = StreamField( + [("menu_item", MainMenuItemBlock())], # , ("menu_parent", MenuParentBlock()) + verbose_name="Položky", + blank=True, + use_json_field=True, + ) + + menu_panels = [ + MultiFieldPanel( + [ + FieldPanel("important_item_name"), + FieldPanel("important_item_page"), + FieldPanel("important_item_url"), + ], + heading="Důležitá položka menu", + ), + MultiFieldPanel( + [ + FieldPanel("menu"), + ], + heading="Další obsah menu", + ), + ] + + class Meta: + abstract = True + + +class PageInMenuMixin(Page): + def get_menu_title(self) -> str: + for menu_item in self.root_page.menu: + if menu_item.value["page"] is None: + continue + + if menu_item.value["page"].id == self.id: + return menu_item.value["title"] + + return self.title + + class Meta: + abstract = True + + +class MainHomePageMixin( + MainMenuMixin, + RoutablePageMixin, + ExtendedMetadataHomePageMixin, + MetadataPageMixin, + ArticlesMixin, + Page, +): + # header + + menu_button_name = models.CharField( + verbose_name="Text na tlačítku pro zapojení", max_length=16 + ) + + menu_button_content = StreamField( + [ + ("navbar_menu_item", NavbarMenuItemBlock()), + ], + verbose_name="Obsah menu pro zapojení se", + blank=True, + use_json_field=True, + ) + + # content + # NOTE: Needs to be overriden + content = StreamField( + [], + verbose_name="Hlavní obsah", + blank=True, + use_json_field=True, + ) + + # footer + footer_other_links = StreamField( + [ + ("other_links", OtherLinksBlock()), + ], + verbose_name="Odkazy v zápatí webu", + blank=True, + use_json_field=True, + ) + + footer_person_list = StreamField( + [("person", PersonContactBlock())], + verbose_name="Osoby v zápatí webu", + blank=True, + max_num=6, + use_json_field=True, + ) + + # settings + @property + def gdpr_and_cookies_page(self): + # NOTE: Must be implemented + raise NotImplementedError + + matomo_id = models.IntegerField( + "Matomo ID pro sledování návštěvnosti", blank=True, null=True + ) + + social_links = StreamField( + [ + ("social_links", SocialLinkBlock()), + ], + verbose_name="Odkazy na sociální sítě", + blank=True, + use_json_field=True, + ) + + content_panels = Page.content_panels + [ + FieldPanel("content"), + FieldPanel("footer_other_links"), + FieldPanel("footer_person_list"), + ] + + promote_panels = make_promote_panels(admin_help.build(admin_help.IMPORTANT_TITLE)) + + settings_panels = [ + FieldPanel("menu_button_name"), + FieldPanel("menu_button_content"), + PageChooserPanel("gdpr_and_cookies_page"), + FieldPanel("social_links"), + FieldPanel("matomo_id"), + ] + + ### EDIT HANDLERS + + edit_handler = TabbedInterface( + [ + ObjectList(content_panels, heading="Obsah"), + ObjectList(promote_panels, heading="Propagovat"), + ObjectList(settings_panels, heading="Nastavení"), + ObjectList(MainMenuMixin.menu_panels, heading="Menu"), + ] + ) + + ### RELATIONS + + subpage_types = [] + + ### OTHERS + + class Meta: + verbose_name = "Hlavní stránka" + abstract = True + + @property + def article_page_model(self): + # NOTE: Must be overridden + raise NotImplementedError + + @property + def articles_page_model(self): + # NOTE: Must be overridden + raise NotImplementedError + + @property + def contact_page_model(self): + # NOTE: Must be overridden + raise NotImplementedError + + @property + def search_page_model(self): + # NOTE: Must be overridden + raise NotImplementedError + + @cached_property + def gdpr_and_cookies_url(self): + if self.gdpr_and_cookies_page: + return self.gdpr_and_cookies_page.url + + return "#" + + @staticmethod + def get_404_response(request): + return render(request, "main/404.html", status=404) + + def get_context(self, request, *args, **kwargs): + context = super().get_context(request, args, kwargs) + + context["article_data_list"] = self.materialize_shared_articles_query( + self.append_all_shared_articles_query(self.article_page_model.objects.live().all()) + .live() + .order_by("-union_date")[:3] + ) + + return context + + def get_region_response(self, request): + context = {} + if request.GET.get("region", None) == "VSK": + sorted_article_qs = self.article_page_model.objects.filter( + region__isnull=False + ).order_by("-date") + + context = {"article_data_list": sorted_article_qs[:3]} + else: + sorted_article_qs = self.article_page_model.objects.filter( + region=request.GET.get("region", None) + )[:3] + + context = {"article_data_list": sorted_article_qs[:3]} + + data = { + "html": render( + request, "main/includes/small_article_preview.html", context + ).content.decode("utf-8") + } + + return JsonResponse(data=data, safe=False) + + def serve(self, request, *args, **kwargs): + if request.META.get("HTTP_X_REQUESTED_WITH") == "XMLHttpRequest": + if "region" in request.GET: + return self.get_region_response(request) + + return super().serve(request, *args, **kwargs) + + @cached_property + def newsletter_subscribe_url(self): + newsletter_subscribe = self.reverse_subpage("newsletter_subscribe") + return ( + self.url + newsletter_subscribe + if self.url is not None + else newsletter_subscribe + ) # preview fix + + @property + def articles_page(self): + return self._first_subpage_of_type(self.articles_page_model) + + @property + def people_page(self): + return self._first_subpage_of_type(MainPeoplePage) + + @property + def contact_page(self): + return self._first_subpage_of_type(self.contact_page_model) + + @property + def search_page(self): + return self._first_subpage_of_type(self.search_page_model) + + @property + def root_page(self): + return self + + @route(r"^prihlaseni-k-newsletteru/$") + def newsletter_subscribe(self, request): + if request.method == "POST": + form = SubscribeForm(request.POST) + if form.is_valid(): + subscribe_to_newsletter( + form.cleaned_data["email"], settings.PIRATICZ_NEWSLETTER_CID + ) + + messages.success( + request, + "Zkontroluj si prosím schránku, poslali jsme ti potvrzovací email.", + ) + + try: + page = ( + Page.objects.filter(id=form.cleaned_data["return_page_id"]) + .live() + .first() + ) + return HttpResponseRedirect(page.full_url) + except Page.DoesNotExist: + return HttpResponseRedirect(self.url) + + messages.error( + request, + "Tvůj prohlížeč nám odeslal špatná data. Prosím, zkus to znovu.", + ) + + return HttpResponseRedirect(self.url) + + @route(r"^feeds/atom/$") + def view_feed(self, request): + from shared.feeds import LatestArticlesFeed # noqa + + return LatestArticlesFeed()(request, self.articles_page.id) + + def _first_subpage_of_type(self, page_type) -> Page or None: + try: + return self.get_descendants().type(page_type).live().specific()[0] + except IndexError: + return None + + @route(r"^sdilene/$", name="shared") + def shared(self, request): + return self.setup_article_page_context(request) -- GitLab