diff --git a/bedrock/anonym/blocks.py b/bedrock/anonym/blocks.py index d89f5f616d5..ba71adde5ad 100644 --- a/bedrock/anonym/blocks.py +++ b/bedrock/anonym/blocks.py @@ -12,6 +12,8 @@ from wagtail_link_block.blocks import LinkBlock from wagtail_thumbnail_choice_block import ThumbnailChoiceBlock +from bedrock.cms.blocks import UUIDBlock + BASIC_TEXT_FEATURES = [ "bold", "italic", @@ -401,7 +403,23 @@ class Meta: icon = "doc-full" +class AnalyticsSettings(blocks.StructBlock): + analytics_id = UUIDBlock( + label="Analytics ID", + help_text="Unique identifier for analytics tracking. Leave blank to auto-generate.", + required=False, + ) + + class Meta: + icon = "cog" + collapsed = True + label = "Settings" + label_format = "Analytics ID: {analytics_id}" + form_classname = "compact-form struct-block" + + class LinkWithTextBlock(blocks.StructBlock): + settings = AnalyticsSettings() label = blocks.CharBlock(label="Link Text") link = LinkBlock() @@ -456,6 +474,19 @@ class Meta: icon = "doc-full" +class CaseStudyItemWithAnalyticsBlock(blocks.StructBlock): + page = PageChooserBlock("anonym.AnonymCaseStudyItemPage") + analytics_id = UUIDBlock( + label="Analytics ID", + help_text="Unique identifier for analytics tracking. Leave blank to auto-generate.", + required=False, + ) + + class Meta: + label = "Case Study" + label_format = "Case Study - {page}" + + class CaseStudyListBlock(blocks.StructBlock): """ Display a list of case study items with their logos, client names, and descriptions. @@ -465,7 +496,7 @@ class CaseStudyListBlock(blocks.StructBlock): """ case_study_items = blocks.ListBlock( - PageChooserBlock("anonym.AnonymCaseStudyItemPage"), + CaseStudyItemWithAnalyticsBlock(), min_num=1, max_num=3, default=[], diff --git a/bedrock/anonym/management/commands/populate_case_study_analytics_ids.py b/bedrock/anonym/management/commands/populate_case_study_analytics_ids.py new file mode 100644 index 00000000000..e88602a8231 --- /dev/null +++ b/bedrock/anonym/management/commands/populate_case_study_analytics_ids.py @@ -0,0 +1,45 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +import copy +from uuid import uuid4 + +from django.core.management.base import BaseCommand + +from bedrock.anonym.models import AnonymContentSubPage, AnonymIndexPage + + +class Command(BaseCommand): + help = "Restructures CaseStudyListBlock items from int PKs to {page, analytics_id} structs (idempotent)" + + def handle(self, *args, **options): + page_models = [AnonymIndexPage, AnonymContentSubPage] + total = 0 + for Model in page_models: + for page in Model.objects.all(): + if not page.content: + continue + content = copy.deepcopy(list(page.content.raw_data)) + changed = False + for block in content: + if block.get("type") != "section": + continue + for inner_block in block.get("value", {}).get("section_content", []): + if inner_block.get("type") != "case_study_item_list_block": + continue + for item in inner_block.get("value", {}).get("case_study_items", []): + item_val = item.get("value") + if isinstance(item_val, int): + # If the item is a bare PK, set a new object as the value + item["value"] = {"page": item_val, "analytics_id": str(uuid4())} + changed = True + elif isinstance(item_val, dict) and not item_val.get("analytics_id"): + # If the item has an empty analytics_id, fill it in + item_val["analytics_id"] = str(uuid4()) + changed = True + if changed: + page.content = content + page.save(update_fields=["content"]) + total += 1 + self.stdout.write(self.style.SUCCESS(f"Updated {total} page(s).")) diff --git a/bedrock/anonym/management/commands/populate_link_block_analytics_ids.py b/bedrock/anonym/management/commands/populate_link_block_analytics_ids.py new file mode 100644 index 00000000000..e83467ad27a --- /dev/null +++ b/bedrock/anonym/management/commands/populate_link_block_analytics_ids.py @@ -0,0 +1,56 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +import copy +from uuid import uuid4 + +from django.core.management.base import BaseCommand + +from bedrock.anonym.models import ( + AnonymCaseStudyItemPage, + AnonymContentSubPage, + AnonymIndexPage, + AnonymNewsItemPage, +) + + +class Command(BaseCommand): + help = "Injects settings.analytics_id into LinkWithTextBlock instances that are missing it (idempotent)" + + def handle(self, *args, **options): + page_models = [ + AnonymIndexPage, + AnonymContentSubPage, + AnonymNewsItemPage, + AnonymCaseStudyItemPage, + ] + total = 0 + for Model in page_models: + for page in Model.objects.all(): + if not page.content: + continue + content = copy.deepcopy(list(page.content.raw_data)) + changed = False + for block in content: + block_type = block.get("type") + value = block.get("value", {}) + if block_type == "section": + for link_item in value.get("action", []): + link_val = link_item.get("value", link_item) + settings = link_val.setdefault("settings", {}) + if not settings.get("analytics_id"): + settings["analytics_id"] = str(uuid4()) + changed = True + elif block_type == "call_to_action": + for link_item in value.get("button", []): + link_val = link_item.get("value", link_item) + settings = link_val.setdefault("settings", {}) + if not settings.get("analytics_id"): + settings["analytics_id"] = str(uuid4()) + changed = True + if changed: + page.content = content + page.save(update_fields=["content"]) + total += 1 + self.stdout.write(self.style.SUCCESS(f"Updated {total} page(s).")) diff --git a/bedrock/anonym/management/commands/populate_nav_button_analytics_ids.py b/bedrock/anonym/management/commands/populate_nav_button_analytics_ids.py new file mode 100644 index 00000000000..dd09f878586 --- /dev/null +++ b/bedrock/anonym/management/commands/populate_nav_button_analytics_ids.py @@ -0,0 +1,32 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +import copy +from uuid import uuid4 + +from django.core.management.base import BaseCommand + +from bedrock.anonym.models import AnonymIndexPage + + +class Command(BaseCommand): + help = "Injects analytics_id into NavigationLinkBlock items with has_button_appearance=True that are missing it (idempotent)" + + def handle(self, *args, **options): + total = 0 + for page in AnonymIndexPage.objects.all(): + if not page.navigation: + continue + navigation = copy.deepcopy(list(page.navigation.raw_data)) + changed = False + for item in navigation: + value = item.get("value", {}) + if value.get("has_button_appearance") and not value.get("analytics_id"): + value["analytics_id"] = str(uuid4()) + changed = True + if changed: + page.navigation = navigation + page.save(update_fields=["navigation"]) + total += 1 + self.stdout.write(self.style.SUCCESS(f"Updated {total} page(s).")) diff --git a/bedrock/anonym/migrations/0013_alter_anonymcasestudyitempage_content_and_more.py b/bedrock/anonym/migrations/0013_alter_anonymcasestudyitempage_content_and_more.py new file mode 100644 index 00000000000..d92d550aef7 --- /dev/null +++ b/bedrock/anonym/migrations/0013_alter_anonymcasestudyitempage_content_and_more.py @@ -0,0 +1,1664 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +# Generated by Django 5.2.12 on 2026-04-16 19:30 + +from django.db import migrations + +import wagtail.admin.forms.choosers +import wagtail.fields + + +class Migration(migrations.Migration): + dependencies = [ + ("anonym", "0012_alter_anonymcasestudyitempage_content_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="anonymcasestudyitempage", + name="content", + field=wagtail.fields.StreamField( + [("intro_text", 1), ("rich_text", 3), ("blockquote", 5), ("figure", 10), ("call_to_action", 28)], + blank=True, + block_lookup={ + 0: ("wagtail.blocks.RichTextBlock", (), {"features": ["bold", "italic", "link", "superscript", "subscript", "strikethrough"]}), + 1: ("wagtail.blocks.StructBlock", [[("text", 0)]], {}), + 2: ( + "wagtail.blocks.RichTextBlock", + (), + { + "features": [ + "h2", + "h3", + "h4", + "h5", + "bold", + "italic", + "underline", + "ol", + "ul", + "hr", + "link", + "image", + "embed", + "superscript", + "subscript", + "strikethrough", + "blockquote", + ] + }, + ), + 3: ("wagtail.blocks.StructBlock", [[("text", 2)]], {}), + 4: ("wagtail.blocks.CharBlock", (), {"label": "Author", "required": False}), + 5: ("wagtail.blocks.StructBlock", [[("text", 0), ("author", 4)]], {}), + 6: ( + "wagtail.blocks.BooleanBlock", + (), + { + "default": False, + "help_text": "The Default width is constrained to the layout grid with a max-width, centered on the page.", + "inline_form": True, + "label": "Make Full Width", + "required": False, + }, + ), + 7: ("wagtail.blocks.StructBlock", [[("make_full_width", 6)]], {}), + 8: ("wagtail.images.blocks.ImageChooserBlock", (), {}), + 9: ( + "wagtail.blocks.RichTextBlock", + (), + {"features": ["bold", "italic", "link", "superscript", "subscript", "strikethrough"], "label": "Caption", "required": False}, + ), + 10: ("wagtail.blocks.StructBlock", [[("settings", 7), ("image", 8), ("caption", 9)]], {}), + 11: ( + "wagtail.blocks.CharBlock", + (), + {"help_text": "Optional: Add an ID to make this section linkable from navigation", "max_length": 100, "required": False}, + ), + 12: ("wagtail.blocks.StructBlock", [[("anchor_id", 11)]], {}), + 13: ( + "wagtail.blocks.RichTextBlock", + (), + { + "features": ["bold", "italic", "link", "superscript", "subscript", "strikethrough"], + "help_text": "Use bold to make parts of this text black.", + }, + ), + 14: ( + "bedrock.cms.blocks.UUIDBlock", + (), + { + "help_text": "Unique identifier for analytics tracking. Leave blank to auto-generate.", + "label": "Analytics ID", + "required": False, + }, + ), + 15: ("wagtail.blocks.StructBlock", [[("analytics_id", 14)]], {}), + 16: ("wagtail.blocks.CharBlock", (), {"label": "Link Text"}), + 17: ( + "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, + }, + ), + 18: ("wagtail.blocks.PageChooserBlock", (), {"form_classname": "page_link", "label": "Page", "required": False}), + 19: ("wagtail.documents.blocks.DocumentChooserBlock", (), {"form_classname": "file_link", "label": "File", "required": False}), + 20: ( + "wagtail.blocks.CharBlock", + (), + { + "form_classname": "custom_url_link url_field", + "label": "Custom URL", + "max_length": 300, + "required": False, + "validators": [wagtail.admin.forms.choosers.URLOrAbsolutePathValidator()], + }, + ), + 21: ("wagtail.blocks.CharBlock", (), {"form_classname": "anchor_link", "label": "#", "max_length": 300, "required": False}), + 22: ("wagtail.blocks.EmailBlock", (), {"required": False}), + 23: ("wagtail.blocks.CharBlock", (), {"form_classname": "phone_link", "label": "Phone", "max_length": 30, "required": False}), + 24: ( + "wagtail.blocks.BooleanBlock", + (), + {"form_classname": "new_window_toggle", "label": "Open in new window", "required": False}, + ), + 25: ( + "wagtail.blocks.StructBlock", + [ + [ + ("link_to", 17), + ("page", 18), + ("file", 19), + ("custom_url", 20), + ("anchor", 21), + ("email", 22), + ("phone", 23), + ("new_window", 24), + ] + ], + {}, + ), + 26: ("wagtail.blocks.StructBlock", [[("settings", 15), ("label", 16), ("link", 25)]], {}), + 27: ("wagtail.blocks.ListBlock", (26,), {"default": [], "max_num": 1, "min_num": 0}), + 28: ("wagtail.blocks.StructBlock", [[("settings", 12), ("heading", 13), ("button", 27)]], {}), + }, + null=True, + ), + ), + migrations.AlterField( + model_name="anonymcontentsubpage", + name="content", + field=wagtail.fields.StreamField( + [("section", 55), ("competitor_table", 62), ("toggle_items", 71), ("call_to_action", 73)], + blank=True, + block_lookup={ + 0: ( + "wagtail.blocks.CharBlock", + (), + { + "help_text": "Optional: Add an ID to make this section linkable from navigation (e.g., 'overview', 'features')", + "max_length": 100, + "required": False, + }, + ), + 1: ( + "wagtail.blocks.ChoiceBlock", + [], + {"choices": [("", "---"), ("index", "Index"), ("top_glow", "Top Glow")], "inline_form": True, "required": False}, + ), + 2: ("wagtail.blocks.StructBlock", [[("anchor_id", 0), ("theme", 1)]], {}), + 3: ( + "wagtail.blocks.RichTextBlock", + (), + {"features": ["bold", "italic", "link", "superscript", "subscript", "strikethrough"], "required": False}, + ), + 4: ( + "wagtail.blocks.RichTextBlock", + (), + { + "features": ["bold", "italic", "link", "superscript", "subscript", "strikethrough"], + "help_text": "Use Bold to make parts of this text black.", + }, + ), + 5: ( + "wagtail.blocks.BooleanBlock", + (), + { + "default": False, + "help_text": "The Default width is constrained to the layout grid with a max-width, centered on the page.", + "inline_form": True, + "label": "Make Full Width", + "required": False, + }, + ), + 6: ("wagtail.blocks.StructBlock", [[("make_full_width", 5)]], {}), + 7: ("wagtail.images.blocks.ImageChooserBlock", (), {}), + 8: ("wagtail.blocks.StructBlock", [[("settings", 6), ("image", 7)]], {}), + 9: ("wagtail.blocks.BooleanBlock", (), {"default": False, "help_text": "The default behavior is stacked", "required": False}), + 10: ( + "wagtail.blocks.BooleanBlock", + (), + {"default": False, "help_text": "Add divider lines between cards on desktop", "required": False}, + ), + 11: ("wagtail.blocks.StructBlock", [[("scrollable_on_mobile", 9), ("dividers_between_cards_on_desktop", 10)]], {}), + 12: ( + "wagtail.blocks.ChoiceBlock", + [], + { + "choices": [ + ("", "---"), + ("IRL", "IRL"), + ("accessibility", "Accessibility"), + ("accounts", "Accounts"), + ("add-search-engine", "Add Search Engine"), + ("ai", "AI"), + ("alert", "Alert"), + ("arrow-down", "Arrow Down"), + ("arrow-left-white", "Arrow Left White"), + ("arrow-left", "Arrow Left"), + ("arrow-right-white", "Arrow Right White"), + ("arrow-right", "Arrow Right"), + ("arrow-up", "Arrow Up"), + ("audio-card", "Audio Card"), + ("audio-mute", "Audio Mute"), + ("audio", "Audio"), + ("auto-play-block", "Auto Play Block"), + ("back", "Back"), + ("bell", "Bell"), + ("beta", "Beta"), + ("blog", "Blog"), + ("bookmark-menu", "Bookmark Menu"), + ("bookmark-narrow", "Bookmark Narrow"), + ("bookmark-remove", "Bookmark Remove"), + ("bookmark", "Bookmark"), + ("brightness", "Brightness"), + ("browser", "Browser"), + ("calendar", "Calendar"), + ("careers", "Careers"), + ("caret-down-white", "Caret Down White"), + ("caret-down", "Caret Down"), + ("caret-up", "Caret Up"), + ("chat", "Chat"), + ("check", "Check"), + ("close-white", "Close White"), + ("close", "Close"), + ("cloud", "Cloud"), + ("command-console", "Command Console"), + ("command-noautohide", "Command Noautohide"), + ("common-voice", "Common Voice"), + ("copy", "Copy"), + ("current-view", "Current View"), + ("customize", "Customize"), + ("cut", "Cut"), + ("dashboard", "Dashboard"), + ("data-collection", "Data Collection"), + ("data-insights", "Data Insights"), + ("data-pie", "Data Pie"), + ("default-browser", "Default Browser"), + ("delete", "Delete"), + ("desktop", "Desktop"), + ("dev-edition", "Dev Edition"), + ("developer-innovations", "Developer Innovations"), + ("developer", "Developer"), + ("dictionaries", "Dictionaries"), + ("dock-bottom", "Dock Bottom"), + ("dock-left", "Dock Left"), + ("dock-right", "Dock Right"), + ("dock-undock", "Dock Undock"), + ("download-white", "Download White"), + ("download", "Download"), + ("earth", "Earth"), + ("edit-write", "Edit Write"), + ("email", "Email"), + ("enterprise", "Enterprise"), + ("event", "Event"), + ("expand-white", "Expand White"), + ("expand", "Expand"), + ("experiments", "Experiments"), + ("extension-available-update", "Extension Available Update"), + ("extension-recent-updates", "Extension Recent Updates"), + ("extensions-legacy", "Extensions Legacy"), + ("extensions", "Extensions"), + ("external-link-white", "External Link White"), + ("external-link", "External Link"), + ("eye-closed", "Eye Closed"), + ("eye-open", "Eye Open"), + ("facebook-container", "Facebook Container"), + ("features", "Features"), + ("feeback", "Feeback"), + ("file-code", "File Code"), + ("file-image", "File Image"), + ("file-lock", "File Lock"), + ("file-music", "File Music"), + ("file-text", "File Text"), + ("file", "File"), + ("fire-tv", "Fire TV"), + ("firefox-reality", "Firefox Reality"), + ("folder-open", "Folder Open"), + ("folder-plus", "Folder Plus"), + ("folder-save", "Folder Save"), + ("folder", "Folder"), + ("font", "Font"), + ("forget", "Forget"), + ("forward", "Forward"), + ("full-screen-disabled", "Full Screen Disabled"), + ("full-screen-exit", "Full Screen Exit"), + ("full-screen", "Full Screen"), + ("gear", "Gear"), + ("get-involved", "Get Involved"), + ("globe-white", "Globe White"), + ("globe", "Globe"), + ("hashtag-narrow", "Hashtag Narrow"), + ("hashtag", "Hashtag"), + ("headphone", "Headphone"), + ("heart", "Heart"), + ("heart-rate", "Heart Rate"), + ("heart-white", "Heart White"), + ("help", "Help"), + ("highlight", "Highlight"), + ("history", "History"), + ("home", "Home"), + ("hubs", "Hubs"), + ("identity-notification", "Identity Notification"), + ("identity", "Identity"), + ("image", "Image"), + ("import", "Import"), + ("inbox", "Inbox"), + ("info", "Info"), + ("labs", "Labs"), + ("language", "Language"), + ("library", "Library"), + ("layer", "Layer"), + ("link", "Link"), + ("listen", "Listen"), + ("lite", "Lite"), + ("location-disabled", "Location Disabled"), + ("location-macos-disabled", "Location Macos Disabled"), + ("location-macos", "Location Macos"), + ("location-pin", "Location Pin"), + ("location-windows-disabled", "Location Windows Disabled"), + ("location-windows", "Location Windows"), + ("location", "Location"), + ("lock", "Lock"), + ("lockbox", "Lockbox"), + ("login", "Login"), + ("mail", "Mail"), + ("maximize", "Maximize"), + ("megaphone", "Megaphone"), + ("menu-white", "Menu White"), + ("menu", "Menu"), + ("microphone-disabled", "Microphone Disabled"), + ("microphone", "Microphone"), + ("midi", "Midi"), + ("minimize", "Minimize"), + ("minus", "Minus"), + ("mobile-narrow", "Mobile Narrow"), + ("mobile", "Mobile"), + ("monitor", "Monitor"), + ("more-horizontal", "More Horizontal"), + ("more-vertical", "More Vertical"), + ("mountain", "Mountain"), + ("mouse-pointer-disabled", "Mouse Pointer Disabled"), + ("mouse-pointer", "Mouse Pointer"), + ("mozilla", "Mozilla"), + ("new", "New"), + ("nightly", "Nightly"), + ("notes", "Notes"), + ("notifications-disabled", "Notifications Disabled"), + ("notifications", "Notifications"), + ("open-in-new", "Open In New"), + ("open", "Open"), + ("opensource", "Opensource"), + ("overflow", "Overflow"), + ("paperclip-narrow", "Paperclip Narrow"), + ("paperclip", "Paperclip"), + ("paste", "Paste"), + ("pause-white", "Pause White"), + ("pause", "Pause"), + ("performance", "Performance"), + ("photon", "Photon"), + ("pin-remove", "Pin Remove"), + ("pin", "Pin"), + ("play-white", "Play White"), + ("play", "Play"), + ("plugin-disabled", "Plugin Disabled"), + ("plugin", "Plugin"), + ("plus", "Plus"), + ("pocket-list", "Pocket List"), + ("pocket-remove", "Pocket Remove"), + ("pocket", "Pocket"), + ("popular", "Popular"), + ("popup-block", "Popup Block"), + ("preferences", "Preferences"), + ("pricetag", "Pricetag"), + ("pricetag-white", "Pricetag White"), + ("printer", "Printer"), + ("privacy", "Privacy"), + ("private-browsing", "Private Browsing"), + ("protocol", "Protocol"), + ("proton", "Proton"), + ("query", "Query"), + ("quit", "Quit"), + ("quote", "Quote"), + ("read", "Read"), + ("reader-mode", "Reader Mode"), + ("redo", "Redo"), + ("refresh", "Refresh"), + ("release-notes", "Release Notes"), + ("reminders", "Reminders"), + ("report-narrow", "Report Narrow"), + ("report", "Report"), + ("resources", "Resources"), + ("restore-session", "Restore Session"), + ("rhombus-layers", "Rhombus Layers"), + ("rhombus-layers-white", "Rhombus Layers White"), + ("screen-share-disabled", "Screen Share Disabled"), + ("screen-share", "Screen Share"), + ("screenshot", "Screenshot"), + ("search-white", "Search White"), + ("search", "Search"), + ("secure-broken", "Secure Broken"), + ("secure-mixed", "Secure Mixed"), + ("secure", "Secure"), + ("security", "Security"), + ("send-to-device", "Send To Device"), + ("send", "Send"), + ("settings", "Settings"), + ("share-windows", "Share Windows"), + ("share", "Share"), + ("shield", "Shield"), + ("sidebar", "Sidebar"), + ("sign-in", "Sign In"), + ("sign-up", "Sign Up"), + ("sound-off", "Sound Off"), + ("sound-on", "Sound On"), + ("sparkles", "Sparkles"), + ("star", "Star"), + ("stop", "Stop"), + ("store-data-disabled", "Store Data Disabled"), + ("store-data", "Store Data"), + ("sub-item", "Sub Item"), + ("subscribe", "Subscribe"), + ("sync", "Sync"), + ("tab-mobile", "Tab Mobile"), + ("tab-new", "Tab New"), + ("tab", "Tab"), + ("tablet", "Tablet"), + ("thumbs-up-narrow", "Thumbs Up Narrow"), + ("thumbs-up", "Thumbs Up"), + ("toggle-off", "Toggle Off"), + ("toggle-on", "Toggle On"), + ("toolbar", "Toolbar"), + ("top-sites", "Top Sites"), + ("tracing-protection-disabled", "Tracing Protection Disabled"), + ("tracking-protection", "Tracking Protection"), + ("trash-narrow", "Trash Narrow"), + ("trash", "Trash"), + ("turbo-mode", "Turbo Mode"), + ("undo", "Undo"), + ("update", "Update"), + ("user", "User"), + ("users", "Users"), + ("video-card", "Video Card"), + ("video-recoder-disabled", "Video Recoder Disabled"), + ("video-recorder", "Video Recorder"), + ("warning", "Warning"), + ("watch", "Watch"), + ("web-of-things", "Web Of Things"), + ("web-vr", "Web Vr"), + ("window-new", "Window New"), + ("window", "Window"), + ("zoom-in", "Zoom In"), + ("zoom-out", "Zoom Out"), + ], + "inline_form": True, + "required": False, + }, + ), + 13: ("wagtail.blocks.CharBlock", (), {"label": "Heading", "required": False}), + 14: ("wagtail.blocks.RichTextBlock", (), {"features": ["bold", "italic", "link", "superscript", "subscript", "strikethrough"]}), + 15: ("wagtail.blocks.StructBlock", [[("icon", 12), ("heading", 13), ("text", 14)]], {}), + 16: ("wagtail.blocks.CharBlock", (), {"label": "Heading"}), + 17: ( + "bedrock.cms.blocks.UUIDBlock", + (), + { + "help_text": "Unique identifier for analytics tracking. Leave blank to auto-generate.", + "label": "Analytics ID", + "required": False, + }, + ), + 18: ("wagtail.blocks.StructBlock", [[("analytics_id", 17)]], {}), + 19: ("wagtail.blocks.CharBlock", (), {"label": "Link Text"}), + 20: ( + "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, + }, + ), + 21: ("wagtail.blocks.PageChooserBlock", (), {"form_classname": "page_link", "label": "Page", "required": False}), + 22: ("wagtail.documents.blocks.DocumentChooserBlock", (), {"form_classname": "file_link", "label": "File", "required": False}), + 23: ( + "wagtail.blocks.CharBlock", + (), + { + "form_classname": "custom_url_link url_field", + "label": "Custom URL", + "max_length": 300, + "required": False, + "validators": [wagtail.admin.forms.choosers.URLOrAbsolutePathValidator()], + }, + ), + 24: ("wagtail.blocks.CharBlock", (), {"form_classname": "anchor_link", "label": "#", "max_length": 300, "required": False}), + 25: ("wagtail.blocks.EmailBlock", (), {"required": False}), + 26: ("wagtail.blocks.CharBlock", (), {"form_classname": "phone_link", "label": "Phone", "max_length": 30, "required": False}), + 27: ( + "wagtail.blocks.BooleanBlock", + (), + {"form_classname": "new_window_toggle", "label": "Open in new window", "required": False}, + ), + 28: ( + "wagtail.blocks.StructBlock", + [ + [ + ("link_to", 20), + ("page", 21), + ("file", 22), + ("custom_url", 23), + ("anchor", 24), + ("email", 25), + ("phone", 26), + ("new_window", 27), + ] + ], + {}, + ), + 29: ("wagtail.blocks.StructBlock", [[("settings", 18), ("label", 19), ("link", 28)]], {}), + 30: ("wagtail.blocks.ListBlock", (29,), {"default": [], "max_num": 1, "min_num": 0}), + 31: ("wagtail.blocks.StructBlock", [[("logo", 7), ("heading", 16), ("text", 14), ("button", 30)]], {}), + 32: ("wagtail.snippets.blocks.SnippetChooserBlock", ("anonym.Person",), {}), + 33: ("wagtail.blocks.StructBlock", [[("person", 32), ("link", 30)]], {}), + 34: ("wagtail.blocks.StreamBlock", [[("icon_card", 15), ("logo_card", 31), ("person_card", 33)]], {"max_num": 4, "min_num": 1}), + 35: ("wagtail.blocks.StructBlock", [[("settings", 11), ("cards", 34)]], {}), + 36: ("wagtail.blocks.PageChooserBlock", (), {"page_type": ["anonym.AnonymNewsItemPage", "anonym.AnonymCaseStudyItemPage"]}), + 37: ( + "wagtail.blocks.ListBlock", + (36,), + {"default": [], "help_text": "Select news items or case studies to display.", "max_num": 6, "min_num": 1}, + ), + 38: ("wagtail.blocks.StructBlock", [[("pages", 37)]], {}), + 39: ("wagtail.blocks.PageChooserBlock", ("anonym.AnonymCaseStudyItemPage",), {}), + 40: ("wagtail.blocks.StructBlock", [[("page", 39), ("analytics_id", 17)]], {}), + 41: ( + "wagtail.blocks.ListBlock", + (40,), + { + "default": [], + "help_text": "Select case study pages to display. Each will show as a card with logo, client name, and description.", + "max_num": 3, + "min_num": 1, + }, + ), + 42: ("wagtail.blocks.StructBlock", [[("case_study_items", 41)]], {}), + 43: ("wagtail.blocks.CharBlock", (), {"help_text": "Section label, e.g. 'Publishers' or 'Advertisers'", "max_length": 50}), + 44: ("wagtail.blocks.StreamBlock", [[("logo", 7), ("label", 43)]], {"max_num": 24, "min_num": 1}), + 45: ("wagtail.blocks.StructBlock", [[("items", 44)]], {}), + 46: ("wagtail.blocks.ListBlock", (32,), {"default": [], "max_num": 8, "min_num": 1}), + 47: ("wagtail.blocks.StructBlock", [[("people", 46)]], {}), + 48: ("wagtail.blocks.StructBlock", [[("heading_text", 16), ("supporting_text", 14)]], {}), + 49: ("wagtail.blocks.ListBlock", (48,), {"min_num": 0}), + 50: ("wagtail.blocks.StructBlock", [[("heading_text", 16), ("subheading_text", 3), ("second_column", 49)]], {}), + 51: ( + "wagtail.blocks.RichTextBlock", + (), + { + "features": [ + "h2", + "h3", + "h4", + "h5", + "bold", + "italic", + "underline", + "ol", + "ul", + "hr", + "link", + "image", + "embed", + "superscript", + "subscript", + "strikethrough", + "blockquote", + ] + }, + ), + 52: ("wagtail.blocks.StructBlock", [[("text", 51)]], {}), + 53: ( + "wagtail.blocks.RichTextBlock", + (), + { + "features": [ + "h2", + "h3", + "h4", + "h5", + "bold", + "italic", + "underline", + "ol", + "ul", + "hr", + "link", + "image", + "embed", + "superscript", + "subscript", + "strikethrough", + "blockquote", + ], + "template": "anonym/blocks/rich-text.html", + }, + ), + 54: ( + "wagtail.blocks.StreamBlock", + [ + [ + ("figure_block", 8), + ("cards_list", 35), + ("stat_card_list_block", 38), + ("case_study_item_list_block", 42), + ("logo_list_block", 45), + ("people_list", 47), + ("two_column", 50), + ("legal_rich_text", 52), + ("rich_text", 53), + ] + ], + {"required": False}, + ), + 55: ( + "wagtail.blocks.StructBlock", + [ + [ + ("settings", 2), + ("superheading_text", 3), + ("heading_text", 4), + ("subheading_text", 3), + ("section_content", 54), + ("action", 30), + ] + ], + {}, + ), + 56: ("wagtail.blocks.CharBlock", (), {"label": "Row text"}), + 57: ( + "wagtail.blocks.BooleanBlock", + (), + {"default": False, "help_text": "Traditional Tracking & Measurement Technology", "required": False}, + ), + 58: ("wagtail.blocks.BooleanBlock", (), {"default": False, "help_text": "Data Clean Rooms", "required": False}), + 59: ("wagtail.blocks.BooleanBlock", (), {"default": False, "help_text": "Anonym", "required": False}), + 60: ("wagtail.blocks.StructBlock", [[("text", 56), ("tradition_tracking", 57), ("clean_rooms", 58), ("anonym", 59)]], {}), + 61: ("wagtail.blocks.ListBlock", (60,), {"min_num": 1}), + 62: ("wagtail.blocks.StructBlock", [[("heading_text", 16), ("subheading_text", 14), ("rows", 61)]], {}), + 63: ( + "wagtail.blocks.CharBlock", + (), + {"help_text": "Optional: Add an ID to make this section linkable from navigation", "max_length": 100, "required": False}, + ), + 64: ("wagtail.blocks.StructBlock", [[("anchor_id", 63)]], {}), + 65: ( + "wagtail.blocks.ChoiceBlock", + [], + { + "choices": [ + ("IRL", "IRL"), + ("accessibility", "Accessibility"), + ("accounts", "Accounts"), + ("add-search-engine", "Add Search Engine"), + ("ai", "AI"), + ("alert", "Alert"), + ("arrow-down", "Arrow Down"), + ("arrow-left-white", "Arrow Left White"), + ("arrow-left", "Arrow Left"), + ("arrow-right-white", "Arrow Right White"), + ("arrow-right", "Arrow Right"), + ("arrow-up", "Arrow Up"), + ("audio-card", "Audio Card"), + ("audio-mute", "Audio Mute"), + ("audio", "Audio"), + ("auto-play-block", "Auto Play Block"), + ("back", "Back"), + ("bell", "Bell"), + ("beta", "Beta"), + ("blog", "Blog"), + ("bookmark-menu", "Bookmark Menu"), + ("bookmark-narrow", "Bookmark Narrow"), + ("bookmark-remove", "Bookmark Remove"), + ("bookmark", "Bookmark"), + ("brightness", "Brightness"), + ("browser", "Browser"), + ("calendar", "Calendar"), + ("careers", "Careers"), + ("caret-down-white", "Caret Down White"), + ("caret-down", "Caret Down"), + ("caret-up", "Caret Up"), + ("chat", "Chat"), + ("check", "Check"), + ("close-white", "Close White"), + ("close", "Close"), + ("cloud", "Cloud"), + ("command-console", "Command Console"), + ("command-noautohide", "Command Noautohide"), + ("common-voice", "Common Voice"), + ("copy", "Copy"), + ("current-view", "Current View"), + ("customize", "Customize"), + ("cut", "Cut"), + ("dashboard", "Dashboard"), + ("data-collection", "Data Collection"), + ("data-insights", "Data Insights"), + ("data-pie", "Data Pie"), + ("default-browser", "Default Browser"), + ("delete", "Delete"), + ("desktop", "Desktop"), + ("dev-edition", "Dev Edition"), + ("developer-innovations", "Developer Innovations"), + ("developer", "Developer"), + ("dictionaries", "Dictionaries"), + ("dock-bottom", "Dock Bottom"), + ("dock-left", "Dock Left"), + ("dock-right", "Dock Right"), + ("dock-undock", "Dock Undock"), + ("download-white", "Download White"), + ("download", "Download"), + ("earth", "Earth"), + ("edit-write", "Edit Write"), + ("email", "Email"), + ("enterprise", "Enterprise"), + ("event", "Event"), + ("expand-white", "Expand White"), + ("expand", "Expand"), + ("experiments", "Experiments"), + ("extension-available-update", "Extension Available Update"), + ("extension-recent-updates", "Extension Recent Updates"), + ("extensions-legacy", "Extensions Legacy"), + ("extensions", "Extensions"), + ("external-link-white", "External Link White"), + ("external-link", "External Link"), + ("eye-closed", "Eye Closed"), + ("eye-open", "Eye Open"), + ("facebook-container", "Facebook Container"), + ("features", "Features"), + ("feeback", "Feeback"), + ("file-code", "File Code"), + ("file-image", "File Image"), + ("file-lock", "File Lock"), + ("file-music", "File Music"), + ("file-text", "File Text"), + ("file", "File"), + ("fire-tv", "Fire TV"), + ("firefox-reality", "Firefox Reality"), + ("folder-open", "Folder Open"), + ("folder-plus", "Folder Plus"), + ("folder-save", "Folder Save"), + ("folder", "Folder"), + ("font", "Font"), + ("forget", "Forget"), + ("forward", "Forward"), + ("full-screen-disabled", "Full Screen Disabled"), + ("full-screen-exit", "Full Screen Exit"), + ("full-screen", "Full Screen"), + ("gear", "Gear"), + ("get-involved", "Get Involved"), + ("globe-white", "Globe White"), + ("globe", "Globe"), + ("hashtag-narrow", "Hashtag Narrow"), + ("hashtag", "Hashtag"), + ("headphone", "Headphone"), + ("heart", "Heart"), + ("heart-rate", "Heart Rate"), + ("heart-white", "Heart White"), + ("help", "Help"), + ("highlight", "Highlight"), + ("history", "History"), + ("home", "Home"), + ("hubs", "Hubs"), + ("identity-notification", "Identity Notification"), + ("identity", "Identity"), + ("image", "Image"), + ("import", "Import"), + ("inbox", "Inbox"), + ("info", "Info"), + ("labs", "Labs"), + ("language", "Language"), + ("library", "Library"), + ("layer", "Layer"), + ("link", "Link"), + ("listen", "Listen"), + ("lite", "Lite"), + ("location-disabled", "Location Disabled"), + ("location-macos-disabled", "Location Macos Disabled"), + ("location-macos", "Location Macos"), + ("location-pin", "Location Pin"), + ("location-windows-disabled", "Location Windows Disabled"), + ("location-windows", "Location Windows"), + ("location", "Location"), + ("lock", "Lock"), + ("lockbox", "Lockbox"), + ("login", "Login"), + ("mail", "Mail"), + ("maximize", "Maximize"), + ("megaphone", "Megaphone"), + ("menu-white", "Menu White"), + ("menu", "Menu"), + ("microphone-disabled", "Microphone Disabled"), + ("microphone", "Microphone"), + ("midi", "Midi"), + ("minimize", "Minimize"), + ("minus", "Minus"), + ("mobile-narrow", "Mobile Narrow"), + ("mobile", "Mobile"), + ("monitor", "Monitor"), + ("more-horizontal", "More Horizontal"), + ("more-vertical", "More Vertical"), + ("mountain", "Mountain"), + ("mouse-pointer-disabled", "Mouse Pointer Disabled"), + ("mouse-pointer", "Mouse Pointer"), + ("mozilla", "Mozilla"), + ("new", "New"), + ("nightly", "Nightly"), + ("notes", "Notes"), + ("notifications-disabled", "Notifications Disabled"), + ("notifications", "Notifications"), + ("open-in-new", "Open In New"), + ("open", "Open"), + ("opensource", "Opensource"), + ("overflow", "Overflow"), + ("paperclip-narrow", "Paperclip Narrow"), + ("paperclip", "Paperclip"), + ("paste", "Paste"), + ("pause-white", "Pause White"), + ("pause", "Pause"), + ("performance", "Performance"), + ("photon", "Photon"), + ("pin-remove", "Pin Remove"), + ("pin", "Pin"), + ("play-white", "Play White"), + ("play", "Play"), + ("plugin-disabled", "Plugin Disabled"), + ("plugin", "Plugin"), + ("plus", "Plus"), + ("pocket-list", "Pocket List"), + ("pocket-remove", "Pocket Remove"), + ("pocket", "Pocket"), + ("popular", "Popular"), + ("popup-block", "Popup Block"), + ("preferences", "Preferences"), + ("pricetag", "Pricetag"), + ("pricetag-white", "Pricetag White"), + ("printer", "Printer"), + ("privacy", "Privacy"), + ("private-browsing", "Private Browsing"), + ("protocol", "Protocol"), + ("proton", "Proton"), + ("query", "Query"), + ("quit", "Quit"), + ("quote", "Quote"), + ("read", "Read"), + ("reader-mode", "Reader Mode"), + ("redo", "Redo"), + ("refresh", "Refresh"), + ("release-notes", "Release Notes"), + ("reminders", "Reminders"), + ("report-narrow", "Report Narrow"), + ("report", "Report"), + ("resources", "Resources"), + ("restore-session", "Restore Session"), + ("rhombus-layers", "Rhombus Layers"), + ("rhombus-layers-white", "Rhombus Layers White"), + ("screen-share-disabled", "Screen Share Disabled"), + ("screen-share", "Screen Share"), + ("screenshot", "Screenshot"), + ("search-white", "Search White"), + ("search", "Search"), + ("secure-broken", "Secure Broken"), + ("secure-mixed", "Secure Mixed"), + ("secure", "Secure"), + ("security", "Security"), + ("send-to-device", "Send To Device"), + ("send", "Send"), + ("settings", "Settings"), + ("share-windows", "Share Windows"), + ("share", "Share"), + ("shield", "Shield"), + ("sidebar", "Sidebar"), + ("sign-in", "Sign In"), + ("sign-up", "Sign Up"), + ("sound-off", "Sound Off"), + ("sound-on", "Sound On"), + ("sparkles", "Sparkles"), + ("star", "Star"), + ("stop", "Stop"), + ("store-data-disabled", "Store Data Disabled"), + ("store-data", "Store Data"), + ("sub-item", "Sub Item"), + ("subscribe", "Subscribe"), + ("sync", "Sync"), + ("tab-mobile", "Tab Mobile"), + ("tab-new", "Tab New"), + ("tab", "Tab"), + ("tablet", "Tablet"), + ("thumbs-up-narrow", "Thumbs Up Narrow"), + ("thumbs-up", "Thumbs Up"), + ("toggle-off", "Toggle Off"), + ("toggle-on", "Toggle On"), + ("toolbar", "Toolbar"), + ("top-sites", "Top Sites"), + ("tracing-protection-disabled", "Tracing Protection Disabled"), + ("tracking-protection", "Tracking Protection"), + ("trash-narrow", "Trash Narrow"), + ("trash", "Trash"), + ("turbo-mode", "Turbo Mode"), + ("undo", "Undo"), + ("update", "Update"), + ("user", "User"), + ("users", "Users"), + ("video-card", "Video Card"), + ("video-recoder-disabled", "Video Recoder Disabled"), + ("video-recorder", "Video Recorder"), + ("warning", "Warning"), + ("watch", "Watch"), + ("web-of-things", "Web Of Things"), + ("web-vr", "Web Vr"), + ("window-new", "Window New"), + ("window", "Window"), + ("zoom-in", "Zoom In"), + ("zoom-out", "Zoom Out"), + ], + "inline_form": True, + }, + ), + 66: ("wagtail.blocks.CharBlock", (), {}), + 67: ("wagtail.blocks.StructBlock", [[("first_section", 55), ("second_section", 55)]], {}), + 68: ("wagtail.blocks.StreamBlock", [[("two_column_block", 67)]], {"required": True}), + 69: ("wagtail.blocks.StructBlock", [[("icon", 65), ("toggle_text", 66), ("toggle_content", 68)]], {}), + 70: ("wagtail.blocks.StreamBlock", [[("toggle_items", 69)]], {"required": True}), + 71: ("wagtail.blocks.StructBlock", [[("settings", 64), ("toggle_items", 70)]], {}), + 72: ( + "wagtail.blocks.RichTextBlock", + (), + { + "features": ["bold", "italic", "link", "superscript", "subscript", "strikethrough"], + "help_text": "Use bold to make parts of this text black.", + }, + ), + 73: ("wagtail.blocks.StructBlock", [[("settings", 64), ("heading", 72), ("button", 30)]], {}), + }, + null=True, + ), + ), + migrations.AlterField( + model_name="anonymindexpage", + name="content", + field=wagtail.fields.StreamField( + [("section", 55), ("call_to_action", 59)], + blank=True, + block_lookup={ + 0: ( + "wagtail.blocks.CharBlock", + (), + { + "help_text": "Optional: Add an ID to make this section linkable from navigation (e.g., 'overview', 'features')", + "max_length": 100, + "required": False, + }, + ), + 1: ( + "wagtail.blocks.ChoiceBlock", + [], + {"choices": [("", "---"), ("index", "Index"), ("top_glow", "Top Glow")], "inline_form": True, "required": False}, + ), + 2: ("wagtail.blocks.StructBlock", [[("anchor_id", 0), ("theme", 1)]], {}), + 3: ( + "wagtail.blocks.RichTextBlock", + (), + {"features": ["bold", "italic", "link", "superscript", "subscript", "strikethrough"], "required": False}, + ), + 4: ( + "wagtail.blocks.RichTextBlock", + (), + { + "features": ["bold", "italic", "link", "superscript", "subscript", "strikethrough"], + "help_text": "Use Bold to make parts of this text black.", + }, + ), + 5: ( + "wagtail.blocks.BooleanBlock", + (), + { + "default": False, + "help_text": "The Default width is constrained to the layout grid with a max-width, centered on the page.", + "inline_form": True, + "label": "Make Full Width", + "required": False, + }, + ), + 6: ("wagtail.blocks.StructBlock", [[("make_full_width", 5)]], {}), + 7: ("wagtail.images.blocks.ImageChooserBlock", (), {}), + 8: ("wagtail.blocks.StructBlock", [[("settings", 6), ("image", 7)]], {}), + 9: ("wagtail.blocks.BooleanBlock", (), {"default": False, "help_text": "The default behavior is stacked", "required": False}), + 10: ( + "wagtail.blocks.BooleanBlock", + (), + {"default": False, "help_text": "Add divider lines between cards on desktop", "required": False}, + ), + 11: ("wagtail.blocks.StructBlock", [[("scrollable_on_mobile", 9), ("dividers_between_cards_on_desktop", 10)]], {}), + 12: ( + "wagtail.blocks.ChoiceBlock", + [], + { + "choices": [ + ("", "---"), + ("IRL", "IRL"), + ("accessibility", "Accessibility"), + ("accounts", "Accounts"), + ("add-search-engine", "Add Search Engine"), + ("ai", "AI"), + ("alert", "Alert"), + ("arrow-down", "Arrow Down"), + ("arrow-left-white", "Arrow Left White"), + ("arrow-left", "Arrow Left"), + ("arrow-right-white", "Arrow Right White"), + ("arrow-right", "Arrow Right"), + ("arrow-up", "Arrow Up"), + ("audio-card", "Audio Card"), + ("audio-mute", "Audio Mute"), + ("audio", "Audio"), + ("auto-play-block", "Auto Play Block"), + ("back", "Back"), + ("bell", "Bell"), + ("beta", "Beta"), + ("blog", "Blog"), + ("bookmark-menu", "Bookmark Menu"), + ("bookmark-narrow", "Bookmark Narrow"), + ("bookmark-remove", "Bookmark Remove"), + ("bookmark", "Bookmark"), + ("brightness", "Brightness"), + ("browser", "Browser"), + ("calendar", "Calendar"), + ("careers", "Careers"), + ("caret-down-white", "Caret Down White"), + ("caret-down", "Caret Down"), + ("caret-up", "Caret Up"), + ("chat", "Chat"), + ("check", "Check"), + ("close-white", "Close White"), + ("close", "Close"), + ("cloud", "Cloud"), + ("command-console", "Command Console"), + ("command-noautohide", "Command Noautohide"), + ("common-voice", "Common Voice"), + ("copy", "Copy"), + ("current-view", "Current View"), + ("customize", "Customize"), + ("cut", "Cut"), + ("dashboard", "Dashboard"), + ("data-collection", "Data Collection"), + ("data-insights", "Data Insights"), + ("data-pie", "Data Pie"), + ("default-browser", "Default Browser"), + ("delete", "Delete"), + ("desktop", "Desktop"), + ("dev-edition", "Dev Edition"), + ("developer-innovations", "Developer Innovations"), + ("developer", "Developer"), + ("dictionaries", "Dictionaries"), + ("dock-bottom", "Dock Bottom"), + ("dock-left", "Dock Left"), + ("dock-right", "Dock Right"), + ("dock-undock", "Dock Undock"), + ("download-white", "Download White"), + ("download", "Download"), + ("earth", "Earth"), + ("edit-write", "Edit Write"), + ("email", "Email"), + ("enterprise", "Enterprise"), + ("event", "Event"), + ("expand-white", "Expand White"), + ("expand", "Expand"), + ("experiments", "Experiments"), + ("extension-available-update", "Extension Available Update"), + ("extension-recent-updates", "Extension Recent Updates"), + ("extensions-legacy", "Extensions Legacy"), + ("extensions", "Extensions"), + ("external-link-white", "External Link White"), + ("external-link", "External Link"), + ("eye-closed", "Eye Closed"), + ("eye-open", "Eye Open"), + ("facebook-container", "Facebook Container"), + ("features", "Features"), + ("feeback", "Feeback"), + ("file-code", "File Code"), + ("file-image", "File Image"), + ("file-lock", "File Lock"), + ("file-music", "File Music"), + ("file-text", "File Text"), + ("file", "File"), + ("fire-tv", "Fire TV"), + ("firefox-reality", "Firefox Reality"), + ("folder-open", "Folder Open"), + ("folder-plus", "Folder Plus"), + ("folder-save", "Folder Save"), + ("folder", "Folder"), + ("font", "Font"), + ("forget", "Forget"), + ("forward", "Forward"), + ("full-screen-disabled", "Full Screen Disabled"), + ("full-screen-exit", "Full Screen Exit"), + ("full-screen", "Full Screen"), + ("gear", "Gear"), + ("get-involved", "Get Involved"), + ("globe-white", "Globe White"), + ("globe", "Globe"), + ("hashtag-narrow", "Hashtag Narrow"), + ("hashtag", "Hashtag"), + ("headphone", "Headphone"), + ("heart", "Heart"), + ("heart-rate", "Heart Rate"), + ("heart-white", "Heart White"), + ("help", "Help"), + ("highlight", "Highlight"), + ("history", "History"), + ("home", "Home"), + ("hubs", "Hubs"), + ("identity-notification", "Identity Notification"), + ("identity", "Identity"), + ("image", "Image"), + ("import", "Import"), + ("inbox", "Inbox"), + ("info", "Info"), + ("labs", "Labs"), + ("language", "Language"), + ("library", "Library"), + ("layer", "Layer"), + ("link", "Link"), + ("listen", "Listen"), + ("lite", "Lite"), + ("location-disabled", "Location Disabled"), + ("location-macos-disabled", "Location Macos Disabled"), + ("location-macos", "Location Macos"), + ("location-pin", "Location Pin"), + ("location-windows-disabled", "Location Windows Disabled"), + ("location-windows", "Location Windows"), + ("location", "Location"), + ("lock", "Lock"), + ("lockbox", "Lockbox"), + ("login", "Login"), + ("mail", "Mail"), + ("maximize", "Maximize"), + ("megaphone", "Megaphone"), + ("menu-white", "Menu White"), + ("menu", "Menu"), + ("microphone-disabled", "Microphone Disabled"), + ("microphone", "Microphone"), + ("midi", "Midi"), + ("minimize", "Minimize"), + ("minus", "Minus"), + ("mobile-narrow", "Mobile Narrow"), + ("mobile", "Mobile"), + ("monitor", "Monitor"), + ("more-horizontal", "More Horizontal"), + ("more-vertical", "More Vertical"), + ("mountain", "Mountain"), + ("mouse-pointer-disabled", "Mouse Pointer Disabled"), + ("mouse-pointer", "Mouse Pointer"), + ("mozilla", "Mozilla"), + ("new", "New"), + ("nightly", "Nightly"), + ("notes", "Notes"), + ("notifications-disabled", "Notifications Disabled"), + ("notifications", "Notifications"), + ("open-in-new", "Open In New"), + ("open", "Open"), + ("opensource", "Opensource"), + ("overflow", "Overflow"), + ("paperclip-narrow", "Paperclip Narrow"), + ("paperclip", "Paperclip"), + ("paste", "Paste"), + ("pause-white", "Pause White"), + ("pause", "Pause"), + ("performance", "Performance"), + ("photon", "Photon"), + ("pin-remove", "Pin Remove"), + ("pin", "Pin"), + ("play-white", "Play White"), + ("play", "Play"), + ("plugin-disabled", "Plugin Disabled"), + ("plugin", "Plugin"), + ("plus", "Plus"), + ("pocket-list", "Pocket List"), + ("pocket-remove", "Pocket Remove"), + ("pocket", "Pocket"), + ("popular", "Popular"), + ("popup-block", "Popup Block"), + ("preferences", "Preferences"), + ("pricetag", "Pricetag"), + ("pricetag-white", "Pricetag White"), + ("printer", "Printer"), + ("privacy", "Privacy"), + ("private-browsing", "Private Browsing"), + ("protocol", "Protocol"), + ("proton", "Proton"), + ("query", "Query"), + ("quit", "Quit"), + ("quote", "Quote"), + ("read", "Read"), + ("reader-mode", "Reader Mode"), + ("redo", "Redo"), + ("refresh", "Refresh"), + ("release-notes", "Release Notes"), + ("reminders", "Reminders"), + ("report-narrow", "Report Narrow"), + ("report", "Report"), + ("resources", "Resources"), + ("restore-session", "Restore Session"), + ("rhombus-layers", "Rhombus Layers"), + ("rhombus-layers-white", "Rhombus Layers White"), + ("screen-share-disabled", "Screen Share Disabled"), + ("screen-share", "Screen Share"), + ("screenshot", "Screenshot"), + ("search-white", "Search White"), + ("search", "Search"), + ("secure-broken", "Secure Broken"), + ("secure-mixed", "Secure Mixed"), + ("secure", "Secure"), + ("security", "Security"), + ("send-to-device", "Send To Device"), + ("send", "Send"), + ("settings", "Settings"), + ("share-windows", "Share Windows"), + ("share", "Share"), + ("shield", "Shield"), + ("sidebar", "Sidebar"), + ("sign-in", "Sign In"), + ("sign-up", "Sign Up"), + ("sound-off", "Sound Off"), + ("sound-on", "Sound On"), + ("sparkles", "Sparkles"), + ("star", "Star"), + ("stop", "Stop"), + ("store-data-disabled", "Store Data Disabled"), + ("store-data", "Store Data"), + ("sub-item", "Sub Item"), + ("subscribe", "Subscribe"), + ("sync", "Sync"), + ("tab-mobile", "Tab Mobile"), + ("tab-new", "Tab New"), + ("tab", "Tab"), + ("tablet", "Tablet"), + ("thumbs-up-narrow", "Thumbs Up Narrow"), + ("thumbs-up", "Thumbs Up"), + ("toggle-off", "Toggle Off"), + ("toggle-on", "Toggle On"), + ("toolbar", "Toolbar"), + ("top-sites", "Top Sites"), + ("tracing-protection-disabled", "Tracing Protection Disabled"), + ("tracking-protection", "Tracking Protection"), + ("trash-narrow", "Trash Narrow"), + ("trash", "Trash"), + ("turbo-mode", "Turbo Mode"), + ("undo", "Undo"), + ("update", "Update"), + ("user", "User"), + ("users", "Users"), + ("video-card", "Video Card"), + ("video-recoder-disabled", "Video Recoder Disabled"), + ("video-recorder", "Video Recorder"), + ("warning", "Warning"), + ("watch", "Watch"), + ("web-of-things", "Web Of Things"), + ("web-vr", "Web Vr"), + ("window-new", "Window New"), + ("window", "Window"), + ("zoom-in", "Zoom In"), + ("zoom-out", "Zoom Out"), + ], + "inline_form": True, + "required": False, + }, + ), + 13: ("wagtail.blocks.CharBlock", (), {"label": "Heading", "required": False}), + 14: ("wagtail.blocks.RichTextBlock", (), {"features": ["bold", "italic", "link", "superscript", "subscript", "strikethrough"]}), + 15: ("wagtail.blocks.StructBlock", [[("icon", 12), ("heading", 13), ("text", 14)]], {}), + 16: ("wagtail.blocks.CharBlock", (), {"label": "Heading"}), + 17: ( + "bedrock.cms.blocks.UUIDBlock", + (), + { + "help_text": "Unique identifier for analytics tracking. Leave blank to auto-generate.", + "label": "Analytics ID", + "required": False, + }, + ), + 18: ("wagtail.blocks.StructBlock", [[("analytics_id", 17)]], {}), + 19: ("wagtail.blocks.CharBlock", (), {"label": "Link Text"}), + 20: ( + "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, + }, + ), + 21: ("wagtail.blocks.PageChooserBlock", (), {"form_classname": "page_link", "label": "Page", "required": False}), + 22: ("wagtail.documents.blocks.DocumentChooserBlock", (), {"form_classname": "file_link", "label": "File", "required": False}), + 23: ( + "wagtail.blocks.CharBlock", + (), + { + "form_classname": "custom_url_link url_field", + "label": "Custom URL", + "max_length": 300, + "required": False, + "validators": [wagtail.admin.forms.choosers.URLOrAbsolutePathValidator()], + }, + ), + 24: ("wagtail.blocks.CharBlock", (), {"form_classname": "anchor_link", "label": "#", "max_length": 300, "required": False}), + 25: ("wagtail.blocks.EmailBlock", (), {"required": False}), + 26: ("wagtail.blocks.CharBlock", (), {"form_classname": "phone_link", "label": "Phone", "max_length": 30, "required": False}), + 27: ( + "wagtail.blocks.BooleanBlock", + (), + {"form_classname": "new_window_toggle", "label": "Open in new window", "required": False}, + ), + 28: ( + "wagtail.blocks.StructBlock", + [ + [ + ("link_to", 20), + ("page", 21), + ("file", 22), + ("custom_url", 23), + ("anchor", 24), + ("email", 25), + ("phone", 26), + ("new_window", 27), + ] + ], + {}, + ), + 29: ("wagtail.blocks.StructBlock", [[("settings", 18), ("label", 19), ("link", 28)]], {}), + 30: ("wagtail.blocks.ListBlock", (29,), {"default": [], "max_num": 1, "min_num": 0}), + 31: ("wagtail.blocks.StructBlock", [[("logo", 7), ("heading", 16), ("text", 14), ("button", 30)]], {}), + 32: ("wagtail.snippets.blocks.SnippetChooserBlock", ("anonym.Person",), {}), + 33: ("wagtail.blocks.StructBlock", [[("person", 32), ("link", 30)]], {}), + 34: ("wagtail.blocks.StreamBlock", [[("icon_card", 15), ("logo_card", 31), ("person_card", 33)]], {"max_num": 4, "min_num": 1}), + 35: ("wagtail.blocks.StructBlock", [[("settings", 11), ("cards", 34)]], {}), + 36: ("wagtail.blocks.PageChooserBlock", (), {"page_type": ["anonym.AnonymNewsItemPage", "anonym.AnonymCaseStudyItemPage"]}), + 37: ( + "wagtail.blocks.ListBlock", + (36,), + {"default": [], "help_text": "Select news items or case studies to display.", "max_num": 6, "min_num": 1}, + ), + 38: ("wagtail.blocks.StructBlock", [[("pages", 37)]], {}), + 39: ("wagtail.blocks.PageChooserBlock", ("anonym.AnonymCaseStudyItemPage",), {}), + 40: ("wagtail.blocks.StructBlock", [[("page", 39), ("analytics_id", 17)]], {}), + 41: ( + "wagtail.blocks.ListBlock", + (40,), + { + "default": [], + "help_text": "Select case study pages to display. Each will show as a card with logo, client name, and description.", + "max_num": 3, + "min_num": 1, + }, + ), + 42: ("wagtail.blocks.StructBlock", [[("case_study_items", 41)]], {}), + 43: ("wagtail.blocks.CharBlock", (), {"help_text": "Section label, e.g. 'Publishers' or 'Advertisers'", "max_length": 50}), + 44: ("wagtail.blocks.StreamBlock", [[("logo", 7), ("label", 43)]], {"max_num": 24, "min_num": 1}), + 45: ("wagtail.blocks.StructBlock", [[("items", 44)]], {}), + 46: ("wagtail.blocks.ListBlock", (32,), {"default": [], "max_num": 8, "min_num": 1}), + 47: ("wagtail.blocks.StructBlock", [[("people", 46)]], {}), + 48: ("wagtail.blocks.StructBlock", [[("heading_text", 16), ("supporting_text", 14)]], {}), + 49: ("wagtail.blocks.ListBlock", (48,), {"min_num": 0}), + 50: ("wagtail.blocks.StructBlock", [[("heading_text", 16), ("subheading_text", 3), ("second_column", 49)]], {}), + 51: ( + "wagtail.blocks.RichTextBlock", + (), + { + "features": [ + "h2", + "h3", + "h4", + "h5", + "bold", + "italic", + "underline", + "ol", + "ul", + "hr", + "link", + "image", + "embed", + "superscript", + "subscript", + "strikethrough", + "blockquote", + ] + }, + ), + 52: ("wagtail.blocks.StructBlock", [[("text", 51)]], {}), + 53: ( + "wagtail.blocks.RichTextBlock", + (), + { + "features": [ + "h2", + "h3", + "h4", + "h5", + "bold", + "italic", + "underline", + "ol", + "ul", + "hr", + "link", + "image", + "embed", + "superscript", + "subscript", + "strikethrough", + "blockquote", + ], + "template": "anonym/blocks/rich-text.html", + }, + ), + 54: ( + "wagtail.blocks.StreamBlock", + [ + [ + ("figure_block", 8), + ("cards_list", 35), + ("stat_card_list_block", 38), + ("case_study_item_list_block", 42), + ("logo_list_block", 45), + ("people_list", 47), + ("two_column", 50), + ("legal_rich_text", 52), + ("rich_text", 53), + ] + ], + {"required": False}, + ), + 55: ( + "wagtail.blocks.StructBlock", + [ + [ + ("settings", 2), + ("superheading_text", 3), + ("heading_text", 4), + ("subheading_text", 3), + ("section_content", 54), + ("action", 30), + ] + ], + {}, + ), + 56: ( + "wagtail.blocks.CharBlock", + (), + {"help_text": "Optional: Add an ID to make this section linkable from navigation", "max_length": 100, "required": False}, + ), + 57: ("wagtail.blocks.StructBlock", [[("anchor_id", 56)]], {}), + 58: ( + "wagtail.blocks.RichTextBlock", + (), + { + "features": ["bold", "italic", "link", "superscript", "subscript", "strikethrough"], + "help_text": "Use bold to make parts of this text black.", + }, + ), + 59: ("wagtail.blocks.StructBlock", [[("settings", 57), ("heading", 58), ("button", 30)]], {}), + }, + null=True, + ), + ), + migrations.AlterField( + model_name="anonymnewsitempage", + name="content", + field=wagtail.fields.StreamField( + [("intro_text", 1), ("rich_text", 3), ("blockquote", 5), ("figure", 10), ("call_to_action", 28)], + blank=True, + block_lookup={ + 0: ("wagtail.blocks.RichTextBlock", (), {"features": ["bold", "italic", "link", "superscript", "subscript", "strikethrough"]}), + 1: ("wagtail.blocks.StructBlock", [[("text", 0)]], {}), + 2: ( + "wagtail.blocks.RichTextBlock", + (), + { + "features": [ + "h2", + "h3", + "h4", + "h5", + "bold", + "italic", + "underline", + "ol", + "ul", + "hr", + "link", + "image", + "embed", + "superscript", + "subscript", + "strikethrough", + "blockquote", + ] + }, + ), + 3: ("wagtail.blocks.StructBlock", [[("text", 2)]], {}), + 4: ("wagtail.blocks.CharBlock", (), {"label": "Author", "required": False}), + 5: ("wagtail.blocks.StructBlock", [[("text", 0), ("author", 4)]], {}), + 6: ( + "wagtail.blocks.BooleanBlock", + (), + { + "default": False, + "help_text": "The Default width is constrained to the layout grid with a max-width, centered on the page.", + "inline_form": True, + "label": "Make Full Width", + "required": False, + }, + ), + 7: ("wagtail.blocks.StructBlock", [[("make_full_width", 6)]], {}), + 8: ("wagtail.images.blocks.ImageChooserBlock", (), {}), + 9: ( + "wagtail.blocks.RichTextBlock", + (), + {"features": ["bold", "italic", "link", "superscript", "subscript", "strikethrough"], "label": "Caption", "required": False}, + ), + 10: ("wagtail.blocks.StructBlock", [[("settings", 7), ("image", 8), ("caption", 9)]], {}), + 11: ( + "wagtail.blocks.CharBlock", + (), + {"help_text": "Optional: Add an ID to make this section linkable from navigation", "max_length": 100, "required": False}, + ), + 12: ("wagtail.blocks.StructBlock", [[("anchor_id", 11)]], {}), + 13: ( + "wagtail.blocks.RichTextBlock", + (), + { + "features": ["bold", "italic", "link", "superscript", "subscript", "strikethrough"], + "help_text": "Use bold to make parts of this text black.", + }, + ), + 14: ( + "bedrock.cms.blocks.UUIDBlock", + (), + { + "help_text": "Unique identifier for analytics tracking. Leave blank to auto-generate.", + "label": "Analytics ID", + "required": False, + }, + ), + 15: ("wagtail.blocks.StructBlock", [[("analytics_id", 14)]], {}), + 16: ("wagtail.blocks.CharBlock", (), {"label": "Link Text"}), + 17: ( + "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, + }, + ), + 18: ("wagtail.blocks.PageChooserBlock", (), {"form_classname": "page_link", "label": "Page", "required": False}), + 19: ("wagtail.documents.blocks.DocumentChooserBlock", (), {"form_classname": "file_link", "label": "File", "required": False}), + 20: ( + "wagtail.blocks.CharBlock", + (), + { + "form_classname": "custom_url_link url_field", + "label": "Custom URL", + "max_length": 300, + "required": False, + "validators": [wagtail.admin.forms.choosers.URLOrAbsolutePathValidator()], + }, + ), + 21: ("wagtail.blocks.CharBlock", (), {"form_classname": "anchor_link", "label": "#", "max_length": 300, "required": False}), + 22: ("wagtail.blocks.EmailBlock", (), {"required": False}), + 23: ("wagtail.blocks.CharBlock", (), {"form_classname": "phone_link", "label": "Phone", "max_length": 30, "required": False}), + 24: ( + "wagtail.blocks.BooleanBlock", + (), + {"form_classname": "new_window_toggle", "label": "Open in new window", "required": False}, + ), + 25: ( + "wagtail.blocks.StructBlock", + [ + [ + ("link_to", 17), + ("page", 18), + ("file", 19), + ("custom_url", 20), + ("anchor", 21), + ("email", 22), + ("phone", 23), + ("new_window", 24), + ] + ], + {}, + ), + 26: ("wagtail.blocks.StructBlock", [[("settings", 15), ("label", 16), ("link", 25)]], {}), + 27: ("wagtail.blocks.ListBlock", (26,), {"default": [], "max_num": 1, "min_num": 0}), + 28: ("wagtail.blocks.StructBlock", [[("settings", 12), ("heading", 13), ("button", 27)]], {}), + }, + null=True, + ), + ), + ] diff --git a/bedrock/anonym/migrations/0014_alter_anonymindexpage_navigation.py b/bedrock/anonym/migrations/0014_alter_anonymindexpage_navigation.py new file mode 100644 index 00000000000..1ac37246728 --- /dev/null +++ b/bedrock/anonym/migrations/0014_alter_anonymindexpage_navigation.py @@ -0,0 +1,86 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +# Generated by Django 5.2.12 on 2026-04-16 19:34 + +from django.db import migrations + +import wagtail.admin.forms.choosers +import wagtail.fields + + +class Migration(migrations.Migration): + dependencies = [ + ("anonym", "0013_alter_anonymcasestudyitempage_content_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="anonymindexpage", + name="navigation", + field=wagtail.fields.StreamField( + [("link", 12)], + blank=True, + block_lookup={ + 0: ("wagtail.blocks.CharBlock", (), {"help_text": "Text to display for this navigation link", "max_length": 50}), + 1: ( + "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, + }, + ), + 2: ("wagtail.blocks.PageChooserBlock", (), {"form_classname": "page_link", "label": "Page", "required": False}), + 3: ("wagtail.documents.blocks.DocumentChooserBlock", (), {"form_classname": "file_link", "label": "File", "required": False}), + 4: ( + "wagtail.blocks.CharBlock", + (), + { + "form_classname": "custom_url_link url_field", + "label": "Custom URL", + "max_length": 300, + "required": False, + "validators": [wagtail.admin.forms.choosers.URLOrAbsolutePathValidator()], + }, + ), + 5: ("wagtail.blocks.CharBlock", (), {"form_classname": "anchor_link", "label": "#", "max_length": 300, "required": False}), + 6: ("wagtail.blocks.EmailBlock", (), {"required": False}), + 7: ("wagtail.blocks.CharBlock", (), {"form_classname": "phone_link", "label": "Phone", "max_length": 30, "required": False}), + 8: ("wagtail.blocks.BooleanBlock", (), {"form_classname": "new_window_toggle", "label": "Open in new window", "required": False}), + 9: ( + "wagtail.blocks.StructBlock", + [[("link_to", 1), ("page", 2), ("file", 3), ("custom_url", 4), ("anchor", 5), ("email", 6), ("phone", 7), ("new_window", 8)]], + {}, + ), + 10: ( + "wagtail.blocks.BooleanBlock", + (), + {"default": False, "help_text": "This link should look like a button", "required": False}, + ), + 11: ( + "wagtail.blocks.CharBlock", + (), + { + "help_text": "Unique identifier for analytics tracking. Leave blank for standard nav links.", + "label": "Analytics ID", + "required": False, + }, + ), + 12: ("wagtail.blocks.StructBlock", [[("link_text", 0), ("link", 9), ("has_button_appearance", 10), ("analytics_id", 11)]], {}), + }, + help_text="Configure the navigation menu items.", + null=True, + ), + ), + ] diff --git a/bedrock/anonym/migrations/0015_anonymnewsitempage_analytics_id.py b/bedrock/anonym/migrations/0015_anonymnewsitempage_analytics_id.py new file mode 100644 index 00000000000..6c8b611cc7f --- /dev/null +++ b/bedrock/anonym/migrations/0015_anonymnewsitempage_analytics_id.py @@ -0,0 +1,21 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +# Generated by Django 5.2.12 on 2026-04-16 19:37 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("anonym", "0014_alter_anonymindexpage_navigation"), + ] + + operations = [ + migrations.AddField( + model_name="anonymnewsitempage", + name="analytics_id", + field=models.CharField(blank=True, help_text="Unique identifier for analytics tracking. Leave blank to auto-generate.", max_length=36), + ), + ] diff --git a/bedrock/anonym/migrations/0016_populate_analytics_ids.py b/bedrock/anonym/migrations/0016_populate_analytics_ids.py new file mode 100644 index 00000000000..50bd69d54a7 --- /dev/null +++ b/bedrock/anonym/migrations/0016_populate_analytics_ids.py @@ -0,0 +1,57 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +import sys +from uuid import uuid4 + +from django.core.management import call_command +from django.db import migrations + +from bedrock.base.config_manager import config + + +def _should_skip(): + return "pytest" in sys.modules or config("SQLITE_EXPORT_MODE", parser=bool, default="false") + + +def populate_news_item_analytics_ids(apps, schema_editor): + if _should_skip(): + return + AnonymNewsItemPage = apps.get_model("anonym", "AnonymNewsItemPage") + for page in AnonymNewsItemPage.objects.filter(analytics_id=""): + page.analytics_id = str(uuid4()) + page.save(update_fields=["analytics_id"]) + + +def inject_analytics_ids_into_link_blocks(apps, schema_editor): + if _should_skip(): + return + call_command("populate_link_block_analytics_ids") + + +def restructure_case_study_list_blocks(apps, schema_editor): + if _should_skip(): + return + call_command("populate_case_study_analytics_ids") + + +def populate_nav_button_analytics_ids(apps, schema_editor): + if _should_skip(): + return + call_command("populate_nav_button_analytics_ids") + + +class Migration(migrations.Migration): + atomic = False + + dependencies = [ + ("anonym", "0015_anonymnewsitempage_analytics_id"), + ] + + operations = [ + migrations.RunPython(populate_news_item_analytics_ids, migrations.RunPython.noop), + migrations.RunPython(inject_analytics_ids_into_link_blocks, migrations.RunPython.noop), + migrations.RunPython(restructure_case_study_list_blocks, migrations.RunPython.noop), + migrations.RunPython(populate_nav_button_analytics_ids, migrations.RunPython.noop), + ] diff --git a/bedrock/anonym/models.py b/bedrock/anonym/models.py index a2f26ab615a..25710a5ffb7 100644 --- a/bedrock/anonym/models.py +++ b/bedrock/anonym/models.py @@ -2,6 +2,8 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. +from uuid import uuid4 + from django.conf import settings from django.core.mail import EmailMessage from django.db import models @@ -141,6 +143,12 @@ class AnonymNewsItemPage(AbstractStatCardPage): use_json_field=True, ) + analytics_id = models.CharField( + max_length=36, + blank=True, + help_text="Unique identifier for analytics tracking. Leave blank to auto-generate.", + ) + content_panels = ( AbstractBedrockCMSPage.content_panels + [ @@ -156,10 +164,19 @@ class AnonymNewsItemPage(AbstractStatCardPage): ] ) + promote_panels = AbstractBedrockCMSPage.promote_panels + [ + FieldPanel("analytics_id"), + ] + # Since the concept of a AnonymNewsItemPage is similar to an # AnonymCaseStudyItemPage, use the AnonymCaseStudyItemPage template. template = "anonym/anonym_case_study_item_page.html" + def save(self, *args, **kwargs): + if not self.analytics_id: + self.analytics_id = str(uuid4()) + super().save(*args, **kwargs) + @property def category_label(self): return "Press" diff --git a/bedrock/anonym/templates/anonym/anonym_news.html b/bedrock/anonym/templates/anonym/anonym_news.html index 5e8fa01be70..a3d76869b54 100644 --- a/bedrock/anonym/templates/anonym/anonym_news.html +++ b/bedrock/anonym/templates/anonym/anonym_news.html @@ -41,7 +41,10 @@