diff --git a/app/core/migrations/0009_remove_partner_partner_type.py b/app/core/migrations/0009_remove_partner_partner_type.py new file mode 100644 index 0000000..41f7d7f --- /dev/null +++ b/app/core/migrations/0009_remove_partner_partner_type.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.7 on 2024-08-14 19:46 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0008_partnertype_type_description_partnertype_type_icon'), + ] + + operations = [ + migrations.RemoveField( + model_name='partner', + name='partner_type', + ), + ] diff --git a/app/core/migrations/0010_partner_partner_type.py b/app/core/migrations/0010_partner_partner_type.py new file mode 100644 index 0000000..aad41c6 --- /dev/null +++ b/app/core/migrations/0010_partner_partner_type.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.7 on 2024-08-14 19:47 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('partners', '0010_remove_ourpartnerspage_call_to_action_description_and_more'), + ('core', '0009_remove_partner_partner_type'), + ] + + operations = [ + migrations.AddField( + model_name='partner', + name='partner_type', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='partners.partnershiptemplatepage'), + ), + ] diff --git a/app/core/migrations/0011_remove_partner_partner_type_partner_partner_type.py b/app/core/migrations/0011_remove_partner_partner_type_partner_partner_type.py new file mode 100644 index 0000000..61879e2 --- /dev/null +++ b/app/core/migrations/0011_remove_partner_partner_type_partner_partner_type.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.7 on 2024-08-14 20:01 + +from django.db import migrations +import modelcluster.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('partners', '0010_remove_ourpartnerspage_call_to_action_description_and_more'), + ('core', '0010_partner_partner_type'), + ] + + operations = [ + migrations.RemoveField( + model_name='partner', + name='partner_type', + ), + migrations.AddField( + model_name='partner', + name='partner_type', + field=modelcluster.fields.ParentalManyToManyField(blank=True, null=True, to='partners.partnershiptemplatepage'), + ), + ] diff --git a/app/core/migrations/0012_remove_partner_partner_type_partner_partner_type.py b/app/core/migrations/0012_remove_partner_partner_type_partner_partner_type.py new file mode 100644 index 0000000..809b3b7 --- /dev/null +++ b/app/core/migrations/0012_remove_partner_partner_type_partner_partner_type.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.7 on 2024-08-14 20:59 + +from django.db import migrations +import wagtail.blocks +import wagtail.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0011_remove_partner_partner_type_partner_partner_type'), + ] + + operations = [ + migrations.RemoveField( + model_name='partner', + name='partner_type', + ), + migrations.AddField( + model_name='partner', + name='partner_type', + field=wagtail.fields.StreamField([('type', wagtail.blocks.PageChooserBlock(page_type=['partners.PartnershipTemplatePage']))], blank=True, null=True, use_json_field=True), + ), + ] diff --git a/app/core/migrations/0013_delete_partnertype.py b/app/core/migrations/0013_delete_partnertype.py new file mode 100644 index 0000000..ddc5ffd --- /dev/null +++ b/app/core/migrations/0013_delete_partnertype.py @@ -0,0 +1,16 @@ +# Generated by Django 4.2.7 on 2024-08-14 21:33 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0012_remove_partner_partner_type_partner_partner_type'), + ] + + operations = [ + migrations.DeleteModel( + name='PartnerType', + ), + ] diff --git a/app/core/models.py b/app/core/models.py index 70b32cc..3b6b7fb 100644 --- a/app/core/models.py +++ b/app/core/models.py @@ -8,33 +8,6 @@ from wagtail.admin.panels import FieldPanel, MultiFieldPanel, InlinePanel -@register_snippet -class PartnerType(models.Model): - type_name = models.CharField() - type_icon = models.ForeignKey( - "wagtailimages.Image", - null=True, - on_delete=models.SET_NULL, - related_name="+", - help_text="Icon for the partner type" - ) - type_description = RichTextField(blank=True) - - panels = [ - FieldPanel("type_name"), - MultiFieldPanel([ - FieldPanel('type_icon'), - FieldPanel('type_description'), - ], heading="These will show in the Partner With Us page - an info block is automatically created for each partner type.") - ] - - def __str__(self): - return self.type_name - - class Meta: - verbose_name_plural = "Partner Types" - - @register_snippet class Partner(models.Model): partner_name = models.CharField() @@ -46,19 +19,13 @@ class Partner(models.Model): help_text="Partner logo" ) partner_url = models.URLField(blank=True) - partner_type = models.ForeignKey( - 'core.PartnerType', - null=True, - blank=True, - on_delete=models.SET_NULL, - related_name='+' - ) + partner_type = StreamField([('type', PageChooserBlock(page_type="partners.PartnershipTemplatePage"))], use_json_field=True, null=True, blank=True) panels = [ FieldPanel("partner_name"), FieldPanel("partner_logo"), FieldPanel("partner_url"), - FieldPanel("partner_type", widget=forms.RadioSelect), + FieldPanel("partner_type"), ] def __str__(self): diff --git a/app/misc/migrations/0012_documentcollectionpage.py b/app/misc/migrations/0012_documentcollectionpage.py new file mode 100644 index 0000000..e007001 --- /dev/null +++ b/app/misc/migrations/0012_documentcollectionpage.py @@ -0,0 +1,37 @@ +# Generated by Django 4.2.7 on 2024-08-14 22:35 + +from django.db import migrations, models +import django.db.models.deletion +import wagtail.blocks +import wagtail.documents.blocks +import wagtail.fields +import wagtail.images.blocks + + +class Migration(migrations.Migration): + + dependencies = [ + ('wagtailimages', '0025_alter_image_file_alter_rendition_file'), + ('wagtailcore', '0089_log_entry_data_json_null_to_object'), + ('misc', '0011_rename_body_text_privacypolicypage_brief_body_text_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='DocumentCollectionPage', + 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')), + ('header_description', wagtail.fields.RichTextField(blank=True)), + ('document_access_prefix_text', models.CharField(default='Access', help_text="Text to prefix the name of the document in the link to the document; if the document's title is 'Cool Doc', and this field is 'Access', the link for the document would show as 'Access Cool Doc'.")), + ('documents', wagtail.fields.StreamField([('block', wagtail.blocks.StructBlock([('icon', wagtail.images.blocks.ImageChooserBlock()), ('document', wagtail.documents.blocks.DocumentChooserBlock()), ('description', wagtail.blocks.RichTextBlock())]))], blank=True, null=True, use_json_field=True)), + ('sidebar_box_title', models.CharField(blank=True)), + ('sidebar_box_button_text', models.CharField(blank=True)), + ('sidebar_box_button_link', wagtail.fields.StreamField([('page', wagtail.blocks.PageChooserBlock()), ('url', wagtail.blocks.URLBlock())], blank=True, use_json_field=True)), + ('header_image', models.ForeignKey(blank=True, help_text='Header image', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image')), + ], + options={ + 'abstract': False, + }, + bases=('wagtailcore.page',), + ), + ] diff --git a/app/misc/migrations/0013_alter_documentcollectionpage_documents.py b/app/misc/migrations/0013_alter_documentcollectionpage_documents.py new file mode 100644 index 0000000..cfb2ec3 --- /dev/null +++ b/app/misc/migrations/0013_alter_documentcollectionpage_documents.py @@ -0,0 +1,22 @@ +# Generated by Django 4.2.7 on 2024-08-14 22:45 + +from django.db import migrations +import wagtail.blocks +import wagtail.documents.blocks +import wagtail.fields +import wagtail.images.blocks + + +class Migration(migrations.Migration): + + dependencies = [ + ('misc', '0012_documentcollectionpage'), + ] + + operations = [ + migrations.AlterField( + model_name='documentcollectionpage', + name='documents', + field=wagtail.fields.StreamField([('block', wagtail.blocks.StructBlock([('icon', wagtail.images.blocks.ImageChooserBlock(blank=True, null=True)), ('document', wagtail.documents.blocks.DocumentChooserBlock()), ('description', wagtail.blocks.RichTextBlock(blank=True, null=True))]))], blank=True, null=True, use_json_field=True), + ), + ] diff --git a/app/misc/migrations/0014_alter_documentcollectionpage_documents.py b/app/misc/migrations/0014_alter_documentcollectionpage_documents.py new file mode 100644 index 0000000..58cabe1 --- /dev/null +++ b/app/misc/migrations/0014_alter_documentcollectionpage_documents.py @@ -0,0 +1,22 @@ +# Generated by Django 4.2.7 on 2024-08-14 22:46 + +from django.db import migrations +import wagtail.blocks +import wagtail.documents.blocks +import wagtail.fields +import wagtail.images.blocks + + +class Migration(migrations.Migration): + + dependencies = [ + ('misc', '0013_alter_documentcollectionpage_documents'), + ] + + operations = [ + migrations.AlterField( + model_name='documentcollectionpage', + name='documents', + field=wagtail.fields.StreamField([('block', wagtail.blocks.StructBlock([('icon', wagtail.images.blocks.ImageChooserBlock(blank=True, null=True, required=False)), ('document', wagtail.documents.blocks.DocumentChooserBlock()), ('description', wagtail.blocks.RichTextBlock(blank=True, null=True, required=False))]))], blank=True, null=True, use_json_field=True), + ), + ] diff --git a/app/misc/models.py b/app/misc/models.py index 86ca5a6..8b5aff3 100644 --- a/app/misc/models.py +++ b/app/misc/models.py @@ -1,10 +1,13 @@ from django.db import models +from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from wagtail.admin.panels import FieldPanel, MultiFieldPanel from wagtail.fields import RichTextField, StreamField from wagtail.blocks import CharBlock, StreamBlock, StructBlock, URLBlock, RichTextBlock, PageChooserBlock from wagtail.images.blocks import ImageChooserBlock from wagtail.models import Page +from wagtail.documents.blocks import DocumentChooserBlock + from app.core.models import LinkOrPageBlock @@ -233,3 +236,61 @@ class DataPrinciplesPage(Page): FieldPanel('footer_button_link'), ], heading="Footer"), ] + + +class DocumentCollectionPage(Page): + def get_context(self, request, *args, **kwargs): + context = super().get_context(request, *args, **kwargs) + + documents = context['page'].documents + page = request.GET.get('page', 1) + paginator = Paginator(documents, 6) # if you want more/less items per page (i.e., per load), change the number here to something else + try: + documents = paginator.page(page) + except PageNotAnInteger: + documents = paginator.page(1) + except EmptyPage: + documents = paginator.page(paginator.num_pages) + + context['documents'] = documents + context['paginator'] = paginator + return context + + header_image = models.ForeignKey( + "wagtailimages.Image", + null=True, + blank=True, + on_delete=models.SET_NULL, + related_name="+", + help_text="Header image" + ) + header_description = RichTextField(blank=True) + + document_access_prefix_text = models.CharField(default="Access", help_text="Text to prefix the name of the document in the link to the document; if the document's title is 'Cool Doc', and this field is 'Access', the link for the document would show as 'Access Cool Doc'.") + documents = StreamField([ + ('block', StructBlock([ + ('icon', ImageChooserBlock(blank=True, null=True, required=False)), + ('document', DocumentChooserBlock()), + ('description', RichTextBlock(blank=True, null=True, required=False)) + ])) + ], use_json_field=True, null=True, blank=True) + + sidebar_box_title = models.CharField(blank=True) + sidebar_box_button_text = models.CharField(blank=True) + sidebar_box_button_link = StreamField(LinkOrPageBlock(), use_json_field=True, blank=True) + + content_panels = Page.content_panels + [ + MultiFieldPanel([ + FieldPanel('header_image'), + FieldPanel('header_description'), + ], heading="Header"), + MultiFieldPanel([ + FieldPanel('document_access_prefix_text'), + FieldPanel('documents'), + ], heading="Documents"), + MultiFieldPanel([ + FieldPanel('sidebar_box_title'), + FieldPanel('sidebar_box_button_text'), + FieldPanel('sidebar_box_button_link'), + ], heading="Sidebar"), + ] diff --git a/app/misc/templates/misc/code_of_conduct_page.html b/app/misc/templates/misc/code_of_conduct_page.html index fe91ddc..a921052 100644 --- a/app/misc/templates/misc/code_of_conduct_page.html +++ b/app/misc/templates/misc/code_of_conduct_page.html @@ -18,28 +18,28 @@

{{page.title}}

- {{page.intro|safe}} + {{page.intro|richtext}}

{{page.short_version_title}}

- {{page.short_version_body|safe}} + {{page.short_version_body|richtext}}

{{page.full_version_title}}

- {{page.full_version_body|safe}} + {{page.full_version_body|richtext}}

{{page.complaint_handling_title}}

- {{page.complaint_handling_body|safe}} + {{page.complaint_handling_body|richtext}}
diff --git a/app/misc/templates/misc/document_collection_page.html b/app/misc/templates/misc/document_collection_page.html new file mode 100644 index 0000000..adc3d78 --- /dev/null +++ b/app/misc/templates/misc/document_collection_page.html @@ -0,0 +1,54 @@ +{% extends "base.html" %} +{% load static %} +{% load wagtailcore_tags %} +{% load wagtailimages_tags %} +{% load compress %} +{% block body_class %}template-documentcollectionpage{% endblock %} +{% block extra_css %} + {% compress css %} + {% endcompress css %} +{% endblock extra_css %} + +{% block content %} + {% include "ui/components/PageHeaderWithBlur.html" with title=page.title image=page.header_image subtitle=page.header_description endinlarge=True full_length=True %} + +
+
+
+
+ {% for doc in documents %} +
+ {% image doc.value.icon original class="w-full h-auto hidden lg:block" %} +
+

+ {{doc.value.document.title}} +

+
+ {{doc.value.description}} +
+

+ {% include "ui/components/BaseLink.html" with linktext=page.document_access_prefix_text|add:" "|add:doc.value.document.title linkurl=doc.value.document.url %} +

+
+
+ +
+ {% endfor %} +
+ {% include "ui/components/utilities/PaginatorNavigation.html" with paginator=paginator %} +
+
+
+ +
+
+
+
+{% endblock %} diff --git a/app/misc/templates/misc/privacy_policy_page.html b/app/misc/templates/misc/privacy_policy_page.html index dc01071..950f0dd 100644 --- a/app/misc/templates/misc/privacy_policy_page.html +++ b/app/misc/templates/misc/privacy_policy_page.html @@ -18,11 +18,11 @@

{{page.title}}

- {{page.intro|safe}} + {{page.intro|richtext}}
- {{page.brief_body_text|safe}} + {{page.brief_body_text|richtext}}

{{page.table_of_contents_title}} @@ -41,7 +41,7 @@

{{forloop.counter}}. {{section.value.title}}

- {{section.value.body|safe}} + {{section.value.body|richtext}} {% endfor %}
diff --git a/app/our_work/models.py b/app/our_work/models.py index e93f540..e283b6f 100644 --- a/app/our_work/models.py +++ b/app/our_work/models.py @@ -55,7 +55,6 @@ def get_project_paginator(self, request, context): query = Q() for area in impact_areas: if request.GET.get(f"ia{area.id}", ''): - print(area.id, area) query = query | Q(impact_area_list__contains=[{'type': 'impact_area', 'value': area.id }]) projects_list = projects_list.filter(query).distinct() diff --git a/app/partners/migrations/0008_ourpartnerspage_call_to_action_description_and_more.py b/app/partners/migrations/0008_ourpartnerspage_call_to_action_description_and_more.py new file mode 100644 index 0000000..b26c38d --- /dev/null +++ b/app/partners/migrations/0008_ourpartnerspage_call_to_action_description_and_more.py @@ -0,0 +1,82 @@ +# Generated by Django 4.2.7 on 2024-08-13 21:53 + +from django.db import migrations, models +import django.db.models.deletion +import modelcluster.fields +import wagtail.blocks +import wagtail.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0008_partnertype_type_description_partnertype_type_icon'), + ('wagtailcore', '0089_log_entry_data_json_null_to_object'), + ('wagtailimages', '0025_alter_image_file_alter_rendition_file'), + ('partners', '0007_remove_partnerwithuspage_partnership_types'), + ] + + operations = [ + migrations.AddField( + model_name='ourpartnerspage', + name='call_to_action_description', + field=wagtail.fields.RichTextField(blank=True, null=True), + ), + migrations.AddField( + model_name='ourpartnerspage', + name='call_to_action_link_text', + field=models.CharField(default='Call to Action Link'), + ), + migrations.AddField( + model_name='ourpartnerspage', + name='call_to_action_link_url', + field=wagtail.fields.StreamField([('page', wagtail.blocks.PageChooserBlock()), ('url', wagtail.blocks.URLBlock())], blank=True, use_json_field=True), + ), + migrations.AddField( + model_name='ourpartnerspage', + name='call_to_action_title', + field=models.CharField(default='Call to Action'), + ), + migrations.AddField( + model_name='ourpartnerspage', + name='meet_partners_title', + field=models.CharField(default='Meet Our Partners'), + ), + migrations.AddField( + model_name='ourpartnerspage', + name='related_news_title', + field=models.CharField(default='Related News'), + ), + migrations.AddField( + model_name='ourpartnerspage', + name='related_projects_title', + field=models.CharField(default='Related Projects'), + ), + migrations.AddField( + model_name='ourpartnerspage', + name='view_partners_link', + field=wagtail.fields.StreamField([('page', wagtail.blocks.PageChooserBlock()), ('url', wagtail.blocks.URLBlock())], blank=True, use_json_field=True), + ), + migrations.AddField( + model_name='ourpartnerspage', + name='view_partners_text', + field=models.CharField(default='View All Partners'), + ), + migrations.CreateModel( + name='PartnershipTemplatePage', + 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')), + ('header_text', wagtail.fields.RichTextField(blank=True)), + ('intro', wagtail.fields.RichTextField(blank=True)), + ('body', wagtail.fields.RichTextField(blank=True)), + ('related_projects', wagtail.fields.StreamField([('project_page', wagtail.blocks.PageChooserBlock(page_type=['projects.IndividualProjectPage']))], blank=True, null=True, use_json_field=True)), + ('related_news', wagtail.fields.StreamField([('news_page', wagtail.blocks.PageChooserBlock(page_type=['news.IndividualNewsPage']))], blank=True, null=True, use_json_field=True)), + ('featured_partners', modelcluster.fields.ParentalManyToManyField(blank=True, to='core.partner')), + ('header_image', models.ForeignKey(blank=True, help_text='Header image', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image')), + ], + options={ + 'abstract': False, + }, + bases=('wagtailcore.page',), + ), + ] diff --git a/app/partners/migrations/0009_partnershiptemplatepage_external_icon.py b/app/partners/migrations/0009_partnershiptemplatepage_external_icon.py new file mode 100644 index 0000000..842333e --- /dev/null +++ b/app/partners/migrations/0009_partnershiptemplatepage_external_icon.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.7 on 2024-08-13 23:02 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('wagtailimages', '0025_alter_image_file_alter_rendition_file'), + ('partners', '0008_ourpartnerspage_call_to_action_description_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='partnershiptemplatepage', + name='external_icon', + field=models.ForeignKey(blank=True, help_text='An icon to be shown on the Partner With Us page.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image'), + ), + ] diff --git a/app/partners/migrations/0010_remove_ourpartnerspage_call_to_action_description_and_more.py b/app/partners/migrations/0010_remove_ourpartnerspage_call_to_action_description_and_more.py new file mode 100644 index 0000000..8d30f8a --- /dev/null +++ b/app/partners/migrations/0010_remove_ourpartnerspage_call_to_action_description_and_more.py @@ -0,0 +1,96 @@ +# Generated by Django 4.2.7 on 2024-08-13 23:17 + +from django.db import migrations, models +import wagtail.blocks +import wagtail.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('partners', '0009_partnershiptemplatepage_external_icon'), + ] + + operations = [ + migrations.RemoveField( + model_name='ourpartnerspage', + name='call_to_action_description', + ), + migrations.RemoveField( + model_name='ourpartnerspage', + name='call_to_action_link_text', + ), + migrations.RemoveField( + model_name='ourpartnerspage', + name='call_to_action_link_url', + ), + migrations.RemoveField( + model_name='ourpartnerspage', + name='call_to_action_title', + ), + migrations.RemoveField( + model_name='ourpartnerspage', + name='meet_partners_title', + ), + migrations.RemoveField( + model_name='ourpartnerspage', + name='related_news_title', + ), + migrations.RemoveField( + model_name='ourpartnerspage', + name='related_projects_title', + ), + migrations.RemoveField( + model_name='ourpartnerspage', + name='view_partners_link', + ), + migrations.RemoveField( + model_name='ourpartnerspage', + name='view_partners_text', + ), + migrations.AddField( + model_name='partnerwithuspage', + name='call_to_action_description', + field=wagtail.fields.RichTextField(blank=True, null=True), + ), + migrations.AddField( + model_name='partnerwithuspage', + name='call_to_action_link_text', + field=models.CharField(default='Call to Action Link'), + ), + migrations.AddField( + model_name='partnerwithuspage', + name='call_to_action_link_url', + field=wagtail.fields.StreamField([('page', wagtail.blocks.PageChooserBlock()), ('url', wagtail.blocks.URLBlock())], blank=True, use_json_field=True), + ), + migrations.AddField( + model_name='partnerwithuspage', + name='call_to_action_title', + field=models.CharField(default='Call to Action'), + ), + migrations.AddField( + model_name='partnerwithuspage', + name='meet_partners_title', + field=models.CharField(default='Meet Our Partners'), + ), + migrations.AddField( + model_name='partnerwithuspage', + name='related_news_title', + field=models.CharField(default='Related News'), + ), + migrations.AddField( + model_name='partnerwithuspage', + name='related_projects_title', + field=models.CharField(default='Related Projects'), + ), + migrations.AddField( + model_name='partnerwithuspage', + name='view_partners_link', + field=wagtail.fields.StreamField([('page', wagtail.blocks.PageChooserBlock()), ('url', wagtail.blocks.URLBlock())], blank=True, use_json_field=True), + ), + migrations.AddField( + model_name='partnerwithuspage', + name='view_partners_text', + field=models.CharField(default='View All Partners'), + ), + ] diff --git a/app/partners/models.py b/app/partners/models.py index 31f32d4..b01feb8 100644 --- a/app/partners/models.py +++ b/app/partners/models.py @@ -1,3 +1,4 @@ +from django import forms from django.db import models from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.db.models import Q @@ -7,19 +8,80 @@ from wagtail.blocks import CharBlock, StreamBlock, StructBlock, URLBlock, RichTextBlock, PageChooserBlock from wagtail.images.blocks import ImageChooserBlock from wagtail.models import Page -from app.core.models import LinkOrPageBlock, Partner, PartnerType +from modelcluster.fields import ParentalKey, ParentalManyToManyField + +from app.core.models import LinkOrPageBlock, Partner + + +# The "partners" snippet is in the core app's models. + +class PartnershipTemplatePage(Page): + parent_page_types = [ + 'partners.PartnerWithUsPage' + ] + + external_icon = models.ForeignKey( + "wagtailimages.Image", + null=True, + blank=True, + on_delete=models.SET_NULL, + related_name="+", + help_text="An icon to be shown on the Partner With Us page." + ) + + header_image = models.ForeignKey( + "wagtailimages.Image", + null=True, + blank=True, + on_delete=models.SET_NULL, + related_name="+", + help_text="Header image" + ) + header_text = RichTextField(blank=True) + + intro = RichTextField(blank=True) + body = RichTextField(blank=True, features=[ + 'h1', 'h2', 'h3', 'h4', 'bold', 'italic', 'link', 'ol', 'ul', 'hr', 'document-link', 'image', 'embed', 'code', 'blockquote' + ]) + + related_projects = StreamField([ + ('project_page', PageChooserBlock(page_type="projects.IndividualProjectPage")) + ], use_json_field=True, null=True, blank=True) + related_news = StreamField([ + ('news_page', PageChooserBlock(page_type="news.IndividualNewsPage")) + ], use_json_field=True, null=True, blank=True) + featured_partners = ParentalManyToManyField('core.Partner', blank=True) + + content_panels = Page.content_panels + [ + FieldPanel('external_icon'), + MultiFieldPanel([ + FieldPanel('header_image'), + FieldPanel('header_text'), + ], heading="Header"), + MultiFieldPanel([ + FieldPanel('intro'), + FieldPanel('body'), + ], heading="Body"), + MultiFieldPanel([ + FieldPanel('related_projects'), + FieldPanel('related_news'), + FieldPanel('featured_partners', widget=forms.CheckboxSelectMultiple), + ], heading="Related"), + ] -# The "partners" and "partner types" snippets are in the core app's models. class PartnerWithUsPage(Page): def get_context(self, request, *args, **kwargs): context = super().get_context(request, *args, **kwargs) context['partners'] = Partner.objects.all() - context['partner_types'] = PartnerType.objects.all() return context + subpage_types = [ + 'partners.PartnershipTemplatePage' + ] + max_count = 1 header_image = models.ForeignKey( @@ -47,6 +109,17 @@ def get_context(self, request, *args, **kwargs): black_box_link_text = models.CharField(default="Email to partnerships@hotosm.org") black_box_link_email = models.EmailField(default="partnerships@hotosm.org", max_length=254) + related_projects_title = models.CharField(default="Related Projects") + related_news_title = models.CharField(default="Related News") + meet_partners_title = models.CharField(default="Meet Our Partners") + view_partners_text = models.CharField(default="View All Partners") + view_partners_link = StreamField(LinkOrPageBlock(), use_json_field=True, blank=True) + + call_to_action_title = models.CharField(default="Call to Action") + call_to_action_description = RichTextField(null=True, blank=True) + call_to_action_link_text = models.CharField(default="Call to Action Link") + call_to_action_link_url = StreamField(LinkOrPageBlock(), use_json_field=True, blank=True) + content_panels = Page.content_panels + [ MultiFieldPanel([ FieldPanel('header_image'), @@ -69,6 +142,19 @@ def get_context(self, request, *args, **kwargs): FieldPanel('black_box_link_text'), FieldPanel('black_box_link_email'), ], heading="Dogear Boxes"), + MultiFieldPanel([ + FieldPanel('related_projects_title'), + FieldPanel('related_news_title'), + FieldPanel('meet_partners_title'), + FieldPanel('view_partners_text'), + FieldPanel('view_partners_link'), + MultiFieldPanel([ + FieldPanel('call_to_action_title'), + FieldPanel('call_to_action_description'), + FieldPanel('call_to_action_link_text'), + FieldPanel('call_to_action_link_url'), + ], heading="Call to Action"), + ], heading="Partnership Type page"), ] @@ -78,11 +164,11 @@ def get_context(self, request, *args, **kwargs): partners = Partner.objects.all() - p_types = PartnerType.objects.all() + p_types = PartnershipTemplatePage.objects.live().filter(locale=context['page'].locale) query = Q() for p_type in p_types: - if request.GET.get(str(p_type), ''): - query = query | Q(partner_type__type_name=p_type) + if request.GET.get("pt" + str(p_type.id), ''): + query = query | Q(partner_type__contains=[{'type': 'type', 'value': p_type.id }]) partners = partners.filter(query).distinct() page = request.GET.get('page', 1) diff --git a/app/partners/templates/partners/our_partners_page.html b/app/partners/templates/partners/our_partners_page.html index 2af7fa2..1706030 100644 --- a/app/partners/templates/partners/our_partners_page.html +++ b/app/partners/templates/partners/our_partners_page.html @@ -28,8 +28,8 @@
{% for p_type in partner_types %}
- - + +
{% endfor %} diff --git a/app/partners/templates/partners/partner_with_us_page.html b/app/partners/templates/partners/partner_with_us_page.html index 252921b..752f088 100644 --- a/app/partners/templates/partners/partner_with_us_page.html +++ b/app/partners/templates/partners/partner_with_us_page.html @@ -22,14 +22,16 @@ {% include "ui/components/SectionHeadingWithUnderline.html" with title=page.partnership_types_title %}
- {% for ptype in partner_types %} + {% for ptype in page.get_children %}
- {% image ptype.type_icon original class="h-8 w-8 mt-2" %} -

- {{ptype.type_name}} -

+ {% image ptype.specific.external_icon original class="h-8 w-8 mt-2" %} + +

+ {{ptype.specific.title}} +

+
- {{ptype.type_description|richtext}} + {{ptype.specific.intro|richtext}}
{% endfor %} diff --git a/app/partners/templates/partners/partnership_template_page.html b/app/partners/templates/partners/partnership_template_page.html new file mode 100644 index 0000000..889a663 --- /dev/null +++ b/app/partners/templates/partners/partnership_template_page.html @@ -0,0 +1,70 @@ +{% extends "base.html" %} +{% load static %} +{% load wagtailcore_tags %} +{% load wagtailimages_tags %} +{% load compress %} +{% block body_class %}template-partnershiptemplatepage{% endblock %} +{% block extra_css %} + {% compress css %} + {% endcompress css %} +{% endblock extra_css %} + +{% block content %} + {% include "ui/components/PageHeaderWithBlur.html" with title=page.title subtitle=page.header_text image=page.header_image full_length=True %} + +
+
+
+
+
+ {{page.intro|richtext}} +
+
+ {{page.body|richtext}} +
+
+ +
+
+ {% if page.related_projects %} +
+

+ {{page.get_parent.specific.related_projects_title}} +

+
+ {% for project in page.related_projects %} + {% include "ui/components/projects/ProjectPreviewBlockNews.html" with project=project.value %} + {% endfor %} +
+
+ {% endif %} + + {% if page.related_projects and page.related_news %}
{% endif %} + + {% if page.related_news %} +
+

+ {{page.get_parent.specific.related_news_title}} +

+
+ {% for news in page.related_news %} + {% include "ui/components/news/NewsPreviewBlockNews.html" with news=news.value %} + {% endfor %} +
+
+ {% endif %} +
+
+
+ +
+ {% include "ui/components/FlexTitleWithLink.html" with class="mb-8" title=page.get_parent.specific.meet_partners_title linktext=page.get_parent.specific.view_partners_text linkurl=page.get_parent.specific.view_partners_link underline=True %} + {% include "ui/components/partners/PartnerViewBlock.html" with partners=page.featured_partners.all %} +
+ +
+ {% include "ui/components/FooterBannerWithTextAndLink.html" with text=page.get_parent.specific.call_to_action_title description=page.get_parent.specific.call_to_action_description buttontext=page.get_parent.specific.call_to_action_link_text linkurl=page.get_parent.specific.call_to_action_link_url %} +
+
+
+{% endblock %} \ No newline at end of file diff --git a/app/tools_and_resources/migrations/0003_alter_toolsandresourcespage_large_panels_and_more.py b/app/tools_and_resources/migrations/0003_alter_toolsandresourcespage_large_panels_and_more.py new file mode 100644 index 0000000..c309fe0 --- /dev/null +++ b/app/tools_and_resources/migrations/0003_alter_toolsandresourcespage_large_panels_and_more.py @@ -0,0 +1,38 @@ +# Generated by Django 4.2.7 on 2024-08-15 19:35 + +from django.db import migrations, models +import django.db.models.deletion +import wagtail.blocks +import wagtail.fields +import wagtail.images.blocks + + +class Migration(migrations.Migration): + + dependencies = [ + ('wagtailcore', '0089_log_entry_data_json_null_to_object'), + ('wagtailimages', '0025_alter_image_file_alter_rendition_file'), + ('tools_and_resources', '0002_alter_toolsandresourcespage_get_connected_button_text'), + ] + + operations = [ + migrations.AlterField( + model_name='toolsandresourcespage', + name='large_panels', + field=wagtail.fields.StreamField([('panel', wagtail.blocks.StructBlock([('image', wagtail.images.blocks.ImageChooserBlock()), ('title', wagtail.blocks.CharBlock()), ('description', wagtail.blocks.RichTextBlock()), ('link_text', wagtail.blocks.CharBlock()), ('link', wagtail.blocks.StreamBlock([('page', wagtail.blocks.PageChooserBlock()), ('url', wagtail.blocks.URLBlock())], required=False))]))], blank=True, null=True, use_json_field=True), + ), + migrations.CreateModel( + name='ResourceAndLearningPage', + 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')), + ('intro', wagtail.fields.RichTextField(blank=True)), + ('large_panels', wagtail.fields.StreamField([('panel', wagtail.blocks.StructBlock([('image', wagtail.images.blocks.ImageChooserBlock()), ('title', wagtail.blocks.CharBlock()), ('description', wagtail.blocks.RichTextBlock()), ('link_text', wagtail.blocks.CharBlock()), ('link', wagtail.blocks.StreamBlock([('page', wagtail.blocks.PageChooserBlock()), ('url', wagtail.blocks.URLBlock())], required=False))]))], blank=True, null=True, use_json_field=True)), + ('go_back_prefix_text', models.CharField(default='Go Back to')), + ('header_image', models.ForeignKey(blank=True, help_text='Header image', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image')), + ], + options={ + 'abstract': False, + }, + bases=('wagtailcore.page',), + ), + ] diff --git a/app/tools_and_resources/migrations/0004_openmappingsolutionspage.py b/app/tools_and_resources/migrations/0004_openmappingsolutionspage.py new file mode 100644 index 0000000..40fa660 --- /dev/null +++ b/app/tools_and_resources/migrations/0004_openmappingsolutionspage.py @@ -0,0 +1,37 @@ +# Generated by Django 4.2.7 on 2024-08-19 18:15 + +from django.db import migrations, models +import django.db.models.deletion +import wagtail.blocks +import wagtail.fields +import wagtail.images.blocks + + +class Migration(migrations.Migration): + + dependencies = [ + ('wagtailimages', '0025_alter_image_file_alter_rendition_file'), + ('wagtailcore', '0089_log_entry_data_json_null_to_object'), + ('tools_and_resources', '0003_alter_toolsandresourcespage_large_panels_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='OpenMappingSolutionsPage', + 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')), + ('intro', wagtail.fields.RichTextField(blank=True)), + ('solutions', wagtail.fields.StreamField([('solution', wagtail.blocks.StructBlock([('banner', wagtail.images.blocks.ImageChooserBlock()), ('description', wagtail.blocks.RichTextBlock())]))], blank=True, null=True, use_json_field=True)), + ('cta_title', models.CharField(default='Get Started with Open Mapping')), + ('cta_description', wagtail.fields.RichTextField(blank=True)), + ('cta_button_text', models.CharField(default='Start Open Mapping')), + ('cta_button_link', wagtail.fields.StreamField([('page', wagtail.blocks.PageChooserBlock()), ('url', wagtail.blocks.URLBlock())], blank=True, use_json_field=True)), + ('go_back_prefix_text', models.CharField(default='Go Back to')), + ('header_image', models.ForeignKey(blank=True, help_text='Header image', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image')), + ], + options={ + 'abstract': False, + }, + bases=('wagtailcore.page',), + ), + ] diff --git a/app/tools_and_resources/migrations/0005_alter_openmappingsolutionspage_solutions.py b/app/tools_and_resources/migrations/0005_alter_openmappingsolutionspage_solutions.py new file mode 100644 index 0000000..4d718b3 --- /dev/null +++ b/app/tools_and_resources/migrations/0005_alter_openmappingsolutionspage_solutions.py @@ -0,0 +1,21 @@ +# Generated by Django 4.2.7 on 2024-08-19 18:26 + +from django.db import migrations +import wagtail.blocks +import wagtail.fields +import wagtail.images.blocks + + +class Migration(migrations.Migration): + + dependencies = [ + ('tools_and_resources', '0004_openmappingsolutionspage'), + ] + + operations = [ + migrations.AlterField( + model_name='openmappingsolutionspage', + name='solutions', + field=wagtail.fields.StreamField([('solution', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('banner', wagtail.images.blocks.ImageChooserBlock()), ('description', wagtail.blocks.RichTextBlock()), ('items', wagtail.blocks.StreamBlock([('item', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('description', wagtail.blocks.RichTextBlock()), ('links', wagtail.blocks.StreamBlock([('link', wagtail.blocks.StructBlock([('linktext', wagtail.blocks.CharBlock()), ('linkurl', wagtail.blocks.StreamBlock([('page', wagtail.blocks.PageChooserBlock()), ('url', wagtail.blocks.URLBlock())], required=False))]))], required=False))]))]))]))], blank=True, null=True, use_json_field=True), + ), + ] diff --git a/app/tools_and_resources/models.py b/app/tools_and_resources/models.py index 75db779..5526039 100644 --- a/app/tools_and_resources/models.py +++ b/app/tools_and_resources/models.py @@ -7,13 +7,15 @@ from wagtail.images.blocks import ImageChooserBlock from modelcluster.fields import ParentalKey, ParentalManyToManyField +from app.core.models import LinkOrPageBlock + class LargePanel(StructBlock): image = ImageChooserBlock() title = CharBlock() description = RichTextBlock() link_text = CharBlock() - link_url = URLBlock(required=False) + link = LinkOrPageBlock(required=False) class ToolsAndResourcesPage(Page): @@ -98,3 +100,82 @@ class ToolsAndResourcesPage(Page): FieldPanel('black_box_link_url'), ], heading="Dogear Boxes"), ] + + +class ResourceAndLearningPage(Page): + max_count = 1 + + header_image = models.ForeignKey( + "wagtailimages.Image", + null=True, + blank=True, + on_delete=models.SET_NULL, + related_name="+", + help_text="Header image" + ) + + intro = RichTextField(blank=True) + + large_panels = StreamField([('panel', LargePanel())], use_json_field=True, null=True, blank=True) + + go_back_prefix_text = models.CharField(default="Go Back to") + + content_panels = Page.content_panels + [ + FieldPanel('header_image'), + FieldPanel('intro'), + FieldPanel('large_panels'), + FieldPanel('go_back_prefix_text'), + ] + + +class OpenMappingSolutionIndividualItemBlock(StructBlock): + title = CharBlock() + description = RichTextBlock() + links = StreamBlock([ + ('link', StructBlock([ + ('linktext', CharBlock()), + ('linkurl', LinkOrPageBlock(required=False)), + ])) + ], required=False) + + +class OpenMappingSolutionBlock(StructBlock): + title = CharBlock() + banner = ImageChooserBlock() + description = RichTextBlock() + items = StreamBlock([('item', OpenMappingSolutionIndividualItemBlock())]) + + +class OpenMappingSolutionsPage(Page): + max_count = 1 + + header_image = models.ForeignKey( + "wagtailimages.Image", + null=True, + blank=True, + on_delete=models.SET_NULL, + related_name="+", + help_text="Header image" + ) + + intro = RichTextField(blank=True) + + solutions = StreamField([('solution',OpenMappingSolutionBlock())], use_json_field=True, blank=True, null=True) + + cta_title = models.CharField(default="Get Started with Open Mapping") + cta_description = RichTextField(blank=True) + cta_button_text = models.CharField(default="Start Open Mapping") + cta_button_link = StreamField(LinkOrPageBlock(), use_json_field=True, blank=True) + + go_back_prefix_text = models.CharField(default="Go Back to") + + content_panels = Page.content_panels + [ + FieldPanel('header_image'), + FieldPanel('intro'), + FieldPanel('solutions'), + FieldPanel('cta_title'), + FieldPanel('cta_description'), + FieldPanel('cta_button_text'), + FieldPanel('cta_button_link'), + FieldPanel('go_back_prefix_text'), + ] diff --git a/app/tools_and_resources/templates/tools_and_resources/open_mapping_solutions_page.html b/app/tools_and_resources/templates/tools_and_resources/open_mapping_solutions_page.html new file mode 100644 index 0000000..eff227e --- /dev/null +++ b/app/tools_and_resources/templates/tools_and_resources/open_mapping_solutions_page.html @@ -0,0 +1,89 @@ +{% extends "base.html" %} +{% load static %} +{% load wagtailcore_tags %} +{% load wagtailimages_tags %} +{% load compress %} +{% block body_class %}template-openmappingsolutionspage{% endblock %} +{% block extra_css %} + {% compress css %} + {% endcompress css %} +{% endblock extra_css %} + +{% block content %} + {% include "ui/components/PageHeaderWithBlur.html" with title=page.title image=page.header_image endinlarge=True full_length=True %} + +
+
+
+ {{page.intro|safe}} +
+ +
+ {% for solution in page.solutions %} +
+
+

+ {{solution.value.title}} +

+
+
+ {% include "ui/components/icon_svgs/LinkCaret.html" %} +
+
+
+
+ {% image solution.value.banner original class="aspect-[9/2] object-cover" %} +
+ {{solution.value.description}} +
+
+
+
+ {% for item in solution.value.items %} +
+ {{item.value.title}} +
+ {% endfor %} +
+
+
+ {% for item in solution.value.items %} +
+

+ {{item.value.title}} +

+
+ {{item.value.description}} +
+ +
+ {% endfor %} +
+
+
+
+
+ {% endfor %} +
+ +
+ {% include "ui/components/FooterBannerWithTextAndLink.html" with text=page.cta_title description=page.cta_description buttontext=page.cta_button_text buttonurl=page.cta_button_link %} +
+ +

+ {% include "ui/components/BaseLink.html" with linktext=page.go_back_prefix_text|add:" "|add:page.get_parent.specific.title linkurl=page.get_parent.url %} +

+
+
+{% endblock %} diff --git a/app/tools_and_resources/templates/tools_and_resources/resource_and_learning_page.html b/app/tools_and_resources/templates/tools_and_resources/resource_and_learning_page.html new file mode 100644 index 0000000..eed6304 --- /dev/null +++ b/app/tools_and_resources/templates/tools_and_resources/resource_and_learning_page.html @@ -0,0 +1,34 @@ +{% extends "base.html" %} +{% load static %} +{% load wagtailcore_tags %} +{% load wagtailimages_tags %} +{% load compress %} +{% block body_class %}template-resourceandlearningpage{% endblock %} +{% block extra_css %} + {% compress css %} + {% endcompress css %} +{% endblock extra_css %} + +{% block content %} + {% include "ui/components/PageHeaderWithBlur.html" with title=page.title image=page.header_image endinlarge=True full_length=True %} + +
+
+
+
+ {{page.intro|safe}} +
+
+ +
+ {% for panel in page.large_panels %} + {% include "ui/components/misc_panels/LinkBlockWithImage.html" with title=panel.value.title image=panel.value.image description=panel.value.description linktext=panel.value.link_text linkurl=panel.value.link %} + {% endfor %} +
+ +

+ {% include "ui/components/BaseLink.html" with linktext=page.go_back_prefix_text|add:" "|add:page.get_parent.specific.title linkurl=page.get_parent.url %} +

+
+
+{% endblock %} diff --git a/app/tools_and_resources/templates/tools_and_resources/tools_and_resources_page.html b/app/tools_and_resources/templates/tools_and_resources/tools_and_resources_page.html index 4eff124..e666a9f 100644 --- a/app/tools_and_resources/templates/tools_and_resources/tools_and_resources_page.html +++ b/app/tools_and_resources/templates/tools_and_resources/tools_and_resources_page.html @@ -38,7 +38,7 @@

{{page.intro_header}}

{% for panel in page.large_panels %} - {% include "ui/components/misc_panels/LinkBlockWithImage.html" with title=panel.value.title image=panel.value.image description=panel.value.description linktext=panel.value.link_text linkurl=panel.value.link_url %} + {% include "ui/components/misc_panels/LinkBlockWithImage.html" with title=panel.value.title image=panel.value.image description=panel.value.description linktext=panel.value.link_text linkurl=panel.value.link %} {% endfor %}
diff --git a/app/ui/templates/ui/components/FooterBannerWithTextAndLink.html b/app/ui/templates/ui/components/FooterBannerWithTextAndLink.html index 243407f..06b1ba3 100644 --- a/app/ui/templates/ui/components/FooterBannerWithTextAndLink.html +++ b/app/ui/templates/ui/components/FooterBannerWithTextAndLink.html @@ -12,7 +12,7 @@

{{ text }}

{% if description %} -
+
{{description|richtext}}
{% endif %} diff --git a/app/ui/templates/ui/components/icon_svgs/ExternalLinkIcon.html b/app/ui/templates/ui/components/icon_svgs/ExternalLinkIcon.html index b4efa35..4cca73a 100644 --- a/app/ui/templates/ui/components/icon_svgs/ExternalLinkIcon.html +++ b/app/ui/templates/ui/components/icon_svgs/ExternalLinkIcon.html @@ -1,6 +1,6 @@ - - - + + + diff --git a/hot_osm/static/css/hot_osm.css b/hot_osm/static/css/hot_osm.css index 3603611..587bc93 100644 --- a/hot_osm/static/css/hot_osm.css +++ b/hot_osm/static/css/hot_osm.css @@ -8,7 +8,7 @@ --hot-navy: #20365B; --hot-navy-grey: #2C3038; --hot-dark-grey: #68707F; - --hot-slate-grey: #929D83; + --hot-slate-grey: #929DB3; --hot-light-grey: #E1E0E0; --hot-off-white: #F0EFEF; --hot-barely-not-white: #FBFBFB;