diff --git a/Jenkinsfile b/Jenkinsfile index dc8a02f..4dbe351 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -152,6 +152,19 @@ PY parameters: [string(name: 'PROJECT_NAME', value: 'mandelstudio')] } } + stage('Normalize Services Menu') { + agent { label 'built-in' } + options { + timeout(time: 5, unit: 'MINUTES') + } + steps { + sh ''' + set -e + REMOTE_CMD="cd '${STAGING_AUDIT_PROJECT_DIR}' && '${STAGING_AUDIT_MANAGE}' normalize_services_menu" + sudo -n -u mandel -g www-data /srv/apps/mandel-dashboard/.venv/bin/python /srv/apps/mandel-dashboard/bin/deploy_stg_from_jenkins.py "${STAGING_AUDIT_PROJECT_NAME}" --command "$REMOTE_CMD" + ''' + } + } stage('Post-Deploy Multilingual Audit') { agent { label 'built-in' } options { diff --git a/mandelstudio/management/commands/normalize_services_menu.py b/mandelstudio/management/commands/normalize_services_menu.py new file mode 100644 index 0000000..bb6092d --- /dev/null +++ b/mandelstudio/management/commands/normalize_services_menu.py @@ -0,0 +1,115 @@ +from __future__ import annotations + +from dataclasses import dataclass + +from django.core.management.base import BaseCommand + +from wagtail.models import Locale, Page + + +@dataclass(frozen=True) +class _MenuChange: + page_id: int + locale: str + title: str + before: bool + after: bool + + +def _services_root(default_locale: Locale) -> Page | None: + page = ( + Page.objects.filter(locale=default_locale, slug="diensten") + .specific() + .first() + ) + if page is not None: + return page + + return ( + Page.objects.filter(locale=default_locale, title__iexact="Diensten") + .specific() + .first() + ) + + +class Command(BaseCommand): + help = ( + "Normalize the Services/Diensten dropdown across locales by using the " + "default-locale in-menu children as the allowlist and applying that to " + "all translated Services pages." + ) + + def add_arguments(self, parser): + parser.add_argument( + "--dry-run", + action="store_true", + help="Print the changes without writing to the database.", + ) + + def handle(self, *args, **options): + dry_run: bool = options["dry_run"] + + default_locale = Locale.get_default() + services = _services_root(default_locale) + if services is None: + self.stderr.write( + "Could not find the default-locale Services/Diensten page " + f"(locale={default_locale.language_code})." + ) + return 1 + + allowed_keys = set( + Page.objects.child_of(services) + .live() + .in_menu() + .values_list("translation_key", flat=True) + ) + + if not allowed_keys: + self.stderr.write( + "Default-locale Services page has no in-menu children; " + "refusing to hide menu items across locales." + ) + return 2 + + changes: list[_MenuChange] = [] + + translated_services_pages = Page.objects.filter( + translation_key=services.translation_key + ).specific() + + for translated_services in translated_services_pages: + children = Page.objects.child_of(translated_services).specific() + for child in children: + before = bool(child.show_in_menus) + after = bool(child.translation_key in allowed_keys and child.live) + if before == after: + continue + changes.append( + _MenuChange( + page_id=child.id, + locale=child.locale.language_code, + title=child.title, + before=before, + after=after, + ) + ) + if not dry_run: + child.show_in_menus = after + child.save(update_fields=["show_in_menus"]) + + if not changes: + self.stdout.write("No changes needed.") + return 0 + + for change in changes: + self.stdout.write( + f"[{change.locale}] #{change.page_id} {change.title}: " + f"show_in_menus {change.before} -> {change.after}" + ) + + self.stdout.write( + f"Done. Changed {len(changes)} page(s). dry_run={dry_run}" + ) + return 0 +