Skip to content
Snippets Groups Projects
models.py 14.43 KiB
from django import forms
from django.core.paginator import Paginator
from django.db import models
from django.utils.translation import gettext_lazy
from modelcluster.contrib.taggit import ClusterTaggableManager
from modelcluster.fields import ParentalKey
from taggit.models import TaggedItemBase
from wagtail.admin.edit_handlers import (
    FieldPanel,
    HelpPanel,
    InlinePanel,
    MultiFieldPanel,
    ObjectList,
    PublishingPanel,
    StreamFieldPanel,
    TabbedInterface,
)
from wagtail.contrib.forms.edit_handlers import FormSubmissionsPanel
from wagtail.contrib.forms.models import AbstractForm, AbstractFormField
from wagtail.contrib.table_block.blocks import TableBlock
from wagtail.core import blocks
from wagtail.core.fields import StreamField
from wagtail.core.models import Page
from wagtail.images.blocks import ImageChooserBlock
from wagtail.images.edit_handlers import ImageChooserPanel
from wagtailmetadata.models import MetadataPageMixin

from shared.models import ArticleMixin, SubpageMixin
from tuning import help

from .constants import (
    ALIGN_CHOICES,
    ALIGN_CSS,
    ARTICLES_PER_LINE,
    ARTICLES_PER_PAGE,
    BLACK_ON_WHITE,
    COLOR_CHOICES,
    COLOR_CSS,
    LEFT,
    RICH_TEXT_FEATURES,
)


class ColorBlock(blocks.StructBlock):
    """
    Intended as parent class for blocks with color option.
    """

    color = blocks.ChoiceBlock(COLOR_CHOICES, label="barva", default=BLACK_ON_WHITE)

    def get_context(self, value, parent_context=None):
        context = super().get_context(value, parent_context=parent_context)
        if "css_class" not in context:
            context["css_class"] = []
        context["css_class"] += COLOR_CSS[value["color"]]
        return context


class AlignBlock(blocks.StructBlock):
    """
    Intended as parent class for blocks with align option.
    """

    align = blocks.ChoiceBlock(
        ALIGN_CHOICES, label="zarovnání", default=LEFT, widget=forms.RadioSelect
    )

    def get_context(self, value, parent_context=None):
        context = super().get_context(value, parent_context=parent_context)
        if "css_class" not in context:
            context["css_class"] = []
        context["css_class"] += ALIGN_CSS[value["align"]]
        return context


class ColumnsTextBlock(blocks.StructBlock):
    left_text = blocks.RichTextBlock(label="levý sloupec", features=RICH_TEXT_FEATURES)
    right_text = blocks.RichTextBlock(
        label="pravý sloupec", features=RICH_TEXT_FEATURES
    )

    class Meta:
        label = "text dva sloupce"
        icon = "doc-full"
        group = "texty"
        template = "uniweb/blocks/text_columns.html"


class AdvancedColumnsTextBlock(ColorBlock, AlignBlock):
    left_text = blocks.RichTextBlock(label="levý sloupec", features=RICH_TEXT_FEATURES)
    right_text = blocks.RichTextBlock(
        label="pravý sloupec", features=RICH_TEXT_FEATURES
    )

    class Meta:
        label = "text dva sloupce (pokročilý)"
        icon = "doc-full"
        group = "texty"
        template = "uniweb/blocks/advanced_text_columns.html"


class AdvancedTitleBlock(ColorBlock, AlignBlock):
    title = blocks.CharBlock(label="nadpis")

    class Meta:
        label = "nadpis (pokročilý)"
        icon = "title"
        group = "nadpisy"
        template = "uniweb/blocks/advanced_title.html"


class PictureTitleBlock(ColorBlock):
    title = blocks.CharBlock(label="nadpis")
    picture = ImageChooserBlock(
        label="obrázek",
        help_text="rozměr na výšku 75px nebo více (obrázek bude zmenšen na výšku 75px)",
    )

    class Meta:
        label = "nadpis (s obrázkem)"
        icon = "title"
        group = "nadpisy"
        template = "uniweb/blocks/picture_title.html"


class AdvancedTextBlock(ColorBlock, AlignBlock):
    text = blocks.RichTextBlock(label="text", features=RICH_TEXT_FEATURES)

    class Meta:
        label = "text (pokročilý)"
        icon = "doc-full"
        group = "texty"
        template = "uniweb/blocks/advanced_text.html"


class PictureListBlock(ColorBlock):
    items = blocks.ListBlock(
        blocks.RichTextBlock(label="odstavec", features=RICH_TEXT_FEATURES),
        label="odstavce",
    )
    picture = ImageChooserBlock(
        label="obrázek",
        # TODO rozměry v helpu
        help_text="rozměr 25x25px nebo více (obrázek bude zmenšen na 25x25px)",
    )

    class Meta:
        label = "seznam z obrázkovými odrážkami"
        icon = "list-ul"
        group = "texty"
        template = "uniweb/blocks/picture_list.html"


class ArticlesBlock(blocks.StructBlock):
    page = blocks.PageChooserBlock(
        label="sekce článků", page_type=["uniweb.UniwebArticlesIndexPage"]
    )
    lines = blocks.IntegerBlock(
        label="počet řádků",
        default=1,
        help_text="zobrazí se tři články na řádek",
    )

    class Meta:
        label = "články"
        icon = "folder-open-1"
        group = "ostatní"
        template = "uniweb/blocks/articles.html"

    def get_context(self, value, parent_context=None):
        context = super().get_context(value, parent_context=parent_context)
        count = value["lines"] * ARTICLES_PER_LINE
        context["articles"] = (
            value["page"]
            .get_children()
            .live()
            .specific()
            .order_by("-uniwebarticlepage__date")[:count]
        )
        return context


class MenuItemBlock(blocks.StructBlock):
    name = blocks.CharBlock(label="název")
    page = blocks.PageChooserBlock(
        label="stránka",
        page_type=[
            "uniweb.UniwebHomePage",
            "uniweb.UniwebFlexiblePage",
            "uniweb.UniwebArticlesIndexPage",
            "uniweb.UniwebFormPage",
        ],
    )

    class Meta:
        label = "stránka"


CONTENT_STREAM_BLOCKS = [
    (
        "title",
        blocks.CharBlock(
            label="nadpis",
            icon="title",
            group="nadpisy",
            template="uniweb/blocks/title.html",
        ),
    ),
    ("advanced_title", AdvancedTitleBlock()),
    ("picture_title", PictureTitleBlock()),
    (
        "text",
        blocks.RichTextBlock(
            label="text",
            features=RICH_TEXT_FEATURES,
            group="texty",
            template="uniweb/blocks/text.html",
        ),
    ),
    ("advanced_text", AdvancedTextBlock()),
    ("text_columns", ColumnsTextBlock()),
    ("advanced_text_columns", AdvancedColumnsTextBlock()),
    (
        "gallery",
        blocks.ListBlock(
            ImageChooserBlock(label="obrázek"),
            label="galerie",
            icon="image",
            group="ostatní",
            template="uniweb/blocks/gallery.html",
        ),
    ),
    ("picture_list", PictureListBlock()),
    (
        "table",
        TableBlock(
            label="tabulka",
            group="ostatní",
            template="uniweb/blocks/table.html",
        ),
    ),
    ("articles", ArticlesBlock()),
]


class UniwebArticleTag(TaggedItemBase):
    content_object = ParentalKey("uniweb.UniwebArticlePage", on_delete=models.CASCADE)


class UniwebHomePage(Page, MetadataPageMixin):
    ### FIELDS

    content = StreamField(
        CONTENT_STREAM_BLOCKS, verbose_name="obsah stránky", blank=True
    )

    # settings
    matomo_id = models.IntegerField(
        "Matomo ID pro sledování návštěvnosti", blank=True, null=True
    )
    top_menu = StreamField(
        [("item", MenuItemBlock())],
        verbose_name="horní menu",
        blank=True,
    )
    narrow_layout = models.BooleanField(
        "zúžený obsah stránky",
        default=False,
        help_text="užší stránka je vhodná pro lepší čitelnost textů",
    )

    ### PANELS

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

    promote_panels = [
        MultiFieldPanel(
            [
                FieldPanel("seo_title"),
                FieldPanel("search_description"),
                ImageChooserPanel("search_image"),
                HelpPanel(help.build(help.IMPORTANT_TITLE)),
            ],
            gettext_lazy("Common page configuration"),
        ),
    ]

    settings_panels = [
        MultiFieldPanel(
            [
                FieldPanel("matomo_id"),
                FieldPanel("narrow_layout"),
            ],
            "nastavení webu",
        )
    ]

    menu_panels = [StreamFieldPanel("top_menu")]

    edit_handler = TabbedInterface(
        [
            ObjectList(content_panels, heading=gettext_lazy("Content")),
            ObjectList(promote_panels, heading=gettext_lazy("Promote")),
            ObjectList(
                settings_panels, heading=gettext_lazy("Settings"), classname="settings"
            ),
            ObjectList(menu_panels, heading="Menu"),
        ]
    )

    ### RELATIONS

    subpage_types = [
        "uniweb.UniwebFlexiblePage",
        "uniweb.UniwebArticlesIndexPage",
        "uniweb.UniwebFormPage",
    ]

    ### OTHERS

    class Meta:
        verbose_name = "Univerzální web"

    @property
    def root_page(self):
        return self


class UniwebFlexiblePage(Page, SubpageMixin, MetadataPageMixin):
    ### FIELDS

    content = StreamField(
        CONTENT_STREAM_BLOCKS, verbose_name="obsah stránky", blank=True
    )

    ### PANELS

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

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

    settings_panels = []

    ### RELATIONS

    parent_page_types = [
        "uniweb.UniwebHomePage",
        "uniweb.UniwebFlexiblePage",
        "uniweb.UniwebFormPage",
    ]
    subpage_types = ["uniweb.UniwebFlexiblePage", "uniweb.UniwebFormPage"]

    ### OTHERS

    class Meta:
        verbose_name = "Flexibilní stránka"


class UniwebArticlesIndexPage(Page, SubpageMixin, MetadataPageMixin):
    ### FIELDS

    ### PANELS

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

    settings_panels = []

    ### RELATIONS

    parent_page_types = ["uniweb.UniwebHomePage"]
    subpage_types = ["uniweb.UniwebArticlePage"]

    ### OTHERS

    class Meta:
        verbose_name = "Sekce článků"

    def get_context(self, request):
        context = super().get_context(request)
        num = request.GET.get("page")
        tag = request.GET.get("tag")

        articles = (
            self.get_children().live().specific().order_by("-uniwebarticlepage__date")
        )
        if tag is not None:
            articles = articles.filter(uniwebarticlepage__tags__name=tag)

        context["articles"] = Paginator(articles, ARTICLES_PER_PAGE).get_page(num)
        context["tags"] = UniwebArticleTag.tags_for(UniwebArticlePage).filter(
            uniwebarticlepage__in=articles
        )
        context["active_tag"] = tag
        return context


class UniwebArticlePage(Page, ArticleMixin, SubpageMixin, MetadataPageMixin):
    ### FIELDS

    tags = ClusterTaggableManager(through=UniwebArticleTag, blank=True)

    ### PANELS

    content_panels = ArticleMixin.content_panels + [FieldPanel("tags")]

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

    settings_panels = [PublishingPanel()]

    ### RELATIONS

    parent_page_types = ["uniweb.UniwebArticlesIndexPage"]
    subpage_types = []

    ### OTHERS

    class Meta:
        verbose_name = "Článek"

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


class UniwebFormField(AbstractFormField):
    page = ParentalKey(
        "UniwebFormPage", on_delete=models.CASCADE, related_name="form_fields"
    )


class UniwebFormPage(AbstractForm, SubpageMixin, MetadataPageMixin):
    ### FIELDS

    content_before = StreamField(
        CONTENT_STREAM_BLOCKS, verbose_name="obsah stránky před formulářem", blank=True
    )
    content_after = StreamField(
        CONTENT_STREAM_BLOCKS, verbose_name="obsah stránky za formulářem", blank=True
    )
    content_landing = StreamField(
        CONTENT_STREAM_BLOCKS,
        verbose_name="obsah stránky zobrazené po odeslání formuláře",
        blank=True,
    )

    ### PANELS

    content_panels = AbstractForm.content_panels + [
        FormSubmissionsPanel(),
        StreamFieldPanel("content_before"),
        InlinePanel("form_fields", label="formulář"),
        StreamFieldPanel("content_after"),
        StreamFieldPanel("content_landing"),
    ]

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

    submissions_panels = [
        FormSubmissionsPanel(),
    ]

    edit_handler = TabbedInterface(
        [
            ObjectList(content_panels, heading=gettext_lazy("Content")),
            ObjectList(promote_panels, heading=gettext_lazy("Promote")),
            ObjectList(submissions_panels, heading="Data z formuláře"),
        ]
    )

    ### RELATIONS

    parent_page_types = [
        "uniweb.UniwebHomePage",
        "uniweb.UniwebFlexiblePage",
        "uniweb.UniwebFormPage",
    ]
    subpage_types = ["uniweb.UniwebFlexiblePage", "uniweb.UniwebFormPage"]

    ### OTHERS

    class Meta:
        verbose_name = "Formulářová stránka"