diff --git a/Dockerfile b/Dockerfile index 33a734a..3bbac6c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,7 +23,7 @@ LABEL org.hotosm.fmtm.app-name="backend" \ RUN set -ex \ && apt-get update \ && DEBIAN_FRONTEND=noninteractive apt-get install \ - -y --no-install-recommends "locales" "ca-certificates" "gettext" \ + -y --no-install-recommends "locales" "ca-certificates" "gettext" "libmagickwand-dev" \ && DEBIAN_FRONTEND=noninteractive apt-get upgrade -y \ && rm -rf /var/lib/apt/lists/* \ && update-ca-certificates @@ -61,6 +61,7 @@ RUN set -ex \ "libwebp-dev" \ "nodejs" \ "npm" \ + "libmagickwand-dev" \ && rm -rf /var/lib/apt/lists/* COPY --from=extract-deps \ /opt/python/requirements.txt /opt/python/ @@ -94,6 +95,7 @@ RUN set -ex \ "libwebp-dev" \ "nodejs" \ "npm" \ + "libmagickwand-dev" \ && rm -rf /var/lib/apt/lists/* # Copy the entrypoint script into the Docker image COPY --chown=wagtail:wagtail container-entrypoint.sh / diff --git a/app/core/models.py b/app/core/models.py index 638183c..3064bb4 100644 --- a/app/core/models.py +++ b/app/core/models.py @@ -44,6 +44,7 @@ class LinkOrPageBlock(StreamBlock): page = PageChooserBlock() url = URLBlock() document = DocumentChooserBlock() + other = CharBlock(help_text="Only use this option as a last resort. The other fields are preferred. In cases like email 'mailto' links, however, this field can be used. Ensure that your provided link will function as intended prior to publishing live.") class Meta: max_num = 1 diff --git a/app/events/migrations/0010_remove_eventownerpage_view_all_events_url_and_more.py b/app/events/migrations/0010_remove_eventownerpage_view_all_events_url_and_more.py new file mode 100644 index 0000000..fc9bd83 --- /dev/null +++ b/app/events/migrations/0010_remove_eventownerpage_view_all_events_url_and_more.py @@ -0,0 +1,35 @@ +# Generated by Django 4.2.7 on 2024-09-18 19:59 + +from django.db import migrations, models +import wagtail.blocks +import wagtail.documents.blocks +import wagtail.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('events', '0009_alter_eventownerpage_category_select_and_more'), + ] + + operations = [ + migrations.RemoveField( + model_name='eventownerpage', + name='view_all_events_url', + ), + migrations.AddField( + model_name='eventownerpage', + name='view_all_events_link', + field=wagtail.fields.StreamField([('page', wagtail.blocks.PageChooserBlock()), ('url', wagtail.blocks.URLBlock()), ('document', wagtail.documents.blocks.DocumentChooserBlock())], blank=True, use_json_field=True), + ), + migrations.AlterField( + model_name='individualeventpage', + name='end_date_time', + field=models.DateTimeField(help_text='This datetime is in UTC.'), + ), + migrations.AlterField( + model_name='individualeventpage', + name='start_date_time', + field=models.DateTimeField(help_text='This datetime is in UTC.'), + ), + ] diff --git a/app/events/migrations/0011_eventownerpage_remove_filters_text.py b/app/events/migrations/0011_eventownerpage_remove_filters_text.py new file mode 100644 index 0000000..520326e --- /dev/null +++ b/app/events/migrations/0011_eventownerpage_remove_filters_text.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.7 on 2024-10-03 22:49 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('events', '0010_remove_eventownerpage_view_all_events_url_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='eventownerpage', + name='remove_filters_text', + field=models.CharField(default='Remove All Filters'), + ), + ] diff --git a/app/events/migrations/0012_alter_eventownerpage_view_all_events_link.py b/app/events/migrations/0012_alter_eventownerpage_view_all_events_link.py new file mode 100644 index 0000000..6d32813 --- /dev/null +++ b/app/events/migrations/0012_alter_eventownerpage_view_all_events_link.py @@ -0,0 +1,21 @@ +# Generated by Django 4.2.7 on 2024-10-10 17:41 + +from django.db import migrations +import wagtail.blocks +import wagtail.documents.blocks +import wagtail.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('events', '0011_eventownerpage_remove_filters_text'), + ] + + operations = [ + migrations.AlterField( + model_name='eventownerpage', + name='view_all_events_link', + field=wagtail.fields.StreamField([('page', wagtail.blocks.PageChooserBlock()), ('url', wagtail.blocks.URLBlock()), ('document', wagtail.documents.blocks.DocumentChooserBlock()), ('other', wagtail.blocks.CharBlock(help_text="Only use this option as a last resort. The other fields are preferred. In cases like email 'mailto' links, however, this field can be used. Ensure that your provided link will function as intended prior to publishing live."))], blank=True, use_json_field=True), + ), + ] diff --git a/app/events/migrations/0013_alter_individualeventpage_extended_description.py b/app/events/migrations/0013_alter_individualeventpage_extended_description.py new file mode 100644 index 0000000..b61c1eb --- /dev/null +++ b/app/events/migrations/0013_alter_individualeventpage_extended_description.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.7 on 2024-10-21 21:37 + +from django.db import migrations +import wagtail.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('events', '0012_alter_eventownerpage_view_all_events_link'), + ] + + operations = [ + migrations.AlterField( + model_name='individualeventpage', + name='extended_description', + field=wagtail.fields.StreamField([('text_block', 0)], block_lookup={0: ('wagtail.blocks.RichTextBlock', (), {'features': ['h2', 'h3', 'h4', 'bold', 'italic', 'link', 'ol', 'ul', 'hr', 'document-link', 'image', 'embed', 'code', 'blockquote']})}, null=True), + ), + ] diff --git a/app/events/migrations/0014_eventownerpage_applied_text.py b/app/events/migrations/0014_eventownerpage_applied_text.py new file mode 100644 index 0000000..2ec165d --- /dev/null +++ b/app/events/migrations/0014_eventownerpage_applied_text.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.7 on 2024-10-24 18:38 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('events', '0013_alter_individualeventpage_extended_description'), + ] + + operations = [ + migrations.AddField( + model_name='eventownerpage', + name='applied_text', + field=models.CharField(default='applied', help_text='This will be a suffix to a number, used to indicate how many filters are applied currently in some field.'), + ), + ] diff --git a/app/events/migrations/0015_eventownerpage_date_date_text_and_more.py b/app/events/migrations/0015_eventownerpage_date_date_text_and_more.py new file mode 100644 index 0000000..b2c474d --- /dev/null +++ b/app/events/migrations/0015_eventownerpage_date_date_text_and_more.py @@ -0,0 +1,28 @@ +# Generated by Django 4.2.7 on 2024-11-06 23:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('events', '0014_eventownerpage_applied_text'), + ] + + operations = [ + migrations.AddField( + model_name='eventownerpage', + name='date_date_text', + field=models.CharField(default='date'), + ), + migrations.AddField( + model_name='eventownerpage', + name='date_from_text', + field=models.CharField(default='From'), + ), + migrations.AddField( + model_name='eventownerpage', + name='date_to_text', + field=models.CharField(default='To'), + ), + ] diff --git a/app/events/migrations/0016_alter_eventownerpage_date_date_text.py b/app/events/migrations/0016_alter_eventownerpage_date_date_text.py new file mode 100644 index 0000000..68ee471 --- /dev/null +++ b/app/events/migrations/0016_alter_eventownerpage_date_date_text.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.7 on 2024-11-07 17:06 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('events', '0015_eventownerpage_date_date_text_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='eventownerpage', + name='date_date_text', + field=models.CharField(default='Filter by Date'), + ), + ] diff --git a/app/events/models.py b/app/events/models.py index 1d48f22..72d0344 100644 --- a/app/events/models.py +++ b/app/events/models.py @@ -12,6 +12,9 @@ from wagtail.snippets.models import register_snippet from app.members.models import IndividualMappingHubPage +from app.core.models import LinkOrPageBlock + +from datetime import datetime class EventOwnerPage(Page): @@ -27,7 +30,7 @@ def get_context(self, request, *args, **kwargs): categories = EventCategory.objects.all() query = Q() for category in categories: - if request.GET.get(str(category), ''): + if request.GET.get("cat" + str(category), ''): query = query | Q(event_categories=category) events_list = events_list.filter(query) @@ -35,16 +38,22 @@ def get_context(self, request, *args, **kwargs): event_host_types = EventHostType.objects.all() query = Q() for host_type in event_host_types: - if request.GET.get(str(host_type), ''): + if request.GET.get("htype" + str(host_type.id), ''): query = query | Q(event_host_type=host_type) events_list = events_list.filter(query) hubs = IndividualMappingHubPage.objects.live().filter(locale=context['page'].locale) query = Q() for hub in hubs: - if request.GET.get(str(hub), ''): + if request.GET.get("hub" + str(hub.id), ''): query = query | Q(event_region_hub=hub) events_list = events_list.filter(query).distinct() + + from_date = request.GET.get("fromdate") + from_date = datetime.strptime(from_date, "%Y-%m-%d") if from_date else datetime.min + to_date = request.GET.get("todate") + to_date = datetime.strptime(to_date, "%Y-%m-%d") if to_date else datetime.max + events_list = events_list.filter(Q(start_date_time__range=[from_date,to_date]) | Q(end_date_time__range=[from_date,to_date])) match request.GET.get('sort', ''): case 'sort.new': @@ -88,9 +97,10 @@ def get_context(self, request, *args, **kwargs): 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) + view_all_events_link = StreamField(LinkOrPageBlock(), use_json_field=True, blank=True) event_read_more_text = models.CharField(default="Read more") + applied_text = models.CharField(default="applied", help_text="This will be a suffix to a number, used to indicate how many filters are applied currently in some field.") keyword_search_hint = models.CharField(default="Search by keyword") filter_by_country = models.CharField(default="Filter by Country") host_type_select = models.CharField(default="Filter by Host Type") @@ -99,11 +109,16 @@ def get_context(self, request, *args, **kwargs): sort_by_old = models.CharField(default="Sort by Old") sort_by_titlea = models.CharField(default="Sort by Title Alphabetical") sort_by_titlez = models.CharField(default="Sort by Title Reverse Alphabetical") + date_date_text = models.CharField(default="Filter by Date") + date_from_text = models.CharField(default="From") + date_to_text = models.CharField(default="To") search_button_text = models.CharField(default="Search") + remove_filters_text = models.CharField(default="Remove All Filters") results_text = models.CharField(default="Results") content_panels = Page.content_panels + [ MultiFieldPanel([ + FieldPanel('applied_text'), FieldPanel('keyword_search_hint'), FieldPanel('filter_by_country'), FieldPanel('host_type_select'), @@ -112,7 +127,11 @@ def get_context(self, request, *args, **kwargs): FieldPanel('sort_by_old'), FieldPanel('sort_by_titlea'), FieldPanel('sort_by_titlez'), + FieldPanel('date_from_text'), + FieldPanel('date_to_text'), + FieldPanel('date_date_text'), FieldPanel('search_button_text'), + FieldPanel('remove_filters_text'), FieldPanel('results_text'), ], heading="Event Search Page"), MultiFieldPanel([ @@ -123,7 +142,7 @@ def get_context(self, request, *args, **kwargs): FieldPanel('rsvp_button_text'), FieldPanel('more_events_title'), FieldPanel('view_all_events_text'), - FieldPanel('view_all_events_url'), + FieldPanel('view_all_events_link'), FieldPanel('event_read_more_text'), ], heading="Individual Event Page"), ] @@ -159,13 +178,13 @@ class Meta: verbose_name_plural = "Event Categories" -class IndividualEventPage(Page): +class IndividualEventPage(Page): parent_page_types = [ 'events.EventOwnerPage' ] - start_date_time = models.DateTimeField() - end_date_time = models.DateTimeField() + start_date_time = models.DateTimeField(help_text="This datetime is in UTC.") + end_date_time = models.DateTimeField(help_text="This datetime is in UTC.") image = models.ForeignKey( "wagtailimages.Image", @@ -178,7 +197,7 @@ class IndividualEventPage(Page): 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' + 'h2', 'h3', 'h4', 'bold', 'italic', 'link', 'ol', 'ul', 'hr', 'document-link', 'image', 'embed', 'code', 'blockquote' ])) ], use_json_field=True, null=True) diff --git a/app/events/templates/events/event_owner_page.html b/app/events/templates/events/event_owner_page.html index 3ffad23..07ec418 100644 --- a/app/events/templates/events/event_owner_page.html +++ b/app/events/templates/events/event_owner_page.html @@ -10,10 +10,18 @@ {% endblock extra_css %} {% block content %} -
+

{{page.title}}

-
+
{% comment %} KEYWORD SEARCH {% endcomment %}
@@ -25,7 +33,7 @@

{{page.title}}

- {{page.host_type_select}} + {{page.host_type_select}} {% include "ui/components/utilities/FormAppliedSuffix.html" with key="htype" applied_text=page.applied_text %}

{% include "ui/components/icon_svgs/LinkCaret.html" with class="rotate-90 text-hot-red" %}
@@ -34,7 +42,7 @@

{{page.title}}

{% for event_host_type in event_host_types %}
- +
{% endfor %}
@@ -44,7 +52,7 @@

{{page.title}}

- {{page.category_select}} + {{page.category_select}} {% include "ui/components/utilities/FormAppliedSuffix.html" with key="cat" applied_text=page.applied_text %}

{% include "ui/components/icon_svgs/LinkCaret.html" with class="rotate-90 text-hot-red" %}
@@ -53,7 +61,7 @@

{{page.title}}

{% for category in categories %}
- +
{% endfor %}
@@ -63,7 +71,7 @@

{{page.title}}

- {{page.filter_by_country}} + {{page.filter_by_country}} {% include "ui/components/utilities/FormAppliedSuffix.html" with key="hub" applied_text=page.applied_text %}

{% include "ui/components/icon_svgs/LinkCaret.html" with class="rotate-90 text-hot-red" %}
@@ -72,7 +80,7 @@

{{page.title}}

{% for hub in hubs %}
- +
{% endfor %}
@@ -95,22 +103,51 @@

{{page.title}}

+
+
+

+ {{page.date_date_text}} +

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

{{page.date_from_text}}

+
+ +
+

{{page.date_to_text}}

+
+ +
+
+
+
+ +

+ {{page.remove_filters_text}} + {% include "ui/components/icon_svgs/RefreshIcon.html" with class="ml-4" %} +

+
+ {% comment %} EVENT ITEMS {% endcomment %} -

{{events_paginator.count}} {{page.results_text}}

-
- {% for event in events %} - {% include "ui/components/events/EventPreviewBlockEvent.html" with event=event showimage=True %} - {% endfor %} +
+

{{events_paginator.count}} {{page.results_text}}

+
+ {% for event in events %} + {% include "ui/components/events/EventPreviewBlockEvent.html" with event=event showimage=True %} + {% endfor %} +
{% comment %} PAGE NAVIGATION {% endcomment %} - {% include "ui/components/utilities/PaginatorNavigation.html" with paginator=events_paginator current_page=current_page %} + {% include "ui/components/utilities/PaginatorNavigationHTMX.html" with paginator=events_paginator current_page=current_page %}
{% endblock %} diff --git a/app/events/templates/events/individual_event_page.html b/app/events/templates/events/individual_event_page.html index 0598ed3..3ff34b6 100644 --- a/app/events/templates/events/individual_event_page.html +++ b/app/events/templates/events/individual_event_page.html @@ -3,36 +3,36 @@ {% load wagtailcore_tags %} {% load wagtailimages_tags %} {% load compress %} -{% block body_class %}template-individualmappinghubpage{% endblock %} +{% block body_class %}template-individualeventpage{% 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 %} + {{ page.start_date_time }} {{page.start_date_time.tzinfo}} – {% if page.start_date_time.date == page.end_date_time.date %}{{ page.end_date_time.time }} {% else %} {{page.end_date_time}} {% endif %} {{page.start_date_time.tzinfo}}

-
+
{% comment %} BODY {% endcomment %}
- {% image page.image original class="pb-8" %} + {% image page.image original class="mb-8 aspect-[8/5] object-cover" %}
- {{ page.intro|safe }} + {{ page.intro|richtext }}
{{ page.extended_description }} - {% include "ui/components/sharers/ShareSection.html" with class="mt-10" %} + {% include "ui/components/sharers/ShareSection.html" with class="mt-20" %}
{% comment %} SIDEBAR {% endcomment %} -
+