Skip to content

Commit 5473ab0

Browse files
Merge pull request #28 from hotosm/feat/ind-event-page
ind event page, navigation, staging
2 parents d394c78 + 07b981b commit 5473ab0

File tree

63 files changed

+1277
-40
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+1277
-40
lines changed

.env.example

+5
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ DB_PASSWORD=${DB_PASSWORD:-postgres}
1515
DB_HOST=${DB_HOST:-db}
1616
DB_PORT=${DB_PORT:-5432}
1717

18+
# aws settings
19+
AWS_STORAGE_BUCKET_NAME=${AWS_STORAGE_BUCKET_NAME}
20+
AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
21+
AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
22+
1823
# Integration settings
1924
BROWSER_STACK_USER=${BROWSER_STACK_USER:-}
2025
BROWSER_ACCESS_KEY=${BROWSERSTACK_API_KEY:-}

Dockerfile

+3-1
Original file line numberDiff line numberDiff line change
@@ -157,5 +157,7 @@ FROM runtime as prod
157157
HEALTHCHECK --start-period=10s --interval=5s --retries=20 --timeout=5s \
158158
CMD curl --fail http://localhost:8000 || exit 1
159159
# Pre-compile packages to .pyc (init speed gains)
160+
USER root
160161
RUN python -c "import compileall; compileall.compile_path(maxlevels=10, quiet=1)"
161-
CMD ["gunicorn", "hot_osm.wsgi:application"]
162+
USER wagtail
163+
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "hot_osm.wsgi:application"]

app/events/__init__.py

Whitespace-only changes.

app/events/admin.py

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from django.contrib import admin
2+
3+
# Register your models here.

app/events/apps.py

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from django.apps import AppConfig
2+
3+
4+
class EventsConfig(AppConfig):
5+
default_auto_field = 'django.db.models.BigAutoField'
6+
name = 'app.events'

app/events/migrations/0001_initial.py

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Generated by Django 4.2.7 on 2024-06-05 19:43
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
import wagtail.blocks
6+
import wagtail.fields
7+
8+
9+
class Migration(migrations.Migration):
10+
11+
initial = True
12+
13+
dependencies = [
14+
('wagtailcore', '0089_log_entry_data_json_null_to_object'),
15+
('wagtailimages', '0025_alter_image_file_alter_rendition_file'),
16+
]
17+
18+
operations = [
19+
migrations.CreateModel(
20+
name='EventOwnerPage',
21+
fields=[
22+
('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')),
23+
('event_location_title', models.CharField(default='Event Location')),
24+
('join_event_title', models.CharField(default='Join This Event')),
25+
('rsvp_button_text', models.CharField(default='RSVP')),
26+
('more_events_title', models.CharField(default='More Events')),
27+
('view_all_events_text', models.CharField(default='View all Events')),
28+
('view_all_events_url', models.URLField(blank=True)),
29+
],
30+
options={
31+
'abstract': False,
32+
},
33+
bases=('wagtailcore.page',),
34+
),
35+
migrations.CreateModel(
36+
name='IndividualEventPage',
37+
fields=[
38+
('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')),
39+
('start_date_time', models.DateTimeField()),
40+
('end_date_time', models.DateTimeField()),
41+
('intro', wagtail.fields.RichTextField(blank=True)),
42+
('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)),
43+
('event_location', models.CharField(blank=True)),
44+
('rsvp_description', wagtail.fields.RichTextField(blank=True)),
45+
('rsvp_link', models.URLField(blank=True)),
46+
('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')),
47+
],
48+
options={
49+
'abstract': False,
50+
},
51+
bases=('wagtailcore.page',),
52+
),
53+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Generated by Django 4.2.7 on 2024-06-05 20:25
2+
3+
from django.db import migrations, models
4+
import wagtail.blocks
5+
import wagtail.fields
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
dependencies = [
11+
('events', '0001_initial'),
12+
]
13+
14+
operations = [
15+
migrations.AddField(
16+
model_name='individualeventpage',
17+
name='more_events',
18+
field=wagtail.fields.StreamField([('event', wagtail.blocks.PageChooserBlock(page_type=['events.IndividualEventPage']))], blank=True, null=True, use_json_field=True),
19+
),
20+
migrations.AlterField(
21+
model_name='individualeventpage',
22+
name='rsvp_link',
23+
field=models.URLField(blank=True, help_text='If both RSVP Description and Link are empty, the RSVP section will be hidden.'),
24+
),
25+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Generated by Django 4.2.7 on 2024-06-05 22:09
2+
3+
from django.db import migrations, models
4+
import wagtail.fields
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
('events', '0002_individualeventpage_more_events_and_more'),
11+
]
12+
13+
operations = [
14+
migrations.AddField(
15+
model_name='eventownerpage',
16+
name='event_read_more_text',
17+
field=models.CharField(default='Read more'),
18+
),
19+
migrations.AlterField(
20+
model_name='individualeventpage',
21+
name='rsvp_description',
22+
field=wagtail.fields.RichTextField(blank=True, help_text='If this field is empty, the RSVP description will not appear.'),
23+
),
24+
migrations.AlterField(
25+
model_name='individualeventpage',
26+
name='rsvp_link',
27+
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.'),
28+
),
29+
]

app/events/migrations/__init__.py

Whitespace-only changes.

app/events/models.py

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
from django.db import models
2+
3+
from wagtail.models import Page
4+
from wagtail.fields import RichTextField, StreamField
5+
from wagtail.admin.panels import FieldPanel, MultiFieldPanel, PageChooserPanel
6+
from wagtail.blocks import CharBlock, StreamBlock, StructBlock, URLBlock, RichTextBlock, PageChooserBlock
7+
from modelcluster.fields import ParentalKey, ParentalManyToManyField
8+
9+
10+
class EventOwnerPage(Page):
11+
max_count = 1
12+
13+
event_location_title = models.CharField(default="Event Location")
14+
join_event_title = models.CharField(default="Join This Event")
15+
rsvp_button_text = models.CharField(default="RSVP")
16+
more_events_title = models.CharField(default="More Events")
17+
view_all_events_text = models.CharField(default="View all Events")
18+
view_all_events_url = models.URLField(blank=True)
19+
event_read_more_text = models.CharField(default="Read more")
20+
21+
content_panels = Page.content_panels + [
22+
FieldPanel('event_location_title'),
23+
FieldPanel('join_event_title'),
24+
FieldPanel('rsvp_button_text'),
25+
FieldPanel('more_events_title'),
26+
FieldPanel('view_all_events_text'),
27+
FieldPanel('view_all_events_url'),
28+
FieldPanel('event_read_more_text'),
29+
]
30+
31+
32+
class IndividualEventPage(Page):
33+
parent_page_type = [
34+
'projects.ProjectOwnerPage'
35+
]
36+
37+
start_date_time = models.DateTimeField()
38+
end_date_time = models.DateTimeField()
39+
40+
image = models.ForeignKey(
41+
"wagtailimages.Image",
42+
null=True,
43+
blank=True,
44+
on_delete=models.SET_NULL,
45+
related_name="+",
46+
help_text="Image to represent the event"
47+
)
48+
intro = RichTextField(blank=True)
49+
extended_description = StreamField([
50+
('text_block', RichTextBlock(features=[
51+
'h1', 'h2', 'h3', 'h4', 'bold', 'italic', 'link', 'ol', 'ul', 'hr', 'document-link', 'image', 'embed', 'code', 'blockquote'
52+
]))
53+
], use_json_field=True, null=True)
54+
55+
event_location = models.CharField(blank=True)
56+
rsvp_description = RichTextField(blank=True, help_text="If this field is empty, the RSVP description will not appear.")
57+
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.")
58+
59+
more_events = StreamField([
60+
('event', PageChooserBlock(page_type="events.IndividualEventPage"))
61+
], use_json_field=True, null=True, blank=True)
62+
63+
content_panels = Page.content_panels + [
64+
MultiFieldPanel([
65+
FieldPanel('start_date_time'),
66+
FieldPanel('end_date_time'),
67+
], heading="Date and Time"),
68+
MultiFieldPanel([
69+
FieldPanel('image'),
70+
FieldPanel('intro'),
71+
FieldPanel('extended_description'),
72+
], heading="Body"),
73+
MultiFieldPanel([
74+
FieldPanel('event_location'),
75+
FieldPanel('rsvp_description'),
76+
FieldPanel('rsvp_link'),
77+
FieldPanel('more_events'),
78+
], heading="Sidebar")
79+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
{% extends "base.html" %}
2+
{% load static %}
3+
{% load wagtailcore_tags %}
4+
{% load wagtailimages_tags %}
5+
{% load compress %}
6+
{% block body_class %}template-individualmappinghubpage{% endblock %}
7+
{% block extra_css %}
8+
{% compress css %}
9+
{% endcompress css %}
10+
{% endblock extra_css %}
11+
12+
{% block content %}
13+
<div class="max-w-7xl mx-auto">
14+
{% comment %} HEADER {% endcomment %}
15+
<div class="base-article px-10 py-5">
16+
<h1>{{ page.title }}</h1>
17+
<p class="mt-2">
18+
{{ page.start_date_time }} &#x2013; {% if page.start_date_time.date == page.end_date_time.date %}{{ page.end_date_time.time }} {% else %} {{page.end_date_time}} {% endif %}
19+
</p>
20+
</div>
21+
22+
<div class="grid grid-cols-1 lg:grid-cols-3 px-10 gap-8 pb-20">
23+
{% comment %} BODY {% endcomment %}
24+
<div class="base-article-m py-5 col-span-2">
25+
{% image page.image original class="pb-8" %}
26+
<div class="text-intro">
27+
{{ page.intro|safe }}
28+
</div>
29+
{{ page.extended_description }}
30+
31+
{% include "ui/components/sharers/ShareSection.html" with class="mt-10" %}
32+
</div>
33+
34+
{% comment %} SIDEBAR {% endcomment %}
35+
<div class="base-article py-5 sidebar">
36+
<h1>
37+
{{page.get_parent.specific.event_location_title}}
38+
</h1>
39+
<p class="my-2">
40+
{{page.event_location}}
41+
</p>
42+
<hr class="my-6" />
43+
44+
{% if page.rsvp_description or page.rsvp_link %}
45+
<div class="bg-hot-off-white p-8 my-8">
46+
<h1 class="mb-4">
47+
{{page.get_parent.specific.join_event_title}}
48+
</h1>
49+
{{page.rsvp_description|safe}}
50+
51+
{% if page.rsvp_link %}
52+
<a href="{{page.rsvp_link}}" class="[&_button]:mt-4">
53+
{% include "components/branded_elements/button.html" with text=page.get_parent.specific.rsvp_button_text %}
54+
</a>
55+
{% endif %}
56+
</div>
57+
{% endif %}
58+
59+
<div>
60+
<h1>
61+
{{page.get_parent.specific.more_events_title}}
62+
</h1>
63+
<div class="text-intro [&_span]:text-black my-4">
64+
{% include "ui/components/BaseLink.html" with linktext=page.get_parent.specific.view_all_events_text linkurl=page.get_parent.specific.view_all_events_url %}
65+
</div>
66+
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-1 gap-12 mt-10">
67+
{% for event in page.more_events %}
68+
{% include "ui/components/events/EventPreviewBlockEvent.html" with event=event.value %}
69+
{% endfor %}
70+
</div>
71+
</div>
72+
</div>
73+
</div>
74+
</div>
75+
{% endblock %}

app/events/tests.py

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from django.test import TestCase
2+
3+
# Create your tests here.

app/events/views.py

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from django.shortcuts import render
2+
3+
# Create your views here.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Generated by Django 4.2.7 on 2024-06-05 23:44
2+
3+
from django.db import migrations
4+
import wagtail.blocks
5+
import wagtail.fields
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
dependencies = [
11+
('projects', '0020_remove_individualprojectpage_black_box_link_text_and_more'),
12+
]
13+
14+
operations = [
15+
migrations.AddField(
16+
model_name='individualprojectpage',
17+
name='related_events',
18+
field=wagtail.fields.StreamField([('event_page', wagtail.blocks.PageChooserBlock(page_type=['events.IndividualEventPage']))], blank=True, null=True, use_json_field=True),
19+
),
20+
]

app/projects/models.py

+5
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,10 @@ class IndividualProjectPage(Page):
123123
('news_page', PageChooserBlock(page_type="news.IndividualNewsPage"))
124124
], use_json_field=True, null=True, blank=True)
125125

126+
related_events = StreamField([
127+
('event_page', PageChooserBlock(page_type="events.IndividualEventPage"))
128+
], use_json_field=True, null=True, blank=True)
129+
126130
project_contributors = StreamField([('contributor', PageChooserBlock(page_type="members.IndividualMemberPage"))], use_json_field=True, null=True, blank=True)
127131

128132
content_panels = Page.content_panels + [
@@ -150,6 +154,7 @@ class IndividualProjectPage(Page):
150154
FieldPanel('contact'),
151155
MultiFieldPanel([
152156
FieldPanel('related_news'),
157+
FieldPanel('related_events'),
153158
], heading="Related Pages"),
154159
], heading="Sidebar"),
155160
MultiFieldPanel([

app/projects/templates/projects/individual_project_page.html

+14-7
Original file line numberDiff line numberDiff line change
@@ -79,14 +79,21 @@ <h1 class="text-h2 font-bold mb-4">
7979
{% endif %}
8080

8181
{% comment %} RELATED EVENTS {% endcomment %}
82-
<div class="my-12">
83-
<h1 class="text-h2 font-bold mb-4">
84-
{{ page.get_parent.specific.related_events_title }}
85-
</h1>
86-
<div class="[&_span]:text-black text-intro">
87-
{% include "ui/components/BaseLink.html" with linkurl=page.get_parent.specific.view_all_events_url linktext=page.get_parent.specific.view_all_events_text %}
82+
{% if page.related_events %}
83+
<div class="my-12">
84+
<h1 class="text-h2 font-bold mb-4">
85+
{{ page.get_parent.specific.related_events_title }}
86+
</h1>
87+
<div class="[&_span]:text-black text-intro">
88+
{% include "ui/components/BaseLink.html" with linkurl=page.get_parent.specific.view_all_events_url linktext=page.get_parent.specific.view_all_events_text %}
89+
</div>
90+
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-1 gap-12 mt-10">
91+
{% for event in page.related_events %}
92+
{% include "ui/components/events/EventPreviewBlockEvent.html" with event=event.value %}
93+
{% endfor %}
94+
</div>
8895
</div>
89-
</div>
96+
{% endif %}
9097
</div>
9198
</div>
9299

0 commit comments

Comments
 (0)