Skip to content
Snippets Groups Projects
Select Git revision
  • bc09371246073b46d441ae5ac95c225ea6dbb5a9
  • test default protected
  • master protected
  • original
  • pirati-backup protected
  • beta-2
  • beta-1
  • v3.1.4
  • v3.1.3
  • v3.1.2
  • v3.1.1
  • v3.1.0
  • v3.0.16
  • v3.0.15
  • v3.0.14
  • v3.0.13
  • v3.0.12
  • v3.0.11
  • v3.0.10
  • v3.0.9
  • v3.0.8
  • v3.0.7
  • v3.0.6
  • v3.0.5
  • v3.0.4
25 results

url_names.py

Blame
  • models.py 26.51 KiB
    import random
    
    from django.core.exceptions import ValidationError
    from django.core.paginator import Paginator
    from django.db import models
    from django.shortcuts import render
    from django.utils.translation import gettext_lazy
    from modelcluster.contrib.taggit import ClusterTaggableManager
    from modelcluster.fields import ParentalKey
    from taggit.models import Tag, TaggedItemBase
    from wagtail.admin.edit_handlers import (
        CommentPanel,
        FieldPanel,
        MultiFieldPanel,
        ObjectList,
        PageChooserPanel,
        StreamFieldPanel,
        TabbedInterface,
    )
    from wagtail.admin.forms import WagtailAdminPageForm
    from wagtail.contrib.table_block.blocks import TableBlock
    from wagtail.core.blocks import RichTextBlock
    from wagtail.core.fields import RichTextField, StreamField
    from wagtail.core.models import Page
    from wagtail.images.edit_handlers import ImageChooserPanel
    from wagtailmetadata.models import MetadataPageMixin
    
    from calendar_utils.models import CalendarMixin
    from shared.models import ArticleMixin, MenuMixin, SubpageMixin
    from uniweb.constants import RICH_TEXT_FEATURES
    
    from . import blocks
    
    
    class RegionHomePage(MenuMixin, MetadataPageMixin, CalendarMixin, Page):
        ### FIELDS
    
        subheader = StreamField(
            [
                ("header_simple", blocks.HomepageSimpleHeaderBlock()),
                ("header", blocks.HomepageHeaderBlock()),
            ],
            verbose_name="Blok pod headerem",
            blank=True,
        )
        articles_title = models.CharField("Nadpis článků", max_length=256)
        election_countdown_datetime = models.DateTimeField(
            "Datum a čas pro odpočet do voleb",
            null=True,
            blank=True,
            help_text="Pro skrytí nechte nevyplněné",
        )
        show_calendar_on_hp = models.BooleanField(
            "Zobrazit kalendář dole na homepage", default=True
        )
    
        region_map_button_text = models.CharField(
            "Text tlačítka mapy", max_length=256, default="Piráti v krajích"
        )
        calendar_button_text = models.CharField(
            "Text tlačítka kalendáře", max_length=256, default="Kalendář"
        )
    
        custom_logo = models.ForeignKey(
            "wagtailimages.Image", blank=True, null=True, on_delete=models.SET_NULL
        )
    
        show_pirati_cz_link = models.BooleanField(
            "Zobrazit v záhlaví odkaz 'pirati.cz'", default=True
        )
        show_eshop_link = models.BooleanField(
            "Zobrazit v záhlaví odkaz na pirátský eshop", default=True
        )
        show_magazine_link = models.BooleanField(
            "Zobrazit v záhlaví odkaz na pirátské listy", default=True
        )
    
        facebook = models.URLField(
            "Facebook URL",
            blank=True,
            null=True,
            default="https://www.facebook.com/ceska.piratska.strana",
        )
        twitter = models.URLField(
            "Twitter URL",
            blank=True,
            null=True,
            default="https://www.twitter.com/PiratskaStrana",
        )
        youtube = models.URLField(
            "YouTube URL",
            blank=True,
            null=True,
            default="https://www.youtube.com/channel/UC_zxYLGrkmrYazYt0MzyVlA",
        )
        instagram = models.URLField(
            "Instagram URL",
            blank=True,
            null=True,
            default="https://www.instagram.com/pirati.cz/",
        )
        flickr = models.URLField(
            "Flickr URL",
            blank=True,
            null=True,
            default="https://www.flickr.com/photos/pirati/",
        )
        forum = models.URLField(
            "Fórum URL", blank=True, null=True, default="https://forum.pirati.cz/"
        )
    
        contact_email = models.EmailField("kontaktni email", max_length=250, blank=True)
        contact_phone = models.TextField("kontaktni telefon", max_length=250, blank=True)
        contact_newcomers = models.URLField(
            "URL pro zájemce o členství",
            blank=True,
            null=True,
            default="https://nalodeni.pirati.cz",
        )
    
        donation_page = models.URLField(
            "URL pro příjem darů (tlačítko Přispěj)",
            blank=True,
            null=True,
            default="https://dary.pirati.cz",
        )
    
        # Lide uvedeni v paticce
        footperson_coord_title = models.CharField(
            "Název funkce", max_length=128, default="Koordinátor"
        )
        footperson_coord = models.ForeignKey(
            "region.RegionPersonPage",
            verbose_name="Koordinátor",
            on_delete=models.PROTECT,
            null=True,
            blank=True,
            related_name="+",
        )
        footperson_electman_title = models.CharField(
            "Název funkce", max_length=128, default="Volební manažer"
        )
        footperson_electman = models.ForeignKey(
            "region.RegionPersonPage",
            verbose_name="Volební manažer",
            on_delete=models.PROTECT,
            null=True,
            blank=True,
            related_name="+",
        )
        footperson_media_title = models.CharField(
            "Název funkce", max_length=128, default="Kontakt pro média"
        )
        footperson_media = models.ForeignKey(
            "region.RegionPersonPage",
            verbose_name="Kontakt pro média",
            on_delete=models.PROTECT,
            null=True,
            blank=True,
            related_name="+",
        )
    
        # settings
        matomo_id = models.IntegerField(
            "Matomo ID pro sledování návštěvnosti", blank=True, null=True
        )
        fallback_image = models.ForeignKey(
            "wagtailimages.Image",
            on_delete=models.PROTECT,
            null=True,
            related_name="+",
        )
    
        ### PANELS
    
        content_panels = Page.content_panels + [
            StreamFieldPanel("subheader"),
            FieldPanel("articles_title"),
            FieldPanel("election_countdown_datetime"),
            FieldPanel("show_calendar_on_hp"),
        ]
    
        promote_panels = MetadataPageMixin.promote_panels + [
            MultiFieldPanel(
                [
                    FieldPanel("facebook"),
                    FieldPanel("forum"),
                    FieldPanel("twitter"),
                    FieldPanel("youtube"),
                ],
                gettext_lazy("Social page configuration"),
            ),
        ]
    
        settings_panels = [
            ImageChooserPanel("custom_logo"),
            FieldPanel("matomo_id"),
            MultiFieldPanel(
                [
                    FieldPanel("show_pirati_cz_link"),
                    FieldPanel("show_eshop_link"),
                    FieldPanel("show_magazine_link"),
                    FieldPanel("donation_page"),
                    FieldPanel("contact_newcomers"),
                    FieldPanel("facebook"),
                    FieldPanel("twitter"),
                    FieldPanel("youtube"),
                    FieldPanel("instagram"),
                    FieldPanel("flickr"),
                    FieldPanel("forum"),
                ],
                gettext_lazy("Odkazy na webu"),
            ),
            MultiFieldPanel(
                [
                    FieldPanel("footperson_coord_title"),
                    PageChooserPanel("footperson_coord"),
                    FieldPanel("footperson_electman_title"),
                    PageChooserPanel("footperson_electman"),
                    FieldPanel("footperson_media_title"),
                    PageChooserPanel("footperson_media"),
                ],
                gettext_lazy("Lidé v zápatí stránky"),
            ),
            MultiFieldPanel(
                [
                    FieldPanel("region_map_button_text"),
                    FieldPanel("calendar_button_text"),
                    FieldPanel("calendar_url"),
                ],
                gettext_lazy("Nastavení lišty s kalendářem a mapou"),
            ),
            ImageChooserPanel("fallback_image"),
            CommentPanel(),
        ]
    
        ### EDIT HANDLERS
        edit_handler = TabbedInterface(
            [
                ObjectList(content_panels, heading="Obsah"),
                ObjectList(MetadataPageMixin.promote_panels, heading="Propagovat"),
                ObjectList(settings_panels, heading="Nastavení"),
                ObjectList(MenuMixin.menu_panels, heading="Menu"),
            ]
        )
    
        ### RELATIONS
    
        subpage_types = [
            "region.RegionArticlesPage",
            "region.RegionCenterPage",
            "region.RegionContactPage",
            "region.RegionCrossroadPage",
            "region.RegionCustomPage",
            "region.RegionElectionPage",
            "region.RegionPeoplePage",
            "region.RegionProgramPage",
            "region.RegionTagsPage",
        ]
    
        ### OTHERS
    
        class Meta:
            verbose_name = "Krajské sdružení"
    
        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
    
        @property
        def articles(self):
            return (
                self.get_descendants()
                .type(RegionArticlePage)
                .live()
                .specific()
                .order_by("-regionarticlepage__date")[:6]
            )
    
        @property
        def articles_page(self):
            return self._first_subpage_of_type(RegionArticlesPage)
    
        @property
        def center_page(self):
            return self._first_subpage_of_type(RegionCenterPage)
    
        @property
        def contact_page(self):
            return self._first_subpage_of_type(RegionContactPage)
    
        @property
        def election_page(self):
            return self._first_subpage_of_type(RegionElectionPage)
    
        @staticmethod
        def get_404_response(request):
            return render(request, "region/404.html", status=404)
    
        @property
        def people_page(self):
            return self._first_subpage_of_type(RegionPeoplePage)
    
        @property
        def program_page(self):
            return self._first_subpage_of_type(RegionProgramPage)
    
        @property
        def root_page(self):
            return self
    
        @property
        def tags_page(self):
            return self._first_subpage_of_type(RegionTagsPage)
    
        @property
        def has_calendar(self):
            return self.calendar_id is not None
    
    
    class RegionArticleTag(TaggedItemBase):
        content_object = ParentalKey("region.RegionArticlePage", on_delete=models.CASCADE)
    
    
    class RegionArticlePage(ArticleMixin, SubpageMixin, MetadataPageMixin, Page):
        ### FIELDS
    
        author_page = models.ForeignKey(
            "region.RegionPersonPage", on_delete=models.SET_NULL, null=True, blank=True
        )
        is_black = models.BooleanField("Má tmavé pozadí?", default=False)
        tags = ClusterTaggableManager(through=RegionArticleTag, blank=True)
    
        ### PANELS
    
        content_panels = ArticleMixin.content_panels + [
            FieldPanel("author_page"),
            FieldPanel("is_black"),
            FieldPanel("tags"),
        ]
    
        ### RELATIONS
    
        parent_page_types = ["region.RegionArticlesPage"]
        subpage_types = []
    
        ### OTHERS
    
        class Meta:
            verbose_name = "Aktualita"
    
        def get_context(self, request):
            context = super().get_context(request)
            context["related_articles"] = (
                self.get_siblings(inclusive=False)
                .live()
                .specific()
                .order_by("-regionarticlepage__date")[:3]
            )
            return context
    
    
    class RegionArticlesPage(SubpageMixin, MetadataPageMixin, Page):
        ### FIELDS
    
        max_items = models.IntegerField("Počet článků na stránce", default=12)
    
        ### PANELS
    
        content_panels = Page.content_panels + [
            FieldPanel("max_items"),
        ]
    
        settings_panels = [CommentPanel()]
    
        ### RELATIONS
    
        parent_page_types = ["region.RegionHomePage"]
        subpage_types = ["region.RegionArticlePage"]
    
        ### OTHERS
    
        class Meta:
            verbose_name = "Aktuality"
    
        def get_context(self, request):
            context = super().get_context(request)
            context["articles"] = Paginator(
                self.get_children().live().specific().order_by("-regionarticlepage__date"),
                self.max_items,
            ).get_page(request.GET.get("page"))
            return context
    
    
    class RegionContactPage(SubpageMixin, MetadataPageMixin, Page):
        ### FIELDS
    
        contact_people = StreamField(
            [("item", blocks.ContactItemBlock())],
            verbose_name="Kontakty",
            blank=True,
        )
        text = RichTextField("Text", blank=True, features=RICH_TEXT_FEATURES)
    
        ### PANELS
    
        content_panels = Page.content_panels + [
            StreamFieldPanel("contact_people"),
            FieldPanel("text"),
        ]
    
        ### RELATIONS
    
        parent_page_types = ["region.RegionHomePage"]
        subpage_types = []
    
        ### OTHERS
    
        class Meta:
            verbose_name = "Kontakty"
    
    
    class RegionTagsPage(SubpageMixin, MetadataPageMixin, Page):
        ### PANELS
    
        settings_panels = [CommentPanel()]
    
        ### RELATIONS
    
        parent_page_types = ["region.RegionHomePage"]
        subpage_types = []
    
        ### OTHERS
    
        class Meta:
            verbose_name = "Stránka s tagy"
    
        def get_context(self, request, *args, **kwargs) -> dict:
            context = super().get_context(request)
    
            # Potřebujeme IDčka článků pro správnou root_page pro filtrování zobrazených
            # tagů i samotných stránek, protože se filtrují přes specifický field
            # (tags__slug)
            site_article_ids = (
                self.root_page.articles_page.get_children()
                .live()
                .specific()
                .values_list("id", flat=True)
            )
    
            # Naplním "tag" a "article_page_list" parametry
            context.update(**self.get_tag_and_articles(request, site_article_ids))
            context["tag_list"] = self.get_tag_qs(site_article_ids)
    
            # Pro obecnou paginaci posílám "extra_query", abych si podržel tag pro další GET
            context["extra_query"] = "&tag={}".format(request.GET.get("tag", ""))
            return context
    
        def get_tag_and_articles(self, request, site_article_ids: list) -> dict:
            """
            Vrátí vyfiltrované články podle tagu a page query pro z daného "výběru"
            pro danou stránku (site_article_ids). Lepší by bylo články a tag řešit
            separátně, ale pak by se musel rozpadnout ten try/except na více bloků.
            """
            article_page_qs = RegionArticlePage.objects.filter(id__in=site_article_ids)
    
            try:
                tag = RegionArticleTag.objects.filter(tag__slug=request.GET["tag"])[0].tag
                article_page_qs = article_page_qs.filter(tags__slug=tag.slug)
            except (KeyError, IndexError):
                tag = None
    
            return {
                "article_page_list": Paginator(
                    article_page_qs.order_by("-date"),
                    self.root_page.articles_page.max_items,
                ).get_page(request.GET.get("page")),
                "tag": tag,
            }
    
        @staticmethod
        def get_tag_qs(site_article_ids: list) -> models.QuerySet:
            """
            Getuje Tagy pouze pro RegionArticlePage omezeno IDčky getnutých přes
            root_page. Počítá, kolik článků je s daným tagem.
            """
            return (
                Tag.objects.filter(regionarticlepage__id__in=site_article_ids)
                .order_by("slug")
                .annotate(count=models.Count("slug"))
                .values("name", "slug", "count")
            )
    
    
    class RegionPersonTag(TaggedItemBase):
        content_object = ParentalKey("region.RegionPersonPage", on_delete=models.CASCADE)
    
    
    class RegionPersonPageForm(WagtailAdminPageForm):
        def clean(self):
            cleaned_data = super().clean()
            email = cleaned_data.get("email", None)
            if email:
                pages_with_email = RegionPersonPage.objects.filter(email=email)
                num_pages_with_email = len(pages_with_email)
                if num_pages_with_email > 1 or (
                    num_pages_with_email == 1 and pages_with_email[0] != self.instance
                ):
                    raise ValidationError({"email": "Stránka s tímto emailem již existuje"})
            return cleaned_data
    
    
    class RegionPersonPage(SubpageMixin, MetadataPageMixin, Page):
        base_form_class = RegionPersonPageForm
        ### FIELDS
    
        job = models.CharField(
            "Povolání",
            max_length=128,
            blank=True,
            null=True,
            help_text="Např. 'Informatik'",
        )
        job_function = models.CharField(
            "Funkce", max_length=128, blank=True, null=True, help_text="Např. 'Předseda'"
        )
        background_photo = models.ForeignKey(
            "wagtailimages.Image",
            on_delete=models.PROTECT,
            blank=True,
            null=True,
            related_name="+",
            verbose_name="obrázek do záhlaví",
        )
        profile_photo = models.ForeignKey(
            "wagtailimages.Image",
            on_delete=models.PROTECT,
            blank=True,
            null=True,
            related_name="+",
            verbose_name="profilová fotka",
        )
        text = RichTextField("text", blank=True, features=RICH_TEXT_FEATURES)
    
        email = models.EmailField("Email", null=True)
        show_email = models.BooleanField("Zobrazovat email na stránce?", default=True)
        phone = models.CharField("Telefon", max_length=16, blank=True, null=True)
        city = models.CharField("Město/obec", max_length=64, blank=True, null=True)
        age = models.IntegerField("Věk", blank=True, null=True)
        is_pirate = models.BooleanField("Je členem Pirátské strany?", default=True)
    
        facebook_url = models.URLField("Odkaz na Facebook", blank=True, null=True)
        instagram_url = models.URLField("Odkaz na Instagram", blank=True, null=True)
        twitter_url = models.URLField("Odkaz na Twitter", blank=True, null=True)
        youtube_url = models.URLField("Odkaz na Youtube kanál", blank=True, null=True)
        flickr_url = models.URLField("Odkaz na Flickr", blank=True, null=True)
        custom_web_url = models.URLField("Odkaz na vlastní web", blank=True, null=True)
        other_urls = StreamField(
            [("other_url", blocks.PersonUrlBlock())],
            verbose_name="Další odkaz",
            blank=True,
        )
    
        ### PANELS
    
        content_panels = Page.content_panels + [
            MultiFieldPanel(
                [
                    FieldPanel("job"),
                    FieldPanel("job_function"),
                ],
                "Základní údaje",
            ),
            MultiFieldPanel(
                [
                    ImageChooserPanel("profile_photo"),
                    ImageChooserPanel("background_photo"),
                ],
                "Fotky",
            ),
            FieldPanel("text"),
            MultiFieldPanel(
                [
                    FieldPanel("email"),
                    FieldPanel("show_email"),
                    FieldPanel("phone"),
                    FieldPanel("city"),
                    FieldPanel("age"),
                    FieldPanel("is_pirate"),
                ],
                "Kontaktní informace",
            ),
            MultiFieldPanel(
                [
                    FieldPanel("facebook_url"),
                    FieldPanel("instagram_url"),
                    FieldPanel("twitter_url"),
                    FieldPanel("youtube_url"),
                    FieldPanel("flickr_url"),
                    FieldPanel("custom_web_url"),
                ],
                "Sociální sítě",
            ),
        ]
    
        settings_panels = []
    
        ### RELATIONS
    
        parent_page_types = ["region.RegionPeoplePage"]
        subpage_types = []
    
        ### OTHERS
    
        class Meta:
            verbose_name = "Detail osoby"
    
        def get_background_photo(self):
            """
            Vrací background_photo pro pozadí na stránce, pokud není nastaveno,
            vezme falbback z homepage
            """
            return (
                self.background_photo
                if self.background_photo
                else self.root_page.fallback_image
            )
    
        def get_context(self, request):
            context = super().get_context(request)
            # Na strance detailu cloveka se vpravo zobrazuji 3 dalsi nahodne profily
            context["random_people"] = list(
                self.get_siblings(inclusive=False).live().specific()
            )
            random.shuffle(context["random_people"])
            context["random_people"] = context["random_people"][:3]
            return context
    
    
    class RegionPeoplePage(SubpageMixin, MetadataPageMixin, Page):
        ### FIELDS
    
        content = StreamField(
            [("people_group", blocks.PeopleGroupListBlock())],
            verbose_name="Obsah stránky",
            blank=True,
        )
    
        ### PANELS
    
        content_panels = Page.content_panels + [StreamFieldPanel("content")]
    
        settings_panels = [CommentPanel()]
    
        ### RELATIONS
    
        parent_page_types = ["region.RegionHomePage"]
        subpage_types = ["region.RegionPersonPage"]
    
        ### OTHERS
    
        class Meta:
            verbose_name = "Lidé"
    
    
    class RegionElectionBasePage(SubpageMixin, MetadataPageMixin, Page):
        ### FIELDS
        text = RichTextField("text", blank=True, features=RICH_TEXT_FEATURES)
    
        ### PANELS
    
        content_panels = Page.content_panels + [
            FieldPanel("text"),
        ]
    
        ### RELATIONS
    
        parent_page_types = ["region.RegionElectionPage"]
        subpage_types = []
    
        def get_context(self, request, *args, **kwargs):
            context = super().get_context(request, *args, **kwargs)
            context["election_points"] = (
                self.get_siblings().type(RegionElectionPointPage).live().specific()
            )
            context["strategy_page"] = self.get_parent().specific.strategy_page
            return context
    
        class Meta:
            abstract = True
    
    
    class RegionAfterElectionPage(RegionElectionBasePage):
        class Meta:
            verbose_name = "Povolební strategie"
    
    
    class RegionElectionPointPage(RegionElectionBasePage):
        ### FIELDS
        guarantor = models.ForeignKey("region.RegionPersonPage", on_delete=models.PROTECT)
        list_image = models.ForeignKey(
            "wagtailimages.Image",
            on_delete=models.PROTECT,
            related_name="+",
        )
        perex = models.TextField("Perex", help_text="Pro přehled volebního programu")
    
        ### PANELS
    
        content_panels = RegionElectionBasePage.content_panels + [
            PageChooserPanel("guarantor"),
            ImageChooserPanel("list_image"),
            FieldPanel("perex"),
        ]
    
        ### OTHERS
    
        class Meta:
            verbose_name = "Bod programu voleb"
    
    
    class RegionElectionPage(SubpageMixin, MetadataPageMixin, Page):
        ### FIELDS
        header = StreamField(
            [("election_header_block", blocks.ElectionHeaderBlock())],
            verbose_name="Obsah headeru",
            blank=True,
        )
    
        content = StreamField(
            [("candidate_list", blocks.CandidateListBlock())],
            verbose_name="Obsah stránky",
            blank=True,
        )
    
        ### PANELS
    
        content_panels = Page.content_panels + [
            StreamFieldPanel("header"),
            StreamFieldPanel("content"),
        ]
    
        ### RELATIONS
    
        parent_page_types = ["region.RegionHomePage"]
        subpage_types = [
            "region.RegionElectionPointPage",
            "region.RegionAfterElectionPage",
        ]
    
        ### OTHERS
    
        class Meta:
            verbose_name = "Volební rozcestník"
    
        def get_context(self, request, *args, **kwargs):
            context = super().get_context(request, *args, **kwargs)
            context["election_points"] = (
                self.get_children().live().type(RegionElectionPointPage).specific()
            )
            return context
    
        @property
        def strategy_page(self):
            try:
                return (
                    self.get_descendants()
                    .type(RegionAfterElectionPage)
                    .live()
                    .specific()[0]
                )
            except IndexError:
                return None
    
    
    class RegionProgramPage(SubpageMixin, MetadataPageMixin, Page):
        ### FIELDS
    
        perex = models.TextField("Perex", blank=True)
        content = StreamField(
            [
                ("static_program_block", blocks.StaticProgramBlock()),
                ("redmine_program_block", blocks.RedmineProgramBlock()),
            ],
            verbose_name="obsah stránky",
            blank=True,
        )
    
        ### PANELS
    
        content_panels = Page.content_panels + [
            FieldPanel("perex"),
            StreamFieldPanel("content"),
        ]
    
        ### RELATIONS
    
        parent_page_types = ["region.RegionHomePage"]
        subpage_types = []
    
        ### OTHERS
    
        class Meta:
            verbose_name = "Program"
    
    
    class RegionCenterPage(CalendarMixin, SubpageMixin, MetadataPageMixin, Page):
        ### FIELDS
    
        perex = models.TextField("Perex", blank=True, null=True)
        background_photo = models.ForeignKey(
            "wagtailimages.Image",
            on_delete=models.PROTECT,
            blank=True,
            null=True,
            related_name="+",
        )
        content = StreamField(
            [
                ("text", RichTextBlock()),
                ("table", TableBlock()),
            ],
            verbose_name="Obsah",
            blank=True,
        )
        text = RichTextField("Text", null=True)
        sidebar_content = StreamField(
            [("address", blocks.AddressBlock()), ("contact", blocks.CenterContactBlock())],
            verbose_name="Obsah bočního panelu",
            blank=True,
        )
    
        ### PANELS
    
        content_panels = Page.content_panels + [
            FieldPanel("perex"),
            ImageChooserPanel("background_photo"),
            FieldPanel("text"),
            StreamFieldPanel("content"),
            FieldPanel("calendar_url"),
            StreamFieldPanel("sidebar_content"),
        ]
    
        ### RELATIONS
    
        parent_page_types = ["region.RegionHomePage"]
        subpage_types = []
    
        ### OTHERS
    
        class Meta:
            verbose_name = "Stránka pirátského centra"
    
        def get_background_photo(self):
            """
            Vrací background_photo pro pozadí na stránce, pokud není nastaveno,
            vezme falbback z homepage
            """
            return (
                self.background_photo
                if self.background_photo
                else self.root_page.fallback_image
            )
    
        @property
        def has_calendar(self):
            return self.calendar_id is not None
    
    
    class RegionCrossroadPage(SubpageMixin, MetadataPageMixin, Page):
    
        ### FIELDS
    
        cards_content = StreamField(
            [("cards", blocks.CardLinkWithHeadlineBlock())],
            verbose_name="Karty rozcestníku",
            blank=True,
        )
    
        content = StreamField(
            [
                ("people_group", blocks.PeopleGroupListBlock()),
            ],
            verbose_name="Obsah stránky",
            blank=True,
        )
    
        ### PANELS
    
        content_panels = Page.content_panels + [
            StreamFieldPanel("cards_content"),
            StreamFieldPanel("content"),
        ]
    
        ### RELATIONS
    
        parent_page_types = ["region.RegionHomePage"]
        subpage_types = [
            "region.RegionAfterElectionPage",
            "region.RegionArticlePage",
            "region.RegionArticlesPage",
            "region.RegionCenterPage",
            "region.RegionContactPage",
            "region.RegionCrossroadPage",
            "region.RegionCustomPage",
            "region.RegionElectionPage",
            "region.RegionElectionPointPage",
            "region.RegionPeoplePage",
            "region.RegionPersonPage",
            "region.RegionProgramPage",
            "region.RegionTagsPage",
        ]
        ### OTHERS
    
        class Meta:
            verbose_name = "Rozcestník s kartami"
    
    
    class RegionCustomPage(SubpageMixin, MetadataPageMixin, Page):
    
        ### FIELDS
    
        content = StreamField(
            [
                ("text", RichTextBlock()),
                ("table", TableBlock()),
                ("people_group", blocks.PeopleGroupListBlock()),
            ],
            verbose_name="Obsah",
            blank=True,
        )
    
        ### PANELS
    
        content_panels = Page.content_panels + [
            StreamFieldPanel("content"),
        ]
    
        ### RELATIONS
    
        parent_page_types = ["region.RegionHomePage", "region.RegionCrossroadPage"]
        subpage_types = []
    
        ### OTHERS
    
        class Meta:
            verbose_name = "Libovolná vlastní stránka"