diff --git a/district/blocks.py b/district/blocks.py index 4de02c597be0f9709a32546685f52dbb87c92563..f17fee7a69f0b661191a65be9926a1099e8b4d58 100644 --- a/district/blocks.py +++ b/district/blocks.py @@ -123,11 +123,11 @@ class CardLinkBlock(CardLinkBlockMixin): class Meta: template = "district/blocks/card_link_block.html" icon = "link" - label = "Karta odkazu" + label = "Karta s odkazem" class CardLinkWithHeadlineBlock(CardLinkWithHeadlineBlockMixin): - card_items = ListBlock(CardLinkBlock(), label="Karty odkazu") + card_items = ListBlock(CardLinkBlock(), label="Karty s odkazy") class Meta: template = "district/blocks/card_link_with_headline_block.html" diff --git a/district/migrations/0106_alter_districtcrossroadpage_cards_content.py b/district/migrations/0106_alter_districtcrossroadpage_cards_content.py new file mode 100644 index 0000000000000000000000000000000000000000..3f106be32e610f7ea096b90721e171c23e0e644e --- /dev/null +++ b/district/migrations/0106_alter_districtcrossroadpage_cards_content.py @@ -0,0 +1,21 @@ +# Generated by Django 4.1.6 on 2023-02-28 07:51 + +from django.db import migrations +import wagtail.blocks +import wagtail.fields +import wagtail.images.blocks + + +class Migration(migrations.Migration): + + dependencies = [ + ('district', '0105_alter_districtarticlepage_content'), + ] + + operations = [ + migrations.AlterField( + model_name='districtcrossroadpage', + name='cards_content', + field=wagtail.fields.StreamField([('cards', wagtail.blocks.StructBlock([('headline', wagtail.blocks.CharBlock(label='Titulek bloku', required=False)), ('card_items', wagtail.blocks.ListBlock(wagtail.blocks.StructBlock([('image', wagtail.images.blocks.ImageChooserBlock(label='Obrázek')), ('title', wagtail.blocks.CharBlock(label='Titulek', required=True)), ('text', wagtail.blocks.RichTextBlock(label='Krátký text pod nadpisem', required=False)), ('page', wagtail.blocks.PageChooserBlock(label='Stránka', page_type=['district.DistrictArticlePage', 'district.DistrictArticlesPage', 'district.DistrictCenterPage', 'district.DistrictContactPage', 'district.DistrictCrossroadPage', 'district.DistrictCustomPage', 'district.DistrictElectionCampaignPage', 'district.DistrictElectionProgramPage', 'district.DistrictElectionRootPage', 'district.DistrictPeoplePage', 'district.DistrictPersonPage', 'district.DistrictPostElectionStrategyPage', 'district.DistrictProgramPage'], required=False)), ('link', wagtail.blocks.URLBlock(label='Odkaz', required=False))]), label='Karty s odkazy'))]))], blank=True, use_json_field=True, verbose_name='Karty rozcestníku'), + ), + ] diff --git a/district/templates/district/base.html b/district/templates/district/base.html index c621364257e59fc041e38f02205a80f8fe1840c8..0f20b30bd7e8579b8006969b559f882b8250e118 100644 --- a/district/templates/district/base.html +++ b/district/templates/district/base.html @@ -59,7 +59,7 @@ <div class="navbar__brand my-4 flex items-center lg:block lg:pr-8 lg:my-0"> <a href="/"> {% if page.root_page.custom_logo %} - {% image page.root_page.custom_logo width-128 as logo %} + {% image page.root_page.custom_logo width-512 as logo %} <img src="{{ logo.url }}" class="w-10 lg:w-full lg:border-r lg:border-grey-300 lg:pr-8" diff --git a/main/blocks.py b/main/blocks.py index 71c244416bc018c0cc2a4bfa5ae146e2cf8d461f..04cab96d35c50b3452f2f4af2ceaa5e5253a2545 100644 --- a/main/blocks.py +++ b/main/blocks.py @@ -53,7 +53,7 @@ class CardLinkBlock(CardLinkBlockMixin): class Meta: template = "main/blocks/card_link_block.html" icon = "link" - label = "Karta odkazu" + label = "Karta s odkazem" class CTAMixin(StructBlock): @@ -347,12 +347,12 @@ class TwoTextColumnBlock(StructBlock): text_column_2 = RichTextBlock(label="Druhý sloupec textu") class Meta: - icon = "text" + icon = "doc-full" label = "Text ve dvou sloupcích" class CardLinkWithHeadlineBlock(CardLinkWithHeadlineBlockMixin): - card_items = ListBlock(CardLinkBlock(), label="Karty odkazu") + card_items = ListBlock(CardLinkBlock(), label="Karty s odkazy") class Meta: template = "main/blocks/card_link_with_headline_block.html" @@ -384,3 +384,38 @@ class InstagramPostsBlock(StructBlock): template = "main/blocks/instagram_block.html" icon = "openquote" label = "Instagramové posty" + + +class HoaxBlock(StructBlock): + title = CharBlock(label="Titulek") + hoax = RichTextBlock(label="Hoax") + image = ImageChooserBlock(label="Obrázek") + image_explanation = CharBlock(label="Popis obrázku", required=False) + reality = RichTextBlock(label="Realita") + + class Meta: + icon = "view" + label = "Hoax" + template = "main/blocks/hoax_block.html" + + +class TeamBlock(StructBlock): + title = CharBlock(label="Název sekce týmů") + slug = CharBlock( + label="Slug sekce", + required=False, + help_text="Není třeba vyplňovat, bude automaticky vyplněno", + ) + team_list = ListBlock( + CardLinkWithHeadlineBlock(label="Karta týmu"), + label="Týmy", + ) + + class Meta: + icon = "group" + label = "Týmy" + + def get_prep_value(self, value): + value = super().get_prep_value(value) + value["slug"] = slugify(value["title"]) + return value diff --git a/main/migrations/0046_alter_mainpeoplepage_options_and_more.py b/main/migrations/0046_alter_mainpeoplepage_options_and_more.py new file mode 100644 index 0000000000000000000000000000000000000000..ddf9b7772e5354ed2d5a7e09e2515201afabe59f --- /dev/null +++ b/main/migrations/0046_alter_mainpeoplepage_options_and_more.py @@ -0,0 +1,48 @@ +# Generated by Django 4.1.6 on 2023-02-28 08:10 + +from django.db import migrations, models +import django.db.models.deletion +import shared.models +import wagtail.blocks +import wagtail.fields +import wagtail.images.blocks +import wagtailmetadata.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('wagtailcore', '0083_workflowcontenttype'), + ('wagtailimages', '0025_alter_image_file_alter_rendition_file'), + ('main', '0045_alter_mainprogrampage_program'), + ] + + operations = [ + migrations.AlterModelOptions( + name='mainpeoplepage', + options={'verbose_name': 'Lidé a týmy'}, + ), + migrations.AlterField( + model_name='maincrossroadpage', + name='headlined_cards_content', + field=wagtail.fields.StreamField([('headlined_cards', wagtail.blocks.StructBlock([('headline', wagtail.blocks.CharBlock(label='Titulek bloku', required=False)), ('card_items', wagtail.blocks.ListBlock(wagtail.blocks.StructBlock([('image', wagtail.images.blocks.ImageChooserBlock(label='Obrázek')), ('title', wagtail.blocks.CharBlock(label='Titulek', required=True)), ('text', wagtail.blocks.RichTextBlock(label='Krátký text pod nadpisem', required=False)), ('page', wagtail.blocks.PageChooserBlock(label='Stránka', page_type=['main.MainArticlesPage', 'main.MainArticlePage', 'main.MainProgramPage', 'main.MainPeoplePage', 'main.MainPersonPage', 'main.MainSimplePage', 'main.MainContactPage', 'main.MainCrossroadPage'], required=False)), ('link', wagtail.blocks.URLBlock(label='Odkaz', required=False))]), label='Karty s odkazy'))]))], blank=True, use_json_field=True, verbose_name='Karty rozcestníku s nadpisem'), + ), + migrations.AlterField( + model_name='mainpeoplepage', + name='people', + field=wagtail.fields.StreamField([('people_group', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(label='Titulek')), ('slug', wagtail.blocks.CharBlock(help_text='Není třeba vyplňovat, bude automaticky vyplněno', label='Slug skupiny', required=False)), ('person_list', wagtail.blocks.ListBlock(wagtail.blocks.PageChooserBlock(label='Detail osoby', page_type=['main.MainPersonPage']), label='Skupina osob'))], label='Seznam osob')), ('team_group', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(label='Název sekce týmů')), ('slug', wagtail.blocks.CharBlock(help_text='Není třeba vyplňovat, bude automaticky vyplněno', label='Slug sekce', required=False)), ('team_list', wagtail.blocks.ListBlock(wagtail.blocks.StructBlock([('headline', wagtail.blocks.CharBlock(label='Titulek bloku', required=False)), ('card_items', wagtail.blocks.ListBlock(wagtail.blocks.StructBlock([('image', wagtail.images.blocks.ImageChooserBlock(label='Obrázek')), ('title', wagtail.blocks.CharBlock(label='Titulek', required=True)), ('text', wagtail.blocks.RichTextBlock(label='Krátký text pod nadpisem', required=False)), ('page', wagtail.blocks.PageChooserBlock(label='Stránka', page_type=['main.MainArticlesPage', 'main.MainArticlePage', 'main.MainProgramPage', 'main.MainPeoplePage', 'main.MainPersonPage', 'main.MainSimplePage', 'main.MainContactPage', 'main.MainCrossroadPage'], required=False)), ('link', wagtail.blocks.URLBlock(label='Odkaz', required=False))]), label='Karty s odkazy'))], label='Karta týmu'), label='Týmy'))]))], blank=True, use_json_field=True, verbose_name='Lidé a týmy'), + ), + migrations.CreateModel( + name='MainHoaxPage', + fields=[ + ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), + ('description', wagtail.fields.RichTextField(blank=True, null=True, verbose_name='Popis')), + ('content', wagtail.fields.StreamField([('hoax', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(label='Titulek')), ('hoax', wagtail.blocks.RichTextBlock(label='Hoax')), ('image', wagtail.images.blocks.ImageChooserBlock(label='Obrázek')), ('image_explanation', wagtail.blocks.RichTextBlock(label='Popis obrázku', required=False)), ('reality', wagtail.blocks.RichTextBlock(label='Realita'))]))], blank=True, use_json_field=True, verbose_name='Hoaxy a jejich vysvětlení')), + ('search_image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image', verbose_name='Search image')), + ], + options={ + 'verbose_name': 'Hoaxy', + }, + bases=(shared.models.SubpageMixin, wagtailmetadata.models.WagtailImageMetadataMixin, 'wagtailcore.page', models.Model), + ), + ] diff --git a/main/migrations/0047_alter_mainhoaxpage_content.py b/main/migrations/0047_alter_mainhoaxpage_content.py new file mode 100644 index 0000000000000000000000000000000000000000..630f00253877cdcec277a64004698ec8262690be --- /dev/null +++ b/main/migrations/0047_alter_mainhoaxpage_content.py @@ -0,0 +1,21 @@ +# Generated by Django 4.1.6 on 2023-02-28 10:03 + +from django.db import migrations +import wagtail.blocks +import wagtail.fields +import wagtail.images.blocks + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0046_alter_mainpeoplepage_options_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='mainhoaxpage', + name='content', + field=wagtail.fields.StreamField([('hoax', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(label='Titulek')), ('hoax', wagtail.blocks.RichTextBlock(label='Hoax')), ('image', wagtail.images.blocks.ImageChooserBlock(label='Obrázek')), ('image_explanation', wagtail.blocks.CharBlock(label='Popis obrázku', required=False)), ('reality', wagtail.blocks.RichTextBlock(label='Realita'))]))], blank=True, use_json_field=True, verbose_name='Hoaxy a jejich vysvětlení'), + ), + ] diff --git a/main/migrations/0053_merge_20230406_1446.py b/main/migrations/0053_merge_20230406_1446.py new file mode 100644 index 0000000000000000000000000000000000000000..ce339473f5c0b340a512536e69ff186ace76c58d --- /dev/null +++ b/main/migrations/0053_merge_20230406_1446.py @@ -0,0 +1,14 @@ +# Generated by Django 4.1.6 on 2023-04-06 12:46 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0047_alter_mainhoaxpage_content'), + ('main', '0052_remove_mainpersonpage_instagram_username_and_more'), + ] + + operations = [ + ] diff --git a/main/models.py b/main/models.py index ad75686395a44c3826aaeab84bc1513ce3a9b395..a28e04ff5a1ac273873c5bc723fe8c976e2a70e5 100644 --- a/main/models.py +++ b/main/models.py @@ -23,6 +23,7 @@ from wagtail.blocks import CharBlock, RichTextBlock from wagtail.contrib.routable_page.models import RoutablePageMixin, route from wagtail.fields import RichTextField, StreamField from wagtail.models import Page +from wagtail.search import index from wagtailmetadata.models import MetadataPageMixin from elections2021.constants import REGION_CHOICES # pozor, import ze sousedního modulu @@ -182,6 +183,7 @@ class MainHomePage( "main.MainSimplePage", "main.MainContactPage", "main.MainCrossroadPage", + "main.MainHoaxPage", ] ### OTHERS @@ -354,7 +356,7 @@ class MainHomePage( class MainArticlesPage( - ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, Page + RoutablePageMixin, ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, Page ): perex = models.TextField() last_import_log = models.TextField( @@ -450,22 +452,23 @@ class MainArticlesPage( return article_data_list - def get_context(self, request, *args, **kwargs): + def get_context(self, request, get_articles: bool = True, *args, **kwargs): ctx = super().get_context(request, args, kwargs) - article_timeline_list = self.get_article_data_list(1) - ctx["article_timeline_list"] = article_timeline_list - ctx["show_next_timeline_articles"] = MainArticlePage.objects.filter( - article_type=ARTICLE_TYPES.WORK_TIMELINE - ).live().count() > len(article_timeline_list) - - article_list = ( - MainArticlePage.objects.filter(article_type=ARTICLE_TYPES.PRESS_RELEASE) - .order_by("-date") - .live()[:11] - ) # dám LIMIT +1, abych věděl, jestli má cenu show_next - ctx["article_article_list"] = article_list[:10] - ctx["show_next_article"] = len(article_list) > 10 + if get_articles: + article_timeline_list = self.get_article_data_list(1) + ctx["article_timeline_list"] = article_timeline_list + ctx["show_next_timeline_articles"] = MainArticlePage.objects.filter( + article_type=ARTICLE_TYPES.WORK_TIMELINE + ).live().count() > len(article_timeline_list) + + article_list = ( + MainArticlePage.objects.filter(article_type=ARTICLE_TYPES.PRESS_RELEASE) + .order_by("-date") + .live()[:11] + ) # dám LIMIT +1, abych věděl, jestli má cenu show_next + ctx["article_article_list"] = article_list[:10] + ctx["show_next_article"] = len(article_list) > 10 return ctx @@ -502,12 +505,69 @@ class MainArticlesPage( } return JsonResponse(data=data, safe=False) + def get_all_articles_search_response(self, request): + article_paginator = Paginator( + MainArticlePage + .objects + .order_by("-date") + .live() + .search(request.GET["q"]), + 10, + ) + article_page = article_paginator.get_page(request.GET.get("page", 1)) + context = {"article_data_list": article_page.object_list} + html_content = render( + request, + "main/includes/person_article_preview.html", + context + ).content + data = { + "html": html_content.decode("utf-8"), + "has_next": article_page.has_next(), + } + return JsonResponse(data=data, safe=False) + + @cached_property + def search_url(self): + return self.url + self.reverse_subpage("search") + + @route(r"^search") + def search(self, request): + if request.method == "GET" and "q" in request.GET: + query = request.GET["q"] + + article_results = ( + MainArticlePage + .objects + .order_by("-date") + .live() + .search(query) + [:11] + ) + + return render( + request, + "main/main_article_search.html", + { + **self.get_context(request, get_articles=False), + "query": query, + "article_results": article_results[:10], + "show_more_articles": len(article_results) > 10, + "sub_heading": f"Vyhledávání „{query}“" + } + ) + else: + return HttpResponseRedirect(self.url) + def serve(self, request, *args, **kwargs): if request.META.get("HTTP_X_REQUESTED_WITH") == "XMLHttpRequest": if "months" in request.GET: return self.get_timeline_articles_response(request) if "page" in request.GET: - return self.get_articles_response(request) + if "q" not in request.GET: + return self.get_articles_response(request) + else: + return self.get_all_articles_search_response(request) return super().serve(request, *args, **kwargs) @@ -567,6 +627,14 @@ class MainArticlePage( ) tags = ClusterTaggableManager(through=MainArticleTag, blank=True) + search_fields = Page.search_fields + [ + index.SearchField("title"), + index.SearchField("author"), + index.SearchField("author_page"), + index.SearchField("perex"), + index.SearchField("content"), + ] + ### PANELS content_panels = ArticleMixin.content_panels + [ @@ -642,8 +710,11 @@ class MainPeoplePage(ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, perex = RichTextField() people = StreamField( - [("people_group", blocks.PeopleGroupBlock(label="Seznam osob"))], - verbose_name="Lidé", + [ + ("people_group", blocks.PeopleGroupBlock(label="Seznam osob")), + ("team_group", blocks.TeamBlock()), + ], + verbose_name="Lidé a týmy", blank=True, use_json_field=True, ) @@ -659,12 +730,16 @@ class MainPeoplePage(ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, ### RELATIONS parent_page_types = ["main.MainHomePage"] - subpage_types = ["main.MainPersonPage"] + subpage_types = [ + "main.MainPersonPage", + "main.MainSimplePage", + "main.MainCrossroadPage", + ] ### OTHERS class Meta: - verbose_name = "Lidé" + verbose_name = "Lidé a týmy" class MainPersonPage(ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, Page): @@ -813,6 +888,7 @@ class MainSimplePage(ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, "main.MainHomePage", "main.MainSimplePage", "main.MainCrossroadPage", + "main.MainPeoplePage", ] subpage_types = ["main.MainSimplePage"] @@ -900,6 +976,7 @@ class MainCrossroadPage( parent_page_types = [ "main.MainHomePage", "main.MainCrossroadPage", + "main.MainPeoplePage", ] subpage_types = [ "main.MainSimplePage", @@ -909,3 +986,44 @@ class MainCrossroadPage( class Meta: verbose_name = "Rozcestník s kartami" + + +class MainHoaxPage( + ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, Page +): + ### FIELDS + + description = RichTextField( + "Popis", + blank=True, + null=True, + ) + + content = StreamField( + [ + (("hoax"), blocks.HoaxBlock()) + ], + verbose_name="Hoaxy a jejich vysvětlení", + blank=True, + use_json_field=True, + ) + + ### PANELS + + content_panels = Page.content_panels + [ + FieldPanel("description"), + FieldPanel("content"), + ] + + promote_panels = make_promote_panels() + + settings_panels = [] + + ### RELATIONS + + parent_page_types = ["main.MainHomePage"] + subpage_types = [] + ### OTHERS + + class Meta: + verbose_name = "Hoaxy" diff --git a/main/static/main/images/hoax.webp b/main/static/main/images/hoax.webp new file mode 100644 index 0000000000000000000000000000000000000000..08eaefd99c7cf520355f540c33f12f12079bb6b6 Binary files /dev/null and b/main/static/main/images/hoax.webp differ diff --git a/main/static/main/images/person-table.png b/main/static/main/images/person-table.png new file mode 100644 index 0000000000000000000000000000000000000000..7558ff7093d4618b37bb4e02f8171132851ea22b Binary files /dev/null and b/main/static/main/images/person-table.png differ diff --git a/main/templates/main/blocks/article_download_block.html b/main/templates/main/blocks/article_download_block.html index 7f61e04febbb136f645e2a53802356206ed55909..102896e798c7e8e56dfee90eb9f996d90609e103 100644 --- a/main/templates/main/blocks/article_download_block.html +++ b/main/templates/main/blocks/article_download_block.html @@ -7,9 +7,12 @@ <span class="font-bold mr-1">Stáhnout soubor: </span> <span class="overflow-hidden text-ellipsis">{{ self.file }}</span> </div> - <a href="{{ self.file.url }}" class="btn btn__slide__wrap h-11 p-0" download> - <span class="btn text-lg bg-black text-white w-32 lg:text-base">Stáhnout</span> - <span class="btn text-lg bg-white text-black w-32 lg:text-base">Stáhnout</span> + <a href="{{ self.file.url }}" class="btn btn--black btn--to-yellow-500 btn--hoveractive uppercase"> + <span class="btn__body-wrap"> + <span class="btn__body text-lg lg:text-base"> + Stáhnout + </span> + </span> </a> </div> </div> diff --git a/main/templates/main/blocks/card_link_with_headline_block.html b/main/templates/main/blocks/card_link_with_headline_block.html index 7a5ce6c5debc4ebee91c83f4ff19b568404a4539..ef0b9003c743065de2a67d5d4535f5c809d215a2 100644 --- a/main/templates/main/blocks/card_link_with_headline_block.html +++ b/main/templates/main/blocks/card_link_with_headline_block.html @@ -4,9 +4,11 @@ {% if self.headline %} <h2 class="head-4xl mt-5 mb-5">{{ self.headline }}</h2> {% endif %} - <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8"> + <ul class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8"> {% for card_block in self.card_items %} - {% include_block card_block %} + <li> + {% include_block card_block %} + </li> {% endfor %} - </div> + </ul> </div> diff --git a/main/templates/main/blocks/hoax_block.html b/main/templates/main/blocks/hoax_block.html new file mode 100644 index 0000000000000000000000000000000000000000..51c465b0d7026bf1c39c6ad202a42dd9bdce7274 --- /dev/null +++ b/main/templates/main/blocks/hoax_block.html @@ -0,0 +1,36 @@ +{% load wagtailimages_tags wagtailcore_tags static %} +{% image self.image width-800 as img %} + +<ui-popout> + <template slot="toggler"> + {{ self.title }} + </template> + <ui-popout-content> + <div class="flex flex-wrap gap-4 lg:flex-nowrap"> + <div class="grow lg:grow-0 lg:basis-2/3 max-w-screen-lg"> + <div class="prose max-w-none"> + {{ self.hoax|richtext }} + </div> + <img + class="mt-10" + src="{% static "main/images/hoax.webp" %}" + alt="Hoax" + > + <h3 class="head-2xl mt-10">Jak to je?</h3> + <div class="prose max-w-none"> + {{ self.reality|richtext }} + </div> + </div> + <div class="lg:basis-1/3"> + <img + class="w-full mb-3" + src="{{ img.url }}" + alt="{{ img.alt }}" + > + <small class="text-grey-300"> + <i>{{ self.image_explanation }}</i> + </small> + </div> + </div> + </ui-popout-content> +</ui-popout> diff --git a/main/templates/main/includes/layout/person_photo_page_header.html b/main/templates/main/includes/layout/person_photo_page_header.html index 6ff48840d83c4e9df921d2cc634dac75dfa49b60..159958958aa990c7f278eaba8c72730ea134b453 100644 --- a/main/templates/main/includes/layout/person_photo_page_header.html +++ b/main/templates/main/includes/layout/person_photo_page_header.html @@ -5,12 +5,22 @@ style="background-image: url('{{ image_url | default_if_none:"https://cc.cz/wp-content/uploads/2022/04/bartos-otv.jpg" }}')" > <div class="grid-container header-max-width pt-16"> - <div class="pl-4 pr-2 col-start-1 col-end-3 sm:col-start-2 sm:col-end-13 sm:pr-0"> - <h1 class="font-alt text-white"> - <span class="text-2xl">{{ before_title | default_if_none:"" }}</span><br> - <span class="font-alt text-7xl">{{ page.title | default_if_none:"" }}</span><span class="text-2xl">{{ after_title | default_if_none:"" }}</span><br> - <span class="font-alt">{{ subtitle | default_if_none:"" }}</span> - </h1> + <div class="pl-4 pr-2 col-start-1 col-end-3 sm:col-start-2 sm:col-end-13 sm:pr-0"> + <h1 class="font-alt text-white"> + <span class="text-xl sm:text-2xl">{{ before_title | default_if_none:"" }}</span><br> + + <div class="flex items-baseline flex-col sm:flex-row"> + <span class="font-alt text-6xl sm:text-7xl grow sm:grow-0">{{ page.title | default_if_none:"" }}</span> + {% if after_title %} + <span class="text-xl sm:text-2xl"> + <span class="text-xl hidden sm:text-2xl sm:inline">, </span>{{ after_title }} + </span> + {% endif %} + </div> + <br> + + <span class="font-alt">{{ subtitle | default_if_none:"" }}</span> + </h1> + </div> </div> - </div> </header> diff --git a/main/templates/main/includes/layout/simple_page_header.html b/main/templates/main/includes/layout/simple_page_header.html index 0c3f226422cc12e2c43c499145947779efbdc124..209527a2c5cc13a71cde147a292c8a0f5f520357 100644 --- a/main/templates/main/includes/layout/simple_page_header.html +++ b/main/templates/main/includes/layout/simple_page_header.html @@ -4,6 +4,11 @@ <h1 class="head-8xl text-white"> {{ page.title }} </h1> + {% if sub_heading %} + <h2 class="head-3xl text-white"> + {{ sub_heading }} + </h2> + {% endif %} </div> </div> </header> diff --git a/main/templates/main/includes/newsletter_section.html b/main/templates/main/includes/newsletter_section.html index 8366bdc5af4364bcf40aec7c1549621661f7fa38..34700766c514e1ebfa4f13dd092f6f99b5817e85 100644 --- a/main/templates/main/includes/newsletter_section.html +++ b/main/templates/main/includes/newsletter_section.html @@ -9,7 +9,7 @@ <img src="{% static 'main/images/lahev.svg' %}" alt="newsletter_icon" class="newsletter-grid__icon text-7xl xl:text-9xl w-32"> </div> <div> - <h5 class="font-alt mb-2 text-6xl xl:text-7xl uppercase"> + <h5 class="font-alt mb-2 text-5xl md:text-6xl xl:text-7xl uppercase"> Odebírej náš newsletter </h5> <span class="inline-block uppercase text-xl xl:text-base font-bold mb-4 xl:w-full"> diff --git a/main/templates/main/main_article_search.html b/main/templates/main/main_article_search.html new file mode 100644 index 0000000000000000000000000000000000000000..4031c09f7081f91257819bb31c2b83a03af889f1 --- /dev/null +++ b/main/templates/main/main_article_search.html @@ -0,0 +1,108 @@ +{% extends "main/base.html" %} +{% load wagtailcore_tags wagtailimages_tags shared_filters %} + +{% block content %} + {% include 'main/includes/layout/simple_page_header.html' with sub_heading=sub_heading %} + + <main role="main"> + <div class="__js-root"> + <div class="grid-container article-section mb-8"> + <div class="grid-full"> + <div class="flex justify-center"> + <div class="flex flex-col items-end gap-3"> + <form + class="flex flex-row" + method="get" + action="{{ page.search_url }}" + > + <input + class="bg-grey-150 w-56 h-10 px-4 text-lg xl:h-14 xl:px-5" + type="text" + id="q" + name="q" + value="{{ query }}" + placeholder="Hledat články..." + aria-label="Vyhledávací box" + > + <button + class="btn btn--yellow-500 btn--to-yellow-600 btn--hoveractive" + aria-label="Vyhledat" + type="submit" + > + <div class="btn__body-wrap h-10 w-12 min-h-0 min-w-0 xl:h-14 xl:w-14"> + <div class="btn__body p-0"> + <i class="ico--search"></i> + </div> + </div> + </button> + </form> + <a href="{{ page.url }}" class="btn btn--black btn--to-yellow-500 btn--hoveractive uppercase"> + <span class="btn__body-wrap"> + <span class="btn__body text-lg lg:text-base"> + Zpět na seznam + </span> + </span> + </a> + </div> + </div> + </div> + </div> + <section class="mb-3 xl:mb-14"> + {% if article_results %} + <div id="searchArticleResultWrapper"> + {% include 'main/includes/person_article_preview.html' with article_data_list=article_results %} + </div> + {% if show_more_articles %} + <div class="grid-container"> + <div class="grid-content-with-right-side"> + <a + onclick="showMoreArticles(event, this)" + href="#" + data-url="{{ page_url }}" + data-page="2" + data-query="{{ query }}" + class="btn btn--black btn--to-yellow-500 btn--hoveractive uppercase" + > + <span class="btn__body-wrap"> + <span class="btn__body text-lg lg:text-base"> + Zobrazit další + </span> + </span> + </a> + </div> + </div> + {% endif %} + {% else %} + <div class="grid-container article-section mb-8"> + <div class="grid-full"> + <p class="text-grey-300 text-center">Žádné výsledky.</p> + </div> + </div> + {% endif %} + </section> + {% include "main/includes/newsletter_section.html" %} + </div> + </main> + + <script type="text/javascript"> + async function showMoreArticles(event, btn) { + event.preventDefault() + + let searchArticleResultWrapper = document.getElementById('searchArticleResultWrapper') + let url = ( + btn.getAttribute('data-url') + + `?page=${btn.getAttribute('data-page')}` + + `&q=${btn.getAttribute('data-query')}` + ) + + const response = await fetch(url, { + method: "GET", + headers: {"X-Requested-With": "XMLHttpRequest"}, + }) + const data = await response.json() + searchArticleResultWrapper.innerHTML += data.html; + if (!data.has_next) { btn.style.display = 'none'; } + btn.setAttribute('data-page', parseInt(btn.getAttribute('data-page')) + 1) + } + </script> +{% endblock content %} diff --git a/main/templates/main/main_articles_page.html b/main/templates/main/main_articles_page.html index 7ecdaa08378bedaf9030a3868063e2c59132413c..c26727f49e8b429b7b94dbb61292bee33de04d3f 100644 --- a/main/templates/main/main_articles_page.html +++ b/main/templates/main/main_articles_page.html @@ -20,13 +20,40 @@ v-slot="{ isCurrentView, toggleView }"> <div class="grid-container article-section mb-8"> <div class="grid-full"> - <div class="flex justify-center"> + <div class="flex flex-col items-center"> <div class="switch"> <a @click="toggleView('timeline')" class="switch__item" :class="{'switch__item--active': isCurrentView('timeline')}">Aktuality</a> <a @click="toggleView('articles')" class="switch__item" :class="{'switch__item--active': isCurrentView('articles')}">Tiskové zprávy</a> </div> + <div class="xl:mt-3"> + <form + class="flex flex-row" + method="get" + action="{{ page.search_url }}" + > + <input + class="bg-grey-150 w-56 h-10 px-4 text-lg xl:h-14 xl:px-5" + type="text" + id="q" + name="q" + placeholder="Hledat články..." + aria-label="Vyhledávací box" + > + <button + class="btn btn--yellow-500 btn--to-yellow-600 btn--hoveractive" + aria-label="Vyhledat" + type="submit" + > + <div class="btn__body-wrap h-10 w-12 min-h-0 min-w-0 xl:h-14 xl:w-14"> + <div class="btn__body p-0"> + <i class="ico--search"></i> + </div> + </div> + </button> + </form> + </div> </div> </div> </div> diff --git a/main/templates/main/main_hoax_page.html b/main/templates/main/main_hoax_page.html new file mode 100644 index 0000000000000000000000000000000000000000..6bfac594994cfd0c5b12db3f0088a93891a13c18 --- /dev/null +++ b/main/templates/main/main_hoax_page.html @@ -0,0 +1,24 @@ +{% extends "main/base.html" %} +{% load wagtailcore_tags %} + +{% block content %} + {% include 'main/includes/layout/simple_page_header.html' %} + <main role="main" class="mb-10 xl:mb-32 __js-root"> + {% if page.description %} + <div class="grid-container mb-2 lg:mb-12"> + <div class="grid-content"> + <div class="prose max-w-none font-condensed text-xl text-black leading-7 mb-12 program-perex"> + {{ page.description|richtext }} + </div> + </div> + </div> + {% endif %} + <div class="grid-container mb-2 lg:mb-12"> + <div class="grid-left-side-with-content"> + {% for block in page.content %} + {% include_block block %} + {% endfor %} + </div> + </div> + </main> +{% endblock %} diff --git a/main/templates/main/main_people_page.html b/main/templates/main/main_people_page.html index 8f535c9d070d94a936f6c262a123d29d91435558..01ae7b43d9c3a787279d50b5a02e9349aecc5cb5 100644 --- a/main/templates/main/main_people_page.html +++ b/main/templates/main/main_people_page.html @@ -34,14 +34,27 @@ </ui-horizontal-scrollable> </div> <div class="flex flex-wrap gap-4"> - {% for people_group in page.people %} - <template v-if="isCurrentView('{{ people_group.value.slug }}-{{ forloop.counter }}')"> - {% for person_page in people_group.value.person_list %} - {% include 'main/includes/person_contact_big.html' %} - {% endfor %} + {% for block in page.people %} + <template v-if="isCurrentView('{{ block.value.slug }}-{{ forloop.counter }}')"> + {% if block.block_type == "people_group" %} + {% for person_page in block.value.person_list %} + {% include 'main/includes/person_contact_big.html' %} + {% endfor %} + {% endif %} </template> {% endfor %} </div> + {% for block in page.people %} + <template v-if="isCurrentView('{{ block.value.slug }}-{{ forloop.counter }}')"> + <div class="grid-left-side-with-content"> + {% if block.block_type == "team_group" %} + {% for card_block in block.value.team_list %} + {% include_block card_block %} + {% endfor %} + {% endif %} + </div> + </template> + {% endfor %} </ui-view-provider> </div> </div> diff --git a/shared/blocks.py b/shared/blocks.py index 1ca1526d574710b5245fe4e7522f21231bb3e98f..10249fced114a2d55ebdbd1f5c9b877bde9c24a8 100644 --- a/shared/blocks.py +++ b/shared/blocks.py @@ -628,7 +628,7 @@ class CardLinkBlockMixin(blocks.StructBlock): class Meta: # template = "" icon = "link" - label = "Karta odkazu" + label = "Karta s odkazem" def clean(self, value): errors = {} @@ -650,7 +650,7 @@ class CardLinkBlockMixin(blocks.StructBlock): class CardLinkWithHeadlineBlockMixin(blocks.StructBlock): headline = blocks.CharBlock(label="Titulek bloku", required=False) - card_items = blocks.ListBlock(CardLinkBlockMixin(), label="Karty odkazu") + card_items = blocks.ListBlock(CardLinkBlockMixin(), label="Karty s odkazy") class Meta: # template = "" diff --git a/uniweb/blocks.py b/uniweb/blocks.py new file mode 100644 index 0000000000000000000000000000000000000000..1aaf9c48bca5524a4594ef6f237a453fb736c356 --- /dev/null +++ b/uniweb/blocks.py @@ -0,0 +1,42 @@ +from wagtail.blocks import ( + CharBlock, + ListBlock, + PageChooserBlock, + StructBlock, + URLBlock, +) + + +class PersonUrlBlock(StructBlock): + title = CharBlock(label="Název", required=True) + url = URLBlock(label="URL", required=True) + custom_icon = CharBlock( + label="Vlastní ikonka ze styleguide", + required=False, + help_text="Pro vlastní ikonku zadejde název ikonky z https://styleguide.pirati.cz/latest/?p=viewall-atoms-icons (bez tečky), např. 'ico--beer'", + ) + + +class PersonCustomPositionBlock(StructBlock): + position = CharBlock(label="Název pozice", required=False) + person = PageChooserBlock( + label="Osoba", + page_type=["uniweb.UniwebPersonPage"], + ) + + class Meta: + icon = "user" + label = "Osoba s volitelnou pozicí" + + +class PeopleGroupListBlock(StructBlock): + group_title = CharBlock(label="Titulek", required=True) + person_list = ListBlock( + PersonCustomPositionBlock(), + label="List osob", + ) + + class Meta: + template = "uniweb/blocks/people_group_block.html" + icon = "list-ul" + label = "Skupina členů" diff --git a/uniweb/migrations/0038_uniwebpersonpage_uniwebpeoplepage.py b/uniweb/migrations/0038_uniwebpersonpage_uniwebpeoplepage.py new file mode 100644 index 0000000000000000000000000000000000000000..aa8762745cfffa846f5ba15f7324f107056ee32d --- /dev/null +++ b/uniweb/migrations/0038_uniwebpersonpage_uniwebpeoplepage.py @@ -0,0 +1,64 @@ +# Generated by Django 4.1.6 on 2023-03-09 23:46 + +from django.db import migrations, models +import django.db.models.deletion +import shared.models +import wagtail.blocks +import wagtail.fields +import wagtailmetadata.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('wagtailcore', '0083_workflowcontenttype'), + ('wagtailimages', '0025_alter_image_file_alter_rendition_file'), + ('uniweb', '0037_alter_uniwebflexiblepage_content_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='UniwebPersonPage', + fields=[ + ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), + ('job', models.CharField(blank=True, help_text="Např. 'Informatik'", max_length=128, null=True, verbose_name='Povolání')), + ('job_function', models.CharField(blank=True, help_text="Např. 'Předseda'", max_length=128, null=True, verbose_name='Funkce')), + ('text', wagtail.fields.RichTextField(blank=True, verbose_name='text')), + ('email', models.EmailField(blank=True, max_length=254, null=True, verbose_name='Email')), + ('show_email', models.BooleanField(default=True, verbose_name='Zobrazovat email na stránce?')), + ('phone', models.CharField(blank=True, max_length=16, null=True, verbose_name='Telefon')), + ('city', models.CharField(blank=True, max_length=64, null=True, verbose_name='Město/obec')), + ('age', models.IntegerField(blank=True, null=True, verbose_name='Věk')), + ('is_pirate', models.BooleanField(default=True, verbose_name='Je členem Pirátské strany?')), + ('other_party', models.CharField(blank=True, help_text='Vyplňte pokud osoba není Pirát', max_length=64, null=True, verbose_name='Strana')), + ('facebook_url', models.URLField(blank=True, null=True, verbose_name='Odkaz na Facebook')), + ('instagram_url', models.URLField(blank=True, null=True, verbose_name='Odkaz na Instagram')), + ('twitter_url', models.URLField(blank=True, null=True, verbose_name='Odkaz na Twitter')), + ('youtube_url', models.URLField(blank=True, null=True, verbose_name='Odkaz na Youtube kanál')), + ('flickr_url', models.URLField(blank=True, null=True, verbose_name='Odkaz na Flickr')), + ('custom_web_url', models.URLField(blank=True, null=True, verbose_name='Odkaz na vlastní web')), + ('other_urls', wagtail.fields.StreamField([('other_url', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock(label='Název', required=True)), ('url', wagtail.blocks.URLBlock(label='URL', required=True)), ('custom_icon', wagtail.blocks.CharBlock(help_text="Pro vlastní ikonku zadejde název ikonky z https://styleguide.pirati.cz/latest/?p=viewall-atoms-icons (bez tečky), např. 'ico--beer'", label='Vlastní ikonka ze styleguide', required=False))]))], blank=True, use_json_field=True, verbose_name='Další odkaz')), + ('background_photo', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='wagtailimages.image', verbose_name='obrázek do záhlaví')), + ('other_party_logo', models.ForeignKey(blank=True, help_text='Vyplňte pokud osoba není Pirát', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='wagtailimages.image', verbose_name='Logo strany')), + ('profile_photo', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='wagtailimages.image', verbose_name='profilová fotka')), + ('search_image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image', verbose_name='Search image')), + ], + options={ + 'verbose_name': 'Detail osoby', + 'ordering': ('title',), + }, + bases=(shared.models.SubpageMixin, wagtailmetadata.models.WagtailImageMetadataMixin, 'wagtailcore.page', models.Model), + ), + migrations.CreateModel( + name='UniwebPeoplePage', + fields=[ + ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), + ('content', wagtail.fields.StreamField([('text', wagtail.blocks.RichTextBlock(features=['h2', 'h3', 'h4', 'h5', 'bold', 'italic', 'ol', 'ul', 'hr', 'link', 'document-link', 'image', 'superscript', 'subscript', 'strikethrough', 'blockquote', 'embed'], label='Textový editor')), ('people_group', wagtail.blocks.StructBlock([('group_title', wagtail.blocks.CharBlock(label='Titulek', required=True)), ('person_list', wagtail.blocks.ListBlock(wagtail.blocks.StructBlock([('position', wagtail.blocks.CharBlock(label='Název pozice', required=False)), ('person', wagtail.blocks.PageChooserBlock(label='Osoba', page_type=['uniweb.UniwebPersonPage']))]), label='List osob'))]))], blank=True, use_json_field=True, verbose_name='Obsah stránky')), + ('search_image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image', verbose_name='Search image')), + ], + options={ + 'verbose_name': 'Lidé', + }, + bases=(shared.models.SubpageMixin, wagtailmetadata.models.WagtailImageMetadataMixin, 'wagtailcore.page', models.Model), + ), + ] diff --git a/uniweb/migrations/0039_alter_uniwebhomepage_top_menu.py b/uniweb/migrations/0039_alter_uniwebhomepage_top_menu.py new file mode 100644 index 0000000000000000000000000000000000000000..7d2e980e4d180e76cc14b0287d5d0789b75f4bc2 --- /dev/null +++ b/uniweb/migrations/0039_alter_uniwebhomepage_top_menu.py @@ -0,0 +1,20 @@ +# Generated by Django 4.1.6 on 2023-03-13 09:58 + +from django.db import migrations +import wagtail.blocks +import wagtail.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('uniweb', '0038_uniwebpersonpage_uniwebpeoplepage'), + ] + + operations = [ + migrations.AlterField( + model_name='uniwebhomepage', + name='top_menu', + field=wagtail.fields.StreamField([('item', wagtail.blocks.StructBlock([('name', wagtail.blocks.CharBlock(label='název')), ('page', wagtail.blocks.PageChooserBlock(label='stránka', page_type=['uniweb.UniwebHomePage', 'uniweb.UniwebFlexiblePage', 'uniweb.UniwebArticlesIndexPage', 'uniweb.UniwebFormPage', 'uniweb.UniwebPeoplePage', 'uniweb.UniwebPersonPage']))]))], blank=True, use_json_field=True, verbose_name='horní menu'), + ), + ] diff --git a/uniweb/models.py b/uniweb/models.py index e6b613cf50ee0eddeaf417428af235fa41ef0cf4..e68eb96c9918f7090a39b255b5adfbb71338d39d 100644 --- a/uniweb/models.py +++ b/uniweb/models.py @@ -17,13 +17,14 @@ from wagtail.admin.panels import ( from wagtail.contrib.forms.models import AbstractForm, AbstractFormField from wagtail.contrib.forms.panels import FormSubmissionsPanel from wagtail.contrib.table_block.blocks import TableBlock -from wagtail.fields import StreamField +from wagtail.fields import RichTextField, StreamField from wagtail.images.blocks import ImageChooserBlock from wagtail.models import Page from wagtailmetadata.models import MetadataPageMixin from calendar_utils.models import CalendarMixin from shared.blocks import ChartBlock +from shared.const import RICH_TEXT_DEFAULT_FEATURES from shared.models import ( ArticleMixin, ExtendedMetadataHomePageMixin, @@ -33,6 +34,11 @@ from shared.models import ( from shared.utils import make_promote_panels from tuning import admin_help +from .blocks import ( + PersonCustomPositionBlock, + PeopleGroupListBlock, + PersonUrlBlock, +) from .constants import ( ALIGN_CHOICES, ALIGN_CSS, @@ -196,6 +202,8 @@ class MenuItemBlock(blocks.StructBlock): "uniweb.UniwebFlexiblePage", "uniweb.UniwebArticlesIndexPage", "uniweb.UniwebFormPage", + "uniweb.UniwebPeoplePage", + "uniweb.UniwebPersonPage", ], ) @@ -381,6 +389,7 @@ class UniwebHomePage( "uniweb.UniwebFlexiblePage", "uniweb.UniwebArticlesIndexPage", "uniweb.UniwebFormPage", + "uniweb.UniwebPeoplePage", ] ### OTHERS @@ -580,3 +589,204 @@ class UniwebFormPage( form = super().get_form_class() form.base_fields["captcha"] = CaptchaField(label="opište písmena z obrázku") return form + + +# Don't waste time making a new mixin for this, +# we'll be doing Octopus imports within a short while. +class UniwebPersonPage( + ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, Page +): + ### 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_DEFAULT_FEATURES) + + email = models.EmailField("Email", null=True, blank=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) + other_party = models.CharField( + "Strana", + max_length=64, + blank=True, + null=True, + help_text="Vyplňte pokud osoba není Pirát", + ) + other_party_logo = models.ForeignKey( + "wagtailimages.Image", + on_delete=models.PROTECT, + blank=True, + null=True, + related_name="+", + verbose_name="Logo strany", + help_text="Vyplňte pokud osoba není Pirát", + ) + + 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", PersonUrlBlock())], + verbose_name="Další odkaz", + blank=True, + use_json_field=True, + ) + + ### PANELS + + content_panels = Page.content_panels + [ + MultiFieldPanel( + [ + FieldPanel("job"), + FieldPanel("job_function"), + ], + "Základní údaje", + ), + MultiFieldPanel( + [ + FieldPanel("profile_photo"), + FieldPanel("background_photo"), + ], + "Fotky", + ), + FieldPanel("text"), + MultiFieldPanel( + [ + FieldPanel("email"), + FieldPanel("show_email"), + FieldPanel("phone"), + FieldPanel("city"), + FieldPanel("age"), + FieldPanel("is_pirate"), + FieldPanel("other_party"), + FieldPanel("other_party_logo"), + ], + "Kontaktní informace", + ), + MultiFieldPanel( + [ + FieldPanel("facebook_url"), + FieldPanel("instagram_url"), + FieldPanel("twitter_url"), + FieldPanel("youtube_url"), + FieldPanel("flickr_url"), + FieldPanel("custom_web_url"), + FieldPanel("other_urls"), + ], + "Sociální sítě", + ), + ] + + settings_panels = [] + + ### RELATIONS + + parent_page_types = ["uniweb.UniwebPeoplePage"] + subpage_types = [] + + ### OTHERS + + class Meta: + verbose_name = "Detail osoby" + ordering = ("title",) + + 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 + + def get_meta_image(self): + return self.search_image or self.profile_photo + + def get_meta_description(self): + if self.search_description: + return self.search_description + + if self.text: + return trim_to_length(strip_all_html_tags(self.text)) + + return None + + +class UniwebPeoplePage( + ExtendedMetadataPageMixin, SubpageMixin, MetadataPageMixin, Page +): + ### FIELDS + + content = StreamField( + [ + ( + "text", + blocks.RichTextBlock( + label="Textový editor", features=RICH_TEXT_DEFAULT_FEATURES + ), + ), + ("people_group", PeopleGroupListBlock()), + ], + verbose_name="Obsah stránky", + blank=True, + use_json_field=True, + ) + + ### PANELS + + content_panels = Page.content_panels + [FieldPanel("content")] + + promote_panels = make_promote_panels() + + settings_panels = [] + + ### RELATIONS + + parent_page_types = ["uniweb.UniwebHomePage"] + subpage_types = ["uniweb.UniwebPersonPage"] + + ### OTHERS + + class Meta: + verbose_name = "Lidé" diff --git a/uniweb/templates/uniweb/blocks/people_group_block.html b/uniweb/templates/uniweb/blocks/people_group_block.html new file mode 100644 index 0000000000000000000000000000000000000000..752458f02285438ff0a0e9c5d014660541bca857 --- /dev/null +++ b/uniweb/templates/uniweb/blocks/people_group_block.html @@ -0,0 +1,12 @@ +<section> + <h2 class="head-heavy-base mb-4"> + {{ self.group_title }} + </h2> + <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 row-gap-8 col-gap-8"> + {% for person_details in self.person_list %} + {% with person_details.person as person_page %} + {% include "shared/person_badge_snippet.html" with title=person_details.position|default:person_page.job_function %} + {% endwith %} + {% endfor %} + </div> +</section> diff --git a/uniweb/templates/uniweb/uniweb_people_page.html b/uniweb/templates/uniweb/uniweb_people_page.html new file mode 100644 index 0000000000000000000000000000000000000000..03470d5c7efe3d27be19391583e7c8816fc321bb --- /dev/null +++ b/uniweb/templates/uniweb/uniweb_people_page.html @@ -0,0 +1,17 @@ +{% extends "uniweb/base.html" %} +{% load wagtailcore_tags wagtailimages_tags shared_filters %} + +{% block content %} + <header> + <h1 itemprop="headline" class="head-alt-md md:head-alt-lg max-w-5xl mb-8"> + {{ page.title }} + </h1> + </header> + + {% for block in page.content %} + {% include_block block %} + {% if not forloop.last %} + <hr class="hr--big"> + {% endif %} + {% endfor %} +{% endblock %} diff --git a/uniweb/templates/uniweb/uniweb_person_page.html b/uniweb/templates/uniweb/uniweb_person_page.html new file mode 100644 index 0000000000000000000000000000000000000000..9d1b880fd2358eddf11323037e7caa2a60c8bc06 --- /dev/null +++ b/uniweb/templates/uniweb/uniweb_person_page.html @@ -0,0 +1,179 @@ +{% extends "uniweb/base.html" %} +{% load static wagtailcore_tags wagtailimages_tags shared_filters %} + +{% block subheader %} + {% image page.get_background_photo fill-1920x500-c75 jpegquality-80 as bg_img %} + + <header class="hero hero--image pt-16 pb-24 lg:pt-32 pb-24" style="--image-url: url({{ bg_img.full_url }})"> + <div class="container container--default text-center lg:text-left"> + <h1 class="head-alt-lg md:head-alt-xl text-shadow-lg max-w-2xl"> + {{ page.title }} + </h1> + <h2 class="head-xs text-shadow-lg mt-2 max-w-xl"> + {{ page.job | default_if_none:"" }} + </h2> + </div> + </header> +{% endblock %} + +{% block container_spacing %}py-8 lg:pb-16{% endblock %} + +{% block content %} + <div class="flex flex-col lg:flex-row lg:space-x-8 xl:space-x-16"> + <section class="lg:w-3/5 xl:w-2/3"> + <div class="content-block w-full"> + {{ page.text|richtext }} + </div> + </section> + + <section class="lg:w-2/5 xl:w-1/3 pt-8 lg:pt-0 order-first lg:order-last candidate-detail__sidebar"> + <div class="container-padding--zero lg:card lg:elevation-10 lg:container-padding--auto"> + <div class="card__body p-4 lg:p-8"> + <div class="text-center mb-8"> + <div class="avatar avatar--2xl lg:avatar--3xl avatar--bordered candidate-detail__avatar"> + {% if page.profile_photo %} + {% image page.profile_photo fill-208x208 as profile_img %} + {% image page.profile_photo fill-416x416 as profile_img_2x %} + <img src="{{ profile_img.url }}" srcset="{{ profile_img.url }}, {{ profile_img_2x.url }} 2x" alt="{{ page.profile_photo }}"> + {% else %} + <img src="{% static "shared/img/unknown_pirate_416x416.jpg" %}" alt="{{ person_page.title }}"/> + {% endif%} + </div> + + </div> + {% if page.facebook_url or page.instagram_url or page.twitter_url or page.youtube_url or page.flickr_url or page.custom_web_url or page.other_urls %} + <div class="social-icon-group space-x-2 text-lg"> + {% if page.facebook_url %} + <a href="{{ page.facebook_url }}" target="_blank" class="social-icon" rel="noreferrer noopener"> + <i class="ico--facebook"></i> + </a> + {% endif %} + {% if page.instagram_url %} + <a href="{{ page.instagram_url }}" target="_blank" class="social-icon" rel="noreferrer noopener"> + <i class="ico--instagram"></i></a> + {% endif %} + {% if page.twitter_url %} + <a href="{{ page.twitter_url }}" target="_blank" class="social-icon" rel="noreferrer noopener"> + <i class="ico--twitter"></i> + </a> + {% endif %} + {% if page.youtube_url %} + <a href="{{ page.youtube_url }}" target="_blank" class="social-icon" rel="noreferrer noopener"> + <i class="ico--youtube"></i> + </a> + {% endif %} + {% if page.flickr_url %} + <a href="{{ page.flickr_url }}" target="_blank" class="social-icon" rel="noreferrer noopener"> + <i class="ico--flickr"></i> + </a> + {% endif %} + {% if page.custom_web_url %} + <a href="{{ page.custom_web_url }}" target="_blank" class="social-icon" rel="noreferrer noopener"> + <i class="ico--globe"></i> + </a> + {% endif %} + {% for person_link_block in page.other_urls %} + <a + href="{{ person_link_block.value.url }}" + target="_blank" + class="social-icon" + rel="noreferrer noopener" + title="{{ person_link_block.value.title }}" + > + <i class="{% firstof person_link_block.value.custom_icon 'ico--globe' %}"></i> + </a> + {% endfor %} + </div> + {% endif %} + + {% if page.is_pirate %} + <hr> + <div class="flex items-center"> + <div class="avatar w-6 mr-2"> + <img src="{% static "elections2021/images/logo-pirati-21px.svg" %}"> + </div> + <span class="font-bold font-condensed">Pirátská strana</span> + </div> + {% endif %} + + {% if not page.is_pirate and page.other_party %} + <hr> + <div class="flex items-center"> + {% if page.other_party_logo %} + {% image page.other_party_logo width-48 as logo_img %} + <div class="avatar w-6 mr-2"> + <img src="{{ logo_img.url }}" alt="{{ page.other_party }}"> + </div> + {% endif %} + <span class="font-bold font-condensed">{{ page.other_party }}</span> + </div> + {% endif %} + + {% if page.phone or page.email and page.show_email %} + <hr> + <div class="content-block"> + <div class="space-y-4"> + {% if page.phone %} + <div> + <h4>Telefon</h4> + <a href="tel:{{ page.phone }}" class="contact-line icon-link content-block--nostyle"> + <i class="ico--phone"></i><span>{{ page.phone }}</span> + </a> + </div> + {% endif %} + + {% if page.email and page.show_email %} + <div> + <h4>Email</h4> + <a href="mailto:{{ page.email }}" class="contact-line icon-link content-block--nostyle"> + <i class="ico--envelope"></i><span>{{ page.email }}</span> + </a> + </div> + {% endif %} + </div> + </div> + {% endif %} + + {% if random_people %} + <hr> + <div class="content-block"> + <h2>Lidé</h2> + + <div class="space-y-4 mt-4"> + {% for person in random_people %} + {% include "shared/person_badge_snippet.html" with person_page=person skipcontacts=1 %} + {% endfor %} + </div> + + <a href="{{ page.root_page.people_page.url }}" + class="btn btn--icon btn--violet-500 btn--fullwidth btn--hoveractive pt-4"> + <div class="btn__body-wrap"> + <div class="btn__body ">Poznejte celý náš tým</div> + <div class="btn__icon "> + <i class="ico--chevron-right"></i> + </div> + </div> + </a> + </div> + {% endif %} + {% comment %} <hr> {% endcomment %} +{# <h2>Kancelář</h2>#} +{# <h4>Poslanecká sněmovna</h4>#} +{# <p>#} +{# Jiřího náměstí 39, 290 33 Poděbrady#} +{# </p>#} +{# <iframe#} +{# src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d82007.99920528589!2d15.688131074078123!3d50.034780639742856!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x470dc94b239307b5%3A0x12d59894ccf624ae!2sPardubice!5e0!3m2!1scs!2scz!4v1589382658695!5m2!1scs!2scz"#} +{# width="100%" height="300" id="mapa-mobile" frameborder="0" style="border:0;" allowfullscreen=""#} +{# aria-hidden="false" tabindex="0"></iframe>#} +{# <h4>Otevírací doba</h4>#} +{# <p>Pondělí 14:00 - 18:00 objednat se přes: kancelář-podebrady@pirati.cz nebo 778 111 462.</p>#} + + <!-- Mobile divider --> + <hr class="block lg:hidden"> + </div> + </div> + </section> + </div> + +{% endblock %}