from captcha.fields import CaptchaField 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 ( CommentPanel, FieldPanel, 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 wagtailmetadata.models import MetadataPageMixin from calendar_utils.models import CalendarMixin from shared.models import ( ArticleMixin, ExtendedMetadataHomePageMixin, ExtendedMetadataPageMixin, SubpageMixin, ) from shared.utils import make_promote_panels from tuning import admin_help from .constants import ( ALIGN_CHOICES, ALIGN_CSS, ARTICLES_PER_LINE, ARTICLES_PER_PAGE, BLACK_ON_WHITE, CALENDAR_EVENTS_CHOICES, COLOR_CHOICES, COLOR_CSS, FUTURE, 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" class CalendarAgendaBlock(blocks.StructBlock): info = blocks.StaticBlock( label="volba kalendáře", admin_text="adresa kalendáře se zadává v nastavení hlavní stránky webu", ) count = blocks.IntegerBlock(label="maximum událostí k zobrazení", default=10) event_type = blocks.ChoiceBlock( CALENDAR_EVENTS_CHOICES, label="druh událostí", default=FUTURE, widget=forms.RadioSelect, ) class Meta: label = "kalendář agenda" icon = "date" group = "ostatní" template = "uniweb/blocks/calendar_agenda.html" def get_context(self, value, parent_context=None): context = super().get_context(value, parent_context=parent_context) count = value["count"] page = context["page"] if page.root_page.has_calendar: if value["event_type"] == FUTURE: context["events"] = page.root_page.calendar.future_events[:count] else: context["events"] = page.root_page.calendar.past_events[:count] else: context["events"] = [] return context 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()), ("calendar_agenda", CalendarAgendaBlock()), ] class UniwebArticleTag(TaggedItemBase): content_object = ParentalKey("uniweb.UniwebArticlePage", on_delete=models.CASCADE) class UniwebHomePage( Page, ExtendedMetadataHomePageMixin, MetadataPageMixin, CalendarMixin ): ### 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ů", ) ### Footer show_logo = models.BooleanField( "zobrazit logo", default=True, help_text="Zobrazit logo" ) show_social_links = models.BooleanField( "zobrazit soc. linky", default=True, help_text="Zobrazit link na sociální sítě" ) show_pirate_buttons = models.BooleanField( "zobrazit pirátská tlačítka", default=True, help_text="Zobrazit pirátská tlačítka", ) ### PANELS content_panels = Page.content_panels + [StreamFieldPanel("content")] promote_panels = make_promote_panels(admin_help.build(admin_help.IMPORTANT_TITLE)) settings_panels = [ MultiFieldPanel( [ FieldPanel("matomo_id"), FieldPanel("title_suffix"), FieldPanel("narrow_layout"), ], "nastavení webu", ), FieldPanel("calendar_url"), MultiFieldPanel( [ FieldPanel("show_logo"), FieldPanel("show_social_links"), FieldPanel("show_pirate_buttons"), ], "nastavení patičky", ), ] 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 @property def has_calendar(self): return self.calendar_id is not None class UniwebFlexiblePage( Page, ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin ): ### FIELDS content = StreamField( CONTENT_STREAM_BLOCKS, verbose_name="obsah stránky", blank=True ) ### PANELS promote_panels = make_promote_panels() 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, ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin ): ### FIELDS ### PANELS promote_panels = make_promote_panels() 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( ArticleMixin, ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, Page ): ### FIELDS tags = ClusterTaggableManager(through=UniwebArticleTag, blank=True) ### PANELS content_panels = ArticleMixin.content_panels + [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 = ["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, ExtendedMetadataPageMixin, 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 + [ StreamFieldPanel("content_before"), InlinePanel("form_fields", label="formulář"), StreamFieldPanel("content_after"), StreamFieldPanel("content_landing"), ] promote_panels = make_promote_panels() 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" def get_form_class(self): form = super().get_form_class() form.base_fields["captcha"] = CaptchaField(label="opište písmena z obrázku") return form