Skip to content
Snippets Groups Projects
models.py 14.53 KiB
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
from django.db import models
from django.utils.translation import gettext_lazy
from wagtail.admin.edit_handlers import (
    CommentPanel,
    FieldPanel,
    HelpPanel,
    MultiFieldPanel,
    PublishingPanel,
    StreamFieldPanel,
)
from wagtail.core import blocks
from wagtail.core.fields import RichTextField, StreamField
from wagtail.core.models import Page
from wagtail.documents.blocks import DocumentChooserBlock
from wagtail.images.blocks import ImageChooserBlock
from wagtail.images.edit_handlers import ImageChooserPanel
from wagtailmetadata.models import MetadataPageMixin

from calendar_utils.models import CalendarMixin
from shared.models import SubpageMixin
from tuning import admin_help

HELP_COMBINED_TITLE = (
    "Pokud není zadán <strong>Titulek stránky</strong>, použije se "
    "<strong>Název</strong> (tab obsah) doplněný o jméno kandidáta."
)


class MetaMixin:
    def get_meta_title(self):
        if self.seo_title:
            return self.seo_title
        return f"{self.title} | {self.root_page.full_name}"


class ContactBlock(blocks.StructBlock):
    name = blocks.CharBlock(label="jméno")
    job = blocks.CharBlock(label="pozice", required=False)
    phone = blocks.CharBlock(label="telefon", required=False)
    email = blocks.EmailBlock(label="email", required=False)
    photo = ImageChooserBlock(label="fotka")

    class Meta:
        icon = "person"
        label = "kontaktní osoba"


class SenatCampaignHomePage(Page, MetadataPageMixin, CalendarMixin):
    ### FIELDS

    # top section
    headline = models.CharField("podtitulek pod jménem", max_length=250, blank=True)
    top_photo = models.ForeignKey(
        "wagtailimages.Image",
        on_delete=models.PROTECT,
        blank=True,
        null=True,
        verbose_name="hlavní fotka",
    )
    claim = models.CharField("slogan pod fotkou", max_length=250, blank=True, null=True)
    # about
    about_left = RichTextField(
        "kdo jsem (levý sloupec)",
        blank=True,
        features=["h4", "bold", "italic", "ol", "ul", "link", "document-link"],
    )
    about_right = RichTextField(
        "kdo jsem (pravý sloupec)",
        blank=True,
        features=["h4", "bold", "italic", "ol", "ul", "link", "document-link"],
    )
    about_gallery = StreamField(
        [("photo", ImageChooserBlock(label="fotka"))],
        verbose_name="kdo jsem - galerie",
        blank=True,
    )
    # financials
    financials = StreamField(
        [
            (
                "link",
                blocks.StructBlock(
                    [
                        ("label", blocks.CharBlock(label="název")),
                        ("url", blocks.URLBlock(label="odkaz")),
                    ],
                    label="odkaz",
                ),
            ),
            (
                "document",
                blocks.StructBlock(
                    [
                        ("label", blocks.CharBlock(label="název")),
                        ("doc", DocumentChooserBlock(label="dokument")),
                    ],
                    label="dokument",
                ),
            ),
        ],
        verbose_name="transparentní financování",
        blank=True,
    )
    # settings
    first_name = models.CharField("jméno", max_length=250)
    last_name = models.CharField("příjmení", max_length=250)
    degree_before = models.CharField(
        "titul před jménem", max_length=250, blank=True, null=True
    )
    degree_after = models.CharField(
        "titul za jménem", max_length=250, blank=True, null=True
    )
    facebook = models.URLField("Facebook URL", blank=True, null=True)
    instagram = models.URLField("Instagram URL", blank=True, null=True)
    twitter = models.URLField("Twitter URL", blank=True, null=True)
    linkedin = models.URLField("LinkedIn URL", blank=True, null=True)
    contacts = StreamField(
        [("item", ContactBlock())], verbose_name="kontaktní osoby", blank=True
    )
    matomo_id = models.IntegerField(
        "Matomo ID pro sledování návštěvnosti", blank=True, null=True
    )

    ### PANELS

    content_panels = Page.content_panels + [
        MultiFieldPanel(
            [
                FieldPanel("degree_before"),
                FieldPanel("first_name"),
                FieldPanel("last_name"),
                FieldPanel("degree_after"),
                FieldPanel("headline"),
            ],
            heading="úvod",
        ),
        MultiFieldPanel(
            [
                ImageChooserPanel("top_photo"),
                FieldPanel("claim"),
            ],
            heading="úvodní foto",
        ),
        MultiFieldPanel(
            [
                FieldPanel("about_left", classname="full"),
                FieldPanel("about_right", classname="full"),
                StreamFieldPanel("about_gallery"),
            ],
            heading="kdo jsem",
        ),
        StreamFieldPanel("financials"),
    ]

    promote_panels = [
        MultiFieldPanel(
            [
                FieldPanel("seo_title"),
                FieldPanel("search_description"),
                ImageChooserPanel("search_image"),
                HelpPanel(
                    admin_help.build(
                        admin_help.IMPORTANT_TITLE,
                        "Pokud není zadán <strong>Search image</strong>, použije se úvodní foto kandidáta.",
                    )
                ),
            ],
            gettext_lazy("Common page configuration"),
        ),
    ]

    settings_panels = [
        MultiFieldPanel(
            [
                FieldPanel("facebook"),
                FieldPanel("instagram"),
                FieldPanel("twitter"),
                FieldPanel("linkedin"),
            ],
            heading="Sociální sítě",
        ),
        FieldPanel("calendar_url"),
        FieldPanel("matomo_id"),
        StreamFieldPanel("contacts"),
        CommentPanel(),
    ]

    ### RELATIONS

    subpage_types = [
        "senat_campaign.SenatCampaignNewsIndexPage",
        "senat_campaign.SenatCampaignProgramPage",
        "senat_campaign.SenatCampaignCookiesPage",
    ]

    ### OTHERS

    # flag for rendering anchor links in menu
    is_home = True

    class Meta:
        verbose_name = "Senát kampaň"

    @property
    def root_page(self):
        return self

    def get_meta_image(self):
        return self.search_image or self.top_photo

    @property
    def full_name(self):
        return f"{self.first_name} {self.last_name}"

    @property
    def has_program(self):
        return self.get_descendants().type(SenatCampaignProgramPage).live().exists()

    @property
    def has_news(self):
        return self.get_descendants().type(SenatCampaignNewsIndexPage).live().exists()

    @property
    def has_calendar(self):
        return self.calendar_id is not None

    @property
    def has_donations(self):
        # TODO
        return False

    @property
    def cookies_page_url(self):
        try:
            return (
                self.get_descendants()
                .type(SenatCampaignCookiesPage)
                .live()
                .get()
                .get_url()
            )
        except Page.DoesNotExist:
            return "#"

    def get_context(self, request):
        context = super().get_context(request)

        # get news
        context["articles"] = (
            self.get_descendants()
            .type(SenatCampaignNewsPage)
            .live()
            .order_by("-senatcampaignnewspage__date")[:3]
        )

        try:
            context["news_url"] = (
                self.get_children()
                .type(SenatCampaignNewsIndexPage)
                .live()
                .get()
                .get_url(request)
            )
        except Page.DoesNotExist:
            context["news_url"] = "#"

        # get page with program
        try:
            context["program_page"] = (
                self.get_children()
                .type(SenatCampaignProgramPage)
                .live()
                .specific()
                .get()
            )
        except Page.DoesNotExist:
            context["program_page"] = None

        return context


class SenatCampaignNewsIndexPage(Page, SubpageMixin, MetaMixin, MetadataPageMixin):
    ### FIELDS

    ### PANELS

    promote_panels = [
        MultiFieldPanel(
            [
                FieldPanel("slug"),
                FieldPanel("seo_title"),
                FieldPanel("search_description"),
                ImageChooserPanel("search_image"),
                HelpPanel(
                    admin_help.build(HELP_COMBINED_TITLE, admin_help.NO_SEARCH_IMAGE)
                ),
            ],
            gettext_lazy("Common page configuration"),
        ),
    ]

    settings_panels = [CommentPanel()]

    ### RELATIONS

    parent_page_types = ["senat_campaign.SenatCampaignHomePage"]
    subpage_types = ["senat_campaign.SenatCampaignNewsPage"]

    ### OTHERS

    # flag for rendering anchor links in menu
    is_home = False

    class Meta:
        verbose_name = "Aktuality"

    def get_context(self, request):
        context = super().get_context(request)
        articles = self.get_children().live().order_by("-senatcampaignnewspage__date")
        paginator = Paginator(articles, 4)

        page = request.GET.get("page")
        try:
            articles = paginator.page(page)
        except PageNotAnInteger:
            # If page is not an integer, deliver first page.
            articles = paginator.page(1)
        except EmptyPage:
            # If page is out of range (e.g. 9999), deliver last page of results.
            articles = paginator.page(paginator.num_pages)

        context["articles"] = articles
        return context


class SenatCampaignNewsPage(Page, SubpageMixin, MetaMixin, MetadataPageMixin):
    ### FIELDS

    date = models.DateField("datum")
    perex = models.TextField("perex")
    body = RichTextField("článek", blank=True)
    photo = models.ForeignKey(
        "wagtailimages.Image",
        verbose_name="fotka",
        on_delete=models.PROTECT,
        null=True,
        blank=True,
    )
    # we will use photo as search image
    search_image = None

    ### PANELS

    content_panels = Page.content_panels + [
        FieldPanel("date"),
        FieldPanel("perex"),
        FieldPanel("body", classname="full"),
        ImageChooserPanel("photo"),
    ]

    promote_panels = [
        MultiFieldPanel(
            [
                FieldPanel("slug"),
                FieldPanel("seo_title"),
                FieldPanel("search_description"),
                HelpPanel(
                    admin_help.build(
                        HELP_COMBINED_TITLE,
                        "Pokud není zadán <strong>Popis vyhledávání</strong>, použije se prvních 150 znaků <strong>Perexu</strong> (tab obsah).",
                    )
                ),
            ],
            gettext_lazy("Common page configuration"),
        ),
    ]

    settings_panels = [PublishingPanel(), CommentPanel()]

    ### RELATIONS

    parent_page_types = ["senat_campaign.SenatCampaignNewsIndexPage"]
    subpage_types = []

    ### OTHERS

    # flag for rendering anchor links in menu
    is_home = False

    class Meta:
        verbose_name = "Aktualita"

    def get_context(self, request):
        context = super().get_context(request)
        context["related_articles"] = (
            self.get_siblings(inclusive=False)
            .live()
            .order_by("-senatcampaignnewspage__date")[:3]
        )
        return context

    @property
    def root_page(self):
        if not hasattr(self, "_root_page"):
            self._root_page = (
                self.get_ancestors().type(SenatCampaignHomePage).specific().get()
            )
        return self._root_page

    def get_meta_image(self):
        return self.photo

    def get_meta_description(self):
        if self.search_description:
            return self.search_description
        if len(self.perex) > 150:
            return str(self.perex)[:150] + "..."
        return self.perex


class ProgramBlock(blocks.StructBlock):
    title = blocks.CharBlock(label="titulek")
    perex = blocks.TextBlock(label="perex na úvodní stránku")
    body = blocks.RichTextBlock(label="text bodu")
    image = ImageChooserBlock(label="ilustrační obrázek")

    class Meta:
        icon = "doc-full"
        label = "programový bod"


class SenatCampaignProgramPage(Page, SubpageMixin, MetaMixin, MetadataPageMixin):
    ### FIELDS

    committee_preference = StreamField(
        [("committee", blocks.CharBlock(label="výbor či komise"))],
        verbose_name="preferované výbory a komise",
        blank=True,
    )
    content = StreamField(
        [("item", ProgramBlock())], verbose_name="programové body", blank=True
    )

    ### PANELS

    content_panels = Page.content_panels + [
        StreamFieldPanel("committee_preference"),
        StreamFieldPanel("content"),
    ]

    promote_panels = [
        MultiFieldPanel(
            [
                FieldPanel("slug"),
                FieldPanel("seo_title"),
                FieldPanel("search_description"),
                ImageChooserPanel("search_image"),
                HelpPanel(
                    admin_help.build(HELP_COMBINED_TITLE, admin_help.NO_SEARCH_IMAGE)
                ),
            ],
            gettext_lazy("Common page configuration"),
        ),
    ]

    settings_panels = [CommentPanel()]

    ### RELATIONS

    parent_page_types = ["senat_campaign.SenatCampaignHomePage"]
    subpage_types = []

    ### OTHERS

    # flag for rendering anchor links in menu
    is_home = False

    class Meta:
        verbose_name = "Program"


class SenatCampaignCookiesPage(Page, SubpageMixin, MetaMixin, MetadataPageMixin):
    ### FIELDS

    body = RichTextField("obsah", blank=True)

    ### PANELS

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

    promote_panels = [
        MultiFieldPanel(
            [
                FieldPanel("slug"),
                FieldPanel("seo_title"),
                FieldPanel("search_description"),
                ImageChooserPanel("search_image"),
                HelpPanel(
                    admin_help.build(HELP_COMBINED_TITLE, admin_help.NO_SEARCH_IMAGE)
                ),
            ],
            gettext_lazy("Common page configuration"),
        ),
    ]

    settings_panels = [CommentPanel()]

    ### RELATIONS

    parent_page_types = ["senat_campaign.SenatCampaignHomePage"]
    subpage_types = []

    ### OTHERS

    # flag for rendering anchor links in menu
    is_home = False

    class Meta:
        verbose_name = "Cookies"