Skip to content

Commit ea24bb1

Browse files
committed
add CMS about us page
1 parent 63ac403 commit ea24bb1

12 files changed

Lines changed: 678 additions & 8 deletions

File tree

bedrock/mozorg/blocks/common.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,13 +340,23 @@ class ShowcaseBlock(blocks.StructBlock):
340340
)
341341

342342
sub_heading = blocks.CharBlock(
343+
required=False,
343344
max_length=255,
344345
help_text="Section sub heading heading. Use sentence case.",
345346
)
346347

348+
two_column_layout = blocks.BooleanBlock(
349+
required=False,
350+
default=False,
351+
label="Make it two column layout",
352+
inline_form=True,
353+
help_text="Make the title and body content into a two-column layout.",
354+
)
355+
347356
cta_divider = DividerBlock(label="Call-to-action")
348357

349358
cta_text = blocks.CharBlock(
359+
required=False,
350360
max_length=50,
351361
label="Link text",
352362
help_text="Use sentence case (e.g., 'Read the report', 'Read more').",
@@ -486,6 +496,26 @@ class Meta:
486496
label_format = "{heading} ({width})"
487497

488498

499+
class ShowcaseGalleryImageBlock(blocks.StructBlock):
500+
"""A single image with alt text for the gallery."""
501+
502+
image = ImageChooserBlock()
503+
504+
image_alt = blocks.CharBlock(
505+
max_length=255,
506+
required=False,
507+
help_text=(
508+
"A concise description of the image for someone who can't see it. "
509+
"See <a href='https://mozmeao.github.io/platform-docs/cms/alt-text/' target='_blank'>alt text guidelines</a> for tips."
510+
),
511+
)
512+
513+
class Meta:
514+
icon = "image"
515+
label = "Showcase Gallery Image"
516+
label_format = "{image}"
517+
518+
489519
class GalleryBlock(blocks.StructBlock):
490520
"""Block for a gallery section with multiple tiles."""
491521

@@ -512,3 +542,76 @@ class Meta:
512542
icon = "grip"
513543
label = "Gallery Section"
514544
label_format = "{heading}"
545+
546+
class ShowcaseGalleryBlockSettings(blocks.StructBlock):
547+
"""Settings for the showcase gallery block."""
548+
549+
anchor_id = blocks.CharBlock(
550+
required=False,
551+
max_length=100,
552+
help_text="Optional: Add an ID to make this section linkable (e.g., 'news', 'gallery').",
553+
)
554+
555+
background_color = blocks.ChoiceBlock(
556+
choices=[
557+
("", "White"),
558+
("m24-t-dark", "Dark"),
559+
("m24-t-green", "Green"),
560+
("m24-t-orange", "Orange"),
561+
("m24-t-pink", "Pink"),
562+
("m24-t-gray", "Gray"),
563+
],
564+
required=False,
565+
help_text="What color should the background be?",
566+
)
567+
568+
class Meta:
569+
icon = "cog"
570+
collapsed = True
571+
label = "Settings"
572+
label_format = "ID: {anchor_id} - Background: {background_color}"
573+
form_classname = "compact-form struct-block"
574+
575+
class ShowcaseGalleryBlock(blocks.StructBlock):
576+
"""A showcase block with a gallery as media."""
577+
578+
settings = ShowcaseGalleryBlockSettings()
579+
580+
text_divider = DividerBlock(label="Text")
581+
582+
heading = blocks.CharBlock(
583+
required=False,
584+
max_length=255,
585+
help_text="Use sentence case.",
586+
)
587+
588+
image_divider = DividerBlock(label="Image")
589+
590+
tiles = blocks.ListBlock(
591+
ShowcaseGalleryImageBlock(),
592+
min_num=1,
593+
help_text="Add gallery tiles. For best results, ensure tile widths add up to 100% per row.",
594+
)
595+
596+
body = blocks.CharBlock(
597+
max_length=1000,
598+
label="Content for section body",
599+
help_text="Use sentence case.",
600+
)
601+
602+
cta_text = blocks.CharBlock(
603+
required=False,
604+
max_length=50,
605+
label="Link text",
606+
help_text="Use sentence case (e.g., 'Read the report', 'Read more').",
607+
)
608+
609+
cta_link = LinkBlock(
610+
label="Link destination",
611+
)
612+
613+
class Meta:
614+
template = "mozorg/cms/blocks/showcase_gallery_block.html"
615+
icon = "grip"
616+
label = "Showcase Gallery section"
617+
label_format = "{heading}"
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# This Source Code Form is subject to the terms of the Mozilla Public
2+
# License, v. 2.0. If a copy of the MPL was not distributed with this
3+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
5+
6+
from bedrock.mozorg.fixtures.base_fixtures import get_placeholder_image, get_test_index_page
7+
from bedrock.mozorg.models import AboutUsPage
8+
9+
10+
def get_showcase_gallery_variants(image_id: int) -> list[dict]:
11+
"""Return list of ShowcaseGalleryBlock data variants for testing.
12+
13+
Args:
14+
image_id: ID of the placeholder image to use
15+
16+
Returns:
17+
List of block data dictionaries representing different configurations
18+
"""
19+
return [
20+
# Variant 1: Default white background, no anchor, multiple tiles
21+
{
22+
"type": "showcase_gallery_block",
23+
"value": {
24+
"settings": {
25+
"background_color": "",
26+
"anchor_id": "",
27+
},
28+
"heading": "Working at Mozilla",
29+
"tiles": [
30+
{"image": image_id, "image_alt": "Mozilla team working together"},
31+
{"image": image_id, "image_alt": "Mozilla office space"},
32+
{"image": image_id, "image_alt": "Mozilla team event"},
33+
{"image": image_id, "image_alt": ""},
34+
],
35+
"body": "Join a team that believes the internet is for everyone.",
36+
"cta_text": "See open roles",
37+
"cta_link": {
38+
"link_to": "custom_url",
39+
"custom_url": "https://www.mozilla.org/careers",
40+
"new_window": False,
41+
},
42+
},
43+
"id": "showcase-gallery-variant-1",
44+
},
45+
# Variant 2: Dark background
46+
{
47+
"type": "showcase_gallery_block",
48+
"value": {
49+
"settings": {
50+
"background_color": "m24-t-dark",
51+
"anchor_id": "",
52+
},
53+
"heading": "Our Culture",
54+
"tiles": [
55+
{"image": image_id, "image_alt": "Mozilla culture photo"},
56+
{"image": image_id, "image_alt": ""},
57+
],
58+
"body": "A mission-driven organization where your work matters.",
59+
"cta_text": "Learn about our culture",
60+
"cta_link": {
61+
"link_to": "custom_url",
62+
"custom_url": "https://www.mozilla.org/about/culture",
63+
"new_window": False,
64+
},
65+
},
66+
"id": "showcase-gallery-variant-2",
67+
},
68+
# Variant 3: Green background with anchor_id
69+
{
70+
"type": "showcase_gallery_block",
71+
"value": {
72+
"settings": {
73+
"background_color": "m24-t-green",
74+
"anchor_id": "careers-section",
75+
},
76+
"heading": "Benefits and Perks",
77+
"tiles": [
78+
{"image": image_id, "image_alt": "Employee benefits"},
79+
{"image": image_id, "image_alt": "Flexible working"},
80+
],
81+
"body": "We offer competitive benefits that support your whole life.",
82+
"cta_text": "View benefits",
83+
"cta_link": {
84+
"link_to": "custom_url",
85+
"custom_url": "https://www.mozilla.org/careers/benefits",
86+
"new_window": False,
87+
},
88+
},
89+
"id": "showcase-gallery-variant-3",
90+
},
91+
# Variant 4: Orange background, new_window=True
92+
{
93+
"type": "showcase_gallery_block",
94+
"value": {
95+
"settings": {
96+
"background_color": "m24-t-orange",
97+
"anchor_id": "",
98+
},
99+
"heading": "Join Mozilla",
100+
"tiles": [
101+
{"image": image_id, "image_alt": "Mozilla team photo"},
102+
{"image": image_id, "image_alt": "Mozilla campus"},
103+
],
104+
"body": "Help us keep the internet open and accessible.",
105+
"cta_text": "Apply now",
106+
"cta_link": {
107+
"link_to": "custom_url",
108+
"custom_url": "https://www.mozilla.org/careers/apply",
109+
"new_window": True,
110+
},
111+
},
112+
"id": "showcase-gallery-variant-4",
113+
},
114+
]
115+
116+
117+
def get_showcase_gallery_test_page() -> AboutUsPage:
118+
"""Create an AboutUsPage with all showcase gallery block variants for testing.
119+
120+
Returns:
121+
AboutUsPage instance with showcase gallery blocks populated
122+
"""
123+
placeholder_image = get_placeholder_image()
124+
variants = get_showcase_gallery_variants(placeholder_image.id)
125+
index_page = get_test_index_page()
126+
127+
test_page = AboutUsPage.objects.filter(slug="showcase-gallery-block-test").first()
128+
if test_page:
129+
return test_page
130+
131+
test_page = AboutUsPage(
132+
title="Showcase Gallery Block Test Page",
133+
slug="showcase-gallery-block-test",
134+
content=variants,
135+
)
136+
137+
index_page.add_child(instance=test_page)
138+
test_page.save_revision().publish()
139+
140+
return test_page

bedrock/mozorg/migrations/0027_aboutuspage_alter_homepage_content.py

Lines changed: 33 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Generated by Django 5.2.13 on 2026-04-15 20:14
2+
3+
import wagtail.admin.forms.choosers
4+
import wagtail.fields
5+
from django.db import migrations
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
dependencies = [
11+
('mozorg', '0027_aboutuspage_alter_homepage_content'),
12+
]
13+
14+
operations = [
15+
migrations.AlterField(
16+
model_name='aboutuspage',
17+
name='content',
18+
field=wagtail.fields.StreamField([('donate_block', 20), ('gallery_block', 34), ('showcase_block', 42), ('showcase_gallery_block', 48), ('transition_block', 50)], blank=True, block_lookup={0: ('wagtail.blocks.CharBlock', (), {'help_text': "Optional: Add an ID to make this section linkable (e.g., 'donate', 'support').", 'max_length': 100, 'required': False}), 1: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('', 'White'), ('m24-t-dark', 'Dark'), ('m24-t-green', 'Green'), ('m24-t-orange', 'Orange'), ('m24-t-pink', 'Pink'), ('m24-t-gray', 'Gray')], 'help_text': 'What color should the background be?', 'required': False}), 2: ('wagtail.blocks.StructBlock', [[('anchor_id', 0), ('background_color', 1)]], {}), 3: ('bedrock.mozorg.blocks.common.DividerBlock', (), {'label': 'Text'}), 4: ('wagtail.blocks.CharBlock', (), {'help_text': 'Use sentence case.', 'max_length': 255}), 5: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'link'], 'help_text': 'Keep this to 2 paragraphs or fewer.'}), 6: ('bedrock.mozorg.blocks.common.DividerBlock', (), {'label': 'Image'}), 7: ('wagtail.images.blocks.ImageChooserBlock', (), {'help_text': 'Ideal image size is 1400 x 700. Image will be cropped to a 2:1 aspect ratio.'}), 8: ('wagtail.blocks.CharBlock', (), {'help_text': "A concise description of the image for someone who can't see it. See <a href='https://mozmeao.github.io/platform-docs/cms/alt-text/' target='_blank'>alt text guidelines</a> for tips.", 'max_length': 255, 'required': False}), 9: ('bedrock.mozorg.blocks.common.DividerBlock', (), {'label': 'Call-to-action'}), 10: ('wagtail.blocks.CharBlock', (), {'help_text': "Use sentence case (e.g., 'Donate', 'Read more').", 'label': 'Link text', 'max_length': 50}), 11: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('page', 'Page'), ('file', 'File'), ('custom_url', 'Custom URL'), ('email', 'Email'), ('anchor', 'Anchor'), ('phone', 'Phone')], 'classname': 'link_choice_type_selector', 'label': 'Link to', 'required': False}), 12: ('wagtail.blocks.PageChooserBlock', (), {'form_classname': 'page_link', 'label': 'Page', 'required': False}), 13: ('wagtail.documents.blocks.DocumentChooserBlock', (), {'form_classname': 'file_link', 'label': 'File', 'required': False}), 14: ('wagtail.blocks.CharBlock', (), {'form_classname': 'custom_url_link url_field', 'label': 'Custom URL', 'max_length': 300, 'required': False, 'validators': [wagtail.admin.forms.choosers.URLOrAbsolutePathValidator()]}), 15: ('wagtail.blocks.CharBlock', (), {'form_classname': 'anchor_link', 'label': '#', 'max_length': 300, 'required': False}), 16: ('wagtail.blocks.EmailBlock', (), {'required': False}), 17: ('wagtail.blocks.CharBlock', (), {'form_classname': 'phone_link', 'label': 'Phone', 'max_length': 30, 'required': False}), 18: ('wagtail.blocks.BooleanBlock', (), {'form_classname': 'new_window_toggle', 'label': 'Open in new window', 'required': False}), 19: ('wagtail.blocks.StructBlock', [[('link_to', 11), ('page', 12), ('file', 13), ('custom_url', 14), ('anchor', 15), ('email', 16), ('phone', 17), ('new_window', 18)]], {'label': 'Link destination'}), 20: ('wagtail.blocks.StructBlock', [[('settings', 2), ('text_divider', 3), ('heading', 4), ('body', 5), ('image_divider', 6), ('image', 7), ('image_alt', 8), ('cta_divider', 9), ('cta_text', 10), ('cta_link', 19)]], {}), 21: ('wagtail.blocks.CharBlock', (), {'help_text': "Optional: Add an ID to make this section linkable (e.g., 'news', 'gallery').", 'max_length': 100, 'required': False}), 22: ('wagtail.blocks.StructBlock', [[('anchor_id', 21), ('background_color', 1)]], {}), 23: ('wagtail.blocks.CharBlock', (), {'max_length': 255, 'required': False}), 24: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'link'], 'required': False}), 25: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('fifth', 'Fifth (20%)'), ('quarter', 'Quarter (25%)'), ('third', 'Third (33%)'), ('half', 'Half (50%)'), ('three-quarters', 'Three-quarters (75%)')], 'help_text': 'Width of the tile in the gallery grid at desktop sizes.'}), 26: ('bedrock.mozorg.blocks.common.DividerBlock', (), {'label': 'Link'}), 27: ('wagtail.images.blocks.ImageChooserBlock', (), {'help_text': 'Upload a 2:1 aspect ratio image at 1400×700px - this is displayed on mobile browsers. Wagtail will crop it for displaying at your chosen aspect ratios for desktop.'}), 28: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('2:1', '2:1 Wide landscape'), ('1:1', '1:1 Square'), ('5:4', '5:4 Landscape'), ('4:5', '4:5 Portrait'), ('2:3', '2:3 Tall portrait')], 'help_text': 'Aspect ratio for the image on desktop. The image will be cropped to fit.'}), 29: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('', 'None'), ('community', 'Community'), ('event', 'Event'), ('impact', 'Impact'), ('partnership', 'Partnership'), ('policy', 'Policy'), ('product', 'Product'), ('program', 'Program'), ('project', 'Project'), ('research', 'Research')], 'required': False}), 30: ('wagtail.blocks.TextBlock', (), {'help_text': "Short blurb about what you're linking to.", 'required': False}), 31: ('wagtail.blocks.CharBlock', (), {'help_text': "Use sentence case (e.g., 'Read more', 'Watch now').", 'label': 'Call to action text (optional)', 'max_length': 100, 'required': False}), 32: ('wagtail.blocks.StructBlock', [[('width', 25), ('link_divider', 26), ('cta_link', 19), ('image_divider', 6), ('image', 27), ('image_ratio', 28), ('image_alt', 8), ('text_divider', 3), ('tag', 29), ('heading', 4), ('body', 30), ('cta_text', 31)]], {}), 33: ('wagtail.blocks.ListBlock', (32,), {'help_text': 'Add gallery tiles. For best results, ensure tile widths add up to 100% per row.', 'min_num': 1}), 34: ('wagtail.blocks.StructBlock', [[('settings', 22), ('heading', 23), ('intro', 24), ('tiles', 33)]], {}), 35: ('wagtail.blocks.CharBlock', (), {'help_text': "Optional: Add an ID to make this section linkable (e.g., 'showcase', 'support').", 'max_length': 100, 'required': False}), 36: ('wagtail.blocks.StructBlock', [[('anchor_id', 35), ('background_color', 1)]], {}), 37: ('wagtail.blocks.CharBlock', (), {'help_text': 'Section heading. Use sentence case.', 'max_length': 255}), 38: ('wagtail.images.blocks.ImageChooserBlock', (), {'help_text': 'Ideal image size is 1376 * 515.'}), 39: ('wagtail.blocks.CharBlock', (), {'help_text': 'Section sub heading heading. Use sentence case.', 'max_length': 255, 'required': False}), 40: ('wagtail.blocks.BooleanBlock', (), {'default': False, 'help_text': 'Make the title and body content into a two-column layout.', 'inline_form': True, 'label': 'Make it two column layout', 'required': False}), 41: ('wagtail.blocks.CharBlock', (), {'help_text': "Use sentence case (e.g., 'Read the report', 'Read more').", 'label': 'Link text', 'max_length': 50, 'required': False}), 42: ('wagtail.blocks.StructBlock', [[('settings', 36), ('text_divider', 3), ('heading', 37), ('body', 5), ('image_divider', 6), ('image', 38), ('image_alt', 8), ('sub_heading', 39), ('two_column_layout', 40), ('cta_divider', 9), ('cta_text', 41), ('cta_link', 19)]], {}), 43: ('wagtail.blocks.CharBlock', (), {'help_text': 'Use sentence case.', 'max_length': 255, 'required': False}), 44: ('wagtail.images.blocks.ImageChooserBlock', (), {}), 45: ('wagtail.blocks.StructBlock', [[('image', 44), ('image_alt', 8)]], {}), 46: ('wagtail.blocks.ListBlock', (45,), {'help_text': 'Add gallery tiles. For best results, ensure tile widths add up to 100% per row.', 'min_num': 1}), 47: ('wagtail.blocks.CharBlock', (), {'help_text': 'Use sentence case.', 'label': 'Content for section body', 'max_length': 1000}), 48: ('wagtail.blocks.StructBlock', [[('settings', 22), ('text_divider', 3), ('heading', 43), ('image_divider', 6), ('tiles', 46), ('body', 47), ('cta_text', 41), ('cta_link', 19)]], {}), 49: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('light', 'White'), ('dark', 'Dark'), ('green', 'Green'), ('orange', 'Orange'), ('pink', 'Pink'), ('gray', 'Gray')]}), 50: ('wagtail.blocks.StructBlock', [[('top_color', 49), ('bottom_color', 49)]], {})}, help_text='Add content blocks for the homepage. Blocks will render in the order shown.', null=True),
19+
),
20+
]

bedrock/mozorg/models.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
SectionBlock,
2121
TwoColumnDetailBlock,
2222
)
23-
from bedrock.mozorg.blocks.common import DonateBlock, GalleryBlock, ShowcaseBlock, SpringboardBlock, TransitionBlock
23+
from bedrock.mozorg.blocks.common import DonateBlock, GalleryBlock, ShowcaseBlock, ShowcaseGalleryBlock, SpringboardBlock, TransitionBlock
2424
from bedrock.mozorg.blocks.leadership import LeadershipSectionBlock
2525
from bedrock.mozorg.blocks.navigation import NavigationLinkBlock
2626

@@ -461,3 +461,45 @@ def get_context(self, request, *args, **kwargs):
461461
context = super().get_context(request, *args, **kwargs)
462462
context["utm_parameters"] = self.get_utm_parameters()
463463
return context
464+
465+
466+
class AboutUsPage(AbstractBedrockCMSPage):
467+
subpage_types = []
468+
469+
max_count = 1 # Ensure there's only one instance of this page
470+
ftl_files = ["mozorg/about-m24"]
471+
472+
content = StreamField(
473+
[
474+
("donate_block", DonateBlock()),
475+
("gallery_block", GalleryBlock()),
476+
("showcase_block", ShowcaseBlock()),
477+
("showcase_gallery_block", ShowcaseGalleryBlock()),
478+
("transition_block", TransitionBlock()),
479+
],
480+
blank=True,
481+
null=True,
482+
use_json_field=True,
483+
help_text="Add content blocks for the homepage. Blocks will render in the order shown.",
484+
)
485+
486+
content_panels = [
487+
FieldPanel("title", help_text="Help identify this page for other editors."),
488+
FieldPanel("content"),
489+
]
490+
491+
template = "mozorg/cms/about/about-us.html"
492+
493+
def get_utm_parameters(self):
494+
return {
495+
**BASE_UTM_PARAMETERS,
496+
"utm_campaign": self.slug or "about-us",
497+
}
498+
499+
def get_context(self, request, *args, **kwargs):
500+
context = super().get_context(request, *args, **kwargs)
501+
context["utm_parameters"] = self.get_utm_parameters()
502+
return context
503+
504+
class Meta:
505+
verbose_name = "About Us Page"

0 commit comments

Comments
 (0)