diff --git a/.env.example b/.env.example index b62a12c..ee4e8d0 100644 --- a/.env.example +++ b/.env.example @@ -15,6 +15,11 @@ DB_PASSWORD=${DB_PASSWORD:-postgres} DB_HOST=${DB_HOST:-db} DB_PORT=${DB_PORT:-5432} +# aws settings +AWS_STORAGE_BUCKET_NAME=${AWS_STORAGE_BUCKET_NAME} +AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} +AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} + # Integration settings BROWSER_STACK_USER=${BROWSER_STACK_USER:-} BROWSER_ACCESS_KEY=${BROWSERSTACK_API_KEY:-} diff --git a/Dockerfile b/Dockerfile index 6391932..33a734a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -157,5 +157,7 @@ FROM runtime as prod HEALTHCHECK --start-period=10s --interval=5s --retries=20 --timeout=5s \ CMD curl --fail http://localhost:8000 || exit 1 # Pre-compile packages to .pyc (init speed gains) +USER root RUN python -c "import compileall; compileall.compile_path(maxlevels=10, quiet=1)" -CMD ["gunicorn", "hot_osm.wsgi:application"] +USER wagtail +CMD ["gunicorn", "--bind", "0.0.0.0:8000", "hot_osm.wsgi:application"] diff --git a/app/events/__init__.py b/app/events/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/events/admin.py b/app/events/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/app/events/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/app/events/apps.py b/app/events/apps.py new file mode 100644 index 0000000..923880a --- /dev/null +++ b/app/events/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class EventsConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'app.events' diff --git a/app/events/migrations/0001_initial.py b/app/events/migrations/0001_initial.py new file mode 100644 index 0000000..f8017ee --- /dev/null +++ b/app/events/migrations/0001_initial.py @@ -0,0 +1,53 @@ +# Generated by Django 4.2.7 on 2024-06-05 19:43 + +from django.db import migrations, models +import django.db.models.deletion +import wagtail.blocks +import wagtail.fields + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('wagtailcore', '0089_log_entry_data_json_null_to_object'), + ('wagtailimages', '0025_alter_image_file_alter_rendition_file'), + ] + + operations = [ + migrations.CreateModel( + name='EventOwnerPage', + 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')), + ('event_location_title', models.CharField(default='Event Location')), + ('join_event_title', models.CharField(default='Join This Event')), + ('rsvp_button_text', models.CharField(default='RSVP')), + ('more_events_title', models.CharField(default='More Events')), + ('view_all_events_text', models.CharField(default='View all Events')), + ('view_all_events_url', models.URLField(blank=True)), + ], + options={ + 'abstract': False, + }, + bases=('wagtailcore.page',), + ), + migrations.CreateModel( + name='IndividualEventPage', + 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')), + ('start_date_time', models.DateTimeField()), + ('end_date_time', models.DateTimeField()), + ('intro', wagtail.fields.RichTextField(blank=True)), + ('extended_description', wagtail.fields.StreamField([('text_block', wagtail.blocks.RichTextBlock(features=['h1', 'h2', 'h3', 'h4', 'bold', 'italic', 'link', 'ol', 'ul', 'hr', 'document-link', 'image', 'embed', 'code', 'blockquote']))], null=True, use_json_field=True)), + ('event_location', models.CharField(blank=True)), + ('rsvp_description', wagtail.fields.RichTextField(blank=True)), + ('rsvp_link', models.URLField(blank=True)), + ('image', models.ForeignKey(blank=True, help_text='Image to represent the event', 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/events/migrations/0002_individualeventpage_more_events_and_more.py b/app/events/migrations/0002_individualeventpage_more_events_and_more.py new file mode 100644 index 0000000..8069bdd --- /dev/null +++ b/app/events/migrations/0002_individualeventpage_more_events_and_more.py @@ -0,0 +1,25 @@ +# Generated by Django 4.2.7 on 2024-06-05 20:25 + +from django.db import migrations, models +import wagtail.blocks +import wagtail.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('events', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='individualeventpage', + name='more_events', + field=wagtail.fields.StreamField([('event', wagtail.blocks.PageChooserBlock(page_type=['events.IndividualEventPage']))], blank=True, null=True, use_json_field=True), + ), + migrations.AlterField( + model_name='individualeventpage', + name='rsvp_link', + field=models.URLField(blank=True, help_text='If both RSVP Description and Link are empty, the RSVP section will be hidden.'), + ), + ] diff --git a/app/events/migrations/0003_eventownerpage_event_read_more_text_and_more.py b/app/events/migrations/0003_eventownerpage_event_read_more_text_and_more.py new file mode 100644 index 0000000..355e00f --- /dev/null +++ b/app/events/migrations/0003_eventownerpage_event_read_more_text_and_more.py @@ -0,0 +1,29 @@ +# Generated by Django 4.2.7 on 2024-06-05 22:09 + +from django.db import migrations, models +import wagtail.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('events', '0002_individualeventpage_more_events_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='eventownerpage', + name='event_read_more_text', + field=models.CharField(default='Read more'), + ), + migrations.AlterField( + model_name='individualeventpage', + name='rsvp_description', + field=wagtail.fields.RichTextField(blank=True, help_text='If this field is empty, the RSVP description will not appear.'), + ), + migrations.AlterField( + model_name='individualeventpage', + name='rsvp_link', + field=models.URLField(blank=True, help_text='If this field is empty, the RSVP button will not appear. If both RSVP Description and Link are empty, the RSVP section will be hidden.'), + ), + ] diff --git a/app/events/migrations/__init__.py b/app/events/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/events/models.py b/app/events/models.py new file mode 100644 index 0000000..47a1d1c --- /dev/null +++ b/app/events/models.py @@ -0,0 +1,79 @@ +from django.db import models + +from wagtail.models import Page +from wagtail.fields import RichTextField, StreamField +from wagtail.admin.panels import FieldPanel, MultiFieldPanel, PageChooserPanel +from wagtail.blocks import CharBlock, StreamBlock, StructBlock, URLBlock, RichTextBlock, PageChooserBlock +from modelcluster.fields import ParentalKey, ParentalManyToManyField + + +class EventOwnerPage(Page): + max_count = 1 + + event_location_title = models.CharField(default="Event Location") + join_event_title = models.CharField(default="Join This Event") + rsvp_button_text = models.CharField(default="RSVP") + more_events_title = models.CharField(default="More Events") + view_all_events_text = models.CharField(default="View all Events") + view_all_events_url = models.URLField(blank=True) + event_read_more_text = models.CharField(default="Read more") + + content_panels = Page.content_panels + [ + FieldPanel('event_location_title'), + FieldPanel('join_event_title'), + FieldPanel('rsvp_button_text'), + FieldPanel('more_events_title'), + FieldPanel('view_all_events_text'), + FieldPanel('view_all_events_url'), + FieldPanel('event_read_more_text'), + ] + + +class IndividualEventPage(Page): + parent_page_type = [ + 'projects.ProjectOwnerPage' + ] + + start_date_time = models.DateTimeField() + end_date_time = models.DateTimeField() + + image = models.ForeignKey( + "wagtailimages.Image", + null=True, + blank=True, + on_delete=models.SET_NULL, + related_name="+", + help_text="Image to represent the event" + ) + intro = RichTextField(blank=True) + extended_description = StreamField([ + ('text_block', RichTextBlock(features=[ + 'h1', 'h2', 'h3', 'h4', 'bold', 'italic', 'link', 'ol', 'ul', 'hr', 'document-link', 'image', 'embed', 'code', 'blockquote' + ])) + ], use_json_field=True, null=True) + + event_location = models.CharField(blank=True) + rsvp_description = RichTextField(blank=True, help_text="If this field is empty, the RSVP description will not appear.") + rsvp_link = models.URLField(blank=True, help_text="If this field is empty, the RSVP button will not appear. If both RSVP Description and Link are empty, the RSVP section will be hidden.") + + more_events = StreamField([ + ('event', PageChooserBlock(page_type="events.IndividualEventPage")) + ], use_json_field=True, null=True, blank=True) + + content_panels = Page.content_panels + [ + MultiFieldPanel([ + FieldPanel('start_date_time'), + FieldPanel('end_date_time'), + ], heading="Date and Time"), + MultiFieldPanel([ + FieldPanel('image'), + FieldPanel('intro'), + FieldPanel('extended_description'), + ], heading="Body"), + MultiFieldPanel([ + FieldPanel('event_location'), + FieldPanel('rsvp_description'), + FieldPanel('rsvp_link'), + FieldPanel('more_events'), + ], heading="Sidebar") + ] diff --git a/app/events/templates/events/individual_event_page.html b/app/events/templates/events/individual_event_page.html new file mode 100644 index 0000000..de51588 --- /dev/null +++ b/app/events/templates/events/individual_event_page.html @@ -0,0 +1,75 @@ +{% extends "base.html" %} +{% load static %} +{% load wagtailcore_tags %} +{% load wagtailimages_tags %} +{% load compress %} +{% block body_class %}template-individualmappinghubpage{% endblock %} +{% block extra_css %} + {% compress css %} + {% endcompress css %} +{% endblock extra_css %} + +{% block content %} +
+ {% comment %} HEADER {% endcomment %} +
+

{{ page.title }}

+

+ {{ page.start_date_time }} – {% if page.start_date_time.date == page.end_date_time.date %}{{ page.end_date_time.time }} {% else %} {{page.end_date_time}} {% endif %} +

+
+ +
+ {% comment %} BODY {% endcomment %} +
+ {% image page.image original class="pb-8" %} +
+ {{ page.intro|safe }} +
+ {{ page.extended_description }} + + {% include "ui/components/sharers/ShareSection.html" with class="mt-10" %} +
+ + {% comment %} SIDEBAR {% endcomment %} +
+

+ {{page.get_parent.specific.event_location_title}} +

+

+ {{page.event_location}} +

+
+ + {% if page.rsvp_description or page.rsvp_link %} +
+

+ {{page.get_parent.specific.join_event_title}} +

+ {{page.rsvp_description|safe}} + + {% if page.rsvp_link %} + + {% include "components/branded_elements/button.html" with text=page.get_parent.specific.rsvp_button_text %} + + {% endif %} +
+ {% endif %} + +
+

+ {{page.get_parent.specific.more_events_title}} +

+
+ {% include "ui/components/BaseLink.html" with linktext=page.get_parent.specific.view_all_events_text linkurl=page.get_parent.specific.view_all_events_url %} +
+
+ {% for event in page.more_events %} + {% include "ui/components/events/EventPreviewBlockEvent.html" with event=event.value %} + {% endfor %} +
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/app/events/tests.py b/app/events/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/app/events/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/app/events/views.py b/app/events/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/app/events/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/app/projects/migrations/0021_individualprojectpage_related_events.py b/app/projects/migrations/0021_individualprojectpage_related_events.py new file mode 100644 index 0000000..8ca2cf8 --- /dev/null +++ b/app/projects/migrations/0021_individualprojectpage_related_events.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.7 on 2024-06-05 23:44 + +from django.db import migrations +import wagtail.blocks +import wagtail.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('projects', '0020_remove_individualprojectpage_black_box_link_text_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='individualprojectpage', + name='related_events', + field=wagtail.fields.StreamField([('event_page', wagtail.blocks.PageChooserBlock(page_type=['events.IndividualEventPage']))], blank=True, null=True, use_json_field=True), + ), + ] diff --git a/app/projects/models.py b/app/projects/models.py index 8631672..70c6c75 100644 --- a/app/projects/models.py +++ b/app/projects/models.py @@ -123,6 +123,10 @@ class IndividualProjectPage(Page): ('news_page', PageChooserBlock(page_type="news.IndividualNewsPage")) ], use_json_field=True, null=True, blank=True) + related_events = StreamField([ + ('event_page', PageChooserBlock(page_type="events.IndividualEventPage")) + ], use_json_field=True, null=True, blank=True) + project_contributors = StreamField([('contributor', PageChooserBlock(page_type="members.IndividualMemberPage"))], use_json_field=True, null=True, blank=True) content_panels = Page.content_panels + [ @@ -150,6 +154,7 @@ class IndividualProjectPage(Page): FieldPanel('contact'), MultiFieldPanel([ FieldPanel('related_news'), + FieldPanel('related_events'), ], heading="Related Pages"), ], heading="Sidebar"), MultiFieldPanel([ diff --git a/app/projects/templates/projects/individual_project_page.html b/app/projects/templates/projects/individual_project_page.html index 85ff86f..630b04d 100644 --- a/app/projects/templates/projects/individual_project_page.html +++ b/app/projects/templates/projects/individual_project_page.html @@ -79,14 +79,21 @@

{% endif %} {% comment %} RELATED EVENTS {% endcomment %} -
-

- {{ page.get_parent.specific.related_events_title }} -

-
- {% include "ui/components/BaseLink.html" with linkurl=page.get_parent.specific.view_all_events_url linktext=page.get_parent.specific.view_all_events_text %} + {% if page.related_events %} +
+

+ {{ page.get_parent.specific.related_events_title }} +

+
+ {% include "ui/components/BaseLink.html" with linkurl=page.get_parent.specific.view_all_events_url linktext=page.get_parent.specific.view_all_events_text %} +
+
+ {% for event in page.related_events %} + {% include "ui/components/events/EventPreviewBlockEvent.html" with event=event.value %} + {% endfor %} +
-
+ {% endif %}
diff --git a/app/ui/templates/patterns/base.html b/app/ui/templates/patterns/base.html new file mode 100644 index 0000000..432ea65 --- /dev/null +++ b/app/ui/templates/patterns/base.html @@ -0,0 +1,23 @@ +{% load static wagtailcore_tags wagtailuserbar %} + + + + + + + Base + + + + + + + + + {% block content %} + {# pattern_library_rendered_pattern is where the pattern library will inject the rendered pattern. #} + {{ pattern_library_rendered_pattern }} + {% endblock %} + + \ No newline at end of file diff --git a/app/ui/templates/ui/components/BaseLink.yaml b/app/ui/templates/ui/components/BaseLink.yaml new file mode 100644 index 0000000..fa66899 --- /dev/null +++ b/app/ui/templates/ui/components/BaseLink.yaml @@ -0,0 +1,3 @@ +context: + linktext: Learn More + linkurl: # \ No newline at end of file diff --git a/app/ui/templates/ui/components/events/EventPreviewBlockEvent.html b/app/ui/templates/ui/components/events/EventPreviewBlockEvent.html new file mode 100644 index 0000000..b117e8b --- /dev/null +++ b/app/ui/templates/ui/components/events/EventPreviewBlockEvent.html @@ -0,0 +1,43 @@ +
+
+ {% if event.start_date_time.date == event.end_date_time.date %} +

+ {{ event.start_date_time.date }} +

+

+ {{event.start_date_time.time}} – {{ event.end_date_time.time }} +

+ {% else %} +

+ {{event.start_date_time}} +

+

+

+ {{event.end_date_time}} +

+ {% endif %} +
+ +
+ +

{{event.title}}

+
{{event.intro|safe}}
+ + +
\ No newline at end of file diff --git a/app/ui/templates/ui/components/icon_svgs/FacebookLogo.html b/app/ui/templates/ui/components/icon_svgs/FacebookLogo.html index b75a6c0..3233b41 100644 --- a/app/ui/templates/ui/components/icon_svgs/FacebookLogo.html +++ b/app/ui/templates/ui/components/icon_svgs/FacebookLogo.html @@ -1,4 +1,4 @@ - + diff --git a/app/ui/templates/ui/components/navigation/FooterNavigation.html b/app/ui/templates/ui/components/navigation/FooterNavigation.html new file mode 100644 index 0000000..38f5427 --- /dev/null +++ b/app/ui/templates/ui/components/navigation/FooterNavigation.html @@ -0,0 +1,52 @@ +{% load homepage_tags %} +{% get_home_page as home_page %} +{% get_navigation as navigation %} +{% load wagtailimages_tags %} + +
+
+
+ {% comment %} LOGO {% endcomment %} +
+ {% include "components/branded_elements/logo.html" %} +
+ + {% comment %} NAVIGATION ITEMS {% endcomment %} +
+ {% for item in navigation %} + {% if item.value.show_in_footer%} +
+

+ {{item.value.title}} +

+ {% for child in item.value.children %} +

+ {{child.value.title}} +

+ {% endfor %} +
+ {% endif %} + {% endfor %} +
+
+ + {% image home_page.footer_candid_seal original %} +
+ + {% comment %} COPYRIGHT + ASSORTED LINKS {% endcomment %} +
+
+ {% for link in home_page.footer_bottom_links %} + +

+ {{link.value.text}} +

+
+ {% endfor %} +
+
+ {{home_page.footer_bottom_copyright|safe}} +
+
+
+
\ No newline at end of file diff --git a/app/ui/templates/ui/components/navigation/HeaderNavbar.html b/app/ui/templates/ui/components/navigation/HeaderNavbar.html new file mode 100644 index 0000000..3bfe9fc --- /dev/null +++ b/app/ui/templates/ui/components/navigation/HeaderNavbar.html @@ -0,0 +1,59 @@ +{% load homepage_tags %} +{% get_navigation as navigation %} +{% get_home_page as home_page %} + +
+
+ {% include "components/branded_elements/logo.html" with class="h-10 my-2" %} + +
+ + {% comment %} MOBILE EXPANDED LIST {% endcomment %} + +
diff --git a/app/ui/templates/ui/components/navigation/HeaderNavbarButton.html b/app/ui/templates/ui/components/navigation/HeaderNavbarButton.html new file mode 100644 index 0000000..22f0dd0 --- /dev/null +++ b/app/ui/templates/ui/components/navigation/HeaderNavbarButton.html @@ -0,0 +1,7 @@ + +
+

+ {{item.title}} +

+
+
\ No newline at end of file diff --git a/app/ui/templates/ui/components/navigation/HeaderNavbarItem.html b/app/ui/templates/ui/components/navigation/HeaderNavbarItem.html new file mode 100644 index 0000000..764ac4b --- /dev/null +++ b/app/ui/templates/ui/components/navigation/HeaderNavbarItem.html @@ -0,0 +1,41 @@ +
+

+ + {{item.title}} + + {% if item.children %} + {% include "ui/components/icon_svgs/LinkCaret.html" with class="rotate-90 text-hot-red" %} + {% endif %} +

+ + {% if item.children %} +
+ {% comment %} CHILDREN NAVBAR ITEMS {% endcomment %} + {% for child in item.children %} +
+

+ + {{child.value.title}} + + {% if child.value.children %} + {% include "ui/components/icon_svgs/LinkCaret.html" with class="text-hot-red caret-arrow ml-2 rotate-90 lg:rotate-0" %} + {% endif %} +

+ + {% comment %} GRANDCHILDREN NAVBAR ITEMS {% endcomment %} + {% if child.value.children%} + + {% endif %} +
+ {% endfor %} +
+ {% endif %} +
\ No newline at end of file diff --git a/app/ui/templates/ui/components/sharers/ShareSection.html b/app/ui/templates/ui/components/sharers/ShareSection.html index 60776e8..a7782da 100644 --- a/app/ui/templates/ui/components/sharers/ShareSection.html +++ b/app/ui/templates/ui/components/sharers/ShareSection.html @@ -1,4 +1,4 @@ -
+

{% if sharetext %} diff --git a/fly.toml b/fly.toml new file mode 100644 index 0000000..3ebe3a0 --- /dev/null +++ b/fly.toml @@ -0,0 +1,46 @@ +# fly.toml app configuration file generated for hotosm-staging-new on 2024-06-11T15:08:20-07:00 +# +# See https://fly.io/docs/reference/configuration/ for information about how to use this file. +# + +app = 'hotosm-staging-new' +primary_region = 'sea' +kill_signal = 'SIGINT' +kill_timeout = '5s' +console_command = '/code/manage.py shell' + +[build] + +[deploy] + strategy = 'rolling' + +[[services]] + protocol = 'tcp' + internal_port = 8000 + + [[services.ports]] + port = 80 + handlers = ['http'] + force_https = true + + [[services.ports]] + port = 443 + handlers = ['tls', 'http'] + + [services.concurrency] + hard_limit = 25 + soft_limit = 20 + + [[services.tcp_checks]] + interval = '30s' + timeout = '10s' + grace_period = '30s' + +[[vm]] + memory = '1gb' + cpu_kind = 'shared' + cpus = 1 + +[[statics]] + guest_path = '/code/static' + url_prefix = '/static/' diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index 672066e..36fb3b6 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -51,6 +51,7 @@ module.exports = { "hot-off-white": "var(--hot-off-white)", "hot-white": "var(--hot-white)", "hot-black": "var(--hot-black)", + "hot-barely-not-white": "var(--hot-barely-not-white)", }, }, }, diff --git a/home/migrations/0018_homepage_navigation.py b/home/migrations/0018_homepage_navigation.py new file mode 100644 index 0000000..3242c14 --- /dev/null +++ b/home/migrations/0018_homepage_navigation.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.7 on 2024-06-10 17:41 + +from django.db import migrations +import wagtail.blocks +import wagtail.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0017_homepage_impact_areas_learn_more'), + ] + + operations = [ + migrations.AddField( + model_name='homepage', + name='navigation', + field=wagtail.fields.StreamField([('blocks', wagtail.blocks.StreamBlock([('title', wagtail.blocks.CharBlock()), ('link_url', wagtail.blocks.URLBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False)), ('link_page', wagtail.blocks.PageChooserBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False))]))], blank=True, null=True, use_json_field=True), + ), + ] diff --git a/home/migrations/0019_alter_homepage_navigation.py b/home/migrations/0019_alter_homepage_navigation.py new file mode 100644 index 0000000..a9023e8 --- /dev/null +++ b/home/migrations/0019_alter_homepage_navigation.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.7 on 2024-06-10 17:45 + +from django.db import migrations +import wagtail.blocks +import wagtail.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0018_homepage_navigation'), + ] + + operations = [ + migrations.AlterField( + model_name='homepage', + name='navigation', + field=wagtail.fields.StreamField([('nav_item', wagtail.blocks.StreamBlock([('blocks', wagtail.blocks.StreamBlock([('title', wagtail.blocks.CharBlock()), ('link_url', wagtail.blocks.URLBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False)), ('link_page', wagtail.blocks.PageChooserBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False))]))]))], blank=True, null=True, use_json_field=True), + ), + ] diff --git a/home/migrations/0020_alter_homepage_navigation.py b/home/migrations/0020_alter_homepage_navigation.py new file mode 100644 index 0000000..62fdda2 --- /dev/null +++ b/home/migrations/0020_alter_homepage_navigation.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.7 on 2024-06-10 17:46 + +from django.db import migrations +import wagtail.blocks +import wagtail.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0019_alter_homepage_navigation'), + ] + + operations = [ + migrations.AlterField( + model_name='homepage', + name='navigation', + field=wagtail.fields.StreamField([('blocks', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('link_url', wagtail.blocks.URLBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False)), ('link_page', wagtail.blocks.PageChooserBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False))]))], blank=True, null=True, use_json_field=True), + ), + ] diff --git a/home/migrations/0021_alter_homepage_navigation.py b/home/migrations/0021_alter_homepage_navigation.py new file mode 100644 index 0000000..dc09e61 --- /dev/null +++ b/home/migrations/0021_alter_homepage_navigation.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.7 on 2024-06-10 17:49 + +from django.db import migrations +import wagtail.blocks +import wagtail.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0020_alter_homepage_navigation'), + ] + + operations = [ + migrations.AlterField( + model_name='homepage', + name='navigation', + field=wagtail.fields.StreamField([('blocks', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('link_url', wagtail.blocks.URLBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False)), ('link_page', wagtail.blocks.PageChooserBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False)), ('children', wagtail.blocks.StreamBlock([('nav_item', wagtail.blocks.StreamBlock([('blocks', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('link_url', wagtail.blocks.URLBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the pagee.', required=False)), ('link_page', wagtail.blocks.PageChooserBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the pagee.', required=False))]))]))], blank=True, null=True, use_json_field=True))]))], blank=True, null=True, use_json_field=True), + ), + ] diff --git a/home/migrations/0022_alter_homepage_navigation.py b/home/migrations/0022_alter_homepage_navigation.py new file mode 100644 index 0000000..6fe8d03 --- /dev/null +++ b/home/migrations/0022_alter_homepage_navigation.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.7 on 2024-06-10 17:55 + +from django.db import migrations +import wagtail.blocks +import wagtail.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0021_alter_homepage_navigation'), + ] + + operations = [ + migrations.AlterField( + model_name='homepage', + name='navigation', + field=wagtail.fields.StreamField([('blocks', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('link_url', wagtail.blocks.URLBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False)), ('link_page', wagtail.blocks.PageChooserBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False)), ('children', wagtail.blocks.StreamBlock([('nav_item', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('link_url', wagtail.blocks.URLBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the pagee.', required=False)), ('link_page', wagtail.blocks.PageChooserBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the pagee.', required=False))]))], blank=True, null=True, use_json_field=True))]))], blank=True, null=True, use_json_field=True), + ), + ] diff --git a/home/migrations/0023_alter_homepage_navigation.py b/home/migrations/0023_alter_homepage_navigation.py new file mode 100644 index 0000000..82d5cf2 --- /dev/null +++ b/home/migrations/0023_alter_homepage_navigation.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.7 on 2024-06-10 17:58 + +from django.db import migrations +import wagtail.blocks +import wagtail.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0022_alter_homepage_navigation'), + ] + + operations = [ + migrations.AlterField( + model_name='homepage', + name='navigation', + field=wagtail.fields.StreamField([('blocks', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('link_url', wagtail.blocks.URLBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False)), ('link_page', wagtail.blocks.PageChooserBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False)), ('children', wagtail.blocks.StreamBlock([('nav_item', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('link_url', wagtail.blocks.URLBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the pagee.', required=False)), ('link_page', wagtail.blocks.PageChooserBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the pagee.', required=False))]))], blank=True, null=True, required=False, use_json_field=True))]))], blank=True, null=True, use_json_field=True), + ), + ] diff --git a/home/migrations/0024_alter_homepage_navigation.py b/home/migrations/0024_alter_homepage_navigation.py new file mode 100644 index 0000000..2b83666 --- /dev/null +++ b/home/migrations/0024_alter_homepage_navigation.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.7 on 2024-06-10 21:28 + +from django.db import migrations +import wagtail.blocks +import wagtail.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0023_alter_homepage_navigation'), + ] + + operations = [ + migrations.AlterField( + model_name='homepage', + name='navigation', + field=wagtail.fields.StreamField([('blocks', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('link_url', wagtail.blocks.URLBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False)), ('link_page', wagtail.blocks.PageChooserBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False)), ('is_button', wagtail.blocks.BooleanBlock(help_text='Buttons will show as a red block with white text, and will show up last.')), ('children', wagtail.blocks.StreamBlock([('nav_item', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('link_url', wagtail.blocks.URLBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the pagee.', required=False)), ('link_page', wagtail.blocks.PageChooserBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the pagee.', required=False))]))], blank=True, null=True, required=False, use_json_field=True))]))], blank=True, null=True, use_json_field=True), + ), + ] diff --git a/home/migrations/0025_alter_homepage_navigation.py b/home/migrations/0025_alter_homepage_navigation.py new file mode 100644 index 0000000..ddbd012 --- /dev/null +++ b/home/migrations/0025_alter_homepage_navigation.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.7 on 2024-06-10 21:30 + +from django.db import migrations +import wagtail.blocks +import wagtail.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0024_alter_homepage_navigation'), + ] + + operations = [ + migrations.AlterField( + model_name='homepage', + name='navigation', + field=wagtail.fields.StreamField([('blocks', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('link_url', wagtail.blocks.URLBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False)), ('link_page', wagtail.blocks.PageChooserBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False)), ('is_button', wagtail.blocks.BooleanBlock(help_text='Buttons will show as a red block with white text, and will show up last.', required=False)), ('children', wagtail.blocks.StreamBlock([('nav_item', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('link_url', wagtail.blocks.URLBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the pagee.', required=False)), ('link_page', wagtail.blocks.PageChooserBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the pagee.', required=False))]))], blank=True, null=True, required=False, use_json_field=True))]))], blank=True, null=True, use_json_field=True), + ), + ] diff --git a/home/migrations/0026_alter_homepage_navigation.py b/home/migrations/0026_alter_homepage_navigation.py new file mode 100644 index 0000000..4853bb4 --- /dev/null +++ b/home/migrations/0026_alter_homepage_navigation.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.7 on 2024-06-10 22:56 + +from django.db import migrations +import wagtail.blocks +import wagtail.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0025_alter_homepage_navigation'), + ] + + operations = [ + migrations.AlterField( + model_name='homepage', + name='navigation', + field=wagtail.fields.StreamField([('blocks', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('link_url', wagtail.blocks.URLBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False)), ('link_page', wagtail.blocks.PageChooserBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False)), ('is_button', wagtail.blocks.BooleanBlock(help_text="Buttons will show as a red block with white text, show up last, and will show up on medium screens at all times. Buttons shouldn't have children.", required=False)), ('show_in_footer', wagtail.blocks.BooleanBlock(help_text="If checked, this item will show in the footer's navigation; otherwise, it will not show up.", required=False)), ('children', wagtail.blocks.StreamBlock([('nav_item', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('link_url', wagtail.blocks.URLBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the pagee.', required=False)), ('link_page', wagtail.blocks.PageChooserBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the pagee.', required=False))]))], blank=True, null=True, required=False, use_json_field=True))]))], blank=True, null=True, use_json_field=True), + ), + ] diff --git a/home/migrations/0027_homepage_footer_candid_seal_and_more.py b/home/migrations/0027_homepage_footer_candid_seal_and_more.py new file mode 100644 index 0000000..680fb3a --- /dev/null +++ b/home/migrations/0027_homepage_footer_candid_seal_and_more.py @@ -0,0 +1,27 @@ +# Generated by Django 4.2.7 on 2024-06-10 23:47 + +from django.db import migrations, models +import django.db.models.deletion +import wagtail.blocks +import wagtail.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('wagtailimages', '0025_alter_image_file_alter_rendition_file'), + ('home', '0026_alter_homepage_navigation'), + ] + + operations = [ + migrations.AddField( + model_name='homepage', + name='footer_candid_seal', + field=models.ForeignKey(blank=True, help_text='The Candid transparency seal image to be shown in the footer; this is shown on all pages.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image'), + ), + migrations.AlterField( + model_name='homepage', + name='navigation', + field=wagtail.fields.StreamField([('blocks', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('link_url', wagtail.blocks.URLBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False)), ('link_page', wagtail.blocks.PageChooserBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False)), ('is_button', wagtail.blocks.BooleanBlock(help_text="Buttons will show as a red block with white text, show up last, and will show up on medium screens at all times. Buttons shouldn't have children.", required=False)), ('show_in_footer', wagtail.blocks.BooleanBlock(help_text="If checked, this item (and its children) will show in the footer's navigation; otherwise, it will not show up.", required=False)), ('children', wagtail.blocks.StreamBlock([('nav_item', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('link_url', wagtail.blocks.URLBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the pagee.', required=False)), ('link_page', wagtail.blocks.PageChooserBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the pagee.', required=False))]))], blank=True, null=True, required=False, use_json_field=True))]))], blank=True, help_text='The items of the navigation; this is shown on all pages.', null=True, use_json_field=True), + ), + ] diff --git a/home/migrations/0028_homepage_footer_bottom_copyright_and_more.py b/home/migrations/0028_homepage_footer_bottom_copyright_and_more.py new file mode 100644 index 0000000..3f09c78 --- /dev/null +++ b/home/migrations/0028_homepage_footer_bottom_copyright_and_more.py @@ -0,0 +1,25 @@ +# Generated by Django 4.2.7 on 2024-06-11 16:47 + +from django.db import migrations +import wagtail.blocks +import wagtail.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0027_homepage_footer_candid_seal_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='homepage', + name='footer_bottom_copyright', + field=wagtail.fields.RichTextField(blank=True), + ), + migrations.AddField( + model_name='homepage', + name='footer_bottom_links', + field=wagtail.fields.StreamField([('link', wagtail.blocks.StructBlock([('text', wagtail.blocks.CharBlock()), ('url', wagtail.blocks.URLBlock(required=False))]))], blank=True, help_text='The links which show in the bottom right corner of the footer; Privacy Policy, Terms and Conditions, etc.', null=True, use_json_field=True), + ), + ] diff --git a/home/migrations/0029_alter_homepage_navigation.py b/home/migrations/0029_alter_homepage_navigation.py new file mode 100644 index 0000000..4f2a0e5 --- /dev/null +++ b/home/migrations/0029_alter_homepage_navigation.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.7 on 2024-06-11 18:34 + +from django.db import migrations +import wagtail.blocks +import wagtail.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0028_homepage_footer_bottom_copyright_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='homepage', + name='navigation', + field=wagtail.fields.StreamField([('blocks', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('link_url', wagtail.blocks.URLBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False)), ('link_page', wagtail.blocks.PageChooserBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False)), ('is_button', wagtail.blocks.BooleanBlock(help_text="Buttons will show as a red block with white text, show up last, and will show up on medium screens at all times. Buttons shouldn't have children.", required=False)), ('show_in_footer', wagtail.blocks.BooleanBlock(help_text="If checked, this item (and its children) will show in the footer's navigation; otherwise, it will not show up.", required=False)), ('children', wagtail.blocks.StreamBlock([('nav_item', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('link_url', wagtail.blocks.URLBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False)), ('link_page', wagtail.blocks.PageChooserBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False)), ('children', wagtail.blocks.StreamBlock([('nav_item', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('link_url', wagtail.blocks.URLBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False)), ('link_page', wagtail.blocks.PageChooserBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False))]))], blank=True, null=True, required=False, use_json_field=True))]))], blank=True, null=True, required=False, use_json_field=True))]))], blank=True, help_text='The items of the navigation; this is shown on all pages.', null=True, use_json_field=True), + ), + ] diff --git a/home/migrations/0030_homepage_navigation_buttons_and_more.py b/home/migrations/0030_homepage_navigation_buttons_and_more.py new file mode 100644 index 0000000..cf2682c --- /dev/null +++ b/home/migrations/0030_homepage_navigation_buttons_and_more.py @@ -0,0 +1,25 @@ +# Generated by Django 4.2.7 on 2024-06-11 19:27 + +from django.db import migrations +import wagtail.blocks +import wagtail.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0029_alter_homepage_navigation'), + ] + + operations = [ + migrations.AddField( + model_name='homepage', + name='navigation_buttons', + field=wagtail.fields.StreamField([('button', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('link_url', wagtail.blocks.URLBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False)), ('link_page', wagtail.blocks.PageChooserBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False))]))], blank=True, help_text='The buttons of the navigation; these show up last in the navbar, and, unlike regular navigation items, show up at all times on medium screens.', null=True, use_json_field=True), + ), + migrations.AlterField( + model_name='homepage', + name='navigation', + field=wagtail.fields.StreamField([('blocks', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('link_url', wagtail.blocks.URLBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False)), ('link_page', wagtail.blocks.PageChooserBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False)), ('show_in_footer', wagtail.blocks.BooleanBlock(help_text="If checked, this item (and its children) will show in the footer's navigation; otherwise, it will not show up.", required=False)), ('children', wagtail.blocks.StreamBlock([('nav_item', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('link_url', wagtail.blocks.URLBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False)), ('link_page', wagtail.blocks.PageChooserBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False)), ('children', wagtail.blocks.StreamBlock([('nav_item', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('link_url', wagtail.blocks.URLBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False)), ('link_page', wagtail.blocks.PageChooserBlock(help_text='A link URL or page must be provided; if both are present, the link will default to the page.', required=False))]))], blank=True, null=True, required=False, use_json_field=True))]))], blank=True, null=True, required=False, use_json_field=True))]))], blank=True, help_text='The items of the navigation; this is shown on all pages.', null=True, use_json_field=True), + ), + ] diff --git a/home/models.py b/home/models.py index 5788483..21a59c0 100644 --- a/home/models.py +++ b/home/models.py @@ -1,6 +1,6 @@ from django.db import models from wagtail.admin.panels import FieldPanel, MultiFieldPanel -from wagtail.blocks import CharBlock, StreamBlock, StructBlock, URLBlock, PageChooserBlock +from wagtail.blocks import CharBlock, StreamBlock, StructBlock, URLBlock, PageChooserBlock, ListBlock, BooleanBlock from wagtail.fields import RichTextField, StreamField from wagtail.models import Page @@ -23,6 +23,31 @@ class CarouselBlock(StreamBlock): slides = SlideBlock() +class ThirdNavigationStructBlock(StructBlock): + title = CharBlock() + link_url = URLBlock(required=False, help_text="A link URL or page must be provided; if both are present, the link will default to the page.") + link_page = PageChooserBlock(required=False, help_text="A link URL or page must be provided; if both are present, the link will default to the page.") + + +class SecondNavigationStructBlock(StructBlock): + title = CharBlock() + link_url = URLBlock(required=False, help_text="A link URL or page must be provided; if both are present, the link will default to the page.") + link_page = PageChooserBlock(required=False, help_text="A link URL or page must be provided; if both are present, the link will default to the page.") + children = StreamBlock([('nav_item',ThirdNavigationStructBlock())], use_json_field=True, null=True, blank=True, required=False) + + +class NavigationStructBlock(StructBlock): + title = CharBlock() + link_url = URLBlock(required=False, help_text="A link URL or page must be provided; if both are present, the link will default to the page.") + link_page = PageChooserBlock(required=False, help_text="A link URL or page must be provided; if both are present, the link will default to the page.") + show_in_footer = BooleanBlock(required=False, help_text="If checked, this item (and its children) will show in the footer's navigation; otherwise, it will not show up.") + children = StreamBlock([('nav_item',SecondNavigationStructBlock())], use_json_field=True, null=True, blank=True, required=False) + + +class NavigationBlock(StreamBlock): + blocks = NavigationStructBlock() + + class HomePage(Page): def get_context(self, request, *args, **kwargs): context = super().get_context(request, *args, **kwargs) @@ -35,6 +60,25 @@ def get_context(self, request, *args, **kwargs): templates = "home/home_page.html" + # Navigation + navigation = StreamField(NavigationBlock(), use_json_field=True, null=True, blank=True, help_text="The items of the navigation; this is shown on all pages.") + navigation_buttons = StreamField([('button', ThirdNavigationStructBlock())], use_json_field=True, null=True, blank=True, help_text="The buttons of the navigation; these show up last in the navbar, and, unlike regular navigation items, show up at all times on medium screens.") + footer_candid_seal = models.ForeignKey( + "wagtailimages.Image", + null=True, + blank=True, + on_delete=models.SET_NULL, + related_name="+", + help_text="The Candid transparency seal image to be shown in the footer; this is shown on all pages.", + ) + footer_bottom_copyright = RichTextField(features=['link'], blank=True) + footer_bottom_links = StreamField([ + ('link', StructBlock([ + ('text', CharBlock()), + ('url', URLBlock(required=False)) + ])) + ], use_json_field=True, null=True, blank=True, help_text="The links which show in the bottom right corner of the footer; Privacy Policy, Terms and Conditions, etc.") + # Hero section image = models.ForeignKey( "wagtailimages.Image", @@ -159,6 +203,13 @@ def get_context(self, request, *args, **kwargs): subscribe_checkbox_text = RichTextField(blank=True, help_text="Ensure that you include a link to the privacy policy within this field.", features=['link']) content_panels = Page.content_panels + [ + MultiFieldPanel([ + FieldPanel('navigation'), + FieldPanel('navigation_buttons'), + FieldPanel('footer_candid_seal'), + FieldPanel('footer_bottom_copyright'), + FieldPanel('footer_bottom_links'), + ], heading="Navigation"), MultiFieldPanel( [ FieldPanel("image"), diff --git a/home/templates/home/home_page.html b/home/templates/home/home_page.html index fc22639..1c9003d 100644 --- a/home/templates/home/home_page.html +++ b/home/templates/home/home_page.html @@ -151,8 +151,8 @@

{% include "components/branded_elements/button.html" with text=page.get_involved_button_text classes="mt-4" %} - - {% include "components/branded_elements/button_bordered.html" with text=page.get_involved_button_text classes="mt-4" %} + + {% include "components/branded_elements/button_bordered.html" with text=page.partner_with_us_button_text classes="mt-4" %}

diff --git a/home/templatetags/homepage_tags.py b/home/templatetags/homepage_tags.py new file mode 100644 index 0000000..8afc110 --- /dev/null +++ b/home/templatetags/homepage_tags.py @@ -0,0 +1,29 @@ +from wagtail.models import Page +from django import template +from django.utils.translation import get_language +from home.models import HomePage + +register = template.Library() + +@register.simple_tag(takes_context=True) +def get_home_page(context): + current_page = context.get('self') + + if current_page is None: + return None + + home_page = current_page.get_ancestors(inclusive=True).type(HomePage).first().specific + + return home_page + + +@register.simple_tag(takes_context=True) +def get_navigation(context): + current_page = context.get('self') + + if current_page is None: + return None + + navigation = current_page.get_ancestors(inclusive=True).type(HomePage).first().specific.navigation + + return navigation diff --git a/hot_osm/locale/en/LC_MESSAGES/django.po b/hot_osm/locale/en/LC_MESSAGES/django.po index 2e28bdc..aed5e2e 100644 --- a/hot_osm/locale/en/LC_MESSAGES/django.po +++ b/hot_osm/locale/en/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-06-03 23:08+0000\n" +"POT-Creation-Date: 2024-06-05 23:25+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/hot_osm/locale/es/LC_MESSAGES/django.po b/hot_osm/locale/es/LC_MESSAGES/django.po index b37f0f5..358a241 100644 --- a/hot_osm/locale/es/LC_MESSAGES/django.po +++ b/hot_osm/locale/es/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-06-03 23:08+0000\n" +"POT-Creation-Date: 2024-06-05 23:25+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/hot_osm/locale/fr/LC_MESSAGES/django.po b/hot_osm/locale/fr/LC_MESSAGES/django.po index bd8581f..3aa0366 100644 --- a/hot_osm/locale/fr/LC_MESSAGES/django.po +++ b/hot_osm/locale/fr/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-06-03 23:08+0000\n" +"POT-Creation-Date: 2024-06-05 23:25+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/hot_osm/settings/base.py b/hot_osm/settings/base.py index 076eac0..2a45387 100644 --- a/hot_osm/settings/base.py +++ b/hot_osm/settings/base.py @@ -14,6 +14,12 @@ import os from dotenv import load_dotenv +import mimetypes +mimetypes.add_type("text/css", ".css", True) + + +# import re +# import dj_database_url # Load environment variables from .env file load_dotenv(".env") @@ -37,6 +43,7 @@ "app.core", "app.mapping_hubs", "app.members", + "app.events", "search", "users", "utils", @@ -63,6 +70,8 @@ "compressor", "wagtail_localize", "wagtail_localize.locales", + "wagtail_modeladmin", + "storages", ] MIDDLEWARE = [ @@ -74,6 +83,7 @@ "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", "django.middleware.security.SecurityMiddleware", + "whitenoise.middleware.WhiteNoiseMiddleware", "wagtail.contrib.redirects.middleware.RedirectMiddleware", ] @@ -112,8 +122,18 @@ "PASSWORD": os.getenv("DB_PASSWORD"), "HOST": os.getenv("DB_HOST"), "PORT": os.getenv("DB_PORT"), + "OPTIONS": { + "sslmode": "disable", + } } } +# DATABASES = { +# "default": dj_database_url.parse( +# re.sub(r"^postgres(ql)?", "postgis", os.getenv("DATABASE_URL", None)), +# conn_max_age=600, +# ssl_require=False, +# ) +# } # Password validation @@ -179,19 +199,6 @@ # https://django-compressor.readthedocs.io COMPRESS_ENABLED = True -# ManifestStaticFilesStorage is recommended in production, to prevent outdated -# JavaScript / CSS assets being served from cache (e.g. after a Wagtail upgrade). -# See https://docs.djangoproject.com/en/4.2/ref/contrib/staticfiles/#manifeststaticfilesstorage -STORAGES = { - "default": { - "BACKEND": "django.core.files.storage.FileSystemStorage", - "LOCATION": os.path.join(BASE_DIR, "media"), - }, - "staticfiles": { - "BACKEND": "django.contrib.staticfiles.storage.ManifestStaticFilesStorage", - }, -} - STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles") STATIC_URL = "/static/" @@ -217,3 +224,32 @@ WAGTAILADMIN_BASE_URL = "http://example.com" AUTH_USER_MODEL = "users.User" + +CSRF_TRUSTED_ORIGINS = [ + "https://hotosm-staging-new.fly.dev", +] + +WHITENOISE_MIMETYPES = { + '.css': 'text/css' +} + +PATTERN_LIBRARY = { + # Groups of templates for the pattern library navigation. The keys + # are the group titles and the values are lists of template name prefixes that will + # be searched to populate the groups. + "SECTIONS": ( + ("components", ["components", "ui/components"]), + ("pages", ["patterns/pages"]), + ), + + # Configure which files to detect as templates. + "TEMPLATE_SUFFIX": ".html", + + # Set which template components should be rendered inside of, + # so they may use page-level component dependencies like CSS. + "PATTERN_BASE_TEMPLATE_NAME": "patterns/base.html", + + # Any template in BASE_TEMPLATE_NAMES or any template that extends a template in + # BASE_TEMPLATE_NAMES is a "page" and will be rendered as-is without being wrapped. + "BASE_TEMPLATE_NAMES": ["patterns/base_page.html"], +} \ No newline at end of file diff --git a/hot_osm/settings/dev.py b/hot_osm/settings/dev.py index d185d2d..60cc27d 100644 --- a/hot_osm/settings/dev.py +++ b/hot_osm/settings/dev.py @@ -8,9 +8,26 @@ EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" -INSTALLED_APPS = INSTALLED_APPS + ["django_browser_reload"] # noqa: F405 +INSTALLED_APPS = INSTALLED_APPS + ["django_browser_reload", "pattern_library",] # noqa: F405 MIDDLEWARE = MIDDLEWARE + ["django_browser_reload.middleware.BrowserReloadMiddleware"] # noqa: F405 +X_FRAME_OPTIONS = "SAMEORIGIN" + +TEMPLATES[0]["OPTIONS"]["builtins"] = ["pattern_library.loader_tags"] + +# ManifestStaticFilesStorage is recommended in production, to prevent outdated +# JavaScript / CSS assets being served from cache (e.g. after a Wagtail upgrade). +# See https://docs.djangoproject.com/en/4.2/ref/contrib/staticfiles/#manifeststaticfilesstorage +STORAGES = { + "default": { + "BACKEND": "django.core.files.storage.FileSystemStorage", + "LOCATION": os.path.join(BASE_DIR, "media"), + }, + "staticfiles": { + "BACKEND": "django.contrib.staticfiles.storage.ManifestStaticFilesStorage", + }, +} + try: from .local import * except ImportError: diff --git a/hot_osm/settings/production.py b/hot_osm/settings/production.py index 9ca4ed7..eb4b923 100644 --- a/hot_osm/settings/production.py +++ b/hot_osm/settings/production.py @@ -1,7 +1,24 @@ from .base import * +# SECURITY WARNING: don't run with debug turned on in production! DEBUG = False +# SECURITY WARNING: define the correct hosts in production! +ALLOWED_HOSTS = ["hotosm-staging-new.fly.dev", "127.0.0.1", "localhost"] + +EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" + +INSTALLED_APPS = INSTALLED_APPS + ["django_browser_reload"] # noqa: F405 +MIDDLEWARE = MIDDLEWARE + ["django_browser_reload.middleware.BrowserReloadMiddleware"] # noqa: F405 + +AWS_STORAGE_BUCKET_NAME = os.getenv("AWS_STORAGE_BUCKET_NAME") +AWS_ACCESS_KEY_ID = os.getenv("AWS_ACCESS_KEY_ID") +AWS_SECRET_ACCESS_KEY = os.getenv("AWS_SECRET_ACCESS_KEY") +AWS_S3_CUSTOM_DOMAIN = '%s.s3.amazonaws.com' % AWS_STORAGE_BUCKET_NAME + +MEDIA_URL = "https://%s/" % AWS_S3_CUSTOM_DOMAIN +DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage' + try: from .local import * except ImportError: diff --git a/hot_osm/static/css/hot_osm.css b/hot_osm/static/css/hot_osm.css index 94fdefa..dfc811e 100644 --- a/hot_osm/static/css/hot_osm.css +++ b/hot_osm/static/css/hot_osm.css @@ -11,6 +11,7 @@ --hot-slate-grey: #929D83; --hot-light-grey: #E1E0E0; --hot-off-white: #F0EFEF; + --hot-barely-not-white: #FBFBFB; --hot-white: #FFFFFF; --hot-black: #000000; --hot-off-black: #101010; diff --git a/hot_osm/templates/base.html b/hot_osm/templates/base.html index 181ba35..2a709ad 100644 --- a/hot_osm/templates/base.html +++ b/hot_osm/templates/base.html @@ -24,14 +24,13 @@ {# Force all links in the live preview panel to be opened in a new tab #} {% if request.in_preview_panel %}{% endif %} {# Global stylesheets #} - {% compress css %} - - {% endcompress css %} + {% block extra_css %}{# Override this in templates to add extra stylesheets #}{% endblock %} + @@ -40,10 +39,12 @@
{% include "components/nav/secondary_desktop_nav.html" %}
- {% include "components/header/header.html" %} + {% comment %} {% include "components/header/header.html" %} {% endcomment %} + {% include "ui/components/navigation/HeaderNavbar.html" %} {% block content %} {% endblock content %}
+ {% include "ui/components/navigation/FooterNavigation.html" %} {% django_browser_reload_script %} {% wagtailuserbar %}
diff --git a/hot_osm/templates/components/branded_elements/button.html b/hot_osm/templates/components/branded_elements/button.html index 8e2c21a..a188a32 100644 --- a/hot_osm/templates/components/branded_elements/button.html +++ b/hot_osm/templates/components/branded_elements/button.html @@ -1,5 +1,5 @@ diff --git a/hot_osm/templates/components/branded_elements/button_bordered.html b/hot_osm/templates/components/branded_elements/button_bordered.html index dc857d8..c1e004e 100644 --- a/hot_osm/templates/components/branded_elements/button_bordered.html +++ b/hot_osm/templates/components/branded_elements/button_bordered.html @@ -1,5 +1,5 @@ diff --git a/hot_osm/templates/components/branded_elements/logo.html b/hot_osm/templates/components/branded_elements/logo.html index 2c2e6b6..a8b3eaf 100644 --- a/hot_osm/templates/components/branded_elements/logo.html +++ b/hot_osm/templates/components/branded_elements/logo.html @@ -1,5 +1,6 @@ {% include "../branded_elements/social_icons.html" %} -