Skip to content
Snippets Groups Projects
Select Git revision
  • 650f89005a182f729f02ac2fda01a972026d3e30
  • test default protected
  • master protected
  • feat/custom-css
  • feat/redesign-improvements-10
  • feat/redesign-improvements-8
  • feat/redesign-fixes-3
  • feat/pirstan-changes
  • feat/separate-import-thread
  • feat/dary-improvements
  • features/add-pdf-page
  • features/add-typed-table
  • features/fix-broken-calendar-categories
  • features/add-embed-to-articles
  • features/create-mastodon-feed-block
  • features/add-custom-numbering-for-candidates
  • features/add-timeline
  • features/create-wordcloud-from-article-page
  • features/create-collapsible-extra-legal-info
  • features/extend-hero-banner
  • features/add-link-to-images
21 results

0043_alter_donateprojectpage_form_title.py

Blame
  • models.py 29.61 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 (
        FieldPanel,
        HelpPanel,
        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,
        ExtendedMetadataHomePageMixin,
        ExtendedMetadataPageMixin,
        MenuMixin,
        SubpageMixin,
    )
    from shared.utils import make_promote_panels
    from tuning import admin_help
    from uniweb.constants import RICH_TEXT_FEATURES
    
    from . import blocks
    from .forms import JekyllImportForm
    
    
    class DistrictHomePage(
        MenuMixin, ExtendedMetadataHomePageMixin, 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(
            "district.DistrictPersonPage",
            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(
            "district.DistrictPersonPage",
            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(
            "district.DistrictPersonPage",
            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 = make_promote_panels(admin_help.build(admin_help.IMPORTANT_TITLE))
    
        settings_panels = [
            ImageChooserPanel("custom_logo"),
            FieldPanel("matomo_id"),
            FieldPanel("title_suffix"),
            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("contact_email"),
                    FieldPanel("contact_phone"),
                    FieldPanel("contact_newcomers"),
                ],
                gettext_lazy("Kontakty"),
            ),
            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"),
        ]
    
        ### 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 = [
            "district.DistrictArticlesPage",
            "district.DistrictCenterPage",
            "district.DistrictContactPage",
            "district.DistrictCrossroadPage",
            "district.DistrictCustomPage",
            "district.DistrictElectionPage",
            "district.DistrictPeoplePage",
            "district.DistrictProgramPage",
            "district.DistrictTagsPage",
        ]
    
        ### OTHERS
    
        class Meta:
            verbose_name = "Místní 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(DistrictArticlePage)
                .live()
                .specific()
                .order_by("-districtarticlepage__date")[:6]
            )
    
        @property
        def articles_page(self):
            return self._first_subpage_of_type(DistrictArticlesPage)
    
        @property
        def center_page(self):
            return self._first_subpage_of_type(DistrictCenterPage)
    
        @property
        def contact_page(self):
            return self._first_subpage_of_type(DistrictContactPage)
    
        @property
        def election_page(self):
            return self._first_subpage_of_type(DistrictElectionPage)
    
        @staticmethod
        def get_404_response(request):
            return render(request, "district/404.html", status=404)
    
        @property
        def people_page(self):
            return self._first_subpage_of_type(DistrictPeoplePage)
    
        @property
        def program_page(self):
            return self._first_subpage_of_type(DistrictProgramPage)
    
        @property
        def root_page(self):
            return self
    
        @property
        def tags_page(self):
            return self._first_subpage_of_type(DistrictTagsPage)
    
        @property
        def has_calendar(self):
            return self.calendar_id is not None
    
    
    class DistrictArticleTag(TaggedItemBase):
        content_object = ParentalKey(
            "district.DistrictArticlePage", on_delete=models.CASCADE
        )
    
    
    class DistrictArticlePage(
        ArticleMixin, ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, Page
    ):
        ### FIELDS
    
        author_page = models.ForeignKey(
            "district.DistrictPersonPage", on_delete=models.SET_NULL, null=True, blank=True
        )
        is_black = models.BooleanField("Má tmavé pozadí?", default=False)
        tags = ClusterTaggableManager(through=DistrictArticleTag, blank=True)
    
        ### PANELS
    
        content_panels = ArticleMixin.content_panels + [
            FieldPanel("author_page"),
            FieldPanel("is_black"),
            FieldPanel("tags"),
        ]
    
        promote_panels = make_promote_panels(
            admin_help.build(admin_help.NO_SEO_TITLE, admin_help.NO_DESCRIPTION_USE_PEREX),
            search_image=False,
        )
    
        ### RELATIONS
    
        parent_page_types = ["district.DistrictArticlesPage"]
        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()  # TODO? filtrovat na stejné tagy?
                .specific()
                .order_by("-districtarticlepage__date")[:3]
            )
            return context
    
    
    class DistrictArticlesPage(
        ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, Page
    ):
        ### FIELDS
    
        last_import_log = models.TextField(
            "Výstup z posledního importu", null=True, blank=True
        )
        max_items = models.IntegerField("Počet článků na stránce", default=12)
    
        ### PANELS
    
        content_panels = Page.content_panels + [
            FieldPanel("max_items"),
        ]
    
        promote_panels = make_promote_panels()
    
        import_panels = [
            MultiFieldPanel(
                [
                    FieldPanel("do_import"),
                    FieldPanel("collection"),
                    FieldPanel("dry_run"),
                    FieldPanel("use_git"),
                    FieldPanel("jekyll_repo_url"),
                    FieldPanel("readonly_log"),
                    HelpPanel(
                        "Import provádějte vždy až po vytvoření stránky aktualit. "
                        'Pro uložení logu je nutné volit možnost "Publikovat", nikoliv'
                        'pouze "Uložit koncept". '
                        "Import proběhne na pozadí a může trvat až několik minut. "
                        "Dejte si po spuštění importu kávu a potom obnovte stránku pro "
                        "zobrazení výsledku importu."
                    ),
                ],
                "import z Jekyll repozitáře",
            ),
        ]
    
        ### EDIT HANDLERS
    
        edit_handler = TabbedInterface(
            [
                ObjectList(content_panels, heading="Obsah"),
                ObjectList(promote_panels, heading="Propagovat"),
                ObjectList(import_panels, heading="Import"),
            ]
        )
    
        ### RELATIONS
    
        parent_page_types = ["district.DistrictHomePage"]
        subpage_types = ["district.DistrictArticlePage"]
    
        ### OTHERS
    
        base_form_class = JekyllImportForm
    
        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("-districtarticlepage__date"),
                self.max_items,
            ).get_page(request.GET.get("page"))
            return context
    
    
    class DistrictContactPage(
        ExtendedMetadataPageMixin, 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"),
        ]
    
        promote_panels = make_promote_panels()
    
        settings_panels = []
    
        ### RELATIONS
    
        parent_page_types = ["district.DistrictHomePage"]
        subpage_types = []
    
        ### OTHERS
    
        class Meta:
            verbose_name = "Kontakty"
    
    
    class DistrictTagsPage(
        ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, Page
    ):
        ### PANELS
    
        promote_panels = make_promote_panels()
    
        settings_panels = []
    
        ### RELATIONS
    
        parent_page_types = ["district.DistrictHomePage"]
        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 = DistrictArticlePage.objects.filter(id__in=site_article_ids)
    
            try:
                tag = DistrictArticleTag.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 DistrictArticlePage omezeno IDčky getnutých přes
            root_page. Počítá, kolik článků je s daným tagem.
            """
            return (
                Tag.objects.filter(districtarticlepage__id__in=site_article_ids)
                .order_by("slug")
                .annotate(count=models.Count("slug"))
                .values("name", "slug", "count")
            )
    
    
    class DistrictPersonTag(TaggedItemBase):
        content_object = ParentalKey(
            "district.DistrictPersonPage", on_delete=models.CASCADE
        )
    
    
    class DistrictPersonPageForm(WagtailAdminPageForm):
        def clean(self):
            cleaned_data = super().clean()
            email = cleaned_data.get("email", None)
            if email:
                pages_with_email = DistrictPersonPage.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 DistrictPersonPage(
        ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, Page
    ):
        ### FIELDS
        base_form_class = DistrictPersonPageForm
    
        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"),
                    StreamFieldPanel("other_urls"),
                ],
                "Sociální sítě",
            ),
        ]
    
        settings_panels = []
    
        ### RELATIONS
    
        parent_page_types = ["district.DistrictPeoplePage"]
        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 DistrictPeoplePage(
        ExtendedMetadataPageMixin, 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")]
    
        promote_panels = make_promote_panels()
    
        settings_panels = []
    
        ### RELATIONS
    
        parent_page_types = ["district.DistrictHomePage"]
        subpage_types = ["district.DistrictPersonPage"]
    
        ### OTHERS
    
        class Meta:
            verbose_name = "Lidé"
    
    
    class DistrictElectionBasePage(
        ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, Page
    ):
        ### FIELDS
        text = RichTextField("text", blank=True, features=RICH_TEXT_FEATURES)
    
        ### PANELS
    
        content_panels = Page.content_panels + [FieldPanel("text")]
    
        promote_panels = make_promote_panels()
    
        settings_panels = []
    
        ### RELATIONS
    
        parent_page_types = ["district.DistrictElectionPage"]
        subpage_types = []
    
        def get_context(self, request, *args, **kwargs):
            context = super().get_context(request, *args, **kwargs)
            context["election_points"] = (
                self.get_siblings().type(DistrictElectionPointPage).live().specific()
            )
            context["strategy_page"] = self.get_parent().specific.strategy_page
            return context
    
        class Meta:
            abstract = True
    
    
    class DistrictAfterElectionPage(DistrictElectionBasePage):
        class Meta:
            verbose_name = "Povolební strategie"
    
    
    class DistrictElectionPointPage(DistrictElectionBasePage):
        ### FIELDS
        guarantor = models.ForeignKey(
            "district.DistrictPersonPage", 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 = DistrictElectionBasePage.content_panels + [
            PageChooserPanel("guarantor"),
            ImageChooserPanel("list_image"),
            FieldPanel("perex"),
        ]
    
        ### OTHERS
    
        class Meta:
            verbose_name = "Bod programu voleb"
    
    
    class DistrictElectionPage(
        ExtendedMetadataPageMixin, 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 = ["district.DistrictHomePage"]
        subpage_types = [
            "district.DistrictElectionPointPage",
            "district.DistrictAfterElectionPage",
        ]
    
        ### 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(DistrictElectionPointPage).specific()
            )
            return context
    
        @property
        def strategy_page(self):
            try:
                return (
                    self.get_descendants()
                    .type(DistrictAfterElectionPage)
                    .live()
                    .specific()[0]
                )
            except IndexError:
                return None
    
    
    class DistrictProgramPage(
        ExtendedMetadataPageMixin, 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"),
        ]
    
        promote_panels = make_promote_panels()
    
        settings_panels = []
    
        ### RELATIONS
    
        parent_page_types = ["district.DistrictHomePage"]
        subpage_types = []
    
        ### OTHERS
    
        class Meta:
            verbose_name = "Program"
    
        def save(self, **kwargs):
            from redmine_utils.functions import fill_data_from_redmine_for_page
    
            fill_data_from_redmine_for_page(self)
            return super().save(**kwargs)
    
    
    class DistrictCenterPage(
        CalendarMixin, ExtendedMetadataPageMixin, 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", blank=True, 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"),
        ]
    
        promote_panels = make_promote_panels()
    
        settings_panels = []
    
        ### RELATIONS
    
        parent_page_types = ["district.DistrictHomePage"]
        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 DistrictCrossroadPage(
        ExtendedMetadataPageMixin, 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"),
        ]
    
        promote_panels = make_promote_panels()
    
        settings_panels = []
    
        ### RELATIONS
    
        parent_page_types = ["district.DistrictHomePage"]
        subpage_types = [
            "district.DistrictAfterElectionPage",
            "district.DistrictArticlePage",
            "district.DistrictArticlesPage",
            "district.DistrictCenterPage",
            "district.DistrictContactPage",
            "district.DistrictCrossroadPage",
            "district.DistrictCustomPage",
            "district.DistrictElectionPage",
            "district.DistrictElectionPointPage",
            "district.DistrictPeoplePage",
            "district.DistrictPersonPage",
            "district.DistrictProgramPage",
            "district.DistrictTagsPage",
        ]
        ### OTHERS
    
        class Meta:
            verbose_name = "Rozcestník s kartami"
    
    
    class DistrictCustomPage(
        ExtendedMetadataPageMixin, 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"),
        ]
    
        promote_panels = make_promote_panels()
    
        settings_panels = []
    
        ### RELATIONS
    
        parent_page_types = ["district.DistrictHomePage", "district.DistrictCrossroadPage"]
        subpage_types = []
    
        ### OTHERS
    
        class Meta:
            verbose_name = "Libovolná vlastní stránka"